mirror of
https://github.com/dbeaver/cloudbeaver.git
synced 2026-10-16 14:50:15 +08:00
Compare commits
408
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3cde53fc44 | ||
|
|
eb61e4ddaf | ||
|
|
de2256264a | ||
|
|
2f04cc5bb0 | ||
|
|
290389e2b3 | ||
|
|
10437a5943 | ||
|
|
aa3a1e72f6 | ||
|
|
d1ab50783e | ||
|
|
a12ab4f2a2 | ||
|
|
2a9e2b9a93 | ||
|
|
21d4ab88fb | ||
|
|
b71010958c | ||
|
|
294b3096cb | ||
|
|
3d0984095b | ||
|
|
e2408d2a47 | ||
|
|
ac2cb461f3 | ||
|
|
0a1616795d | ||
|
|
571882eab0 | ||
|
|
5b8c88c36a | ||
|
|
1906f9d165 | ||
|
|
5e36ef4e27 | ||
|
|
7cd9ae83bc | ||
|
|
bcf4a2b581 | ||
|
|
50f63caa4f | ||
|
|
a68a29aeb0 | ||
|
|
f5c31fbc07 | ||
|
|
aae05e4769 | ||
|
|
39777dc5fa | ||
|
|
4475513341 | ||
|
|
f4b55add99 | ||
|
|
6d13275021 | ||
|
|
2650b4dccd | ||
|
|
50f6835caf | ||
|
|
3e9e71ca6d | ||
|
|
d48cd7a0a7 | ||
|
|
f37f173c49 | ||
|
|
63b25d7cb1 | ||
|
|
67df14033c | ||
|
|
75a64c1c2d | ||
|
|
28fb7f2264 | ||
|
|
5176519fdd | ||
|
|
4d197c9548 | ||
|
|
56daa059a7 | ||
|
|
3491fb42d7 | ||
|
|
bb9872dc2e | ||
|
|
043d55c395 | ||
|
|
b5c6cbfe82 | ||
|
|
152ab83006 | ||
|
|
bea2451776 | ||
|
|
2d0b7d428c | ||
|
|
9078a379b0 | ||
|
|
bc2c204a61 | ||
|
|
2ff806ed7c | ||
|
|
5d2b246d61 | ||
|
|
212623c0a8 | ||
|
|
d08c191a08 | ||
|
|
6b3e911534 | ||
|
|
620a5cb73b | ||
|
|
4cf41da82c | ||
|
|
3844792b84 | ||
|
|
e7f2b8e0a6 | ||
|
|
f2b30d0f1f | ||
|
|
2781ec1eaa | ||
|
|
707cb3ea41 | ||
|
|
5be49b8a7e | ||
|
|
317750ea7d | ||
|
|
5fb7234e24 | ||
|
|
7a93d56459 | ||
|
|
bd1e926929 | ||
|
|
170650e6c2 | ||
|
|
ffc48e1979 | ||
|
|
c4b0dd4650 | ||
|
|
2d955087ea | ||
|
|
b5289c7865 | ||
|
|
740599f41d | ||
|
|
16915940ba | ||
|
|
1c789cf49b | ||
|
|
9474ec9718 | ||
|
|
46a07033ad | ||
|
|
eb907e95f4 | ||
|
|
cb01f6a988 | ||
|
|
b2065ffe62 | ||
|
|
9e8f4b83a6 | ||
|
|
5170fa7dfc | ||
|
|
ad26e0e849 | ||
|
|
eed9cd979e | ||
|
|
1b8ea64cd0 | ||
|
|
fb43b0f567 | ||
|
|
317ba5486a | ||
|
|
4d4049fcd1 | ||
|
|
27cfdb405f | ||
|
|
f1dc4e07bb | ||
|
|
57cc6e4808 | ||
|
|
6bfe58506f | ||
|
|
1d6e5c6ce6 | ||
|
|
3a60e04a84 | ||
|
|
8b33e3a08f | ||
|
|
b021ceec4b | ||
|
|
d318579745 | ||
|
|
8d17ed0ecd | ||
|
|
3199a99b29 | ||
|
|
39008f7170 | ||
|
|
8bf33d539f | ||
|
|
42f6720acd | ||
|
|
0a3cb12c0b | ||
|
|
202823e731 | ||
|
|
ed088fe923 | ||
|
|
df21c3396a | ||
|
|
90ccdc6432 | ||
|
|
dba371a320 | ||
|
|
d96cd8f0d2 | ||
|
|
bd6e7d48f3 | ||
|
|
8b41e4530f | ||
|
|
a555f1e759 | ||
|
|
fdaa40e431 | ||
|
|
4c2f9d92e5 | ||
|
|
a92011eee6 | ||
|
|
61a7757a0e | ||
|
|
a53ca3592f | ||
|
|
9e682617e6 | ||
|
|
16dda810d7 | ||
|
|
6042f16342 | ||
|
|
0afdd06c80 | ||
|
|
3158aebca4 | ||
|
|
99866fdc12 | ||
|
|
a58eb59a32 | ||
|
|
eda1f8720f | ||
|
|
f54f377344 | ||
|
|
81a9be17b8 | ||
|
|
af42d7f50a | ||
|
|
d083ac1330 | ||
|
|
cdb450dfde | ||
|
|
d0d9ee19ab | ||
|
|
9327ce0ac2 | ||
|
|
1fb9dd7b5a | ||
|
|
e82cac479b | ||
|
|
bf6afb0340 | ||
|
|
4779c3b405 | ||
|
|
31ee01b6fa | ||
|
|
025944a867 | ||
|
|
caee9914b8 | ||
|
|
6e2b1b7a17 | ||
|
|
a379c2d7ba | ||
|
|
8b1054a148 | ||
|
|
7fdc8f7e61 | ||
|
|
32bebd4bad | ||
|
|
4221ea7fe0 | ||
|
|
af11aaa8fe | ||
|
|
5c58343a10 | ||
|
|
a565beb777 | ||
|
|
9df99fb62e | ||
|
|
b3127b9aa7 | ||
|
|
f8677a0689 | ||
|
|
f22a936507 | ||
|
|
b5858637d6 | ||
|
|
475c8129fc | ||
|
|
50d846d087 | ||
|
|
16bd3ca575 | ||
|
|
927184e009 | ||
|
|
d4d9081936 | ||
|
|
a0bcfba73d | ||
|
|
09f4828006 | ||
|
|
cde64e255b | ||
|
|
7b134cef81 | ||
|
|
fde7502df8 | ||
|
|
955b28c520 | ||
|
|
5cfc37e26f | ||
|
|
42cb6b3a9d | ||
|
|
ad245e9f4c | ||
|
|
08260e6a55 | ||
|
|
aea12e9fed | ||
|
|
caef729a8d | ||
|
|
12030822ea | ||
|
|
97cf62d333 | ||
|
|
8240c92247 | ||
|
|
62a194c1b0 | ||
|
|
36e954c440 | ||
|
|
a674c183d5 | ||
|
|
4bab1025d7 | ||
|
|
7d21186158 | ||
|
|
eb5f89dae3 | ||
|
|
70292cb9c0 | ||
|
|
950f45833a | ||
|
|
8eb012670d | ||
|
|
413d229028 | ||
|
|
b0fbd41b85 | ||
|
|
660c7d9e1f | ||
|
|
0f79ef21e7 | ||
|
|
d9b5a9a20d | ||
|
|
b200148765 | ||
|
|
18ecc829d3 | ||
|
|
58f51ee5f6 | ||
|
|
0f7c8fe69f | ||
|
|
776a91767d | ||
|
|
9f4f543eb3 | ||
|
|
e4e009e1ec | ||
|
|
895d27eb84 | ||
|
|
a0cbaa89f6 | ||
|
|
32d1ef9a70 | ||
|
|
8362946990 | ||
|
|
d3d71b79f0 | ||
|
|
c344210b8b | ||
|
|
f6aea971ef | ||
|
|
3d9c4391d1 | ||
|
|
19284d33e7 | ||
|
|
dc1e865545 | ||
|
|
4d62ddb5fc | ||
|
|
14df3b3120 | ||
|
|
30f5bd3bb3 | ||
|
|
04a4c19331 | ||
|
|
5bc6ea3731 | ||
|
|
d93d73bb44 | ||
|
|
6cdc6b2ee0 | ||
|
|
96ba18da83 | ||
|
|
a16c138bba | ||
|
|
f728590b90 | ||
|
|
ba7268bb8f | ||
|
|
1b7c81fa24 | ||
|
|
98f8380962 | ||
|
|
2d40781960 | ||
|
|
166efa634f | ||
|
|
fad07f3d73 | ||
|
|
4ca4976184 | ||
|
|
ed4f5e45ed | ||
|
|
a333e5841a | ||
|
|
2c3afd61f0 | ||
|
|
a817dcd92f | ||
|
|
c14e8f5ce3 | ||
|
|
cc3cf21890 | ||
|
|
961430f988 | ||
|
|
759b30a24b | ||
|
|
225ce91fd7 | ||
|
|
f7bdd80e9f | ||
|
|
e42d34ac58 | ||
|
|
89a1af2df9 | ||
|
|
e4260b4cd2 | ||
|
|
956ebaa515 | ||
|
|
bccf47020e | ||
|
|
dcd2b608ea | ||
|
|
84af14d2f7 | ||
|
|
f513f7dc44 | ||
|
|
ffa7f730de | ||
|
|
694d3b241f | ||
|
|
9c8f3c47e6 | ||
|
|
f918c9e25e | ||
|
|
b84031bb84 | ||
|
|
af651f9d10 | ||
|
|
3245943f3d | ||
|
|
fe01b23fce | ||
|
|
8ec052e1b1 | ||
|
|
b10b407270 | ||
|
|
5f5a6743eb | ||
|
|
4b30dc3ba5 | ||
|
|
86e114d471 | ||
|
|
d4d2e2d1fa | ||
|
|
6670cc81ae | ||
|
|
f8a107256b | ||
|
|
4f796e5f9c | ||
|
|
fb6f3c8246 | ||
|
|
f3eaed955f | ||
|
|
339f19ff24 | ||
|
|
0229ed1327 | ||
|
|
6a6c29f38e | ||
|
|
c3054c1f26 | ||
|
|
20b129d5a2 | ||
|
|
a08b9c2f2b | ||
|
|
3a250e2e7a | ||
|
|
951b51b403 | ||
|
|
72fcd7bfe2 | ||
|
|
9ea3de9edf | ||
|
|
729d47fda1 | ||
|
|
ebda152b6b | ||
|
|
430ae8e5cc | ||
|
|
97c12d026b | ||
|
|
ea0997d3b0 | ||
|
|
04a2eed596 | ||
|
|
10d8741403 | ||
|
|
ded139584c | ||
|
|
04b3bd9e2b | ||
|
|
9303f4dacf | ||
|
|
7b258d7ba6 | ||
|
|
76c2d4b7fe | ||
|
|
61d0c43bd2 | ||
|
|
0c07a5cd09 | ||
|
|
3dcae7063c | ||
|
|
635d2f8d7f | ||
|
|
cea06eecae | ||
|
|
0e402cd27d | ||
|
|
d79054a5d8 | ||
|
|
0903025942 | ||
|
|
b36b11008f | ||
|
|
f8acdb58af | ||
|
|
4e4292376e | ||
|
|
d5860dc9e8 | ||
|
|
5301adaed4 | ||
|
|
fc9ef33e1d | ||
|
|
2a8f34682f | ||
|
|
9c6f301fa9 | ||
|
|
a2ccd0b0f7 | ||
|
|
acc3e42f1c | ||
|
|
075fa916f0 | ||
|
|
f7b96db2de | ||
|
|
dff9adaf34 | ||
|
|
86c50a1557 | ||
|
|
7d6c05f50c | ||
|
|
022778f95c | ||
|
|
d752d8ac26 | ||
|
|
e1a029b238 | ||
|
|
644dc2a136 | ||
|
|
768fe00f6a | ||
|
|
1e1f684379 | ||
|
|
ac3ce9e085 | ||
|
|
3cc9c3ce86 | ||
|
|
1660e62391 | ||
|
|
c27fca442b | ||
|
|
318100300f | ||
|
|
9c9e8e4496 | ||
|
|
4789acb76a | ||
|
|
39ad4568d0 | ||
|
|
860b8e2e16 | ||
|
|
4b9ba00663 | ||
|
|
897dd994ba | ||
|
|
e81a150287 | ||
|
|
0d17208be4 | ||
|
|
9c1678c256 | ||
|
|
321d45bd48 | ||
|
|
0f33fb5514 | ||
|
|
e7648b1b4c | ||
|
|
45cfded51f | ||
|
|
9822147d68 | ||
|
|
3b0f52f3ed | ||
|
|
3b032a275d | ||
|
|
a11ca992fe | ||
|
|
5138c73ae5 | ||
|
|
ea36247797 | ||
|
|
a6a1916299 | ||
|
|
b8699e6bcb | ||
|
|
8cff50b0cf | ||
|
|
b06a96dc31 | ||
|
|
446bd81e08 | ||
|
|
09863ea260 | ||
|
|
34b4556773 | ||
|
|
9b4c874e51 | ||
|
|
ddbc9789e7 | ||
|
|
810d0215cb | ||
|
|
3dadc17ff1 | ||
|
|
8c69985083 | ||
|
|
1d5b475482 | ||
|
|
b779b9d855 | ||
|
|
df676913a0 | ||
|
|
fab9d1dd43 | ||
|
|
36dfdc8b33 | ||
|
|
8b0a20cfd7 | ||
|
|
9553c30ee5 | ||
|
|
9c36651aa0 | ||
|
|
24a251cd5b | ||
|
|
a44ba89cd9 | ||
|
|
44077ad27a | ||
|
|
3ae74763ff | ||
|
|
45a63f70ee | ||
|
|
1bafe29e99 | ||
|
|
10ad601f8b | ||
|
|
db9f145609 | ||
|
|
d920227db0 | ||
|
|
874760f780 | ||
|
|
0e286f383f | ||
|
|
40385f3b1c | ||
|
|
4bc74883e0 | ||
|
|
33d1d97fb7 | ||
|
|
296e03ec74 | ||
|
|
2abdbce8ff | ||
|
|
051695451b | ||
|
|
5b7a7fc3d6 | ||
|
|
0ba013e7ad | ||
|
|
2d2f7476b4 | ||
|
|
23a0ad49b9 | ||
|
|
d21a5aac7f | ||
|
|
f9cd51db0f | ||
|
|
3687a645b6 | ||
|
|
c142996db0 | ||
|
|
36d2b46496 | ||
|
|
cb8b1cfc52 | ||
|
|
743afcaad7 | ||
|
|
52c78e1aee | ||
|
|
ff275c36b6 | ||
|
|
9a94639167 | ||
|
|
74d09ee4dc | ||
|
|
360aaed795 | ||
|
|
912ca3fdcd | ||
|
|
32bbfc3a37 | ||
|
|
1e05ad9eea | ||
|
|
4b516c770d | ||
|
|
4e1fed8177 | ||
|
|
4b30d1073b | ||
|
|
269128360d | ||
|
|
07db19635e | ||
|
|
41d9ac4296 | ||
|
|
a536a93ea3 | ||
|
|
aefdca73fa | ||
|
|
5d402d0c20 | ||
|
|
fcd1924853 | ||
|
|
05c0aafb53 | ||
|
|
159b1b8bd6 | ||
|
|
f03c08bde7 | ||
|
|
1b07a66192 | ||
|
|
ce1686e0eb | ||
|
|
091160a59a | ||
|
|
47d2377b39 |
@@ -46,7 +46,7 @@ jobs:
|
||||
- run: corepack enable
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: "lts/*"
|
||||
node-version: "24.13.1"
|
||||
cache: "yarn"
|
||||
cache-dependency-path: |
|
||||
cloudbeaver/webapp/yarn.lock
|
||||
|
||||
@@ -25,7 +25,7 @@ jobs:
|
||||
- run: corepack enable
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: "lts/*"
|
||||
node-version: "24.13.1"
|
||||
cache: "yarn"
|
||||
cache-dependency-path: "./cloudbeaver/webapp/yarn.lock"
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@ on:
|
||||
- opened
|
||||
- synchronize
|
||||
- reopened
|
||||
- ready_for_review
|
||||
push:
|
||||
branches: [devel]
|
||||
|
||||
@@ -24,11 +25,6 @@ jobs:
|
||||
project-deps: ./cloudbeaver/project.deps
|
||||
timeout-minutes: 5
|
||||
|
||||
# build-server:
|
||||
# name: Server
|
||||
# uses: ./.github/workflows/backend-build.yml
|
||||
# secrets: inherit
|
||||
|
||||
build-frontend:
|
||||
name: Frontend
|
||||
uses: ./.github/workflows/frontend-build.yml
|
||||
@@ -38,6 +34,9 @@ jobs:
|
||||
name: Server
|
||||
uses: dbeaver/dbeaver-common/.github/workflows/java-checkstyle.yml@devel
|
||||
secrets: inherit
|
||||
permissions:
|
||||
contents: read
|
||||
pull-requests: read
|
||||
|
||||
lint-frontend:
|
||||
name: Frontend
|
||||
|
||||
Vendored
+1
-2
@@ -14,7 +14,6 @@
|
||||
"redhat.vscode-yaml",
|
||||
"streetsidesoftware.code-spell-checker",
|
||||
"streetsidesoftware.code-spell-checker-russian",
|
||||
"syler.sass-indented",
|
||||
"github.copilot-chat",
|
||||
"yzhang.markdown-all-in-one",
|
||||
"GraphQL.vscode-graphql-syntax",
|
||||
@@ -22,4 +21,4 @@
|
||||
"esbenp.prettier-vscode",
|
||||
"devnaumov.licensify"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
Vendored
+1
-1
@@ -48,6 +48,7 @@
|
||||
"-Declipse.pde.launch=true",
|
||||
"-XstartOnFirstThread",
|
||||
"-Dfile.encoding=UTF-8",
|
||||
"--enable-native-access=ALL-UNNAMED",
|
||||
"--add-modules=ALL-DEFAULT",
|
||||
"--add-opens=java.base/java.io=ALL-UNNAMED",
|
||||
"--add-opens=java.base/java.lang=ALL-UNNAMED",
|
||||
@@ -64,7 +65,6 @@
|
||||
"--add-opens=java.base/jdk.internal.misc=ALL-UNNAMED",
|
||||
"--add-opens=java.base/sun.nio.ch=ALL-UNNAMED",
|
||||
"--add-opens=java.base/sun.security.ssl=ALL-UNNAMED",
|
||||
"--add-opens=java.base/sun.security.action=ALL-UNNAMED",
|
||||
"--add-opens=java.base/sun.security.util=ALL-UNNAMED",
|
||||
"--add-opens=java.security.jgss/sun.security.jgss=ALL-UNNAMED",
|
||||
"--add-opens=java.security.jgss/sun.security.krb5=ALL-UNNAMED",
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "vscode-java-configs",
|
||||
"packageManager": "yarn@4.9.2",
|
||||
"packageManager": "yarn@4.14.1",
|
||||
"devDependencies": {
|
||||
"typescript": "^5.8.3"
|
||||
}
|
||||
|
||||
@@ -0,0 +1,95 @@
|
||||
# CloudBeaver
|
||||
|
||||
## What is CloudBeaver?
|
||||
|
||||
CloudBeaver is an open-source, web-based database management application. Its modular frontend provides the database navigator, SQL editor, data editor, administration, and related features, and communicates with the CloudBeaver server through GraphQL.
|
||||
|
||||
## Frontend
|
||||
|
||||
### Scope
|
||||
|
||||
- These instructions apply to the frontend under `webapp/` in CloudBeaver Community Edition and to any other CloudBeaver edition or product that shares this frontend architecture.
|
||||
- Do not modify `server/`, `deploy/`, or backend configuration unless the task explicitly requires it. Backend GraphQL schemas are an input to frontend code generation, not part of the default edit scope.
|
||||
- Treat `package.json`, `.yarnrc.yml`, and the frontend CI workflows as the source of truth for tool and runtime versions; do not duplicate their version numbers in documentation.
|
||||
|
||||
### Project map
|
||||
|
||||
- `webapp/package.json`: Yarn Plug'n'Play workspace root and repository-wide frontend scripts.
|
||||
- `webapp/packages/core-*`: shared application foundations, services, UI primitives, state, and infrastructure.
|
||||
- `webapp/packages/plugin-*`: feature modules. A package normally registers services and bootstraps in `src/module.ts` and exposes its public API from `src/index.ts`.
|
||||
- `webapp/packages/product-*`: CE product composition and Vite entry points. `product-default` is the runnable/bundled frontend; `product-default-impl` selects the CE plugin set.
|
||||
- `webapp/packages/core-sdk`: GraphQL operations and generated client types.
|
||||
- `webapp/common-typescript/@dbeaver/*` and `webapp/common-react/@dbeaver/*`: shared DBeaver utilities and React components used by the workspace.
|
||||
- Tests are colocated with source as `*.test.ts` or `*.test.tsx`; styles are usually colocated CSS modules.
|
||||
|
||||
### Stack
|
||||
|
||||
TypeScript ES modules, React, MobX, the project DI/module registry, GraphQL Code Generator, Vite, Yarn workspaces with PnP, Vitest and Testing Library, ESLint, Prettier, CSS Modules, and Tailwind-based theming.
|
||||
|
||||
### Setup and common commands
|
||||
|
||||
Use the Node version configured by frontend CI and the Yarn version pinned in `webapp/package.json`. Run frontend commands from `webapp/` unless noted.
|
||||
|
||||
```sh
|
||||
corepack enable
|
||||
cd webapp
|
||||
yarn install --immutable
|
||||
```
|
||||
|
||||
```sh
|
||||
# Start the frontend dev server; a compatible CloudBeaver API must run separately.
|
||||
(cd packages/product-default && yarn dev)
|
||||
|
||||
# From webapp/: repository-wide checks.
|
||||
yarn lint
|
||||
yarn test
|
||||
yarn validate-dependencies
|
||||
|
||||
# Production frontend build.
|
||||
(cd packages/product-default && yarn bundle)
|
||||
```
|
||||
|
||||
For a focused check, use a package's existing script, for example `yarn workspace @cloudbeaver/core-utils test` or `yarn workspace @cloudbeaver/plugin-sql-editor lint`. Do not invent a package script that is absent from its `package.json`.
|
||||
|
||||
### Working conventions
|
||||
|
||||
- Put changes in the smallest package that owns the behavior. Prefer existing `core-*` abstractions and UI primitives before adding cross-package helpers.
|
||||
- Keep dependencies directed from plugins to core: `core-*` packages must never import or depend on `plugin-*` packages. Plugins may use public APIs from core packages and from other plugins, subject to the administration boundary and the no-cycles rule below.
|
||||
- Put administration-only behavior in a separate package whose name ends with `-administration`. Public/non-administration plugins must not import or depend on administration packages.
|
||||
- Keep the workspace package dependency graph acyclic; do not introduce direct or indirect circular dependencies.
|
||||
- Import workspace packages through their public `@cloudbeaver/*` or `@dbeaver/*` exports; never reach into another package's `src/` directory.
|
||||
- When adding a workspace dependency, update the package's `package.json` through Yarn. The configured `ts-project-linker` synchronizes `tsconfig.json` project references during a non-immutable install; edit references manually only if automatic linking cannot complete. Run `yarn validate-dependencies` afterward.
|
||||
- Register services and bootstraps through the package's `src/module.ts`. Change product composition only when enabling or disabling a package for the CE product.
|
||||
- Put user-visible text in the package localization files and follow the neighboring `LocaleService` pattern; do not hardcode UI copy in components.
|
||||
- Add or change GraphQL operations as `.gql` files in `core-sdk`. Regenerate through `yarn workspace @cloudbeaver/core-sdk gql:gen`; never edit the ignored generated `src/sdk.ts` manually.
|
||||
- Preserve valid license headers on existing source files. When modifying an existing source file, update the ending copyright year to the current four-digit calendar year while keeping its original starting year. For new source files, use the template below and replace `<CURRENT_YEAR>` with the current year; never leave the placeholder in committed code. Use the repository ESLint and Prettier configuration rather than manual formatting rules.
|
||||
- Do not edit or commit generated/install artifacts such as `node_modules/`, `.pnp.*`, `lib/`, `dist/`, `coverage/`, or `allure-results/`.
|
||||
- Add focused tests for behavior changes. Before handoff, run lint and tests for affected packages; run the repository-wide checks and product bundle for cross-cutting or product-composition changes.
|
||||
|
||||
### License header for new source files
|
||||
|
||||
```text
|
||||
/*
|
||||
* CloudBeaver - Cloud Database Manager
|
||||
* Copyright (C) 2020-<CURRENT_YEAR> DBeaver Corp and others
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0.
|
||||
* you may not use this file except in compliance with the License.
|
||||
*/
|
||||
```
|
||||
|
||||
## Backend
|
||||
|
||||
- Before backend work, read and follow `../dbeaver/AGENTS.md` for inherited Java and Tycho conventions.
|
||||
- Cloudbeaver does not use SWT or Eclipse RCP, and its backend is headless.
|
||||
- Configuration file is generated using `apps/config-generator`. For making changes there, update `config/template/cloudbeaver-base.conf` or use patches (for specific product parameters).
|
||||
|
||||
### GraphQL API
|
||||
|
||||
- Use consistent names. IDs must use type `ID`; ID arguments and inputs must end with `Id`, for example `projectId`.
|
||||
- Top-level methods follow `{pluginId}{methodName}`, for example `authLogin`, `rmListProjects`, and `navNodeChildren`. Mark new public API with `@since`.
|
||||
- Preserve released APIs: deprecate instead of renaming or removing, include the deprecation version, and remove only after more than one year. EA-only APIs may change before public release.
|
||||
- New arguments and input fields added to a released API must be optional.
|
||||
|
||||
### License header
|
||||
- Please use header from ../dbeaver/docs/license_header.txt for backend source files.
|
||||
@@ -5,13 +5,16 @@
|
||||
Cloud Database Manager - Community Edition.
|
||||
CloudBeaver is a web server that provides a rich web interface. The server itself is a Java application, and the web part is written in TypeScript and React.
|
||||
It is free to use and open-source (licensed under [Apache 2](https://github.com/dbeaver/cloudbeaver/blob/devel/LICENSE) license).
|
||||
See our [WIKI](https://github.com/dbeaver/cloudbeaver/wiki) for more details.
|
||||
|
||||
<a><img src="https://github.com/dbeaver/cloudbeaver/wiki/images/connection-creation-demo.png" width="400"/></a>
|
||||
<img src="https://github.com/dbeaver/cloudbeaver/wiki/images/gis-demo.png" width="400"/>
|
||||
<img src="https://github.com/dbeaver/cloudbeaver/wiki/images/data-transfer-demo.png" width="400"/>
|
||||
<img src="https://github.com/dbeaver/cloudbeaver/wiki/images/sql-editor-demo.png" width="400"/>
|
||||
|
||||
## Documentation
|
||||
- [GitHub WIKI](https://github.com/dbeaver/cloudbeaver/wiki)
|
||||
- [Official documentation](https://dbeaver.com/docs/cloudbeaver/)
|
||||
|
||||
## Run in Docker
|
||||
|
||||
- [Official Docker repository](https://hub.docker.com/r/dbeaver/cloudbeaver)
|
||||
@@ -19,97 +22,85 @@ See our [WIKI](https://github.com/dbeaver/cloudbeaver/wiki) for more details.
|
||||
|
||||
## Demo server
|
||||
|
||||
You can see a live demo of CloudBeaver here: https://demo.cloudbeaver.io
|
||||
You can see a live demo of CloudBeaver server here: https://demo.cloudbeaver.io
|
||||
|
||||
[Database access instructions](https://github.com/dbeaver/cloudbeaver/wiki/Demo-Server)
|
||||
|
||||
## Changelog
|
||||
|
||||
### 25.3.5 2026-02-16
|
||||
### 26.2.0 2026-08-31
|
||||
|
||||
### Changes since 26.1.0
|
||||
|
||||
- AI Assistant:
|
||||
- Added AI Chat to help users generate and fix queries and explore data more easily. It’s integrated with the SQL Editor to run generated queries instantly. All AI features can be turned off in the Server configuration.
|
||||
- Added the ability to use multiple AI configurations for working with the AI chat. Administrators can define tokens, engines and models for OpenAI and Copilot in the Administration panel for each AI profile separately. After that, users can switch between these profiles while using AI features.
|
||||
- Added the ability to cancel a response in the AI Chat to stop response generation when needed.
|
||||
- The "Endpoint" setting was renamed to "API Base URL" for the OpenAI provider.
|
||||
- Added AI engine icons to AI configuration profiles, making it easier to identify AI providers in the Administration panel and AI Chat configuration.
|
||||
- Administration:
|
||||
- Added the database version and the driver version to diagnostic logs.
|
||||
- Fixed the issue of fetching groups during LDAP authorization. CloudBeaver now provides all matched groups regardless of fetching errors.
|
||||
- Added the Last Login time to the user form and the user table in the administration part.
|
||||
- Authorization:
|
||||
- Added the option to upload files for certificates and keys in the SSL authorization configuration.
|
||||
- Fixed an issue causing the application to lose connection to the database after extended uptime. Access is kept automatically during long-running sessions.
|
||||
- Data Editor:
|
||||
- Added the ability to automatically generate INSERT, SELECT, DELETE, and UPDATE statements for the selected values in the Data Editor;
|
||||
- Added a status indicator icon in the top-left corner of the Data Editor with tooltips explaining table editability. The icon shows whether the table has a primary key, is read-only due to connection settings, or contains only read-only columns.
|
||||
- Navigator:
|
||||
- The Connection Info tab was added so that all users can see basic information about the current connection;
|
||||
- Added the ability to duplicate connection configuration in the project navigation tree. The "Clone connection" feature is available in the context menu.
|
||||
- Added the References panel to the Data Editor. The panel displays related records from connected tables. Users can explore table relationships directly.
|
||||
- Added the ability to export generated SQL to a .sql file from the Generate SQL dialog.
|
||||
- Fixed copying of large JSON values in the Data Editor - now the full value can be copied.
|
||||
- Resolved an issue where the Data Editor ignored the text preview's maximum length quota. Increasing this quota now enables the ability to edit large values.
|
||||
- Data Transfer:
|
||||
- Added advanced data import settings, including database-specific replace methods, transaction support, bulk loading, and the option to use a separate connection for data import.
|
||||
- Improved memory usage when importing large CSV files to improve the application performance.
|
||||
- Accessibility:
|
||||
- Improved accessibility for the search on the Users section in the Administration panel. The application allows focusing on the search field with the Tab key.
|
||||
- Added a loader to the Save button in the Administration panel to improve indication of the long-performing operations.
|
||||
- Improved drag-and-drop in the Navigator Tree for objects by enlarging the auto-scroll trigger areas.
|
||||
- Added the ability to expand and collapse code block elements, including JSON, using the keyboard in the Data Editor.
|
||||
- Fixed shortcuts behavior for AZERTY keyboards. Now shortcuts like Ctrl/Cmd + Z work correctly.
|
||||
- Added the ability to use the Enter or space keys to turn checkboxes on/off in the connection properties tab.
|
||||
- General:
|
||||
- The missing pg_dump utility no longer causes errors during initialization or during deployment updates when PostgreSQL is selected as the inner database;
|
||||
- DuckDB: Driver version was updated to 1.4.4.0;
|
||||
- PostgreSQL: Added DDL display support for PostgreSQL policies.
|
||||
|
||||
### 25.3.4 2026-02-02
|
||||
|
||||
- Editors:
|
||||
- Added undo and redo functionality to the Data Editor for cell edits, row operations, and other data modifications. The history retains the last 50 actions across the Data Editor, result sets, and related panels;
|
||||
- The SQL preview panel has been added to the Bind parameters/variables dialog in the SQL Editor, allowing users to review the SQL query with changed values on the fly;
|
||||
- The Tab key can now be used for autocompletion in the SQL editor in the same way as the Enter key;
|
||||
- Fixed an application freeze caused by fetch size canceling for large tables in the Result Set;
|
||||
- Fixed an issue where pressing the Delete key during inline cell editing incorrectly marked the entire row for deletion.
|
||||
- Navigator:
|
||||
- Added independent view preference settings for database connections at both the user and connection levels. Users can configure their preferred navigator view separately from connection-specific settings;
|
||||
- Added "Show children of tables" option to Navigator tree settings. When enabled, the navigator displays nested objects such as columns under tables and views. The setting applies globally across all connections and remains disabled by default;
|
||||
- Added the ability to rename connections via context menu in the Navigator Tree;
|
||||
- General:
|
||||
- Expanded pointer target areas for icons in the navigator, editors, and tabs to a minimum of pixels per accessibility standards. Improved interaction accuracy and reduced errors for users with motor control limitations;
|
||||
- The "Database Native" authentication type has been renamed to "Username/password" for all connectors;
|
||||
- ClickHouse: Fixed an issue with displaying arrays of UUID, IPv4/IPv6, and Map types.
|
||||
|
||||
### 25.3.3 2026-01-19
|
||||
|
||||
- For security reasons, session expiration leads to a complete logout for a user and clears all data from the screen;
|
||||
- Added "Use local formatting" setting to Data Editor. Users can choose how to display numbers and dates: using the OS locale, a custom locale, or keeping values unformatted. This formatting applies only to displayed values. Data in the database remains unchanged;
|
||||
- Added the shortcut Ctrl/Cmd + . to cancel operations in Data Editor;
|
||||
- CloudBeaver now supports long polling as a fallback when WebSockets are unavailable or blocked, ensuring reliable communication for metadata updates and SQL execution;
|
||||
- Fixed a keyboard navigation issue for panels to keep the focus inside;
|
||||
- Clickhouse: Spatial data support was added.
|
||||
|
||||
### 25.3.2 2026-01-05
|
||||
|
||||
- The ability to pin columns has been added to the Data Editor, allowing users to keep key columns (e.g., IDs, names) visible while scrolling through wide tables;
|
||||
- Added a new setting in the SQL Editor to highlight spaces, tabs, and other whitespace characters to help users read, debug, and maintain their scripts. It is turned off by default and can be configured in personal preferences;
|
||||
- Redesigned the connection configuration page. Form fields and sections have been reorganized to provide more space for input and reduce visual clutter;
|
||||
- Clickhouse: driver has been updated to version 0.9.5;
|
||||
- Oracle: Added a new "Set Username to OS_USER" option in the Misc section of Oracle connection settings. When turned on, the tool automatically uses the current database username as the operating system user identifier in the session metadata.
|
||||
|
||||
### 25.3.1 2025-12-22
|
||||
|
||||
- Implemented an ability to add parameters and variables to queries in the SQL Editor. This feature allows for the reuse of queries by changing parameters at the execution stage. It is enabled by default and can be configured in personal preferences;
|
||||
- Added support for mapping users to CloudBeaver teams based on LDAP memberOf group membership;
|
||||
- Added support for previous browser versions, but not older than three years.
|
||||
|
||||
### 25.3.0 2025-12-01
|
||||
|
||||
### Changes since 25.2.0:
|
||||
|
||||
- Administration:
|
||||
- Permission configuration for connections has been simplified, providing a clearer appearance and improved performance;
|
||||
- LDAP authentication now supports case-insensitive Distinguished Names (DNs), allowing login with mixed-case DNS.
|
||||
- Security:
|
||||
- Fixed a security issue in the Spatial Viewer for the HTML content to prevent a third-party script execution;
|
||||
- Fixed the critical vulnerability (CVE-2025-61927) in the Happy DOM library. The library was updated to version 20.0.2.
|
||||
- SQL Editor:
|
||||
- A confirmation prompt has been added to the SQL Editor to prevent accidental execution of potentially dangerous queries. You can customize this behavior in Preferences > SQL Editor;
|
||||
- A search field has been added to the catalog/schema selector on the main toolbar for easy navigation;
|
||||
- Alphabetical order has been added to the autocomplete values in the SQL Editor to simplify object search;
|
||||
- Added support for temporary tables in semantic analysis.
|
||||
- Data Editor:
|
||||
- The ability to review the database query after applying filters and sorting in the Data Editor, Grouping panel, and Result sets has been added. The new Show SQL button is placed on the Filter panel;
|
||||
- The value panel in the Data Editor has been redesigned to give users more space for viewing and editing data. Tab buttons for different data type visualizations are now aligned on a single level, creating a cleaner layout;
|
||||
- Added visualization for column reordering in the Data Editor;
|
||||
- Fixed an issue in the Data Editor where resizing one column in a wide table caused previously resized columns to revert to their original width. Users can resize multiple columns as intended.
|
||||
- General:
|
||||
- A reorder tabs option has been added to the application, allowing users to set editor tabs in custom positions;
|
||||
- Keyboard navigation has been improved across the application. Additional shortcuts are available in the main info panel (top-right corner) to help discover them faster. Relevant keyboard shortcuts are also shown in the tooltips of Data Editor buttons;
|
||||
- Boolean values have been replaced with intuitive checkboxes instead of the true/false text in the metadata editor, making editing easier and faster;
|
||||
- The database navigator now automatically hides empty subfolders in shared projects, reducing visual clutter;
|
||||
- Databases and drivers:
|
||||
- ClickHouse: query canceling support has been added;
|
||||
- DuckDB: fixed an issue where catalog selection was lost upon disconnection;
|
||||
- PostgreSQL: fixed support for GIS data types;
|
||||
- SQL Server: the driver has been updated to version 13.2.1;
|
||||
- The "Show all databases" setting has been added on the connection page for MySQL and MariaDB.
|
||||
## Contribution
|
||||
As a community-driven open-source project, we warmly welcome contributions through GitHub pull requests.
|
||||
|
||||
[We are happy to reward](https://dbeaver.com/help-dbeaver/) our most active contributors every major sprint.
|
||||
- Added connection types to visually distinguish database connections using background colors across the Database Navigator and application UI.
|
||||
- Improved SQL autocomplete suggestion ordering for object names. Now suggestions are sorted alphabetically.
|
||||
- Added a Project Info tab containing the project description to the project context menu.
|
||||
- Fixed synchronization between devices. Users see updated connections and folders in other devices without manual refreshes.
|
||||
- Databases:
|
||||
- ClickHouse
|
||||
- The driver was updated to version 0.10.0
|
||||
- Improved JSON display in the Value panel
|
||||
- Added support for the Map data type visualization for the Data Editor
|
||||
- The Databend driver was updated to version 0.4.8.
|
||||
- LibSQL: Added support for database views.
|
||||
- MySQL: Fixed support for comments starting with '#' symbol in the SQL Editor.
|
||||
- PostgreSQL
|
||||
- Updated the driver to version 42.7.13.
|
||||
- Added a Show full DDL option for schemas and tables to generate complete DDL, including objects within a schema and object comments and privileges.
|
||||
- Fixed an error that occurred when applying filters to database nodes
|
||||
- Fixed generated calls for procedures with OUT parameters
|
||||
- Security:
|
||||
- Removed the ability to rename SQL scripts that start with dots to prevent them from being moved outside the Scripts directory into the root Tree.
|
||||
- Added a new "Auto-create users" setting for the reverse proxy provider, allowing new user creation on login. It is enabled by default.
|
||||
- Fixed a security flaw allowing unauthorized users to test database connections. The application now requires proper permissions for this action, preventing malicious requests and protecting server files.
|
||||
- Fixed a security flaw in the LDAP authentication process. The application validates the username input to prevent unauthorized access and protect directory data.
|
||||
- Fixed a cross-site scripting vulnerability caused by unsanitized user input in the web interface.
|
||||
- Resolved a security flaw related to user-controlled file paths by adding strict validation to prevent unauthorized resource access.
|
||||
- Updated session handling to prevent session fixation vulnerabilities. The application generates new session identifiers upon login.
|
||||
- Updated the PostgreSQL JDBC driver to version 42.7.13, including a fix for a high-severity security vulnerability (CVE-2026-54291).
|
||||
- Fixed the critical vulnerability (CVE-2026-59873) in the tar library. The library was updated to version 7.5.19.
|
||||
- Fixed the critical vulnerability (CVE-2026-9277) in the shell-quote library (updated to version 1.8.4)
|
||||
- Fixed the high vulnerability (CVE-2026-10050) in the jetty-security library. The library was updated to version 12.1.11.
|
||||
- Fixed the high vulnerability (CVE-2026-14257) in the brace-expansion library. The library was updated to version 5.0.8.
|
||||
- Fixed the high vulnerability (GHSA-r28c-9q8g-f849) in the postcss library. The library was updated to version 8.5.18.
|
||||
- Fixed the high vulnerability (GHSA-pm4m-ph32-ghv5) in the js-yaml library. The library was updated to version 5.2.2.
|
||||
- Fixed the high vulnerability (CVE-2026-59880) in the immutable library. The library was updated to version 5.1.8.
|
||||
- Fixed the high vulnerability (CVE-2026-59879) in the fast-uri library. The library was updated to version 3.1.3.
|
||||
- Fixed the high vulnerability (CVE-2026-13311) in the shell-quote library. The library was updated to version 1.9.0.
|
||||
- Fixed the high vulnerability (GHSA-gcfj-64vw-6mp9) in the axios library. The library was updated to version 1.18.0.
|
||||
- Fixed the high vulnerability (CVE-2026-67213) in the nanoid library. The library was updated to version 3.3.17.
|
||||
- Fixed the high vulnerability (CVE-2025-71329) in the less library. The library was updated to version 4.7.0.
|
||||
- Fixed the high vulnerability (CVE-2026-13697) in the undici library. The library was updated to version 8.9.0.
|
||||
- Fixed the high vulnerability (CVE-2026-18446) in the fast-uri library. The library was updated to version 4.1.2.
|
||||
- Fixed the high vulnerability (CVE-2026-69152) in the brace-expansion library. The library was updated to version 5.0.9.
|
||||
- Fixed the high vulnerability (GHSA-gv7w-rqvm-qjhr) in the esbuild library. The library was updated to version 0.28.1.
|
||||
- Fixed the high vulnerability (CVE-2026-12143) in the form-data library. The library was updated to version 4.0.6.
|
||||
|
||||
+1
-13
@@ -1,18 +1,6 @@
|
||||
# Security Policy
|
||||
|
||||
## Supported Versions
|
||||
|
||||
Use this section to tell people about which versions of your project are
|
||||
currently being supported with security updates.
|
||||
|
||||
| Version | Supported |
|
||||
| ------- | --------- |
|
||||
| 22.x | yes |
|
||||
| 23.x | yes |
|
||||
| 24.x | yes |
|
||||
|
||||
## Reporting a Vulnerability
|
||||
|
||||
Please report (suspected) security vulnerabilities to devops@dbeaver.com.
|
||||
You will receive a response from us within 48 hours.
|
||||
Please report (suspected) security vulnerabilities to <security@dbeaver.com>.
|
||||
If the issue is confirmed, we will release a patch as soon as possible, depending on complexity, but historically, within a few days.
|
||||
|
||||
@@ -0,0 +1,61 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<groupId>io.cloudbeaver</groupId>
|
||||
<artifactId>config-generator</artifactId>
|
||||
<version>1.0-SNAPSHOT</version>
|
||||
|
||||
<properties>
|
||||
<maven.compiler.source>21</maven.compiler.source>
|
||||
<maven.compiler.target>21</maven.compiler.target>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>com.google.code.gson</groupId>
|
||||
<artifactId>gson</artifactId>
|
||||
<version>2.14.0</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.codehaus.mojo</groupId>
|
||||
<artifactId>exec-maven-plugin</artifactId>
|
||||
<version>3.1.0</version>
|
||||
<configuration>
|
||||
<mainClass>io.cloudbeaver.ConfigGenerator</mainClass>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<artifactId>maven-resources-plugin</artifactId>
|
||||
<version>3.3.1</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>copy-external-config</id>
|
||||
<phase>process-resources</phase>
|
||||
<goals>
|
||||
<goal>copy-resources</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<outputDirectory>${project.build.outputDirectory}/config</outputDirectory>
|
||||
<resources>
|
||||
<resource>
|
||||
<directory>${project.basedir}/../../config/template</directory>
|
||||
<includes>
|
||||
<include>cloudbeaver-base.conf</include>
|
||||
</includes>
|
||||
</resource>
|
||||
</resources>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</project>
|
||||
@@ -0,0 +1,235 @@
|
||||
/*
|
||||
* DBeaver - Universal Database Manager
|
||||
* Copyright (C) 2010-2026 DBeaver Corp and others
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package io.cloudbeaver;
|
||||
|
||||
import com.google.gson.*;
|
||||
import com.google.gson.reflect.TypeToken;
|
||||
|
||||
import java.io.Reader;
|
||||
import java.net.URL;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.*;
|
||||
|
||||
public class ConfigGenerator {
|
||||
|
||||
private static final Gson gson = new GsonBuilder()
|
||||
.setPrettyPrinting()
|
||||
.disableHtmlEscaping()
|
||||
.create();
|
||||
private static final String EMPTY_STRING = "''";
|
||||
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
String baseConfigPath = System.getProperty("config.base");
|
||||
if (baseConfigPath == null || baseConfigPath.isEmpty()) {
|
||||
URL resource = ConfigGenerator.class.getClassLoader().getResource("config/cloudbeaver-base.conf");
|
||||
if (resource == null) {
|
||||
System.out.println("Base config path is required");
|
||||
return;
|
||||
}
|
||||
baseConfigPath = resource.toURI().getPath();
|
||||
if (System.getProperty("os.name").toLowerCase().contains("win")) {
|
||||
// Fix for Windows in dev mode. Resource URI starts with / (e.g. /C:/some/folder)
|
||||
// And this breaks Java NIO
|
||||
if (baseConfigPath.startsWith("/")) {
|
||||
baseConfigPath = baseConfigPath.substring(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
String outputPath = System.getProperty("config.output");
|
||||
if (outputPath == null || outputPath.isEmpty()) {
|
||||
System.out.println("Output config path is required");
|
||||
return;
|
||||
}
|
||||
|
||||
String configPatches = System.getProperty("config.patches");
|
||||
|
||||
List<String> overrideConfigs = configPatches == null ? List.of() : Arrays.asList(configPatches.split(","));
|
||||
|
||||
Map<String, Object> config = readConfigurationFile(baseConfigPath);
|
||||
System.out.println("Base config loaded");
|
||||
|
||||
for (String overrideConfig : overrideConfigs) {
|
||||
if (Files.exists(Path.of(overrideConfig))) {
|
||||
System.out.println("Applying override config: " + overrideConfig);
|
||||
} else {
|
||||
System.out.println("Override config not found, skipping: " + overrideConfig);
|
||||
continue;
|
||||
}
|
||||
System.out.println("Processing override config: " + overrideConfig);
|
||||
Map<String, Object> overrideConfigMap = readConfigurationFile(overrideConfig);
|
||||
System.out.println("Override config loaded: " + overrideConfig);
|
||||
applyProductConfig(config, overrideConfigMap);
|
||||
System.out.println("Override config applied: " + overrideConfig);
|
||||
}
|
||||
|
||||
Map<String, Object> finalMap = convertToOutputFormat(config);
|
||||
System.out.println("Config converted to output format");
|
||||
|
||||
writeConfigurationFile(finalMap, outputPath);
|
||||
System.out.println("Config written to: " + outputPath);
|
||||
}
|
||||
|
||||
private static Map<String, Object> readConfigurationFile(String filePath) throws Exception {
|
||||
Path path = Path.of(filePath);
|
||||
if (!Files.exists(path)) {
|
||||
throw new IllegalArgumentException("Configuration file not found: " + filePath);
|
||||
}
|
||||
|
||||
try (Reader reader = Files.newBufferedReader(path)) {
|
||||
return gson.fromJson(reader, TypeToken.getParameterized(Map.class, String.class, Object.class).getType());
|
||||
}
|
||||
}
|
||||
|
||||
private static void applyProductConfig(Map<String, Object> baseConfig, Map<String, Object> productConfig) {
|
||||
if (productConfig.containsKey("add")) {
|
||||
Map<String, Object> add = (Map<String, Object>) productConfig.get("add");
|
||||
deepMergeMap(baseConfig, add);
|
||||
}
|
||||
|
||||
if (productConfig.containsKey("remove")) {
|
||||
Map<String, Object> remove = (Map<String, Object>) productConfig.get("remove");
|
||||
deepRemoveMap(baseConfig, remove);
|
||||
}
|
||||
}
|
||||
|
||||
private static void deepMergeMap(Map<String, Object> target, Map<String, Object> source) {
|
||||
for (Map.Entry<String, Object> entry : source.entrySet()) {
|
||||
Object sourceValue = entry.getValue();
|
||||
Object targetValue = target.get(entry.getKey());
|
||||
|
||||
if (sourceValue instanceof Map && targetValue instanceof Map) {
|
||||
deepMergeMap((Map<String, Object>) targetValue, (Map<String, Object>) sourceValue);
|
||||
} else {
|
||||
target.put(entry.getKey(), sourceValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void deepRemoveMap(Map<String, Object> target, Map<String, Object> source) {
|
||||
for (Map.Entry<String, Object> entry : source.entrySet()) {
|
||||
Object sourceValue = entry.getValue();
|
||||
Object targetValue = target.get(entry.getKey());
|
||||
|
||||
if (sourceValue instanceof Map && targetValue instanceof Map) {
|
||||
deepRemoveMap((Map<String, Object>) targetValue, (Map<String, Object>) sourceValue);
|
||||
} else {
|
||||
target.remove(entry.getKey());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void writeConfigurationFile(Map<String, Object> map, String filePath) throws Exception {
|
||||
JsonElement tree = gson.toJsonTree(map);
|
||||
StringBuilder sb = new StringBuilder();
|
||||
writeElement(tree, sb, 0);
|
||||
Files.write(Path.of(filePath), sb.toString().getBytes());
|
||||
}
|
||||
|
||||
private static void writeElement(JsonElement elem, StringBuilder sb, int count) {
|
||||
String indent = " ";
|
||||
if (elem.isJsonObject()) {
|
||||
sb.append("{\n");
|
||||
JsonObject obj = elem.getAsJsonObject();
|
||||
var iterator = obj.entrySet().iterator();
|
||||
|
||||
while (iterator.hasNext()) {
|
||||
var entry = iterator.next();
|
||||
sb.repeat(indent, count + 1).append(entry.getKey()).append(": ");
|
||||
writeElement(entry.getValue(), sb, count + 1);
|
||||
if (iterator.hasNext()) {
|
||||
sb.append(",");
|
||||
}
|
||||
sb.append("\n");
|
||||
}
|
||||
sb.repeat(indent, count).append("}");
|
||||
|
||||
} else if (elem.isJsonArray()) {
|
||||
sb.append("[\n");
|
||||
JsonArray array = elem.getAsJsonArray();
|
||||
for (int i = 0; i < array.size(); i++) {
|
||||
sb.repeat(indent, count + 1);
|
||||
writeElement(array.get(i), sb, count + 1);
|
||||
if (i < array.size() - 1) {
|
||||
sb.append(",");
|
||||
}
|
||||
sb.append("\n");
|
||||
}
|
||||
sb.repeat(indent, count).append("]");
|
||||
|
||||
} else {
|
||||
String valueStr = gson.toJson(elem).replace("\n", "\n" + indent);
|
||||
sb.append(valueStr);
|
||||
}
|
||||
}
|
||||
|
||||
private static Map<String, Object> convertToOutputFormat(Map<String, Object> map) {
|
||||
Map<String, Object> result = new LinkedHashMap<>();
|
||||
for (Map.Entry<String, Object> entry : map.entrySet()) {
|
||||
result.put(entry.getKey(), convertValue(entry.getValue()));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private static Object convertValue(Object value) {
|
||||
if (value instanceof Map) {
|
||||
Map<String, Object> map = (Map<String, Object>) value;
|
||||
if (!map.isEmpty() && map.containsKey("value")) {
|
||||
if (map.containsKey("env")) {
|
||||
String env = (String) map.get("env");
|
||||
Object defValue = map.get("value");
|
||||
return "${" + env + ":" + formatDefaultValue(defValue) + "}";
|
||||
} else {
|
||||
return convertValue(map.get("value"));
|
||||
}
|
||||
}
|
||||
Map<String, Object> result = new LinkedHashMap<>();
|
||||
for (Map.Entry<String, Object> entry : map.entrySet()) {
|
||||
result.put(entry.getKey(), convertValue(entry.getValue()));
|
||||
}
|
||||
return result;
|
||||
} else if (value instanceof List) {
|
||||
List<Object> list = (List<Object>) value;
|
||||
List<Object> result = new ArrayList<>();
|
||||
for (Object item : list) {
|
||||
result.add(convertValue(item));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
private static String formatDefaultValue(Object value) {
|
||||
return switch (value) {
|
||||
case null -> EMPTY_STRING;
|
||||
case String str -> str.isEmpty() ? EMPTY_STRING : str;
|
||||
case Boolean b -> value.toString();
|
||||
case Number number -> {
|
||||
if (number instanceof Double || number instanceof Float) {
|
||||
long longVal = number.longValue();
|
||||
if (longVal == number.doubleValue()) {
|
||||
yield String.valueOf(longVal);
|
||||
}
|
||||
}
|
||||
yield number.toString();
|
||||
}
|
||||
// TODO: handle list elements if needed
|
||||
default -> value.toString();
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -1,99 +0,0 @@
|
||||
{
|
||||
server: {
|
||||
serverPort: "${CLOUDBEAVER_WEB_SERVER_PORT:8978}",
|
||||
forceHttps: "${CLOUDBEAVER_FORCE_HTTPS:false}",
|
||||
|
||||
contentRoot: "web",
|
||||
driversLocation: "drivers",
|
||||
|
||||
sslConfigurationPath:"${CLOUDBEAVER_SSL_CONF_PATH:workspace/.data/ssl-config.xml}",
|
||||
|
||||
rootURI: "${CLOUDBEAVER_ROOT_URI:/}",
|
||||
serviceURI: "/api/",
|
||||
supportedHosts: [],
|
||||
|
||||
productSettings: {
|
||||
# Global properties
|
||||
core.theming.theme: "${CLOUDBEAVER_CORE_THEMING_THEME:system}",
|
||||
core.localization.language: "${CLOUDBEAVER_CORE_LOCALIZATION:en}",
|
||||
plugin.sql-editor.autoSave: "${CLOUDBEAVER_SQL_EDITOR_AUTOSAVE:true}",
|
||||
plugin.sql-editor.disabled: "${CLOUDBEAVER_SQL_EDITOR_DISABLED:false}",
|
||||
# max size of the file that can be uploaded to the editor (in kilobytes)
|
||||
plugin.sql-editor.maxFileSize: "${CLOUDBEAVER_SQL_EDITOR_MAX_FILE_SIZE:10240}",
|
||||
plugin.log-viewer.disabled: "${CLOUDBEAVER_LOG_VIEWER_DISABLED:false}",
|
||||
plugin.log-viewer.logBatchSize: "${CLOUDBEAVER_LOG_VIEWER_LOG_BATCH_SIZE:1000}",
|
||||
plugin.log-viewer.maxLogRecords: "${CLOUDBEAVER_LOG_VIEWER_MAX_LOG_RECORDS:2000}",
|
||||
sql.proposals.insert.table.alias: "${CLOUDBEAVER_SQL_PROPOSALS_INSERT_TABLE_ALIAS:PLAIN}",
|
||||
SQLEditor.ContentAssistant.experimental.mode: "${CLOUDBEAVER_SQL_EDITOR_CONTENT_ASSISTANT_EXPERIMENTAL_MODE:NEW}"
|
||||
},
|
||||
|
||||
expireSessionAfterPeriod: "${CLOUDBEAVER_EXPIRE_SESSION_AFTER_PERIOD:1800000}",
|
||||
bindSessionToIp: "${CLOUDBEAVER_BIND_SESSION_TO_IP:disable}",
|
||||
develMode: "${CLOUDBEAVER_DEVEL_MODE:false}",
|
||||
|
||||
enableSecurityManager: false,
|
||||
|
||||
sm: {
|
||||
enableBruteForceProtection: "${CLOUDBEAVER_BRUTE_FORCE_PROTECTION_ENABLED:true}",
|
||||
maxFailedLogin: "${CLOUDBEAVER_MAX_FAILED_LOGINS:10}",
|
||||
minimumLoginTimeout: "${CLOUDBEAVER_MINIMUM_LOGIN_TIMEOUT:1}",
|
||||
blockLoginPeriod: "${CLOUDBEAVER_BLOCK_PERIOD:300}",
|
||||
passwordPolicy: {
|
||||
minLength: "${CLOUDBEAVER_POLICY_MIN_LENGTH:8}",
|
||||
requireMixedCase: "${CLOUDBEAVER_POLICY_REQUIRE_MIXED_CASE:true}",
|
||||
minNumberCount: "${CLOUDBEAVER_POLICY_MIN_NUMBER_COUNT:1}",
|
||||
minSymbolCount: "${CLOUDBEAVER_POLICY_MIN_SYMBOL_COUNT:0}"
|
||||
}
|
||||
},
|
||||
|
||||
database: {
|
||||
driver: "${CLOUDBEAVER_DB_DRIVER:h2_embedded_v2}",
|
||||
url: "${CLOUDBEAVER_DB_URL:jdbc:h2:${workspace}/.data/cb.h2v2.dat}",
|
||||
schema: "${CLOUDBEAVER_DB_SCHEMA:''}",
|
||||
user: "${CLOUDBEAVER_DB_USER:''}",
|
||||
password: "${CLOUDBEAVER_DB_PASSWORD:''}",
|
||||
initialDataConfiguration: "${CLOUDBEAVER_DB_INITIAL_DATA:conf/initial-data.conf}",
|
||||
pool: {
|
||||
minIdleConnections: "${CLOUDBEAVER_DB_MIN_IDLE_CONNECTIONS:4}",
|
||||
maxIdleConnections: "${CLOUDBEAVER_DB_MAX_IDLE_CONNECTIONS:10}",
|
||||
maxConnections: "${CLOUDBEAVER_DB_MAX_CONNECTIONS:100}",
|
||||
validationQuery: "${CLOUDBEAVER_DB_VALIDATION_QUERY:SELECT 1}"
|
||||
},
|
||||
backupEnabled: "${CLOUDBEAVER_DB_BACKUP_ENABLED:true}"
|
||||
}
|
||||
|
||||
},
|
||||
app: {
|
||||
anonymousAccessEnabled: "${CLOUDBEAVER_APP_ANONYMOUS_ACCESS_ENABLED:true}",
|
||||
anonymousUserRole: user,
|
||||
defaultUserTeam: "${CLOUDBEAVER_APP_DEFAULT_USER_TEAM:user}",
|
||||
grantConnectionsAccessToAnonymousTeam: "${CLOUDBEAVER_APP_GRANT_CONNECTIONS_ACCESS_TO_ANONYMOUS_TEAM:false}",
|
||||
supportsCustomConnections: "${CLOUDBEAVER_APP_SUPPORTS_CUSTOM_CONNECTIONS:false}",
|
||||
showReadOnlyConnectionInfo: "${CLOUDBEAVER_APP_READ_ONLY_CONNECTION_INFO:false}",
|
||||
systemVariablesResolvingEnabled: "${CLOUDBEAVER_SYSTEM_VARIABLES_RESOLVING_ENABLED:false}",
|
||||
|
||||
forwardProxy: "${CLOUDBEAVER_APP_FORWARD_PROXY:false}",
|
||||
|
||||
publicCredentialsSaveEnabled: "${CLOUDBEAVER_APP_PUBLIC_CREDENTIALS_SAVE_ENABLED:true}",
|
||||
adminCredentialsSaveEnabled: "${CLOUDBEAVER_APP_ADMIN_CREDENTIALS_SAVE_ENABLED:true}",
|
||||
|
||||
resourceManagerEnabled: "${CLOUDBEAVER_APP_RESOURCE_MANAGER_ENABLED:true}",
|
||||
|
||||
resourceQuotas: {
|
||||
resourceManagerFileSizeLimit: "${CLOUDBEAVER_RESOURCE_QUOTA_RESOURCE_MANAGER_FILE_SIZE_LIMIT:500000}",
|
||||
sqlMaxRunningQueries: "${CLOUDBEAVER_RESOURCE_QUOTA_SQL_MAX_RUNNING_QUERIES:100}",
|
||||
sqlResultSetRowsLimit: "${CLOUDBEAVER_RESOURCE_QUOTA_SQL_RESULT_SET_ROWS_LIMIT:100000}",
|
||||
sqlTextPreviewMaxLength: "${CLOUDBEAVER_RESOURCE_QUOTA_SQL_TEXT_PREVIEW_MAX_LENGTH:4096}",
|
||||
sqlBinaryPreviewMaxLength: "${CLOUDBEAVER_RESOURCE_QUOTA_SQL_BINARY_PREVIEW_MAX_LENGTH:261120}"
|
||||
},
|
||||
enabledAuthProviders: [
|
||||
"local"
|
||||
],
|
||||
|
||||
disabledBetaFeatures: [
|
||||
|
||||
]
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,271 @@
|
||||
{
|
||||
server: {
|
||||
serverPort: {
|
||||
env: "CLOUDBEAVER_WEB_SERVER_PORT",
|
||||
value: 8978
|
||||
},
|
||||
|
||||
forceHttps: {
|
||||
env: "CLOUDBEAVER_FORCE_HTTPS",
|
||||
value: false
|
||||
},
|
||||
|
||||
contentRoot: {
|
||||
env: "CLOUDBEAVER_CONTENT_ROOT",
|
||||
value: "web"
|
||||
},
|
||||
|
||||
driversLocation: {
|
||||
env: "CLOUDBEAVER_DRIVERS_LOCATION",
|
||||
value: "drivers"
|
||||
},
|
||||
|
||||
sslConfigurationPath: {
|
||||
env: "CLOUDBEAVER_SSL_CONF_PATH",
|
||||
value: "workspace/.data/ssl-config.xml"
|
||||
},
|
||||
|
||||
rootURI: {
|
||||
env: "CLOUDBEAVER_ROOT_URI",
|
||||
value: "/"
|
||||
},
|
||||
|
||||
serviceURI: {
|
||||
env: "CLOUDBEAVER_SERVICE_URI",
|
||||
value: "/api/"
|
||||
},
|
||||
|
||||
supportedHosts: [],
|
||||
|
||||
productSettings: {
|
||||
core.theming.theme: {
|
||||
env: "CLOUDBEAVER_CORE_THEMING_THEME",
|
||||
value: "system"
|
||||
},
|
||||
core.localization.language: {
|
||||
env: "CLOUDBEAVER_CORE_LOCALIZATION",
|
||||
value: "en"
|
||||
},
|
||||
plugin.sql-editor.autoSave: {
|
||||
env: "CLOUDBEAVER_SQL_EDITOR_AUTOSAVE",
|
||||
value: true
|
||||
},
|
||||
plugin.sql-editor.disabled: {
|
||||
env: "CLOUDBEAVER_SQL_EDITOR_DISABLED",
|
||||
value: false
|
||||
},
|
||||
plugin.sql-editor.maxFileSize: {
|
||||
env: "CLOUDBEAVER_SQL_EDITOR_MAX_FILE_SIZE",
|
||||
value: 10240
|
||||
},
|
||||
plugin.log-viewer.disabled: {
|
||||
env: "CLOUDBEAVER_LOG_VIEWER_DISABLED",
|
||||
value: false
|
||||
},
|
||||
plugin.log-viewer.logBatchSize: {
|
||||
env: "CLOUDBEAVER_LOG_VIEWER_LOG_BATCH_SIZE",
|
||||
value: 1000
|
||||
},
|
||||
plugin.log-viewer.maxLogRecords: {
|
||||
env: "CLOUDBEAVER_LOG_VIEWER_MAX_LOG_RECORDS",
|
||||
value: 2000
|
||||
},
|
||||
sql.proposals.insert.table.alias: {
|
||||
env: "CLOUDBEAVER_SQL_PROPOSALS_INSERT_TABLE_ALIAS",
|
||||
value: "PLAIN"
|
||||
},
|
||||
SQLEditor.ContentAssistant.experimental.mode: {
|
||||
env: "CLOUDBEAVER_SQL_EDITOR_CONTENT_ASSISTANT_EXPERIMENTAL_MODE",
|
||||
value: "NEW"
|
||||
}
|
||||
},
|
||||
|
||||
expireSessionAfterPeriod: {
|
||||
env: "CLOUDBEAVER_EXPIRE_SESSION_AFTER_PERIOD",
|
||||
value: 1800000
|
||||
},
|
||||
|
||||
bindSessionToIp: {
|
||||
env: "CLOUDBEAVER_BIND_SESSION_TO_IP",
|
||||
value: "disable"
|
||||
},
|
||||
|
||||
develMode: {
|
||||
env: "CLOUDBEAVER_DEVEL_MODE",
|
||||
value: false
|
||||
},
|
||||
|
||||
sm: {
|
||||
enableBruteForceProtection: {
|
||||
env: "CLOUDBEAVER_BRUTE_FORCE_PROTECTION_ENABLED",
|
||||
value: true
|
||||
},
|
||||
maxFailedLogin: {
|
||||
env: "CLOUDBEAVER_MAX_FAILED_LOGINS",
|
||||
value: 10
|
||||
},
|
||||
minimumLoginTimeout: {
|
||||
env: "CLOUDBEAVER_MINIMUM_LOGIN_TIMEOUT",
|
||||
value: 1
|
||||
},
|
||||
blockLoginPeriod: {
|
||||
env: "CLOUDBEAVER_BLOCK_PERIOD",
|
||||
value: 300
|
||||
},
|
||||
|
||||
passwordPolicy: {
|
||||
minLength: {
|
||||
env: "CLOUDBEAVER_POLICY_MIN_LENGTH",
|
||||
value: 8
|
||||
},
|
||||
requireMixedCase: {
|
||||
env: "CLOUDBEAVER_POLICY_REQUIRE_MIXED_CASE",
|
||||
value: true
|
||||
},
|
||||
minNumberCount: {
|
||||
env: "CLOUDBEAVER_POLICY_MIN_NUMBER_COUNT",
|
||||
value: 1
|
||||
},
|
||||
minSymbolCount: {
|
||||
env: "CLOUDBEAVER_POLICY_MIN_SYMBOL_COUNT",
|
||||
value: 0
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
database: {
|
||||
driver: {
|
||||
env: "CLOUDBEAVER_DB_DRIVER",
|
||||
value: "h2_embedded_v2"
|
||||
},
|
||||
url: {
|
||||
env: "CLOUDBEAVER_DB_URL",
|
||||
value: "jdbc:h2:${workspace}/.data/cb.h2v2.dat"
|
||||
},
|
||||
schema: {
|
||||
env: "CLOUDBEAVER_DB_SCHEMA",
|
||||
value: ""
|
||||
},
|
||||
user: {
|
||||
env: "CLOUDBEAVER_DB_USER",
|
||||
value: ""
|
||||
},
|
||||
password: {
|
||||
env: "CLOUDBEAVER_DB_PASSWORD",
|
||||
value: ""
|
||||
},
|
||||
initialDataConfiguration: {
|
||||
env: "CLOUDBEAVER_DB_INITIAL_DATA",
|
||||
value: "conf/initial-data.conf"
|
||||
},
|
||||
|
||||
pool: {
|
||||
minIdleConnections: {
|
||||
env: "CLOUDBEAVER_DB_MIN_IDLE_CONNECTIONS",
|
||||
value: 4
|
||||
},
|
||||
maxIdleConnections: {
|
||||
env: "CLOUDBEAVER_DB_MAX_IDLE_CONNECTIONS",
|
||||
value: 10
|
||||
},
|
||||
maxConnections: {
|
||||
env: "CLOUDBEAVER_DB_MAX_CONNECTIONS",
|
||||
value: 100
|
||||
},
|
||||
validationQuery: {
|
||||
env: "CLOUDBEAVER_DB_VALIDATION_QUERY",
|
||||
value: "SELECT 1"
|
||||
}
|
||||
},
|
||||
|
||||
backupEnabled: {
|
||||
env: "CLOUDBEAVER_DB_BACKUP_ENABLED",
|
||||
value: true
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
app: {
|
||||
anonymousAccessEnabled: {
|
||||
env: "CLOUDBEAVER_APP_ANONYMOUS_ACCESS_ENABLED",
|
||||
value: true
|
||||
},
|
||||
|
||||
anonymousUserRole: {
|
||||
env: "CLOUDBEAVER_APP_ANONYMOUS_USER_ROLE",
|
||||
value: "user"
|
||||
},
|
||||
|
||||
defaultUserTeam: {
|
||||
env: "CLOUDBEAVER_APP_DEFAULT_USER_TEAM",
|
||||
value: "user"
|
||||
},
|
||||
|
||||
grantConnectionsAccessToAnonymousTeam: {
|
||||
env: "CLOUDBEAVER_APP_GRANT_CONNECTIONS_ACCESS_TO_ANONYMOUS_TEAM",
|
||||
value: false
|
||||
},
|
||||
|
||||
supportsCustomConnections: {
|
||||
env: "CLOUDBEAVER_APP_SUPPORTS_CUSTOM_CONNECTIONS",
|
||||
value: false
|
||||
},
|
||||
|
||||
showReadOnlyConnectionInfo: {
|
||||
env: "CLOUDBEAVER_APP_READ_ONLY_CONNECTION_INFO",
|
||||
value: false
|
||||
},
|
||||
|
||||
systemVariablesResolvingEnabled: {
|
||||
env: "CLOUDBEAVER_SYSTEM_VARIABLES_RESOLVING_ENABLED",
|
||||
value: false
|
||||
},
|
||||
|
||||
forwardProxy: {
|
||||
env: "CLOUDBEAVER_APP_FORWARD_PROXY",
|
||||
value: false
|
||||
},
|
||||
|
||||
publicCredentialsSaveEnabled: {
|
||||
env: "CLOUDBEAVER_APP_PUBLIC_CREDENTIALS_SAVE_ENABLED",
|
||||
value: true
|
||||
},
|
||||
|
||||
adminCredentialsSaveEnabled: {
|
||||
env: "CLOUDBEAVER_APP_ADMIN_CREDENTIALS_SAVE_ENABLED",
|
||||
value: true
|
||||
},
|
||||
|
||||
resourceManagerEnabled: {
|
||||
env: "CLOUDBEAVER_APP_RESOURCE_MANAGER_ENABLED",
|
||||
value: true
|
||||
},
|
||||
|
||||
resourceQuotas: {
|
||||
resourceManagerFileSizeLimit: {
|
||||
env: "CLOUDBEAVER_RESOURCE_QUOTA_RESOURCE_MANAGER_FILE_SIZE_LIMIT",
|
||||
value: 500000
|
||||
},
|
||||
sqlMaxRunningQueries: {
|
||||
env: "CLOUDBEAVER_RESOURCE_QUOTA_SQL_MAX_RUNNING_QUERIES",
|
||||
value: 100
|
||||
},
|
||||
sqlResultSetRowsLimit: {
|
||||
env: "CLOUDBEAVER_RESOURCE_QUOTA_SQL_RESULT_SET_ROWS_LIMIT",
|
||||
value: 100000
|
||||
},
|
||||
sqlTextPreviewMaxLength: {
|
||||
env: "CLOUDBEAVER_RESOURCE_QUOTA_SQL_TEXT_PREVIEW_MAX_LENGTH",
|
||||
value: 4096
|
||||
},
|
||||
sqlBinaryPreviewMaxLength: {
|
||||
env: "CLOUDBEAVER_RESOURCE_QUOTA_SQL_BINARY_PREVIEW_MAX_LENGTH",
|
||||
value: 261120
|
||||
}
|
||||
},
|
||||
|
||||
enabledAuthProviders: ["local"],
|
||||
|
||||
disabledBetaFeatures: []
|
||||
}
|
||||
}
|
||||
@@ -18,7 +18,6 @@ cd ../..
|
||||
echo "Pull dbeaver platform"
|
||||
[ ! -d dbeaver ] && git clone --depth 1 https://github.com/dbeaver/dbeaver.git
|
||||
[ ! -d dbeaver-common ] && git clone --depth 1 https://github.com/dbeaver/dbeaver-common.git
|
||||
[ ! -d dbeaver-jdbc-libsql ] && git clone --depth 1 https://github.com/dbeaver/dbeaver-jdbc-libsql.git
|
||||
|
||||
|
||||
cd cloudbeaver/deploy
|
||||
@@ -32,6 +31,9 @@ if [[ "$?" -ne 0 ]] ; then
|
||||
fi
|
||||
cd ../../../deploy
|
||||
|
||||
echo "Generate cloudbeaver.conf file"
|
||||
mvn -f ../apps/config-generator compile exec:java -Dconfig.output="cloudbeaver/conf/cloudbeaver.conf"
|
||||
|
||||
echo "Copy server packages"
|
||||
|
||||
cp -rp ../server/product/web-server/target/products/io.cloudbeaver.product/all/all/all/* ./cloudbeaver/server
|
||||
|
||||
@@ -8,7 +8,15 @@ mkdir -p ./cloudbeaver/web
|
||||
cd ../../cloudbeaver/webapp
|
||||
|
||||
yarn install --immutable
|
||||
cd ./packages/product-default
|
||||
yarn clear
|
||||
|
||||
cd common-react
|
||||
yarn clear
|
||||
|
||||
cd ../common-typescript
|
||||
yarn clear
|
||||
|
||||
cd ../packages/product-default
|
||||
yarn run bundle
|
||||
|
||||
if [[ "$?" -ne 0 ]] ; then
|
||||
|
||||
+3
-1
@@ -20,7 +20,6 @@ echo Pull dbeaver platform
|
||||
|
||||
IF NOT EXIST dbeaver git clone https://github.com/dbeaver/dbeaver.git
|
||||
IF NOT EXIST dbeaver-common git clone https://github.com/dbeaver/dbeaver-common.git
|
||||
IF NOT EXIST dbeaver-jdbc-libsql git clone https://github.com/dbeaver/dbeaver-jdbc-libsql.git
|
||||
|
||||
cd cloudbeaver\deploy
|
||||
|
||||
@@ -43,6 +42,9 @@ copy ..\config\DefaultConfiguration\GlobalConfiguration\.dbeaver\data-sources.js
|
||||
|
||||
move drivers cloudbeaver >NUL
|
||||
|
||||
echo Generate cloudbeaver.conf file
|
||||
call mvn -f ..\apps\config-generator compile exec:java -Dconfig.output="cloudbeaver\conf\cloudbeaver.conf" || goto :error
|
||||
|
||||
echo "Build static content"
|
||||
|
||||
mkdir .\cloudbeaver\web
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
FROM ubuntu:24.04
|
||||
# Ubuntu 24.04 (Noble Numbat)
|
||||
FROM ubuntu:noble
|
||||
|
||||
MAINTAINER DBeaver Corp, devops@dbeaver.com
|
||||
|
||||
@@ -8,8 +9,6 @@ RUN set -eux; \
|
||||
apt-get update && \
|
||||
apt-get dist-upgrade -y --no-install-recommends && \
|
||||
apt-get install -y --no-install-recommends \
|
||||
# curl required for historical reasons, see https://github.com/adoptium/containers/issues/255
|
||||
curl \
|
||||
# java.lang.UnsatisfiedLinkError: libfontmanager.so: libfreetype.so.6: cannot open shared object file: No such file or directory
|
||||
# java.lang.NoClassDefFoundError: Could not initialize class sun.awt.X11FontManager
|
||||
# https://github.com/docker-library/openjdk/pull/235#issuecomment-424466077
|
||||
@@ -31,7 +30,7 @@ RUN set -eux; \
|
||||
ENV LANG=en_US.UTF-8
|
||||
|
||||
ENV JAVA_HOME=/opt/java/openjdk
|
||||
COPY --from=eclipse-temurin:21 $JAVA_HOME $JAVA_HOME
|
||||
COPY --from=eclipse-temurin:25-jdk-noble $JAVA_HOME $JAVA_HOME
|
||||
ENV PATH="${JAVA_HOME}/bin:${PATH}"
|
||||
|
||||
RUN set -eux; \
|
||||
|
||||
@@ -13,7 +13,7 @@ if [ "$(id -u)" -eq 0 ]; then
|
||||
|
||||
chown -R $DBEAVER_UID:$DBEAVER_GID $PWD/workspace
|
||||
# Execute run-cloudbeaver-server.sh as the dbeaver user with the JAVA_HOME and PATH environment variables
|
||||
exec su "$TARGET_USER" -c "JAVA_HOME=$JAVA_HOME PATH=$PATH ./run-cloudbeaver-server.sh"
|
||||
exec su "$TARGET_USER" -c "JAVA_HOME=$JAVA_HOME PATH=$PATH ./run-cloudbeaver-server.sh $*"
|
||||
else
|
||||
exec ./run-cloudbeaver-server.sh
|
||||
fi
|
||||
exec ./run-cloudbeaver-server.sh "$@"
|
||||
fi
|
||||
|
||||
@@ -17,6 +17,7 @@ If Not Defined JAVA_OPTS (
|
||||
|
||||
java %JAVA_OPTS% ^
|
||||
-Dfile.encoding=UTF-8 ^
|
||||
--enable-native-access=ALL-UNNAMED ^
|
||||
--add-modules=ALL-DEFAULT ^
|
||||
--add-opens=java.base/java.io=ALL-UNNAMED ^
|
||||
--add-opens=java.base/java.lang=ALL-UNNAMED ^
|
||||
@@ -33,7 +34,6 @@ java %JAVA_OPTS% ^
|
||||
--add-opens=java.base/jdk.internal.misc=ALL-UNNAMED ^
|
||||
--add-opens=java.base/sun.nio.ch=ALL-UNNAMED ^
|
||||
--add-opens=java.base/sun.security.ssl=ALL-UNNAMED ^
|
||||
--add-opens=java.base/sun.security.action=ALL-UNNAMED ^
|
||||
--add-opens=java.base/sun.security.util=ALL-UNNAMED ^
|
||||
--add-opens=java.security.jgss/sun.security.jgss=ALL-UNNAMED ^
|
||||
--add-opens=java.security.jgss/sun.security.krb5=ALL-UNNAMED ^
|
||||
|
||||
@@ -11,6 +11,7 @@ echo "Starting Cloudbeaver Server"
|
||||
|
||||
exec java ${JAVA_OPTS} \
|
||||
-Dfile.encoding=UTF-8 \
|
||||
--enable-native-access=ALL-UNNAMED \
|
||||
--add-modules=ALL-DEFAULT \
|
||||
--add-opens=java.base/java.io=ALL-UNNAMED \
|
||||
--add-opens=java.base/java.lang=ALL-UNNAMED \
|
||||
@@ -27,7 +28,6 @@ exec java ${JAVA_OPTS} \
|
||||
--add-opens=java.base/jdk.internal.misc=ALL-UNNAMED \
|
||||
--add-opens=java.base/sun.nio.ch=ALL-UNNAMED \
|
||||
--add-opens=java.base/sun.security.ssl=ALL-UNNAMED \
|
||||
--add-opens=java.base/sun.security.action=ALL-UNNAMED \
|
||||
--add-opens=java.base/sun.security.util=ALL-UNNAMED \
|
||||
--add-opens=java.security.jgss/sun.security.jgss=ALL-UNNAMED \
|
||||
--add-opens=java.security.jgss/sun.security.krb5=ALL-UNNAMED \
|
||||
@@ -35,7 +35,7 @@ exec java ${JAVA_OPTS} \
|
||||
--add-opens=java.sql/java.sql=ALL-UNNAMED \
|
||||
-jar ${launcherJar} \
|
||||
-product io.cloudbeaver.product.ce.product \
|
||||
-web-config conf/cloudbeaver.conf \
|
||||
"$@" \
|
||||
-web-config conf/cloudbeaver.conf \
|
||||
-nl en \
|
||||
-registryMultiLanguage
|
||||
|
||||
@@ -1,3 +1,2 @@
|
||||
dbeaver-common
|
||||
dbeaver-jdbc-libsql
|
||||
dbeaver
|
||||
@@ -3,8 +3,8 @@ Bundle-ManifestVersion: 2
|
||||
Bundle-Vendor: DBeaver Corp
|
||||
Bundle-Name: Cloudbeaver Web Model
|
||||
Bundle-SymbolicName: io.cloudbeaver.model.cli;singleton:=true
|
||||
Bundle-Version: 1.0.5.qualifier
|
||||
Bundle-Release-Date: 20260302
|
||||
Bundle-Version: 1.0.18.qualifier
|
||||
Bundle-Release-Date: 20260921
|
||||
Bundle-RequiredExecutionEnvironment: JavaSE-21
|
||||
Bundle-ActivationPolicy: lazy
|
||||
Require-Bundle: org.jkiss.dbeaver.model.cli;visibility:=reexport
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
<relativePath>../</relativePath>
|
||||
</parent>
|
||||
<artifactId>io.cloudbeaver.model.cli</artifactId>
|
||||
<version>1.0.5-SNAPSHOT</version>
|
||||
<version>1.0.18-SNAPSHOT</version>
|
||||
<packaging>eclipse-plugin</packaging>
|
||||
|
||||
</project>
|
||||
|
||||
+11
-13
@@ -18,27 +18,27 @@ package io.cloudbeaver.model.cli;
|
||||
|
||||
import org.jkiss.code.NotNull;
|
||||
import org.jkiss.code.Nullable;
|
||||
import org.jkiss.dbeaver.model.cli.*;
|
||||
import org.jkiss.dbeaver.model.cli.model.CommandLineAuthenticator;
|
||||
import org.jkiss.dbeaver.model.cli.ApplicationCommandLine;
|
||||
import org.jkiss.dbeaver.model.cli.ApplicationInstanceController;
|
||||
import org.jkiss.dbeaver.model.cli.CLIContextImpl;
|
||||
import org.jkiss.dbeaver.model.cli.CLIRunMeta;
|
||||
import org.jkiss.dbeaver.model.cli.registry.CLICommandDescriptor;
|
||||
import picocli.CommandLine;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class CloudBeaverCommandLine extends ApplicationCommandLine<ApplicationInstanceController> {
|
||||
@Nullable
|
||||
private final CommandLineAuthenticator authenticator;
|
||||
private final CloudBeaverMixin mixin;
|
||||
|
||||
public CloudBeaverCommandLine(@Nullable CommandLineAuthenticator authenticator) {
|
||||
public CloudBeaverCommandLine() {
|
||||
super();
|
||||
this.authenticator = authenticator;
|
||||
this.mixin = new CloudBeaverMixin();
|
||||
}
|
||||
|
||||
public CloudBeaverCommandLine(
|
||||
@Nullable CommandLineAuthenticator authenticator,
|
||||
@NotNull CloudBeaverMixin mixin
|
||||
) {
|
||||
super();
|
||||
this.authenticator = authenticator;
|
||||
this.mixin = mixin;
|
||||
}
|
||||
|
||||
@@ -48,9 +48,6 @@ public class CloudBeaverCommandLine extends ApplicationCommandLine<ApplicationIn
|
||||
@NotNull CLIContextImpl context,
|
||||
@NotNull CLIRunMeta runMeta
|
||||
) {
|
||||
if (authenticator != null) {
|
||||
context.setContextParameter(CLIConstants.CONTEXT_PARAM_AUTHENTICATOR, authenticator);
|
||||
}
|
||||
return new CloudBeaverTopLevelCommand(applicationInstanceController, context, runMeta);
|
||||
}
|
||||
|
||||
@@ -59,9 +56,10 @@ public class CloudBeaverCommandLine extends ApplicationCommandLine<ApplicationIn
|
||||
protected CommandLine initCommandLine(
|
||||
@Nullable ApplicationInstanceController applicationInstanceController,
|
||||
@NotNull CLIContextImpl context,
|
||||
@NotNull CLIRunMeta runMeta
|
||||
@NotNull CLIRunMeta runMeta,
|
||||
@NotNull List<CLICommandDescriptor> commandsToExecute
|
||||
) {
|
||||
var cmd = super.initCommandLine(applicationInstanceController, context, runMeta);
|
||||
var cmd = super.initCommandLine(applicationInstanceController, context, runMeta, commandsToExecute);
|
||||
cmd.addMixin("cloudbeaver", mixin);
|
||||
return cmd;
|
||||
}
|
||||
|
||||
+1
-1
@@ -29,7 +29,7 @@ public class CloudBeaverInstanceServer extends ApplicationInstanceServer<Applica
|
||||
|
||||
public CloudBeaverInstanceServer() throws IOException {
|
||||
super(ApplicationInstanceController.class);
|
||||
this.commandLine = new CloudBeaverCommandLine(null);
|
||||
this.commandLine = new CloudBeaverCommandLine();
|
||||
}
|
||||
|
||||
public CloudBeaverInstanceServer(@NotNull CloudBeaverCommandLine commandLine) throws IOException {
|
||||
|
||||
+3
-1
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* DBeaver - Universal Database Manager
|
||||
* Copyright (C) 2010-2025 DBeaver Corp and others
|
||||
* Copyright (C) 2010-2026 DBeaver Corp and others
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -16,12 +16,14 @@
|
||||
*/
|
||||
package io.cloudbeaver.model.cli;
|
||||
|
||||
import org.jkiss.dbeaver.model.cli.model.NonExecutableOption;
|
||||
import picocli.CommandLine;
|
||||
|
||||
/**
|
||||
* CloudBeaver specific mixin options
|
||||
*/
|
||||
public class CloudBeaverMixin {
|
||||
@NonExecutableOption
|
||||
@CommandLine.Option(
|
||||
names = {"-web-config"},
|
||||
arity = "1",
|
||||
|
||||
+2
@@ -22,10 +22,12 @@ import org.jkiss.dbeaver.model.cli.ApplicationInstanceController;
|
||||
import org.jkiss.dbeaver.model.cli.CLIContextImpl;
|
||||
import org.jkiss.dbeaver.model.cli.CLIRunMeta;
|
||||
import org.jkiss.dbeaver.model.cli.command.AbstractTopLevelCommand;
|
||||
import org.jkiss.dbeaver.model.cli.model.NonExecutableOption;
|
||||
import picocli.CommandLine;
|
||||
|
||||
@CommandLine.Command(name = "cbvr", description = "CloudBeaver commands")
|
||||
public class CloudBeaverTopLevelCommand extends AbstractTopLevelCommand {
|
||||
@NonExecutableOption
|
||||
@CommandLine.Option(
|
||||
names = {NOSPASH_OPTION},
|
||||
hidden = true,
|
||||
|
||||
@@ -3,8 +3,8 @@ Bundle-ManifestVersion: 2
|
||||
Bundle-Vendor: DBeaver Corp
|
||||
Bundle-Name: Cloudbeaver Web Model
|
||||
Bundle-SymbolicName: io.cloudbeaver.model;singleton:=true
|
||||
Bundle-Version: 1.0.96.qualifier
|
||||
Bundle-Release-Date: 20260302
|
||||
Bundle-Version: 1.0.109.qualifier
|
||||
Bundle-Release-Date: 20260921
|
||||
Bundle-RequiredExecutionEnvironment: JavaSE-21
|
||||
Bundle-ActivationPolicy: lazy
|
||||
Require-Bundle: org.jkiss.dbeaver.data.gis;visibility:=reexport,
|
||||
@@ -17,7 +17,6 @@ Require-Bundle: org.jkiss.dbeaver.data.gis;visibility:=reexport,
|
||||
org.jkiss.bundle.logback;visibility:=reexport,
|
||||
org.jkiss.bundle.graphql;visibility:=reexport,
|
||||
org.jkiss.bundle.apache.dbcp,
|
||||
com.google.gson;visibility:=reexport,
|
||||
jakarta.servlet-api;bundle-version:="6.0.0";visibility:=reexport
|
||||
Export-Package: io.cloudbeaver,
|
||||
io.cloudbeaver.auth,
|
||||
@@ -29,6 +28,11 @@ Export-Package: io.cloudbeaver,
|
||||
io.cloudbeaver.websocket.event.client,
|
||||
io.cloudbeaver.websocket.event.task,
|
||||
io.cloudbeaver.model,
|
||||
io.cloudbeaver.model.api.annotation,
|
||||
io.cloudbeaver.model.api.annotation.enums,
|
||||
io.cloudbeaver.model.api.annotation.media,
|
||||
io.cloudbeaver.model.api.annotation.responses,
|
||||
io.cloudbeaver.model.api.annotation.tags,
|
||||
io.cloudbeaver.model.app,
|
||||
io.cloudbeaver.model.config,
|
||||
io.cloudbeaver.model.apilog,
|
||||
|
||||
@@ -13,7 +13,7 @@ meta.io.cloudbeaver.model.app.ServletSystemInformationCollector.smDbProductName.
|
||||
meta.io.cloudbeaver.model.app.ServletSystemInformationCollector.smDbProductVersion.name=Security manager database product version
|
||||
|
||||
meta.io.cloudbeaver.model.WebExpertSettingsProperties.readOnly.name = Read-only connection
|
||||
meta.io.cloudbeaver.model.WebExpertSettingsProperties.autoCommit.name = Auto-commit
|
||||
meta.io.cloudbeaver.model.WebExpertSettingsProperties.autoCommitMode.name = Auto-commit mode
|
||||
meta.io.cloudbeaver.model.WebExpertSettingsProperties.keepAliveInterval.name = Keep-alive interval (seconds)
|
||||
meta.io.cloudbeaver.model.WebExpertSettingsProperties.defaultSchema.name = Default schema
|
||||
meta.io.cloudbeaver.model.WebExpertSettingsProperties.defaultCatalog.name = Default catalog
|
||||
@@ -1,5 +1,5 @@
|
||||
meta.io.cloudbeaver.model.WebExpertSettingsProperties.readOnly.name = Только для чтения
|
||||
meta.io.cloudbeaver.model.WebExpertSettingsProperties.autoCommit.name = Авто-коммит
|
||||
meta.io.cloudbeaver.model.WebExpertSettingsProperties.autoCommitMode.name = Авто-коммит
|
||||
meta.io.cloudbeaver.model.WebExpertSettingsProperties.keepAliveInterval.name = Интервал Keep-alive (в секундах)
|
||||
meta.io.cloudbeaver.model.WebExpertSettingsProperties.defaultSchema.name = Схема по умолчанию
|
||||
meta.io.cloudbeaver.model.WebExpertSettingsProperties.defaultCatalog.name = Каталог по умолчанию
|
||||
@@ -1,4 +1,4 @@
|
||||
meta.io.cloudbeaver.model.WebExpertSettingsProperties.readOnly.name = Kết nối chỉ đọc
|
||||
meta.io.cloudbeaver.model.WebExpertSettingsProperties.autoCommit.name = Tự động commit
|
||||
meta.io.cloudbeaver.model.WebExpertSettingsProperties.autoCommitMode.name = Tự động commit
|
||||
meta.io.cloudbeaver.model.WebExpertSettingsProperties.keepAliveInterval.name = Giữ kết nối (tính bằng giây)
|
||||
meta.io.cloudbeaver.model.WebExpertSettingsProperties.keepAliveInterval.description = Không tự động ngắt kết nối
|
||||
@@ -1,3 +1,3 @@
|
||||
meta.io.cloudbeaver.model.WebExpertSettingsProperties.autoCommit.name = 自动提交
|
||||
meta.io.cloudbeaver.model.WebExpertSettingsProperties.autoCommitMode.name = 自动提交
|
||||
meta.io.cloudbeaver.model.WebExpertSettingsProperties.keepAliveInterval.name = 保持连接 (秒)
|
||||
meta.io.cloudbeaver.model.WebExpertSettingsProperties.keepAliveInterval.description = 无自动断开连接
|
||||
@@ -25,6 +25,11 @@
|
||||
label="Default secret controller" description="Default void secret controller"/>
|
||||
</extension>
|
||||
|
||||
<extension point="io.cloudbeaver.feature">
|
||||
<feature id="ssh" label="SSH tunnels for databases" description="Enables SSH tunnels for secure database connections, allowing access to remote databases through encrypted channels" enabledByDefault="true"/>
|
||||
</extension>
|
||||
|
||||
|
||||
<extension point="org.jkiss.dbeaver.ws.event">
|
||||
<event topicId="cb_session_task" id="cb_session_task_info_updated"
|
||||
class="io.cloudbeaver.websocket.event.task.WSSessionTaskInfoEvent"/>
|
||||
@@ -46,6 +51,8 @@
|
||||
class="io.cloudbeaver.websocket.event.client.WSSessionTaskConfirmationEvent"/>
|
||||
<clientEvent id="cb_client_session_task_with_parameters_confirmation"
|
||||
class="io.cloudbeaver.websocket.event.client.WSSessionTaskWithParametersConfirmationEvent"/>
|
||||
<clientEvent id="cb_client_cancel_action"
|
||||
class="io.cloudbeaver.websocket.event.client.WSSessionCancelActionClientEvent"/>
|
||||
</extension>
|
||||
|
||||
</plugin>
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
<relativePath>../</relativePath>
|
||||
</parent>
|
||||
<artifactId>io.cloudbeaver.model</artifactId>
|
||||
<version>1.0.96-SNAPSHOT</version>
|
||||
<version>1.0.109-SNAPSHOT</version>
|
||||
<packaging>eclipse-plugin</packaging>
|
||||
|
||||
</project>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* DBeaver - Universal Database Manager
|
||||
* Copyright (C) 2010-2024 DBeaver Corp and others
|
||||
* Copyright (C) 2010-2026 DBeaver Corp and others
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -20,6 +20,7 @@ import graphql.ErrorClassification;
|
||||
import graphql.ErrorType;
|
||||
import graphql.GraphQLError;
|
||||
import graphql.language.SourceLocation;
|
||||
import org.jkiss.dbeaver.DBDatabaseException;
|
||||
import org.jkiss.dbeaver.DBException;
|
||||
import org.jkiss.dbeaver.model.sql.SQLState;
|
||||
import org.jkiss.utils.CommonUtils;
|
||||
@@ -146,19 +147,22 @@ public class DBWebException extends DBException implements GraphQLError {
|
||||
} else {
|
||||
return message;
|
||||
}
|
||||
} else if (cause.getMessage() == null && cause.getCause() != null) {
|
||||
return message + ":\n" + cause.getCause().getClass().getSimpleName();
|
||||
}
|
||||
// Preserve enriched web/database messages, but unwrap generic exceptions to their most specific cause.
|
||||
Throwable messageCause = cause instanceof DBWebException || cause instanceof DBDatabaseException
|
||||
? cause
|
||||
: CommonUtils.getRootCause(cause);
|
||||
String causeMessage = messageCause.getMessage();
|
||||
if (CommonUtils.isEmpty(causeMessage)) {
|
||||
causeMessage = messageCause.getClass().getSimpleName();
|
||||
}
|
||||
if (CommonUtils.isEmpty(message)) {
|
||||
if (cause.getMessage() != null) {
|
||||
return cause.getMessage();
|
||||
}
|
||||
return cause.getClass().getName();
|
||||
return causeMessage;
|
||||
}
|
||||
if (CommonUtils.equalObjects(message, cause.getMessage())) {
|
||||
if (CommonUtils.equalObjects(message, causeMessage)) {
|
||||
return message;
|
||||
}
|
||||
return message + ":\n" + cause.getMessage();
|
||||
return message + ":\n" + causeMessage;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+6
-4
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* DBeaver - Universal Database Manager
|
||||
* Copyright (C) 2010-2024 DBeaver Corp and others
|
||||
* Copyright (C) 2010-2026 DBeaver Corp and others
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -27,6 +27,7 @@ import org.jkiss.dbeaver.model.app.DBPDataSourceRegistry;
|
||||
import org.jkiss.dbeaver.model.connection.DBPConnectionConfiguration;
|
||||
import org.jkiss.dbeaver.model.connection.DBPDriver;
|
||||
import org.jkiss.dbeaver.registry.DataSourceDescriptor;
|
||||
import org.jkiss.dbeaver.registry.DataSourcePreferenceStore;
|
||||
import org.jkiss.utils.CommonUtils;
|
||||
|
||||
public class WebConnectionConfigInputHandler<T extends WebConnectionConfig, C extends DataSourceDescriptor> {
|
||||
@@ -76,9 +77,7 @@ public class WebConnectionConfigInputHandler<T extends WebConnectionConfig, C ex
|
||||
}
|
||||
|
||||
dataSource.setFolder(input.getFolder() != null ? registry.getFolder(input.getFolder()) : null);
|
||||
if (input.isDefaultAutoCommit() != null) {
|
||||
dataSource.getConnectionConfiguration().getBootstrap().setDefaultAutoCommit(input.isDefaultAutoCommit());
|
||||
}
|
||||
dataSource.getConnectionConfiguration().getBootstrap().setDefaultAutoCommit(input.isDefaultAutoCommit());
|
||||
WebDataSourceUtils.setConnectionConfiguration(
|
||||
dataSource.getDriver(),
|
||||
dataSource.getConnectionConfiguration(),
|
||||
@@ -93,6 +92,9 @@ public class WebConnectionConfigInputHandler<T extends WebConnectionConfig, C ex
|
||||
input.isSharedCredentials()
|
||||
);
|
||||
dataSource.setConnectionReadOnly(input.isReadOnly());
|
||||
DataSourcePreferenceStore preferenceStore = dataSource.getPreferenceStore();
|
||||
preferenceStore.clear();
|
||||
preferenceStore.setProperties(input.getDefaultUserPreferences());
|
||||
}
|
||||
|
||||
@NotNull
|
||||
|
||||
+3
-2
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* DBeaver - Universal Database Manager
|
||||
* Copyright (C) 2010-2025 DBeaver Corp and others
|
||||
* Copyright (C) 2010-2026 DBeaver Corp and others
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -17,6 +17,7 @@
|
||||
package io.cloudbeaver;
|
||||
|
||||
import io.cloudbeaver.model.session.WebSession;
|
||||
import io.cloudbeaver.utils.WebEventUtils;
|
||||
import org.jkiss.code.NotNull;
|
||||
import org.jkiss.dbeaver.model.DBPDataSourceContainer;
|
||||
import org.jkiss.dbeaver.model.DBPEvent;
|
||||
@@ -46,7 +47,7 @@ public class WebDataSourceConnectEventListener implements DBPEventListener {
|
||||
new WSDataSourceConnectEvent(
|
||||
container.getProject().getId(),
|
||||
container.getId(),
|
||||
webSession.getSessionId(),
|
||||
WebEventUtils.getSmSessionId(webSession),
|
||||
webSession.getUserId()
|
||||
)
|
||||
);
|
||||
|
||||
+12
-20
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* DBeaver - Universal Database Manager
|
||||
* Copyright (C) 2010-2025 DBeaver Corp and others
|
||||
* Copyright (C) 2010-2026 DBeaver Corp and others
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -30,6 +30,7 @@ import org.jkiss.dbeaver.model.app.DBPProject;
|
||||
import org.jkiss.dbeaver.model.connection.DBPConnectionConfiguration;
|
||||
import org.jkiss.dbeaver.model.connection.DBPDriver;
|
||||
import org.jkiss.dbeaver.model.net.DBWNetworkProfile;
|
||||
import org.jkiss.dbeaver.model.net.DBWNetworkProfileManager;
|
||||
import org.jkiss.dbeaver.model.preferences.DBPPreferenceStore;
|
||||
import org.jkiss.dbeaver.model.runtime.DBRProgressMonitor;
|
||||
import org.jkiss.dbeaver.model.secret.DBSSecretController;
|
||||
@@ -258,28 +259,12 @@ public class WebGlobalProjectRegistryProxy implements DBPDataSourceRegistry, Dat
|
||||
dataSourceRegistry.removeSavedFilter(filterName);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public DBWNetworkProfile getNetworkProfile(@Nullable String source, @NotNull String name) {
|
||||
return dataSourceRegistry.getNetworkProfile(source, name);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public List<DBWNetworkProfile> getNetworkProfiles() {
|
||||
public DBWNetworkProfileManager getNetworkProfiles() {
|
||||
return dataSourceRegistry.getNetworkProfiles();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateNetworkProfile(@NotNull DBWNetworkProfile profile) {
|
||||
dataSourceRegistry.updateNetworkProfile(profile);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeNetworkProfile(@NotNull DBWNetworkProfile profile) {
|
||||
dataSourceRegistry.removeNetworkProfile(profile);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public DBAAuthProfile getAuthProfile(@NotNull String id) {
|
||||
@@ -377,6 +362,12 @@ public class WebGlobalProjectRegistryProxy implements DBPDataSourceRegistry, Dat
|
||||
return dataSourceRegistry.getPreferenceStore();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initializeDataSources() {
|
||||
dataSourceRegistry.initializeDataSources();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public DataSourceParseResults loadDataSources(
|
||||
@NotNull List<DBPDataSourceConfigurationStorage> storages,
|
||||
@@ -392,6 +383,7 @@ public class WebGlobalProjectRegistryProxy implements DBPDataSourceRegistry, Dat
|
||||
dataSourceRegistry.saveDataSources();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public DataSourceConfigurationManager getConfigurationManager() {
|
||||
return dataSourceRegistry.getConfigurationManager();
|
||||
@@ -407,12 +399,12 @@ public class WebGlobalProjectRegistryProxy implements DBPDataSourceRegistry, Dat
|
||||
}
|
||||
|
||||
@Override
|
||||
public void persistSecrets(DBSSecretController secretController) throws DBException {
|
||||
public void persistSecrets(@NotNull DBSSecretController secretController) throws DBException {
|
||||
dataSourceRegistry.persistSecrets(secretController);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resolveSecrets(DBSSecretController secretController) throws DBException {
|
||||
public void resolveSecrets(@NotNull DBSSecretController secretController) throws DBException {
|
||||
dataSourceRegistry.resolveSecrets(secretController);
|
||||
}
|
||||
|
||||
|
||||
+2
-2
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* DBeaver - Universal Database Manager
|
||||
* Copyright (C) 2010-2024 DBeaver Corp and others
|
||||
* Copyright (C) 2010-2026 DBeaver Corp and others
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -23,8 +23,8 @@ import org.jkiss.dbeaver.Log;
|
||||
import org.jkiss.dbeaver.model.DBPDataSourceContainer;
|
||||
import org.jkiss.dbeaver.model.DBPEvent;
|
||||
import org.jkiss.dbeaver.model.app.DBPDataSourceRegistry;
|
||||
import org.jkiss.dbeaver.model.auth.SMObjectType;
|
||||
import org.jkiss.dbeaver.model.rm.RMProject;
|
||||
import org.jkiss.dbeaver.model.security.SMObjectType;
|
||||
import org.jkiss.dbeaver.model.security.user.SMObjectPermissions;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* DBeaver - Universal Database Manager
|
||||
* Copyright (C) 2010-2025 DBeaver Corp and others
|
||||
* Copyright (C) 2010-2026 DBeaver Corp and others
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -29,11 +29,11 @@ import org.jkiss.dbeaver.model.DBPDataSourceContainer;
|
||||
import org.jkiss.dbeaver.model.DBPObjectSettingsProvider;
|
||||
import org.jkiss.dbeaver.model.app.DBPDataSourceRegistry;
|
||||
import org.jkiss.dbeaver.model.app.DBPDataSourceRegistryCache;
|
||||
import org.jkiss.dbeaver.model.auth.SMObjectType;
|
||||
import org.jkiss.dbeaver.model.navigator.DBNModel;
|
||||
import org.jkiss.dbeaver.model.rm.RMProject;
|
||||
import org.jkiss.dbeaver.model.rm.RMUtils;
|
||||
import org.jkiss.dbeaver.model.security.SMControllerUtils;
|
||||
import org.jkiss.dbeaver.model.security.SMObjectType;
|
||||
import org.jkiss.dbeaver.model.websocket.event.datasource.WSDataSourceEvent;
|
||||
import org.jkiss.dbeaver.model.websocket.event.datasource.WSDataSourceProperty;
|
||||
import org.jkiss.dbeaver.registry.DataSourceDescriptor;
|
||||
@@ -103,7 +103,7 @@ public class WebSessionProjectImpl extends WebProjectImpl implements DBPAdaptabl
|
||||
return projectSettings;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@NotNull
|
||||
@Override
|
||||
public DBNModel getNavigatorModel() {
|
||||
return webSession.getNavigatorModel();
|
||||
@@ -135,8 +135,11 @@ public class WebSessionProjectImpl extends WebProjectImpl implements DBPAdaptabl
|
||||
if (registryIsLoaded) {
|
||||
return;
|
||||
}
|
||||
getDataSourceRegistry().getDataSources().forEach(this::addConnection);
|
||||
Throwable lastError = getDataSourceRegistry().getLastError();
|
||||
DBPDataSourceRegistry dataSourceRegistry = getDataSourceRegistry();
|
||||
// Connection node paths are resolved through the navigator model, so materialize its lazy database branch first.
|
||||
webSession.initializeProjectNavigator(this);
|
||||
dataSourceRegistry.getDataSources().forEach(this::addConnection);
|
||||
Throwable lastError = dataSourceRegistry.getLastError();
|
||||
if (lastError != null) {
|
||||
webSession.addSessionError(lastError);
|
||||
log.error("Error refreshing connections from project '" + getId() + "'", lastError);
|
||||
@@ -237,6 +240,18 @@ public class WebSessionProjectImpl extends WebProjectImpl implements DBPAdaptabl
|
||||
public synchronized boolean updateProjectDataSources(@NotNull WSDataSourceEvent event) {
|
||||
var sendDataSourceUpdatedEvent = false;
|
||||
DBPDataSourceRegistry registry = getDataSourceRegistry();
|
||||
Map<String, DataSourceDescriptor> dataSourceSnapshots = new HashMap<>();
|
||||
if (WSDataSourceEvent.UPDATED.equals(event.getId())) {
|
||||
for (String dsId : event.getDataSourceIds()) {
|
||||
DBPDataSourceContainer dataSource = registry.getDataSource(dsId);
|
||||
if (dataSource instanceof DataSourceDescriptor descriptor) {
|
||||
DBPDataSourceContainer snapshot = registry.createDataSource(descriptor);
|
||||
if (snapshot instanceof DataSourceDescriptor snapshotDescriptor) {
|
||||
dataSourceSnapshots.put(dsId, snapshotDescriptor);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (WSDataSourceEvent.CREATED.equals(event.getId()) || WSDataSourceEvent.UPDATED.equals(event.getId())) {
|
||||
registry.refreshConfig(event.getDataSourceIds());
|
||||
}
|
||||
@@ -251,15 +266,19 @@ public class WebSessionProjectImpl extends WebProjectImpl implements DBPAdaptabl
|
||||
sendDataSourceUpdatedEvent = true;
|
||||
}
|
||||
case WSDataSourceEvent.UPDATED -> {
|
||||
DataSourceDescriptor snapshot = dataSourceSnapshots.get(dsId);
|
||||
if (snapshot != null && !event.getProperty().hasEffectiveChanges(snapshot, ds)) {
|
||||
continue;
|
||||
}
|
||||
if (event.getProperty() == WSDataSourceProperty.CONFIGURATION) {
|
||||
WebDataSourceUtils.disconnectDataSource(webSession, ds);
|
||||
WebDataSourceUtils.disconnectDataSource(webSession, ds, true);
|
||||
}
|
||||
if (event.getProperty() != WSDataSourceProperty.INTERNAL) {
|
||||
sendDataSourceUpdatedEvent = true;
|
||||
}
|
||||
}
|
||||
case WSDataSourceEvent.DELETED -> {
|
||||
WebDataSourceUtils.disconnectDataSource(webSession, ds);
|
||||
WebDataSourceUtils.disconnectDataSource(webSession, ds, false);
|
||||
if (registry instanceof DBPDataSourceRegistryCache dsrc) {
|
||||
dsrc.removeDataSourceFromList(ds);
|
||||
}
|
||||
@@ -345,7 +364,7 @@ public class WebSessionProjectImpl extends WebProjectImpl implements DBPAdaptabl
|
||||
@NotNull
|
||||
private WebConnectionInfo closeAndDeleteConnection(@NotNull WebConnectionInfo connectionInfo) throws DBWebException {
|
||||
DBPDataSourceContainer dataSourceContainer = connectionInfo.getDataSourceContainer();
|
||||
WebDataSourceUtils.disconnectDataSource(webSession, dataSourceContainer);
|
||||
WebDataSourceUtils.disconnectDataSource(webSession, dataSourceContainer, false);
|
||||
DBPDataSourceRegistry registry = getDataSourceRegistry();
|
||||
registry.removeDataSource(dataSourceContainer);
|
||||
removeConnection(dataSourceContainer);
|
||||
@@ -357,6 +376,13 @@ public class WebSessionProjectImpl extends WebProjectImpl implements DBPAdaptabl
|
||||
return getInputConfigHandler(configInput).createDataSourceContainer();
|
||||
}
|
||||
|
||||
public void updateDataSourceContainerFromInput(
|
||||
@NotNull WebConnectionConfig configInput,
|
||||
@NotNull DataSourceDescriptor dataSource
|
||||
) throws DBWebException {
|
||||
getInputConfigHandler(configInput).updateDataSource(dataSource);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public WebConnectionConfig getConnectionConfigInput(@Nullable Map<String, Object> configMap) {
|
||||
return new WebConnectionConfig(configMap == null ? Map.of() : configMap);
|
||||
|
||||
+7
@@ -69,6 +69,13 @@ public interface SMAuthProviderExternal<AUTH_SESSION extends SMSession> extends
|
||||
@NotNull WebUser user,
|
||||
boolean selfIdentity) throws DBException;
|
||||
|
||||
/**
|
||||
* Whether a user that is not found should be provisioned (auto-created) on external login.
|
||||
*/
|
||||
default boolean isAutoUserProvisioningEnabled(@Nullable SMAuthProviderCustomConfiguration providerConfig) {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Make some post authentication actions
|
||||
*/
|
||||
|
||||
@@ -0,0 +1,62 @@
|
||||
/*
|
||||
* DBeaver - Universal Database Manager
|
||||
* Copyright (C) 2010-2026 DBeaver Corp and others
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package io.cloudbeaver.model;
|
||||
|
||||
import org.jkiss.code.NotNull;
|
||||
import org.jkiss.code.Nullable;
|
||||
import org.jkiss.utils.CommonUtils;
|
||||
|
||||
public enum AutoCommitMode {
|
||||
DEFAULT("Default", null),
|
||||
AUTO_COMMIT("Auto commit", true),
|
||||
MANUAL_COMMIT("Manual commit", false);
|
||||
|
||||
private final String title;
|
||||
private final Boolean value;
|
||||
|
||||
AutoCommitMode(@NotNull String title, @Nullable Boolean value) {
|
||||
this.title = title;
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public String toString() {
|
||||
return title;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public Boolean getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static AutoCommitMode fromValue(@Nullable Boolean value) {
|
||||
if (value == null) {
|
||||
return DEFAULT;
|
||||
}
|
||||
return value ? AUTO_COMMIT : MANUAL_COMMIT;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static Boolean from(@Nullable String mode) {
|
||||
if (CommonUtils.isEmpty(mode)) {
|
||||
return null;
|
||||
}
|
||||
return valueOf(mode).getValue();
|
||||
}
|
||||
}
|
||||
+34
-3
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* DBeaver - Universal Database Manager
|
||||
* Copyright (C) 2010-2024 DBeaver Corp and others
|
||||
* Copyright (C) 2010-2026 DBeaver Corp and others
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -23,6 +23,7 @@ import org.jkiss.dbeaver.model.data.json.JSONUtils;
|
||||
import org.jkiss.dbeaver.model.meta.Property;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@@ -64,6 +65,9 @@ public class WebConnectionConfig {
|
||||
private Boolean defaultAutoCommit;
|
||||
private String defaultCatalogName;
|
||||
private String defaultSchemaName;
|
||||
private String connectionType;
|
||||
@NotNull
|
||||
private Map<String, String> defaultUserPreferences = new LinkedHashMap<>();
|
||||
|
||||
public WebConnectionConfig() {
|
||||
}
|
||||
@@ -99,8 +103,14 @@ public class WebConnectionConfig {
|
||||
expertSettingsValues != null ? expertSettingsValues : params, WebExpertSettingsProperties.PROP_KEEP_ALIVE_INTERVAL, -1);
|
||||
readOnly = JSONUtils.getBoolean(
|
||||
expertSettingsValues != null ? expertSettingsValues : params, WebExpertSettingsProperties.PROP_READ_ONLY);
|
||||
defaultAutoCommit = JSONUtils.getBoolean(
|
||||
expertSettingsValues != null ? expertSettingsValues : params, WebExpertSettingsProperties.PROP_AUTO_COMMIT, true);
|
||||
|
||||
if (expertSettingsValues != null) {
|
||||
defaultAutoCommit = AutoCommitMode.from(
|
||||
JSONUtils.getString(expertSettingsValues, WebExpertSettingsProperties.PROP_AUTO_COMMIT_MODE));
|
||||
} else {
|
||||
defaultAutoCommit = JSONUtils.getBoolean(params, WebExpertSettingsProperties.PROP_AUTO_COMMIT, true);
|
||||
}
|
||||
|
||||
defaultCatalogName = JSONUtils.getString(
|
||||
expertSettingsValues != null ? expertSettingsValues : params, WebExpertSettingsProperties.PROP_DEFAULT_CATALOG);
|
||||
defaultSchemaName = JSONUtils.getString(
|
||||
@@ -111,10 +121,13 @@ public class WebConnectionConfig {
|
||||
String configType = JSONUtils.getString(params, "configurationType");
|
||||
configurationType = configType == null ? null : DBPDriverConfigurationType.valueOf(configType);
|
||||
|
||||
Map<String, Object> stringObjectUserPrefMap = JSONUtils.getObject(params, "defaultUserPreferences");
|
||||
stringObjectUserPrefMap.forEach((key, value) -> defaultUserPreferences.put(key, value.toString()));
|
||||
networkHandlersConfig = new ArrayList<>();
|
||||
for (Map<String, Object> nhc : JSONUtils.getObjectList(params, "networkHandlersConfig")) {
|
||||
networkHandlersConfig.add(new WebNetworkHandlerConfigInput(nhc));
|
||||
}
|
||||
connectionType = JSONUtils.getString(params, "connectionType");
|
||||
}
|
||||
|
||||
@Property
|
||||
@@ -367,4 +380,22 @@ public class WebConnectionConfig {
|
||||
public void setDefaultSchemaName(String defaultSchemaName) {
|
||||
this.defaultSchemaName = defaultSchemaName;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public Map<String, String> getDefaultUserPreferences() {
|
||||
return defaultUserPreferences;
|
||||
}
|
||||
|
||||
public void setDefaultUserPreferences(@NotNull Map<String, String> defaultUserPreferences) {
|
||||
this.defaultUserPreferences = defaultUserPreferences;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public String getConnectionType() {
|
||||
return connectionType;
|
||||
}
|
||||
|
||||
public void setConnectionType(@Nullable String connectionType) {
|
||||
this.connectionType = connectionType;
|
||||
}
|
||||
}
|
||||
|
||||
+52
-9
@@ -39,12 +39,15 @@ import org.jkiss.dbeaver.model.connection.DBPDriverConfigurationType;
|
||||
import org.jkiss.dbeaver.model.impl.auth.AuthModelDatabaseNative;
|
||||
import org.jkiss.dbeaver.model.meta.Property;
|
||||
import org.jkiss.dbeaver.model.navigator.DBNBrowseSettings;
|
||||
import org.jkiss.dbeaver.model.navigator.DBNDataSource;
|
||||
import org.jkiss.dbeaver.model.navigator.DBNDatabaseNode;
|
||||
import org.jkiss.dbeaver.model.navigator.DBNUtils;
|
||||
import org.jkiss.dbeaver.model.preferences.DBPPreferenceStore;
|
||||
import org.jkiss.dbeaver.model.preferences.DBPPropertyDescriptor;
|
||||
import org.jkiss.dbeaver.model.preferences.DBPPropertySource;
|
||||
import org.jkiss.dbeaver.model.rm.RMConstants;
|
||||
import org.jkiss.dbeaver.model.rm.RMProjectPermission;
|
||||
import org.jkiss.dbeaver.model.runtime.DBRRunnableParametrized;
|
||||
import org.jkiss.dbeaver.registry.DataSourcePreferenceStore;
|
||||
import org.jkiss.dbeaver.registry.network.NetworkHandlerDescriptor;
|
||||
import org.jkiss.dbeaver.registry.network.NetworkHandlerRegistry;
|
||||
import org.jkiss.dbeaver.runtime.DBWorkbench;
|
||||
@@ -69,6 +72,11 @@ public class WebConnectionInfo {
|
||||
private static final String FEATURE_PROVIDED = "provided";
|
||||
private static final String FEATURE_MANAGEABLE = "manageable";
|
||||
|
||||
private static final String FEATURE_RESTRICT_DATA_EDIT = "restrictDataEdit";
|
||||
private static final String FEATURE_RESTRICT_SCRIPT_EXECUTE = "restrictScriptExecute";
|
||||
private static final String FEATURE_RESTRICT_DATA_IMPORT = "restrictDataImport";
|
||||
private static final String FEATURE_RESTRICT_METADATA_EDIT = "restrictMetadataEdit";
|
||||
|
||||
private static final String TOOL_SESSION_MANAGER = "sessionManager";
|
||||
|
||||
private final WebSession session;
|
||||
@@ -211,7 +219,8 @@ public class WebConnectionInfo {
|
||||
|
||||
@Property
|
||||
public String getNodePath() {
|
||||
return DBNDataSource.makeDataSourceItemPath(dataSourceContainer);
|
||||
DBNDatabaseNode dsNode = DBNUtils.getNodeByObject(dataSourceContainer);
|
||||
return dsNode == null ? null : dsNode.getNodeUri();
|
||||
}
|
||||
|
||||
@Property
|
||||
@@ -271,6 +280,18 @@ public class WebConnectionInfo {
|
||||
if (dataSourceContainer.isConnectionReadOnly()) {
|
||||
features.add(FEATURE_READ_ONLY);
|
||||
}
|
||||
if (!dataSourceContainer.hasModifyPermission(DBPDataSourcePermission.PERMISSION_EDIT_DATA)) {
|
||||
features.add(FEATURE_RESTRICT_DATA_EDIT);
|
||||
}
|
||||
if (!dataSourceContainer.hasModifyPermission(DBPDataSourcePermission.PERMISSION_EXECUTE_SCRIPTS)) {
|
||||
features.add(FEATURE_RESTRICT_SCRIPT_EXECUTE);
|
||||
}
|
||||
if (!dataSourceContainer.hasModifyPermission(DBPDataSourcePermission.PERMISSION_IMPORT_DATA)) {
|
||||
features.add(FEATURE_RESTRICT_DATA_IMPORT);
|
||||
}
|
||||
if (!dataSourceContainer.hasModifyPermission(DBPDataSourcePermission.PERMISSION_EDIT_METADATA)) {
|
||||
features.add(FEATURE_RESTRICT_METADATA_EDIT);
|
||||
}
|
||||
if (dataSourceContainer.isProvided()) {
|
||||
features.add(FEATURE_PROVIDED);
|
||||
}
|
||||
@@ -293,6 +314,16 @@ public class WebConnectionInfo {
|
||||
return dataSourceContainer.getNavigatorSettings().getOriginalSettings();
|
||||
}
|
||||
|
||||
@Property
|
||||
@NotNull
|
||||
public Map<String, String> getDefaultUserPreferences() {
|
||||
DBPPreferenceStore preferenceStore = dataSourceContainer.getPreferenceStore();
|
||||
if (preferenceStore instanceof DataSourcePreferenceStore dataSourcePreferenceStore) {
|
||||
return dataSourcePreferenceStore.getProperties();
|
||||
}
|
||||
return Collections.emptyMap();
|
||||
}
|
||||
|
||||
@Property
|
||||
@NotNull
|
||||
public List<WebDataFormat> getSupportedDataFormats() {
|
||||
@@ -453,7 +484,7 @@ public class WebConnectionInfo {
|
||||
@Property
|
||||
public Map<String, Object> getExpertSettingsValues() {
|
||||
Map<String, Object> expertSettings = new LinkedHashMap<>();
|
||||
expertSettings.put(WebExpertSettingsProperties.PROP_AUTO_COMMIT, isAutocommit());
|
||||
expertSettings.put(WebExpertSettingsProperties.PROP_AUTO_COMMIT_MODE, AutoCommitMode.fromValue(isAutocommit()));
|
||||
expertSettings.put(WebExpertSettingsProperties.PROP_KEEP_ALIVE_INTERVAL, getKeepAliveInterval());
|
||||
expertSettings.put(WebExpertSettingsProperties.PROP_READ_ONLY, isReadOnly());
|
||||
expertSettings.put(WebExpertSettingsProperties.PROP_DEFAULT_CATALOG, getDefaultCatalogName());
|
||||
@@ -527,12 +558,9 @@ public class WebConnectionInfo {
|
||||
}
|
||||
|
||||
@Property
|
||||
public boolean isAutocommit() {
|
||||
Boolean isAutoCommit = dataSourceContainer.getConnectionConfiguration().getBootstrap().getDefaultAutoCommit();
|
||||
if (isAutoCommit == null) {
|
||||
return true;
|
||||
}
|
||||
return isAutoCommit;
|
||||
@Nullable
|
||||
public Boolean isAutocommit() {
|
||||
return dataSourceContainer.getConnectionConfiguration().getBootstrap().getDefaultAutoCommit();
|
||||
}
|
||||
|
||||
@Property
|
||||
@@ -572,10 +600,25 @@ public class WebConnectionInfo {
|
||||
return tools;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public String getConnectionType() {
|
||||
return dataSourceContainer.getConnectionConfiguration().getConnectionType().getId();
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates param that checks whether credentials were saved only in session.
|
||||
*/
|
||||
public void setCredentialsSavedInSession(@Nullable Boolean credentialsSavedInSession) {
|
||||
this.credentialsSavedInSession = credentialsSavedInSession;
|
||||
}
|
||||
|
||||
/**
|
||||
* Database-dependent driver configuration used to drive data import
|
||||
*/
|
||||
@Property
|
||||
@NotNull
|
||||
public WebDriverConfiguration getDriverConfiguration() {
|
||||
return new WebDriverConfiguration(dataSourceContainer);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+58
@@ -0,0 +1,58 @@
|
||||
/*
|
||||
* DBeaver - Universal Database Manager
|
||||
* Copyright (C) 2010-2026 DBeaver Corp and others
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package io.cloudbeaver.model;
|
||||
|
||||
import org.jkiss.code.NotNull;
|
||||
import org.jkiss.code.Nullable;
|
||||
import org.jkiss.dbeaver.model.meta.Property;
|
||||
|
||||
/**
|
||||
* Generic id/name/description dictionary value.
|
||||
*/
|
||||
public class WebDictionaryValueInfo {
|
||||
|
||||
@NotNull
|
||||
private final String id;
|
||||
@NotNull
|
||||
private final String name;
|
||||
@Nullable
|
||||
private final String description;
|
||||
|
||||
public WebDictionaryValueInfo(@NotNull String id, @NotNull String name, @Nullable String description) {
|
||||
this.id = id;
|
||||
this.name = name;
|
||||
this.description = description;
|
||||
}
|
||||
|
||||
@Property
|
||||
@NotNull
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
@Property
|
||||
@NotNull
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
@Property
|
||||
@Nullable
|
||||
public String getDescription() {
|
||||
return description;
|
||||
}
|
||||
}
|
||||
+75
@@ -0,0 +1,75 @@
|
||||
/*
|
||||
* DBeaver - Universal Database Manager
|
||||
* Copyright (C) 2010-2026 DBeaver Corp and others
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package io.cloudbeaver.model;
|
||||
|
||||
import org.jkiss.code.NotNull;
|
||||
import org.jkiss.dbeaver.model.DBPDataSource;
|
||||
import org.jkiss.dbeaver.model.DBPDataSourceContainer;
|
||||
import org.jkiss.dbeaver.model.DBUtils;
|
||||
import org.jkiss.dbeaver.model.meta.Property;
|
||||
import org.jkiss.dbeaver.model.sql.SQLDialectInsertReplaceMethod;
|
||||
import org.jkiss.dbeaver.model.sql.SQLDialectMetadata;
|
||||
import org.jkiss.dbeaver.model.struct.DBSDataBulkLoader;
|
||||
import org.jkiss.utils.CommonUtils;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class WebDriverConfiguration {
|
||||
|
||||
@NotNull
|
||||
private final DBPDataSourceContainer dataSourceContainer;
|
||||
|
||||
public WebDriverConfiguration(@NotNull DBPDataSourceContainer dataSourceContainer) {
|
||||
this.dataSourceContainer = dataSourceContainer;
|
||||
}
|
||||
|
||||
/**
|
||||
* 'On duplicate key' replace methods supported by this connection's SQL dialect
|
||||
*/
|
||||
@Property
|
||||
@NotNull
|
||||
public List<WebDictionaryValueInfo> getSupportedInsertReplaceMethods() {
|
||||
SQLDialectMetadata dialect = dataSourceContainer.getDriver().getScriptDialect();
|
||||
List<SQLDialectInsertReplaceMethod> replaceMethods = dialect.getSupportedInsertReplaceMethodsDescriptors();
|
||||
if (CommonUtils.isEmpty(replaceMethods)) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
return replaceMethods.stream()
|
||||
.map(method -> new WebDictionaryValueInfo(method.getId(), method.getLabel(), method.getDescription()))
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether the connection's database supports bulk load during data import
|
||||
*/
|
||||
@Property
|
||||
public boolean isSupportsBulkLoad() {
|
||||
DBPDataSource dataSource = dataSourceContainer.getDataSource();
|
||||
return dataSource != null && DBUtils.getAdapter(DBSDataBulkLoader.class, dataSource) != null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether the connection's database supports transaction
|
||||
*/
|
||||
@Property
|
||||
public boolean isSupportsTransactions() {
|
||||
DBPDataSource dataSource = dataSourceContainer.getDataSource();
|
||||
return dataSource != null && dataSource.getInfo().supportsTransactions();
|
||||
}
|
||||
}
|
||||
+43
-3
@@ -1,3 +1,19 @@
|
||||
/*
|
||||
* DBeaver - Universal Database Manager
|
||||
* Copyright (C) 2010-2026 DBeaver Corp and others
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package io.cloudbeaver.model;
|
||||
|
||||
import org.jkiss.code.NotNull;
|
||||
@@ -5,6 +21,7 @@ import org.jkiss.code.Nullable;
|
||||
import org.jkiss.dbeaver.model.DBPObject;
|
||||
import org.jkiss.dbeaver.model.connection.DBPDriver;
|
||||
import org.jkiss.dbeaver.model.connection.DBPDriverConstants;
|
||||
import org.jkiss.dbeaver.model.meta.IPropertyValueListProvider;
|
||||
import org.jkiss.dbeaver.model.meta.IPropertyValueValidator;
|
||||
import org.jkiss.dbeaver.model.meta.Property;
|
||||
import org.jkiss.utils.CommonUtils;
|
||||
@@ -15,6 +32,7 @@ import org.jkiss.utils.CommonUtils;
|
||||
public class WebExpertSettingsProperties implements DBPObject {
|
||||
public static final String PROP_READ_ONLY = "readOnly";
|
||||
public static final String PROP_AUTO_COMMIT = "autocommit";
|
||||
public static final String PROP_AUTO_COMMIT_MODE = "autoCommitMode";
|
||||
public static final String PROP_KEEP_ALIVE_INTERVAL = "keepAliveInterval";
|
||||
public static final String PROP_DEFAULT_CATALOG = "defaultCatalogName";
|
||||
public static final String PROP_DEFAULT_SCHEMA = "defaultSchemaName";
|
||||
@@ -30,9 +48,15 @@ public class WebExpertSettingsProperties implements DBPObject {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Property(order = 2, id = PROP_AUTO_COMMIT, visibleIf = AutoCommitFieldValidator.class)
|
||||
public boolean isAutoCommit() {
|
||||
return true;
|
||||
@NotNull
|
||||
@Property(
|
||||
order = 2,
|
||||
id = PROP_AUTO_COMMIT_MODE,
|
||||
visibleIf = AutoCommitFieldValidator.class,
|
||||
listProvider = AutoCommitListProvider.class
|
||||
)
|
||||
public AutoCommitMode isAutoCommitMode() {
|
||||
return AutoCommitMode.DEFAULT;
|
||||
}
|
||||
|
||||
@Property(order = 3, id = PROP_READ_ONLY, visibleIf = ReadOnlyFieldValidator.class)
|
||||
@@ -40,11 +64,13 @@ public class WebExpertSettingsProperties implements DBPObject {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Property(order = 4, id = PROP_DEFAULT_CATALOG, visibleIf = DefaultCatalogFieldVisibleValidator.class)
|
||||
public String getDefaultCatalog() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Property(order = 5, id = PROP_DEFAULT_SCHEMA, visibleIf = DefaultSchemaFieldVisibleValidator.class)
|
||||
public String getDefaultSchema() {
|
||||
return null;
|
||||
@@ -85,4 +111,18 @@ public class WebExpertSettingsProperties implements DBPObject {
|
||||
return CommonUtils.toBoolean(object.driver.getDriverParameter(DBPDriverConstants.PARAM_SUPPORTS_SCHEMA_SELECTION), true);
|
||||
}
|
||||
}
|
||||
|
||||
public static class AutoCommitListProvider implements IPropertyValueListProvider<WebExpertSettingsProperties> {
|
||||
|
||||
@Override
|
||||
public boolean allowCustomValue() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public Object[] getPossibleValues(@Nullable WebExpertSettingsProperties object) {
|
||||
return AutoCommitMode.values();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+11
-2
@@ -16,6 +16,8 @@
|
||||
*/
|
||||
package io.cloudbeaver.model;
|
||||
|
||||
import org.jkiss.code.NotNull;
|
||||
import org.jkiss.code.Nullable;
|
||||
import org.jkiss.dbeaver.model.data.json.JSONUtils;
|
||||
import org.jkiss.dbeaver.model.net.DBWHandlerType;
|
||||
import org.jkiss.utils.CommonUtils;
|
||||
@@ -28,12 +30,14 @@ import java.util.Map;
|
||||
*/
|
||||
public class WebNetworkHandlerConfigInput {
|
||||
|
||||
@NotNull
|
||||
private final Map<String, Object> cfg;
|
||||
|
||||
public WebNetworkHandlerConfigInput(Map<String, Object> cfg) {
|
||||
public WebNetworkHandlerConfigInput(@NotNull Map<String, Object> cfg) {
|
||||
this.cfg = cfg;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public DBWHandlerType getType() {
|
||||
return CommonUtils.valueOf(DBWHandlerType.class, JSONUtils.getString(cfg, "type"), null);
|
||||
}
|
||||
@@ -42,6 +46,7 @@ public class WebNetworkHandlerConfigInput {
|
||||
return JSONUtils.getString(cfg, "id");
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public Boolean isEnabled() {
|
||||
if (cfg.containsKey("enabled")) {
|
||||
return JSONUtils.getBoolean(cfg, "enabled");
|
||||
@@ -62,11 +67,13 @@ public class WebNetworkHandlerConfigInput {
|
||||
return JSONUtils.getString(cfg, "password");
|
||||
}
|
||||
|
||||
@Deprecated // use secured properties
|
||||
// Deprecated. Use secured properties
|
||||
@Nullable
|
||||
public String getKey() {
|
||||
return JSONUtils.getString(cfg, "key");
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public Boolean isSavePassword() {
|
||||
if (cfg.containsKey("savePassword")) {
|
||||
return JSONUtils.getBoolean(cfg, "savePassword");
|
||||
@@ -75,10 +82,12 @@ public class WebNetworkHandlerConfigInput {
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public Map<String, Object> getProperties() {
|
||||
return JSONUtils.getObjectOrNull(cfg, "properties");
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public Map<String, String> getSecureProperties() {
|
||||
var secureProperties = JSONUtils.getObjectOrNull(cfg, "secureProperties");
|
||||
if (secureProperties == null) {
|
||||
|
||||
@@ -27,6 +27,7 @@ import org.jkiss.dbeaver.model.impl.PropertyDescriptor;
|
||||
import org.jkiss.dbeaver.model.impl.ProviderPropertyDescriptor;
|
||||
import org.jkiss.dbeaver.model.meta.IPropertyValueListProvider;
|
||||
import org.jkiss.dbeaver.model.meta.Property;
|
||||
import org.jkiss.dbeaver.model.meta.PropertyConstraints;
|
||||
import org.jkiss.dbeaver.model.meta.PropertyLength;
|
||||
import org.jkiss.dbeaver.model.preferences.DBPPropertyDescriptor;
|
||||
import org.jkiss.dbeaver.model.preferences.DBPPropertySource;
|
||||
@@ -42,63 +43,58 @@ import java.util.*;
|
||||
* Web connection info
|
||||
*/
|
||||
public class WebPropertyInfo {
|
||||
private WebSession session;
|
||||
private DBPPropertyDescriptor property;
|
||||
private DBPPropertySource propertySource;
|
||||
private boolean showProtected;
|
||||
@NotNull
|
||||
private final WebSession session;
|
||||
@NotNull
|
||||
private final DBPPropertyDescriptor property;
|
||||
@Nullable
|
||||
private final DBPPropertySource propertySource;
|
||||
|
||||
private Object[] validValues;
|
||||
|
||||
private String[] supportedConfigurationTypes = new String[0];
|
||||
|
||||
private Object defaultValue;
|
||||
|
||||
public WebPropertyInfo(WebSession session, DBPPropertyDescriptor property, DBPPropertySource propertySource) {
|
||||
public WebPropertyInfo(
|
||||
@NotNull WebSession session,
|
||||
@NotNull DBPPropertyDescriptor property,
|
||||
@Nullable DBPPropertySource propertySource
|
||||
) {
|
||||
this.session = session;
|
||||
this.property = property;
|
||||
this.propertySource = propertySource;
|
||||
}
|
||||
|
||||
public WebPropertyInfo(WebSession session, DBPPropertyDescriptor property) {
|
||||
this.session = session;
|
||||
this.property = property;
|
||||
}
|
||||
|
||||
public boolean isShowProtected() {
|
||||
return showProtected;
|
||||
}
|
||||
|
||||
public void setShowProtected(boolean showProtected) {
|
||||
this.showProtected = showProtected;
|
||||
public WebPropertyInfo(@NotNull WebSession session, @NotNull DBPPropertyDescriptor property) {
|
||||
this(session, property, null);
|
||||
}
|
||||
|
||||
///////////////////////////////////
|
||||
// General properties
|
||||
///////////////////////////////////
|
||||
|
||||
@NotNull
|
||||
@Property
|
||||
public String getId() {
|
||||
return CommonUtils.toString(property.getId());
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Property
|
||||
public String getDisplayName() {
|
||||
if (property instanceof DBPNamedObjectLocalized) {
|
||||
return ((DBPNamedObjectLocalized) property).getLocalizedName(session.getLocale());
|
||||
if (property instanceof DBPNamedObjectLocalized localized) {
|
||||
return localized.getLocalizedName(session.getLocale());
|
||||
} else {
|
||||
return property.getDisplayName();
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Property
|
||||
public String getDescription() {
|
||||
if (property instanceof DBPObjectWithDescriptionLocalized) {
|
||||
return ((DBPObjectWithDescriptionLocalized) property).getLocalizedDescription(session.getLocale());
|
||||
if (property instanceof DBPObjectWithDescriptionLocalized localized) {
|
||||
return localized.getLocalizedDescription(session.getLocale());
|
||||
} else {
|
||||
return property.getDescription();
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Property
|
||||
public String getHint() {
|
||||
return property.getHint();
|
||||
@@ -109,38 +105,53 @@ public class WebPropertyInfo {
|
||||
return property instanceof ObjectPropertyDescriptor ? ((ObjectPropertyDescriptor) property).getOrderNumber() : -1;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Property
|
||||
public String getCategory() {
|
||||
return property.getCategory();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Property
|
||||
public String getType() {
|
||||
return getDataType();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Property
|
||||
public String getDataType() {
|
||||
Class<?> dataType = property.getDataType();
|
||||
return dataType == null ? null : dataType.getSimpleName();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Property
|
||||
public PropertyLength getLength() {
|
||||
return property.getLength();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Property
|
||||
public Object getDefaultValue() throws DBException {
|
||||
var defaultValue = property.getDefaultValue() == null ? this.defaultValue : property.getDefaultValue();
|
||||
return defaultValue == null ? getValue() : defaultValue;
|
||||
Object defaultValue = property.getDefaultValue();
|
||||
if (property instanceof ObjectPropertyDescriptor && defaultValue == null) {
|
||||
return getValue();
|
||||
}
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Property
|
||||
public PropertyConstraints getConstraints() {
|
||||
return property instanceof ObjectPropertyDescriptor descriptor ? descriptor.getConstraints() : null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Property
|
||||
public Object getValue() throws DBException {
|
||||
Object value = propertySource == null ? null : propertySource.getPropertyValue(session.getProgressMonitor(), property.getId());
|
||||
if (property instanceof ObjectPropertyDescriptor opd &&
|
||||
(!showProtected && opd.isPassword() || opd.isHidden())
|
||||
(opd.isPassword() || opd.isHidden())
|
||||
) {
|
||||
return maskValue(value);
|
||||
} else if (property instanceof PropertyDescriptor pd) {
|
||||
@@ -152,6 +163,7 @@ public class WebPropertyInfo {
|
||||
return value == null ? null : makePropertyValue(value);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Property
|
||||
public Object[] getValidValues() {
|
||||
if (property instanceof IPropertyValueListProvider) {
|
||||
@@ -164,17 +176,18 @@ public class WebPropertyInfo {
|
||||
}
|
||||
return validValues;
|
||||
}
|
||||
return validValues;
|
||||
}
|
||||
return validValues;
|
||||
return null;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Property
|
||||
public String[] getFeatures() {
|
||||
String[] features = property.getFeatures();
|
||||
return features == null ? new String[0] : features;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Property
|
||||
public String[] getSupportedConfigurationTypes() {
|
||||
if (property instanceof ProviderPropertyDescriptor) {
|
||||
@@ -182,7 +195,7 @@ public class WebPropertyInfo {
|
||||
.map(DBPDriverConfigurationType::toString)
|
||||
.toArray(String[]::new);
|
||||
}
|
||||
return supportedConfigurationTypes;
|
||||
return new String[0];
|
||||
}
|
||||
|
||||
@Property
|
||||
@@ -203,12 +216,14 @@ public class WebPropertyInfo {
|
||||
return false;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public String toString() {
|
||||
return CommonUtils.toString(property.getId());
|
||||
}
|
||||
|
||||
private Object makePropertyValue(Object value) {
|
||||
@Nullable
|
||||
private Object makePropertyValue(@Nullable Object value) {
|
||||
if (value == null) {
|
||||
return null;
|
||||
// } else if (value instanceof DBSObject) {
|
||||
@@ -290,21 +305,6 @@ public class WebPropertyInfo {
|
||||
return conditions;
|
||||
}
|
||||
|
||||
|
||||
//TODO: delete after refactoring on front-end
|
||||
public void setDefaultValue(String defaultValue) {
|
||||
this.defaultValue = defaultValue;
|
||||
}
|
||||
//TODO: delete after refactoring on front-end
|
||||
public void setValidValues(Object[] validValues) {
|
||||
this.validValues = validValues;
|
||||
}
|
||||
|
||||
//TODO: delete after refactoring on front-end
|
||||
public void setSupportedConfigurationTypes(String[] supportedConfigurationTypes) {
|
||||
this.supportedConfigurationTypes = supportedConfigurationTypes;
|
||||
}
|
||||
|
||||
public record Condition(@NotNull String expression, @NotNull Type conditionType) {
|
||||
public enum Type {
|
||||
HIDE,
|
||||
|
||||
+41
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
* DBeaver - Universal Database Manager
|
||||
* Copyright (C) 2010-2026 DBeaver Corp and others
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package io.cloudbeaver.model.api.annotation;
|
||||
|
||||
import io.cloudbeaver.model.api.annotation.responses.ApiResponse;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(ElementType.METHOD)
|
||||
public @interface Operation {
|
||||
|
||||
String summary() default "";
|
||||
|
||||
String description() default "";
|
||||
|
||||
String operationId() default "";
|
||||
|
||||
String method() default "";
|
||||
|
||||
Parameter[] parameters() default {};
|
||||
|
||||
ApiResponse[] responses() default {};
|
||||
}
|
||||
+51
@@ -0,0 +1,51 @@
|
||||
/*
|
||||
* DBeaver - Universal Database Manager
|
||||
* Copyright (C) 2010-2026 DBeaver Corp and others
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package io.cloudbeaver.model.api.annotation;
|
||||
|
||||
import io.cloudbeaver.model.api.annotation.enums.Explode;
|
||||
import io.cloudbeaver.model.api.annotation.enums.ParameterIn;
|
||||
import io.cloudbeaver.model.api.annotation.enums.ParameterStyle;
|
||||
import io.cloudbeaver.model.api.annotation.media.ArraySchema;
|
||||
import io.cloudbeaver.model.api.annotation.media.Schema;
|
||||
|
||||
import java.lang.annotation.*;
|
||||
|
||||
@Repeatable(Parameters.class)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target({ ElementType.PARAMETER, ElementType.METHOD, ElementType.FIELD, ElementType.ANNOTATION_TYPE })
|
||||
public @interface Parameter {
|
||||
|
||||
String name() default "";
|
||||
|
||||
String description() default "";
|
||||
|
||||
boolean required() default false;
|
||||
|
||||
String example() default "";
|
||||
|
||||
ParameterIn in() default ParameterIn.DEFAULT;
|
||||
|
||||
Schema schema() default @Schema;
|
||||
|
||||
ArraySchema array() default @ArraySchema;
|
||||
|
||||
ParameterStyle style() default ParameterStyle.DEFAULT;
|
||||
|
||||
Explode explode() default Explode.DEFAULT;
|
||||
|
||||
boolean hidden() default false;
|
||||
}
|
||||
+29
@@ -0,0 +1,29 @@
|
||||
/*
|
||||
* DBeaver - Universal Database Manager
|
||||
* Copyright (C) 2010-2026 DBeaver Corp and others
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package io.cloudbeaver.model.api.annotation;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target({ ElementType.PARAMETER, ElementType.METHOD, ElementType.FIELD, ElementType.ANNOTATION_TYPE })
|
||||
public @interface Parameters {
|
||||
|
||||
Parameter[] value();
|
||||
}
|
||||
+34
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
* DBeaver - Universal Database Manager
|
||||
* Copyright (C) 2010-2026 DBeaver Corp and others
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package io.cloudbeaver.model.api.annotation.enums;
|
||||
|
||||
public enum Explode {
|
||||
TRUE("true"),
|
||||
FALSE("false"),
|
||||
DEFAULT("default");
|
||||
|
||||
private final String value;
|
||||
|
||||
Explode(final String value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
+36
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
* DBeaver - Universal Database Manager
|
||||
* Copyright (C) 2010-2026 DBeaver Corp and others
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package io.cloudbeaver.model.api.annotation.enums;
|
||||
|
||||
public enum ParameterIn {
|
||||
DEFAULT("default"),
|
||||
QUERY("query"),
|
||||
HEADER("header"),
|
||||
PATH("path"),
|
||||
COOKIE("cookie");
|
||||
|
||||
private final String value;
|
||||
|
||||
ParameterIn(final String value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
+39
@@ -0,0 +1,39 @@
|
||||
/*
|
||||
* DBeaver - Universal Database Manager
|
||||
* Copyright (C) 2010-2026 DBeaver Corp and others
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package io.cloudbeaver.model.api.annotation.enums;
|
||||
|
||||
public enum ParameterStyle {
|
||||
MATRIX("matrix"),
|
||||
LABEL("label"),
|
||||
FORM("form"),
|
||||
SIMPLE("simple"),
|
||||
SPACEDELIMITED("spaceDelimited"),
|
||||
PIPEDELIMITED("pipeDelimited"),
|
||||
DEEPOBJECT("deepObject"),
|
||||
DEFAULT("default");
|
||||
|
||||
private final String value;
|
||||
|
||||
ParameterStyle(final String value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
+29
@@ -0,0 +1,29 @@
|
||||
/*
|
||||
* DBeaver - Universal Database Manager
|
||||
* Copyright (C) 2010-2026 DBeaver Corp and others
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package io.cloudbeaver.model.api.annotation.media;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target({ ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER, ElementType.ANNOTATION_TYPE })
|
||||
public @interface ArraySchema {
|
||||
|
||||
Schema schema() default @Schema;
|
||||
}
|
||||
+29
@@ -0,0 +1,29 @@
|
||||
/*
|
||||
* DBeaver - Universal Database Manager
|
||||
* Copyright (C) 2010-2026 DBeaver Corp and others
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package io.cloudbeaver.model.api.annotation.media;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target({ ElementType.METHOD, ElementType.ANNOTATION_TYPE })
|
||||
public @interface Content {
|
||||
|
||||
Schema schema() default @Schema;
|
||||
}
|
||||
+37
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
* DBeaver - Universal Database Manager
|
||||
* Copyright (C) 2010-2026 DBeaver Corp and others
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package io.cloudbeaver.model.api.annotation.media;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target({ ElementType.TYPE, ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER, ElementType.ANNOTATION_TYPE })
|
||||
public @interface Schema {
|
||||
|
||||
String description() default "";
|
||||
|
||||
String example() default "";
|
||||
|
||||
String type() default "";
|
||||
|
||||
Class<?> implementation() default Void.class;
|
||||
|
||||
boolean required() default false;
|
||||
}
|
||||
+35
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
* DBeaver - Universal Database Manager
|
||||
* Copyright (C) 2010-2026 DBeaver Corp and others
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package io.cloudbeaver.model.api.annotation.responses;
|
||||
|
||||
import io.cloudbeaver.model.api.annotation.media.Content;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target({ ElementType.METHOD, ElementType.ANNOTATION_TYPE })
|
||||
public @interface ApiResponse {
|
||||
|
||||
String responseCode() default "default";
|
||||
|
||||
String description() default "";
|
||||
|
||||
Content[] content() default {};
|
||||
}
|
||||
+31
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
* DBeaver - Universal Database Manager
|
||||
* Copyright (C) 2010-2026 DBeaver Corp and others
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package io.cloudbeaver.model.api.annotation.tags;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(ElementType.TYPE)
|
||||
public @interface Tag {
|
||||
|
||||
String name() default "";
|
||||
|
||||
String description() default "";
|
||||
}
|
||||
+27
-26
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* DBeaver - Universal Database Manager
|
||||
* Copyright (C) 2010-2025 DBeaver Corp and others
|
||||
* Copyright (C) 2010-2026 DBeaver Corp and others
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -25,16 +25,18 @@ import org.jkiss.code.Nullable;
|
||||
import org.jkiss.dbeaver.DBException;
|
||||
import org.jkiss.dbeaver.Log;
|
||||
import org.jkiss.dbeaver.model.DBFileController;
|
||||
import org.jkiss.dbeaver.model.app.DBPLockManagerProvider;
|
||||
import org.jkiss.dbeaver.model.app.DBPWorkspace;
|
||||
import org.jkiss.dbeaver.model.auth.SMCredentialsProvider;
|
||||
import org.jkiss.dbeaver.model.auth.SMSessionContext;
|
||||
import org.jkiss.dbeaver.model.data.json.JSONUtils;
|
||||
import org.jkiss.dbeaver.model.fs.lock.LockManager;
|
||||
import org.jkiss.dbeaver.model.fs.lock.shared.SharedFileLockManager;
|
||||
import org.jkiss.dbeaver.model.impl.app.ApplicationRegistry;
|
||||
import org.jkiss.dbeaver.model.impl.app.BaseApplicationImpl;
|
||||
import org.jkiss.dbeaver.model.impl.app.BaseWorkspaceImpl;
|
||||
import org.jkiss.dbeaver.model.rm.RMController;
|
||||
import org.jkiss.dbeaver.model.secret.DBSSecretController;
|
||||
import org.jkiss.dbeaver.model.websocket.event.WSEventController;
|
||||
import org.jkiss.dbeaver.runtime.IVariableResolver;
|
||||
import org.jkiss.dbeaver.utils.GeneralUtils;
|
||||
import org.jkiss.dbeaver.utils.RuntimeUtils;
|
||||
@@ -48,7 +50,7 @@ import java.util.Map;
|
||||
/**
|
||||
* Servlet application
|
||||
*/
|
||||
public abstract class BaseServletApplication extends BaseApplicationImpl implements ServletApplication {
|
||||
public abstract class BaseServletApplication extends BaseApplicationImpl implements ServletApplication, DBPLockManagerProvider {
|
||||
|
||||
public static final String DEFAULT_CONFIG_FILE_PATH = "/etc/cloudbeaver.conf";
|
||||
public static final String CUSTOM_CONFIG_FOLDER = "custom";
|
||||
@@ -59,6 +61,7 @@ public abstract class BaseServletApplication extends BaseApplicationImpl impleme
|
||||
|
||||
private String instanceId;
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public RMController createResourceController(
|
||||
@NotNull SMCredentialsProvider credentialsProvider,
|
||||
@@ -73,10 +76,16 @@ public abstract class BaseServletApplication extends BaseApplicationImpl impleme
|
||||
throw new IllegalStateException("File controller is not supported by " + getClass().getSimpleName());
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@NotNull
|
||||
@Override
|
||||
public Path getDefaultWorkingFolder() {
|
||||
return getServerConfigurationController().getWorkspacePath();
|
||||
public LockManager createLockManager(@NotNull String applicationId, @NotNull Path metadataFolder) throws DBException {
|
||||
return new SharedFileLockManager(applicationId, metadataFolder);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public LockManager createLockManager() throws DBException {
|
||||
return new SharedFileLockManager(getApplicationInstanceId());
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -176,20 +185,20 @@ public abstract class BaseServletApplication extends BaseApplicationImpl impleme
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public static void patchConfigurationWithProperties(
|
||||
Map<String, Object> configProps, IVariableResolver varResolver
|
||||
@NotNull Map<String, Object> configProps,
|
||||
@NotNull IVariableResolver varResolver
|
||||
) {
|
||||
for (Map.Entry<String, Object> entry : configProps.entrySet()) {
|
||||
Object propValue = entry.getValue();
|
||||
if (propValue instanceof String) {
|
||||
entry.setValue(GeneralUtils.replaceVariables((String) propValue, varResolver));
|
||||
if (propValue instanceof String strValue) {
|
||||
entry.setValue(GeneralUtils.replaceVariables(strValue, varResolver));
|
||||
} else if (propValue instanceof Map) {
|
||||
patchConfigurationWithProperties((Map<String, Object>) propValue, varResolver);
|
||||
} else if (propValue instanceof List) {
|
||||
List value = (List) propValue;
|
||||
} else if (propValue instanceof List value) {
|
||||
for (int i = 0; i < value.size(); i++) {
|
||||
Object colItem = value.get(i);
|
||||
if (colItem instanceof String) {
|
||||
value.set(i, GeneralUtils.replaceVariables((String) colItem, varResolver));
|
||||
if (colItem instanceof String strItem) {
|
||||
value.set(i, GeneralUtils.replaceVariables(strItem, varResolver));
|
||||
} else if (colItem instanceof Map) {
|
||||
patchConfigurationWithProperties((Map<String, Object>) colItem, varResolver);
|
||||
}
|
||||
@@ -202,6 +211,8 @@ public abstract class BaseServletApplication extends BaseApplicationImpl impleme
|
||||
@Override
|
||||
public Object start(IApplicationContext context) {
|
||||
initializeApplicationServices();
|
||||
setWorkspacePath(getServerConfigurationController().getWorkspacePath());
|
||||
|
||||
try {
|
||||
startServer();
|
||||
} catch (Exception e) {
|
||||
@@ -239,27 +250,17 @@ public abstract class BaseServletApplication extends BaseApplicationImpl impleme
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public Path getWorkspaceDirectory() {
|
||||
return getServerConfigurationController().getWorkspacePath();
|
||||
}
|
||||
|
||||
|
||||
public String getApplicationId() {
|
||||
try {
|
||||
return getApplicationInstanceId();
|
||||
} catch (DBException e) {
|
||||
return null;
|
||||
// Fallback to hash code
|
||||
return getClass().getSimpleName() + "#" + hashCode();
|
||||
}
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public WSEventController getEventController() {
|
||||
return null;
|
||||
}
|
||||
|
||||
public abstract ServletServerConfigurationController getServerConfigurationController();
|
||||
public abstract ServletServerConfigurationController<?> getServerConfigurationController();
|
||||
|
||||
@Override
|
||||
public boolean isEnvironmentVariablesAccessible() {
|
||||
|
||||
+1
-4
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* DBeaver - Universal Database Manager
|
||||
* Copyright (C) 2010-2024 DBeaver Corp and others
|
||||
* Copyright (C) 2010-2026 DBeaver Corp and others
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -53,9 +53,6 @@ public interface ServletApplication extends DBPApplication {
|
||||
@NotNull
|
||||
Path getDataDirectory(boolean create);
|
||||
|
||||
@NotNull
|
||||
Path getWorkspaceDirectory();
|
||||
|
||||
@NotNull
|
||||
Path getHomeDirectory();
|
||||
|
||||
|
||||
+2
-1
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* DBeaver - Universal Database Manager
|
||||
* Copyright (C) 2010-2025 DBeaver Corp and others
|
||||
* Copyright (C) 2010-2026 DBeaver Corp and others
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -23,6 +23,7 @@ import org.jkiss.code.Nullable;
|
||||
import org.jkiss.dbeaver.DBException;
|
||||
|
||||
public interface ServletAuthApplication extends ServletApplication {
|
||||
@NotNull
|
||||
ServletAuthConfiguration getAuthConfiguration();
|
||||
|
||||
@Nullable
|
||||
|
||||
-7
@@ -115,13 +115,6 @@ public class DBNResourceManagerProject extends DBNAbstractResourceManagerNode {
|
||||
return ((DBNResourceManagerRoot) getParentNode()).getResourceController();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Deprecated
|
||||
@Override
|
||||
public String getNodeItemPath() {
|
||||
return getParentNode().getNodeItemPath() + "/" + getName();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public DBNNode refreshNode(@NotNull DBRProgressMonitor monitor, @Nullable Object source) throws DBException {
|
||||
|
||||
+6
-12
@@ -31,8 +31,6 @@ import org.jkiss.dbeaver.model.rm.RMProject;
|
||||
import org.jkiss.dbeaver.model.rm.RMResource;
|
||||
import org.jkiss.dbeaver.model.rm.RMResourceType;
|
||||
import org.jkiss.dbeaver.model.runtime.DBRProgressMonitor;
|
||||
import org.jkiss.dbeaver.registry.ResourceTypeDescriptor;
|
||||
import org.jkiss.dbeaver.registry.ResourceTypeRegistry;
|
||||
import org.jkiss.utils.ArrayUtils;
|
||||
import org.jkiss.utils.CommonUtils;
|
||||
import org.jkiss.utils.IOUtils;
|
||||
@@ -44,7 +42,7 @@ public class DBNResourceManagerResource extends DBNAbstractResourceManagerNode {
|
||||
|
||||
private final RMResource resource;
|
||||
|
||||
DBNResourceManagerResource(DBNNode parentNode, RMResource resource) {
|
||||
DBNResourceManagerResource(@NotNull DBNNode parentNode, @NotNull RMResource resource) {
|
||||
super(parentNode);
|
||||
this.resource = resource;
|
||||
}
|
||||
@@ -71,6 +69,7 @@ public class DBNResourceManagerResource extends DBNAbstractResourceManagerNode {
|
||||
@Override
|
||||
public DBPImage getNodeIcon() {
|
||||
if (resource.isFolder()) {
|
||||
/*
|
||||
if (getParentNode() instanceof DBNResourceManagerResource) {
|
||||
return DBIcon.TREE_FOLDER;
|
||||
}
|
||||
@@ -82,11 +81,12 @@ public class DBNResourceManagerResource extends DBNAbstractResourceManagerNode {
|
||||
if (folderResType != null) {
|
||||
return folderResType.getFolderIcon();
|
||||
}
|
||||
*/
|
||||
return DBIcon.TREE_FOLDER;
|
||||
} else {
|
||||
var fileExtension = IOUtils.getFileExtension(getNodeDisplayName());
|
||||
if (!CommonUtils.isEmpty(fileExtension)) {
|
||||
RMProject project = getProjectNode();
|
||||
RMProject project = getRmProject();
|
||||
if (project != null) {
|
||||
for (RMResourceType rt : project.getResourceTypes()) {
|
||||
if (ArrayUtils.contains(rt.getFileExtensions(), fileExtension)) {
|
||||
@@ -99,7 +99,8 @@ public class DBNResourceManagerResource extends DBNAbstractResourceManagerNode {
|
||||
}
|
||||
}
|
||||
|
||||
private RMProject getProjectNode() {
|
||||
@Nullable
|
||||
public RMProject getRmProject() {
|
||||
for (DBNNode node = this; node != null; node = node.getParentNode()) {
|
||||
if (node instanceof DBNResourceManagerProject) {
|
||||
return ((DBNResourceManagerProject) node).getProject();
|
||||
@@ -159,13 +160,6 @@ public class DBNResourceManagerResource extends DBNAbstractResourceManagerNode {
|
||||
throw new DBException("Can't detect resource root node");
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Deprecated
|
||||
@Override
|
||||
public String getNodeItemPath() {
|
||||
return getParentNode().getNodeItemPath() + "/" + getNodeDisplayName();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public DBNNode refreshNode(@NotNull DBRProgressMonitor monitor, @Nullable Object source) throws DBException {
|
||||
|
||||
-8
@@ -115,14 +115,6 @@ public class DBNResourceManagerRoot extends DBNNode implements DBPHiddenObject,
|
||||
return projects;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Deprecated
|
||||
@Override
|
||||
public String getNodeItemPath() {
|
||||
// Path doesn't include project name
|
||||
return NodePathType.ext.getPrefix() + getNodeDisplayName();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public DBNNode refreshNode(@NotNull DBRProgressMonitor monitor, @Nullable Object source) throws DBException {
|
||||
|
||||
+13
-13
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* DBeaver - Universal Database Manager
|
||||
* Copyright (C) 2010-2025 DBeaver Corp and others
|
||||
* Copyright (C) 2010-2026 DBeaver Corp and others
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -26,7 +26,9 @@ import org.jkiss.dbeaver.model.DBPDataSourceFolder;
|
||||
import org.jkiss.dbeaver.model.app.DBPDataSourceRegistry;
|
||||
import org.jkiss.dbeaver.model.app.DBPProject;
|
||||
import org.jkiss.dbeaver.model.app.DBPWorkspace;
|
||||
import org.jkiss.dbeaver.model.fs.lock.FileLockController;
|
||||
import org.jkiss.dbeaver.model.fs.lock.LockManager;
|
||||
import org.jkiss.dbeaver.model.fs.lock.LockOptions;
|
||||
import org.jkiss.dbeaver.model.fs.lock.LockTarget;
|
||||
import org.jkiss.dbeaver.model.rm.RMController;
|
||||
import org.jkiss.dbeaver.model.rm.RMEvent;
|
||||
import org.jkiss.dbeaver.model.rm.RMEventManager;
|
||||
@@ -53,11 +55,11 @@ public abstract class BaseLocalResourceController implements RMController {
|
||||
@NotNull
|
||||
protected final DBPWorkspace workspace;
|
||||
@NotNull
|
||||
protected final FileLockController lockController;
|
||||
protected final LockManager lockController;
|
||||
|
||||
protected BaseLocalResourceController(
|
||||
@NotNull DBPWorkspace workspace,
|
||||
@NotNull FileLockController lockController
|
||||
@NotNull LockManager lockController
|
||||
) {
|
||||
this.workspace = workspace;
|
||||
this.lockController = lockController;
|
||||
@@ -65,8 +67,7 @@ public abstract class BaseLocalResourceController implements RMController {
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public RMProject getProject(@NotNull String projectId, boolean readResources, boolean readProperties)
|
||||
throws DBException {
|
||||
public RMProject getProject(@NotNull String projectId, boolean readResources, boolean readProperties) throws DBException {
|
||||
RMProject project = makeProjectFromId(projectId, true);
|
||||
if (project == null) {
|
||||
return null;
|
||||
@@ -108,8 +109,7 @@ public abstract class BaseLocalResourceController implements RMController {
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getProjectsDataSources(@NotNull String projectId, @Nullable String[] dataSourceIds)
|
||||
throws DBException {
|
||||
public String getProjectsDataSources(@NotNull String projectId, @Nullable String[] dataSourceIds) throws DBException {
|
||||
DBPProject projectMetadata = getWebProject(projectId, false);
|
||||
return doFileReadOperation(
|
||||
projectId,
|
||||
@@ -157,7 +157,7 @@ public abstract class BaseLocalResourceController implements RMController {
|
||||
@NotNull String configuration,
|
||||
@Nullable List<String> dataSourceIds
|
||||
) throws DBException {
|
||||
try (var ignoredLock = lockController.lock(projectId, "updateProjectDataSources")) {
|
||||
try (var ignoredLock = lockController.lock(LockTarget.of(projectId), LockOptions.of("updateProjectDataSources"))) {
|
||||
DBPProject project = getWebProject(projectId, false);
|
||||
return doFileWriteOperation(
|
||||
projectId, project.getMetadataFolder(false),
|
||||
@@ -188,7 +188,7 @@ public abstract class BaseLocalResourceController implements RMController {
|
||||
@NotNull String projectId,
|
||||
@NotNull String[] dataSourceIds
|
||||
) throws DBException {
|
||||
try (var ignoredLock = lockController.lock(projectId, "deleteDataSources")) {
|
||||
try (var ignoredLock = lockController.lock(LockTarget.of(projectId), LockOptions.of("deleteDataSources"))) {
|
||||
DBPProject project = getWebProject(projectId, false);
|
||||
doFileWriteOperation(projectId, project.getMetadataFolder(false), () -> {
|
||||
DBPDataSourceRegistry registry = project.getDataSourceRegistry();
|
||||
@@ -213,7 +213,7 @@ public abstract class BaseLocalResourceController implements RMController {
|
||||
@NotNull String projectId,
|
||||
@NotNull String folderPath
|
||||
) throws DBException {
|
||||
try (var ignoredLock = lockController.lock(projectId, "createDatasourceFolder")) {
|
||||
try (var ignoredLock = lockController.lock(LockTarget.of(projectId), LockOptions.of("createDatasourceFolder"))) {
|
||||
DBPProject project = getWebProject(projectId, false);
|
||||
log.debug("Creating data source folder '" + folderPath + "' in project '" + projectId + "'");
|
||||
doFileWriteOperation(projectId, project.getMetadataFolder(false),
|
||||
@@ -241,7 +241,7 @@ public abstract class BaseLocalResourceController implements RMController {
|
||||
@NotNull String[] folderPaths,
|
||||
boolean dropContents
|
||||
) throws DBException {
|
||||
try (var ignoredLock = lockController.lock(projectId, "createDatasourceFolder")) {
|
||||
try (var ignoredLock = lockController.lock(LockTarget.of(projectId), LockOptions.of("createDatasourceFolder"))) {
|
||||
DBPProject project = getWebProject(projectId, false);
|
||||
doFileWriteOperation(projectId, project.getMetadataFolder(false),
|
||||
() -> {
|
||||
@@ -267,7 +267,7 @@ public abstract class BaseLocalResourceController implements RMController {
|
||||
@NotNull String oldPath,
|
||||
@NotNull String newPath
|
||||
) throws DBException {
|
||||
try (var ignoredLock = lockController.lock(projectId, "createDatasourceFolder")) {
|
||||
try (var ignoredLock = lockController.lock(LockTarget.of(projectId), LockOptions.of("createDatasourceFolder"))) {
|
||||
DBPProject project = getWebProject(projectId, false);
|
||||
log.debug("Moving data source folder from '" + oldPath + "' to '" + newPath + "' in project '" + projectId + "'");
|
||||
doFileWriteOperation(projectId, project.getMetadataFolder(false),
|
||||
|
||||
+110
-31
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* DBeaver - Universal Database Manager
|
||||
* Copyright (C) 2010-2025 DBeaver Corp and others
|
||||
* Copyright (C) 2010-2026 DBeaver Corp and others
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -33,24 +33,29 @@ import org.jkiss.dbeaver.model.app.DBPDataSourceRegistry;
|
||||
import org.jkiss.dbeaver.model.app.DBPWorkspace;
|
||||
import org.jkiss.dbeaver.model.auth.SMCredentials;
|
||||
import org.jkiss.dbeaver.model.auth.SMCredentialsProvider;
|
||||
import org.jkiss.dbeaver.model.fs.lock.FileLockController;
|
||||
import org.jkiss.dbeaver.model.auth.SMObjectType;
|
||||
import org.jkiss.dbeaver.model.fs.lock.LockManager;
|
||||
import org.jkiss.dbeaver.model.fs.lock.LockOptions;
|
||||
import org.jkiss.dbeaver.model.fs.lock.LockTarget;
|
||||
import org.jkiss.dbeaver.model.impl.app.BaseProjectImpl;
|
||||
import org.jkiss.dbeaver.model.impl.auth.SessionContextImpl;
|
||||
import org.jkiss.dbeaver.model.navigator.DBNLocalFolder;
|
||||
import org.jkiss.dbeaver.model.net.DBWNetworkProfile;
|
||||
import org.jkiss.dbeaver.model.rm.*;
|
||||
import org.jkiss.dbeaver.model.security.SMAdminController;
|
||||
import org.jkiss.dbeaver.model.security.SMObjectType;
|
||||
import org.jkiss.dbeaver.model.sql.DBQuotaException;
|
||||
import org.jkiss.dbeaver.model.websocket.event.MessageType;
|
||||
import org.jkiss.dbeaver.model.websocket.event.WSSessionLogUpdatedEvent;
|
||||
import org.jkiss.dbeaver.model.websocket.event.datasource.WSDataSourceEvent;
|
||||
import org.jkiss.dbeaver.model.websocket.event.datasource.WSDataSourceProperty;
|
||||
import org.jkiss.dbeaver.model.websocket.event.datasource.WSDatasourceFolderEvent;
|
||||
import org.jkiss.dbeaver.model.websocket.event.datasource.WSNetworkProfileEvent;
|
||||
import org.jkiss.dbeaver.registry.DataSourceDescriptor;
|
||||
import org.jkiss.dbeaver.registry.DataSourceParseResults;
|
||||
import org.jkiss.dbeaver.registry.ResourceTypeDescriptor;
|
||||
import org.jkiss.dbeaver.registry.ResourceTypeRegistry;
|
||||
import org.jkiss.dbeaver.runtime.DBWorkbench;
|
||||
import org.jkiss.dbeaver.utils.GeneralUtils;
|
||||
import org.jkiss.utils.CommonUtils;
|
||||
import org.jkiss.utils.IOUtils;
|
||||
import org.jkiss.utils.Pair;
|
||||
@@ -90,9 +95,10 @@ public class LocalResourceController extends BaseLocalResourceController {
|
||||
@NotNull Path rootPath,
|
||||
@NotNull Path userProjectsPath,
|
||||
@NotNull Path sharedProjectsPath,
|
||||
@NotNull Supplier<SMAdminController> smControllerSupplier
|
||||
@NotNull Supplier<SMAdminController> smControllerSupplier,
|
||||
@NotNull LockManager lockController
|
||||
) throws DBException {
|
||||
super(workspace, new FileLockController(ServletAppUtils.getServletApplication().getApplicationInstanceId()));
|
||||
super(workspace, lockController);
|
||||
this.credentialsProvider = credentialsProvider;
|
||||
this.rootPath = rootPath;
|
||||
this.userProjectsPath = userProjectsPath;
|
||||
@@ -101,7 +107,7 @@ public class LocalResourceController extends BaseLocalResourceController {
|
||||
|
||||
this.globalProjectName = DBWorkbench.getPlatform().getApplication().getDefaultProjectName();
|
||||
this.fileHandlers = RMFileOperationHandlersRegistry.getInstance().getFileHandlers();
|
||||
this.sharedProjectsMetadataInfo = new ProjectsMetadataInfo(sharedProjectsPath);
|
||||
this.sharedProjectsMetadataInfo = new ProjectsMetadataInfo(sharedProjectsPath, lockController);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@@ -280,7 +286,7 @@ public class LocalResourceController extends BaseLocalResourceController {
|
||||
throw new DBException("Error creating shared project path", e);
|
||||
}
|
||||
}
|
||||
validateResourcePath(name);
|
||||
GeneralUtils.validateResourceNameUnconditionally(name);
|
||||
validateProjectName(null, name);
|
||||
var projectPath = sharedProjectsPath.resolve(name);
|
||||
if (Files.exists(projectPath)) {
|
||||
@@ -303,7 +309,7 @@ public class LocalResourceController extends BaseLocalResourceController {
|
||||
@Override
|
||||
public RMProject updateProject(@NotNull String projectId, @NotNull RMProjectInfo projectInfo) throws DBException {
|
||||
validateProjectName(projectId, projectInfo.getName());
|
||||
try (var ignoredLock = lockController.lock(projectId, "updateProject")) {
|
||||
try (var ignoredLock = lockController.lock(LockTarget.of(projectId), LockOptions.of("updateProject"))) {
|
||||
RMLocalProject project = getWebProject(projectId, false);
|
||||
Path targetPath = getProjectPath(projectId);
|
||||
if (!Files.exists(targetPath)) {
|
||||
@@ -330,7 +336,7 @@ public class LocalResourceController extends BaseLocalResourceController {
|
||||
|
||||
@Override
|
||||
public void deleteProject(@NotNull String projectId) throws DBException {
|
||||
try (var ignoredLock = lockController.lock(projectId, "deleteProject")) {
|
||||
try (var ignoredLock = lockController.lock(LockTarget.of(projectId), LockOptions.of("deleteProject"))) {
|
||||
Path targetPath = getProjectPath(projectId);
|
||||
if (!Files.exists(targetPath)) {
|
||||
log.error(MessageFormat.format("Project folder ''{0}'' is not found", projectId));
|
||||
@@ -403,8 +409,13 @@ public class LocalResourceController extends BaseLocalResourceController {
|
||||
registry::createDataSource
|
||||
)
|
||||
);
|
||||
Map<String, DBWNetworkProfile> oldNetworkProfiles = registry.getNetworkProfiles().getProfiles().stream()
|
||||
.collect(Collectors.toMap(
|
||||
DBWNetworkProfile::getProfileId,
|
||||
p -> p
|
||||
));
|
||||
DataSourceParseResults parseResults = super.updateProjectDataSourcesConfig(projectId, configuration, dataSourceIds);
|
||||
sendDataSourcesConfigUpdatedEvent(registry, oldDataSources, parseResults);
|
||||
sendConfigUpdatedEvent(registry, oldDataSources, oldNetworkProfiles, parseResults);
|
||||
return parseResults != null;
|
||||
}
|
||||
|
||||
@@ -497,14 +508,48 @@ public class LocalResourceController extends BaseLocalResourceController {
|
||||
return DBNLocalFolder.makeLocalFolderItemPath(projectId, folderPath);
|
||||
}
|
||||
|
||||
private void sendDataSourcesConfigUpdatedEvent(
|
||||
private void sendConfigUpdatedEvent(
|
||||
@NotNull DBPDataSourceRegistry registry,
|
||||
@NotNull Map<String, DataSourceDescriptor> oldDataSources,
|
||||
@NotNull Map<String, DBWNetworkProfile> oldNetworkProfiles,
|
||||
@Nullable DataSourceParseResults parseResults
|
||||
) {
|
||||
if (parseResults == null || credentialsProvider.getActiveUserCredentials() == null || oldDataSources.isEmpty()) {
|
||||
if (parseResults == null || credentialsProvider.getActiveUserCredentials() == null) {
|
||||
return;
|
||||
}
|
||||
boolean profilesChanged = !parseResults.removedProfiles.isEmpty();
|
||||
|
||||
// We don't need to check for updated profiles if there are already added or removed profiles
|
||||
if (!profilesChanged) {
|
||||
for (DBWNetworkProfile updatedProfile : parseResults.updatedProfiles) {
|
||||
if (oldNetworkProfiles.containsKey(updatedProfile.getProfileId())) {
|
||||
DBWNetworkProfile oldProfile = oldNetworkProfiles.get(updatedProfile.getProfileId());
|
||||
if (!oldProfile.equalConfigurations(updatedProfile)) {
|
||||
profilesChanged = true;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
// profile was added
|
||||
profilesChanged = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (profilesChanged) {
|
||||
ServletAppUtils.getServletApplication().getEventController().addEvent(
|
||||
WSNetworkProfileEvent.update(
|
||||
credentialsProvider.getActiveUserCredentials().getSmSessionId(),
|
||||
credentialsProvider.getActiveUserCredentials().getUserId(),
|
||||
registry.getProject().getId()
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
if (oldDataSources.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
List<String> updatedConfigurationDataSourceIds = new ArrayList<>();
|
||||
List<String> updatedNameDataSourceIds = new ArrayList<>();
|
||||
List<String> updatedInternalConfigurationDataSourceIds = new ArrayList<>();
|
||||
@@ -608,7 +653,6 @@ public class LocalResourceController extends BaseLocalResourceController {
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@@ -699,7 +743,7 @@ public class LocalResourceController extends BaseLocalResourceController {
|
||||
@NotNull String resourcePath,
|
||||
boolean isFolder
|
||||
) throws DBException {
|
||||
try (var ignoredLock = lockController.lock(projectId, "createResource")) {
|
||||
try (var ignoredLock = lockController.lock(LockTarget.of(projectId), LockOptions.of("createResource"))) {
|
||||
validateResourcePath(resourcePath);
|
||||
Path targetPath = getTargetPath(projectId, resourcePath);
|
||||
if (Files.exists(targetPath)) {
|
||||
@@ -731,7 +775,7 @@ public class LocalResourceController extends BaseLocalResourceController {
|
||||
@NotNull String oldResourcePath,
|
||||
@NotNull String newResourcePath
|
||||
) throws DBException {
|
||||
try (var ignoredLock = lockController.lock(projectId, "moveResource")) {
|
||||
try (var ignoredLock = lockController.lock(LockTarget.of(projectId), LockOptions.of("moveResource"))) {
|
||||
var normalizedOldResourcePath = CommonUtils.normalizeResourcePath(oldResourcePath);
|
||||
var normalizedNewResourcePath = CommonUtils.normalizeResourcePath(newResourcePath);
|
||||
if (log.isDebugEnabled()) {
|
||||
@@ -803,7 +847,7 @@ public class LocalResourceController extends BaseLocalResourceController {
|
||||
|
||||
@Override
|
||||
public void deleteResource(@NotNull String projectId, @NotNull String resourcePath, boolean recursive) throws DBException {
|
||||
try (var ignoredLock = lockController.lock(projectId, "deleteResource")) {
|
||||
try (var ignoredLock = lockController.lock(LockTarget.of(projectId), LockOptions.of("deleteResource"))) {
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug("Removing resource from '" + resourcePath + "' in project '" + projectId + "'" + (recursive ? " recursive" : ""));
|
||||
}
|
||||
@@ -891,7 +935,7 @@ public class LocalResourceController extends BaseLocalResourceController {
|
||||
@NotNull byte[] data,
|
||||
boolean forceOverwrite
|
||||
) throws DBException {
|
||||
try (var ignoredLock = lockController.lock(projectId, "setResourceContents")) {
|
||||
try (var ignoredLock = lockController.lock(LockTarget.of(projectId), LockOptions.of("setResourceContents"))) {
|
||||
validateResourcePath(resourcePath);
|
||||
Number fileSizeLimit = ServletAppUtils.getServletApplication()
|
||||
.getAppConfiguration()
|
||||
@@ -936,7 +980,7 @@ public class LocalResourceController extends BaseLocalResourceController {
|
||||
@NotNull String propertyName,
|
||||
@Nullable Object propertyValue
|
||||
) throws DBException {
|
||||
try (var ignoredLock = lockController.lock(projectId, "resourcePropertyUpdate")) {
|
||||
try (var ignoredLock = lockController.lock(LockTarget.of(projectId), LockOptions.of("resourcePropertyUpdate"))) {
|
||||
validateResourcePath(resourcePath);
|
||||
RMLocalProject webProject = getWebProject(projectId, false);
|
||||
doFileWriteOperation(projectId, webProject.getMetadataFilePath(),
|
||||
@@ -957,7 +1001,7 @@ public class LocalResourceController extends BaseLocalResourceController {
|
||||
@NotNull String resourcePath,
|
||||
@NotNull Map<String, Object> properties
|
||||
) throws DBException {
|
||||
try (var ignoredLock = lockController.lock(projectId, "resourcePropertyUpdate")) {
|
||||
try (var ignoredLock = lockController.lock(LockTarget.of(projectId), LockOptions.of("resourcePropertyUpdate"))) {
|
||||
validateResourcePath(resourcePath);
|
||||
RMLocalProject webProject = getWebProject(projectId, false);
|
||||
doFileWriteOperation(projectId, webProject.getMetadataFilePath(),
|
||||
@@ -984,8 +1028,24 @@ public class LocalResourceController extends BaseLocalResourceController {
|
||||
try {
|
||||
while (resourcePath.startsWith("/")) resourcePath = resourcePath.substring(1);
|
||||
Path targetPath = projectPath.resolve(resourcePath).normalize();
|
||||
if (!targetPath.startsWith(projectPath)) {
|
||||
throw new DBException("Invalid resource path");
|
||||
// hack for custom path implementations that returns / as root path
|
||||
Iterator<Path> projectParts = projectPath.iterator();
|
||||
Iterator<Path> targetParts = targetPath.iterator();
|
||||
Iterator<Path> unnormalizedTargetParts = projectPath.resolve(resourcePath).iterator();
|
||||
while (projectParts.hasNext()) {
|
||||
if (!targetParts.hasNext()) {
|
||||
throw new DBException("Invalid resource path");
|
||||
}
|
||||
Path projectPart = projectParts.next();
|
||||
Path targetPart = targetParts.next();
|
||||
unnormalizedTargetParts.next();
|
||||
if (!projectPart.equals(targetPart)) {
|
||||
throw new DBException("Invalid resource path");
|
||||
}
|
||||
}
|
||||
// validating unnormalized target parts to avoid invalid characters in resource names
|
||||
while (unnormalizedTargetParts.hasNext()) {
|
||||
GeneralUtils.validateResourceNameUnconditionally(unnormalizedTargetParts.next().toString());
|
||||
}
|
||||
return targetPath;
|
||||
} catch (InvalidPathException e) {
|
||||
@@ -1103,6 +1163,7 @@ public class LocalResourceController extends BaseLocalResourceController {
|
||||
RMProjectName project = WebRMUtils.parseProjectName(projectId);
|
||||
RMProjectType type = project.getType();
|
||||
String projectName = project.getName();
|
||||
GeneralUtils.validateResourceNameUnconditionally(projectName);
|
||||
switch (type) {
|
||||
case GLOBAL:
|
||||
if (!projectName.equals(globalProjectName)) {
|
||||
@@ -1204,12 +1265,14 @@ public class LocalResourceController extends BaseLocalResourceController {
|
||||
return getProjectPath(projectId).toAbsolutePath().relativize(path).toString().replace('\\', IPath.SEPARATOR);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static Builder builder(
|
||||
SMCredentialsProvider credentialsProvider,
|
||||
DBPWorkspace workspace,
|
||||
Supplier<SMAdminController> smControllerSupplier
|
||||
@NotNull SMCredentialsProvider credentialsProvider,
|
||||
@NotNull DBPWorkspace workspace,
|
||||
@NotNull LockManager lockController,
|
||||
@NotNull Supplier<SMAdminController> smControllerSupplier
|
||||
) {
|
||||
return new Builder(workspace, credentialsProvider, smControllerSupplier);
|
||||
return new Builder(workspace, credentialsProvider, lockController, smControllerSupplier);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -1225,10 +1288,13 @@ public class LocalResourceController extends BaseLocalResourceController {
|
||||
protected Path rootPath;
|
||||
protected Path userProjectsPath;
|
||||
protected Path sharedProjectsPath;
|
||||
protected LockManager lockController;
|
||||
|
||||
protected Builder(
|
||||
DBPWorkspace workspace, SMCredentialsProvider credentialsProvider,
|
||||
Supplier<SMAdminController> smControllerSupplier
|
||||
@NotNull DBPWorkspace workspace,
|
||||
@NotNull SMCredentialsProvider credentialsProvider,
|
||||
@NotNull LockManager lockController,
|
||||
@NotNull Supplier<SMAdminController> smControllerSupplier
|
||||
) {
|
||||
this.workspace = workspace;
|
||||
this.credentialsProvider = credentialsProvider;
|
||||
@@ -1236,25 +1302,38 @@ public class LocalResourceController extends BaseLocalResourceController {
|
||||
this.rootPath = RMUtils.getRootPath();
|
||||
this.userProjectsPath = RMUtils.getUserProjectsPath();
|
||||
this.sharedProjectsPath = RMUtils.getSharedProjectsPath();
|
||||
this.lockController = lockController;
|
||||
}
|
||||
|
||||
public Builder setRootPath(Path rootPath) {
|
||||
@NotNull
|
||||
public Builder setRootPath(@NotNull Path rootPath) {
|
||||
this.rootPath = rootPath;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setUserProjectsPath(Path userProjectsPath) {
|
||||
@NotNull
|
||||
public Builder setUserProjectsPath(@NotNull Path userProjectsPath) {
|
||||
this.userProjectsPath = userProjectsPath;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setSharedProjectsPath(Path sharedProjectsPath) {
|
||||
@NotNull
|
||||
public Builder setSharedProjectsPath(@NotNull Path sharedProjectsPath) {
|
||||
this.sharedProjectsPath = sharedProjectsPath;
|
||||
return this;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public LocalResourceController build() throws DBException {
|
||||
return new LocalResourceController(workspace, credentialsProvider, rootPath, userProjectsPath, sharedProjectsPath, smController);
|
||||
return new LocalResourceController(
|
||||
workspace,
|
||||
credentialsProvider,
|
||||
rootPath,
|
||||
userProjectsPath,
|
||||
sharedProjectsPath,
|
||||
smController,
|
||||
lockController
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+8
-7
@@ -17,14 +17,15 @@
|
||||
package io.cloudbeaver.model.rm.local;
|
||||
|
||||
import com.google.gson.reflect.TypeToken;
|
||||
import io.cloudbeaver.utils.ServletAppUtils;
|
||||
import org.jkiss.code.NotNull;
|
||||
import org.jkiss.code.Nullable;
|
||||
import org.jkiss.dbeaver.DBException;
|
||||
import org.jkiss.dbeaver.Log;
|
||||
import org.jkiss.dbeaver.model.app.DBPProject;
|
||||
import org.jkiss.dbeaver.model.data.json.JSONUtils;
|
||||
import org.jkiss.dbeaver.model.fs.lock.FileLockController;
|
||||
import org.jkiss.dbeaver.model.fs.lock.LockManager;
|
||||
import org.jkiss.dbeaver.model.fs.lock.LockOptions;
|
||||
import org.jkiss.dbeaver.model.fs.lock.LockTarget;
|
||||
import org.jkiss.dbeaver.model.impl.app.BaseProjectImpl;
|
||||
import org.jkiss.dbeaver.model.rm.RMProjectInfo;
|
||||
import org.jkiss.dbeaver.model.rm.RMProjectType;
|
||||
@@ -45,12 +46,12 @@ public class ProjectsMetadataInfo {
|
||||
|
||||
private final Map<String, RMProjectInfo> projectsInfo = new LinkedHashMap<>();
|
||||
private final Path projectsPath;
|
||||
private final FileLockController lockController;
|
||||
private final LockManager lockController;
|
||||
|
||||
|
||||
public ProjectsMetadataInfo(@NotNull Path projectsPath) throws DBException {
|
||||
public ProjectsMetadataInfo(@NotNull Path projectsPath, @NotNull LockManager lockController) throws DBException {
|
||||
this.projectsPath = projectsPath;
|
||||
this.lockController = new FileLockController(ServletAppUtils.getServletApplication().getApplicationInstanceId());
|
||||
this.lockController = lockController;
|
||||
readProjectInfos(projectsPath);
|
||||
}
|
||||
|
||||
@@ -139,7 +140,7 @@ public class ProjectsMetadataInfo {
|
||||
}
|
||||
|
||||
private void saveProjectsInfo() {
|
||||
try (var lock = lockController.lock(PROJECTS_INFO_FILE_NAME, "saveProjectsInfo")) {
|
||||
try (var lock = lockController.lock(LockTarget.of(PROJECTS_INFO_FILE_NAME), LockOptions.of("saveProjectsInfo"))) {
|
||||
log.info("Saving project information");
|
||||
Files.writeString(projectsPath.resolve(PROJECTS_INFO_FILE_NAME), JSONUtils.GSON.toJson(projectsInfo));
|
||||
} catch (IOException e) {
|
||||
@@ -148,4 +149,4 @@ public class ProjectsMetadataInfo {
|
||||
log.error("Error locking file " + PROJECTS_INFO_FILE_NAME + ": " + e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+47
-13
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* DBeaver - Universal Database Manager
|
||||
* Copyright (C) 2010-2024 DBeaver Corp and others
|
||||
* Copyright (C) 2010-2026 DBeaver Corp and others
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -55,14 +55,21 @@ public abstract class BaseWebSession extends AbstractSessionPersistent {
|
||||
@NotNull
|
||||
protected final ServletApplication application;
|
||||
protected volatile long lastAccessTime;
|
||||
@NotNull
|
||||
private final SessionType sessionType;
|
||||
|
||||
private final List<CBWebSessionEventHandler> sessionEventHandlers = new CopyOnWriteArrayList<>();
|
||||
private WebSessionEventsFilter eventsFilter;
|
||||
private final WebSessionWorkspace workspace;
|
||||
|
||||
public BaseWebSession(@NotNull String id, @NotNull ServletApplication application) throws DBException {
|
||||
public BaseWebSession(
|
||||
@NotNull String id,
|
||||
@NotNull ServletApplication application,
|
||||
@NotNull SessionType sessionType
|
||||
) throws DBException {
|
||||
this.id = id;
|
||||
this.application = application;
|
||||
this.sessionType = sessionType;
|
||||
this.createTime = System.currentTimeMillis();
|
||||
this.lastAccessTime = this.createTime;
|
||||
this.workspace = createWebWorkspace();
|
||||
@@ -102,7 +109,7 @@ public abstract class BaseWebSession extends AbstractSessionPersistent {
|
||||
}
|
||||
}
|
||||
|
||||
public abstract void addSessionError(Throwable exception);
|
||||
public abstract void addSessionError(@NotNull Throwable exception);
|
||||
|
||||
public void addEventHandler(@NotNull CBWebSessionEventHandler handler) {
|
||||
synchronized (sessionEventHandlers) {
|
||||
@@ -116,11 +123,21 @@ public abstract class BaseWebSession extends AbstractSessionPersistent {
|
||||
}
|
||||
}
|
||||
|
||||
public synchronized boolean updateSMSession(SMAuthInfo smAuthInfo) throws DBException {
|
||||
public void migrateEventHandlersTo(@NotNull BaseWebSession target) {
|
||||
synchronized (sessionEventHandlers) {
|
||||
for (CBWebSessionEventHandler handler : sessionEventHandlers) {
|
||||
handler.migrateToSession(target);
|
||||
target.addEventHandler(handler);
|
||||
}
|
||||
sessionEventHandlers.clear();
|
||||
}
|
||||
}
|
||||
|
||||
public boolean updateSMSession(SMAuthInfo smAuthInfo) throws DBException {
|
||||
return userContext.refresh(smAuthInfo);
|
||||
}
|
||||
|
||||
public synchronized void refreshUserData() {
|
||||
public void refreshUserData() {
|
||||
try {
|
||||
userContext.refreshPermissions();
|
||||
if (userContext.isAuthorizedInSecurityManager()) {
|
||||
@@ -138,6 +155,17 @@ public abstract class BaseWebSession extends AbstractSessionPersistent {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Refreshes user permissions, teams and accessible projects.
|
||||
* <p>
|
||||
* Unlike {@link #refreshUserData()} this method must not re-create heavyweight session state
|
||||
* (navigator model, session projects, connection caches), so it is safe to call
|
||||
* for foreign sessions on server-wide events.
|
||||
*/
|
||||
public void refreshUserPermissions() {
|
||||
refreshUserData();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public SMAuthSpace getSessionSpace() {
|
||||
@@ -150,8 +178,8 @@ public abstract class BaseWebSession extends AbstractSessionPersistent {
|
||||
return workspace.getAuthContext();
|
||||
}
|
||||
|
||||
protected void clearSessionContext() {
|
||||
this.workspace.getAuthContext().clear();
|
||||
protected synchronized void clearSessionContext() {
|
||||
this.workspace.getAuthContext().clearContext(false);
|
||||
this.workspace.getAuthContext().addSession(this);
|
||||
}
|
||||
|
||||
@@ -176,27 +204,33 @@ public abstract class BaseWebSession extends AbstractSessionPersistent {
|
||||
return lastAccessTime;
|
||||
}
|
||||
|
||||
public synchronized void touchSession() {
|
||||
@NotNull
|
||||
public SessionType getSessionType() {
|
||||
return sessionType;
|
||||
}
|
||||
|
||||
public void touchSession() {
|
||||
this.lastAccessTime = System.currentTimeMillis();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public synchronized WebUserContext getUserContext() {
|
||||
public WebUserContext getUserContext() {
|
||||
return userContext;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
super.close();
|
||||
cleanUpSession(true);
|
||||
super.close();
|
||||
}
|
||||
|
||||
public void close(boolean clearTokens, boolean sendSessionExpiredEvent) {
|
||||
cleanUpSession(sendSessionExpiredEvent);
|
||||
super.close();
|
||||
}
|
||||
|
||||
private void cleanUpSession(boolean sendSessionExpiredEvent) {
|
||||
application.getEventController().addEvent(new WSEventDeleteTempFile(getSessionId()));
|
||||
application.getEventController().addEvent(new WSEventDeleteTempFile(getUserContext().getSmSessionId()));
|
||||
synchronized (sessionEventHandlers) {
|
||||
var sessionExpiredEvent = new WSSessionExpiredEvent();
|
||||
for (CBWebSessionEventHandler sessionEventHandler : sessionEventHandlers) {
|
||||
@@ -226,7 +260,7 @@ public abstract class BaseWebSession extends AbstractSessionPersistent {
|
||||
this.eventsFilter = eventsFilter;
|
||||
}
|
||||
|
||||
public boolean isProjectAccessible(String projectId) {
|
||||
public boolean isProjectAccessible(@NotNull String projectId) {
|
||||
return userContext.getAccessibleProjectIds().contains(projectId);
|
||||
}
|
||||
|
||||
@@ -243,7 +277,7 @@ public abstract class BaseWebSession extends AbstractSessionPersistent {
|
||||
userContext.getAccessibleProjectIds().remove(projectId);
|
||||
}
|
||||
|
||||
public abstract void addSessionMessage(WebServerMessage message);
|
||||
public abstract void addSessionMessage(@NotNull WebServerMessage message);
|
||||
|
||||
@Property
|
||||
public boolean isValid() {
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
/*
|
||||
* DBeaver - Universal Database Manager
|
||||
* Copyright (C) 2010-2026 DBeaver Corp and others
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package io.cloudbeaver.model.session;
|
||||
|
||||
public enum SessionType {
|
||||
WEB,
|
||||
HEADLESS,
|
||||
API_TOKEN
|
||||
}
|
||||
+4
-4
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* DBeaver - Universal Database Manager
|
||||
* Copyright (C) 2010-2024 DBeaver Corp and others
|
||||
* Copyright (C) 2010-2026 DBeaver Corp and others
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -31,16 +31,16 @@ public class WebHeadlessSession extends BaseWebSession {
|
||||
@NotNull String id,
|
||||
@NotNull ServletAuthApplication application
|
||||
) throws DBException {
|
||||
super(id, application);
|
||||
super(id, application, SessionType.HEADLESS);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addSessionError(Throwable exception) {
|
||||
public void addSessionError(@NotNull Throwable exception) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addSessionMessage(WebServerMessage message) {
|
||||
public void addSessionMessage(@NotNull WebServerMessage message) {
|
||||
|
||||
}
|
||||
|
||||
|
||||
+25
-4
@@ -17,6 +17,7 @@
|
||||
package io.cloudbeaver.model.session;
|
||||
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import org.jkiss.code.NotNull;
|
||||
import org.jkiss.code.Nullable;
|
||||
import org.jkiss.utils.HttpConstants;
|
||||
|
||||
@@ -31,12 +32,16 @@ public class WebHttpRequestInfo {
|
||||
private final String lastRemoteAddress;
|
||||
@Nullable
|
||||
private final String lastRemoteUserAgent;
|
||||
@NotNull
|
||||
private final SessionType sessionType;
|
||||
|
||||
public WebHttpRequestInfo(HttpServletRequest request) {
|
||||
this.id = request.getSession() == null ? null : request.getSession().getId();
|
||||
this.locale = request.getAttribute("locale");
|
||||
this.lastRemoteAddress = request.getRemoteAddr();
|
||||
this.lastRemoteUserAgent = request.getHeader(USER_AGENT);
|
||||
this(
|
||||
request.getSession() == null ? null : request.getSession().getId(),
|
||||
request.getAttribute("locale"),
|
||||
request.getRemoteAddr(),
|
||||
request.getHeader(USER_AGENT)
|
||||
);
|
||||
}
|
||||
|
||||
public WebHttpRequestInfo(
|
||||
@@ -44,11 +49,22 @@ public class WebHttpRequestInfo {
|
||||
@Nullable Object locale,
|
||||
@Nullable String lastRemoteAddress,
|
||||
@Nullable String lastRemoteUserAgent
|
||||
) {
|
||||
this(id, locale, lastRemoteAddress, lastRemoteUserAgent, SessionType.WEB);
|
||||
}
|
||||
|
||||
public WebHttpRequestInfo(
|
||||
@Nullable String id,
|
||||
@Nullable Object locale,
|
||||
@Nullable String lastRemoteAddress,
|
||||
@Nullable String lastRemoteUserAgent,
|
||||
@NotNull SessionType sessionType
|
||||
) {
|
||||
this.id = id;
|
||||
this.locale = locale;
|
||||
this.lastRemoteAddress = lastRemoteAddress;
|
||||
this.lastRemoteUserAgent = lastRemoteUserAgent;
|
||||
this.sessionType = sessionType;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@@ -70,4 +86,9 @@ public class WebHttpRequestInfo {
|
||||
public String getLastRemoteUserAgent() {
|
||||
return lastRemoteUserAgent;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public SessionType getSessionType() {
|
||||
return sessionType;
|
||||
}
|
||||
}
|
||||
|
||||
+174
-56
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* DBeaver - Universal Database Manager
|
||||
* Copyright (C) 2010-2025 DBeaver Corp and others
|
||||
* Copyright (C) 2010-2026 DBeaver Corp and others
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -64,6 +64,8 @@ import java.time.Instant;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.concurrent.locks.ReadWriteLock;
|
||||
import java.util.concurrent.locks.ReentrantReadWriteLock;
|
||||
import java.util.stream.Collectors;
|
||||
/**
|
||||
* Web session.
|
||||
@@ -99,6 +101,7 @@ public class WebSession extends BaseWebSession
|
||||
private final List<WebAuthInfo> authTokens = new ArrayList<>();
|
||||
|
||||
private DBNModel navigatorModel;
|
||||
private final ReadWriteLock navigatorModelLock = new ReentrantReadWriteLock(true);
|
||||
private final DBRProgressMonitor progressMonitor = new SessionProgressMonitor();
|
||||
private final Map<String, DBWSessionHandler<WebSession>> sessionHandlers;
|
||||
private final WebDataSourceConnectEventListener connectListener = new WebDataSourceConnectEventListener(this);
|
||||
@@ -113,7 +116,8 @@ public class WebSession extends BaseWebSession
|
||||
CommonUtils.toString(requestInfo.getLocale()),
|
||||
application,
|
||||
sessionHandlers,
|
||||
requestInfo.getLastRemoteAddress()
|
||||
requestInfo.getLastRemoteAddress(),
|
||||
requestInfo.getSessionType()
|
||||
);
|
||||
updateSessionParameters(requestInfo);
|
||||
}
|
||||
@@ -125,7 +129,18 @@ public class WebSession extends BaseWebSession
|
||||
@NotNull Map<String, DBWSessionHandler<WebSession>> sessionHandlers,
|
||||
@Nullable String remoteAddr
|
||||
) throws DBException {
|
||||
super(id, application);
|
||||
this(id, locale, application, sessionHandlers, remoteAddr, SessionType.WEB);
|
||||
}
|
||||
|
||||
protected WebSession(
|
||||
@NotNull String id,
|
||||
@Nullable String locale,
|
||||
@NotNull ServletApplication application,
|
||||
@NotNull Map<String, DBWSessionHandler<WebSession>> sessionHandlers,
|
||||
@Nullable String remoteAddr,
|
||||
@NotNull SessionType sessionType
|
||||
) throws DBException {
|
||||
super(id, application, sessionType);
|
||||
if (CommonUtils.isEmpty(remoteAddr)) {
|
||||
throw new DBException("Remote address cannot be empty");
|
||||
}
|
||||
@@ -185,11 +200,12 @@ public class WebSession extends BaseWebSession
|
||||
this.cacheExpired = cacheExpired;
|
||||
}
|
||||
|
||||
public synchronized WebUser getUser() {
|
||||
public WebUser getUser() {
|
||||
return this.userContext.getUser();
|
||||
}
|
||||
|
||||
public synchronized Map<String, String> getUserMetaParameters() {
|
||||
@NotNull
|
||||
public Map<String, String> getUserMetaParameters() {
|
||||
var user = getUser();
|
||||
if (user == null) {
|
||||
return Map.of();
|
||||
@@ -201,20 +217,20 @@ public class WebSession extends BaseWebSession
|
||||
return allMetaParams;
|
||||
}
|
||||
|
||||
public synchronized String getUserId() {
|
||||
public String getUserId() {
|
||||
return userContext.getUserId();
|
||||
}
|
||||
|
||||
public synchronized boolean hasPermission(String perm) {
|
||||
return getSessionPermissions().contains(DBWConstants.PERMISSION_ADMIN) ||
|
||||
getSessionPermissions().contains(perm);
|
||||
Set<String> sessionPermissions = getSessionPermissions();
|
||||
return sessionPermissions.contains(DBWConstants.PERMISSION_ADMIN) || sessionPermissions.contains(perm);
|
||||
}
|
||||
|
||||
public synchronized boolean isAuthorizedInSecurityManager() {
|
||||
public boolean isAuthorizedInSecurityManager() {
|
||||
return userContext.isAuthorizedInSecurityManager();
|
||||
}
|
||||
|
||||
public synchronized Set<String> getSessionPermissions() {
|
||||
public Set<String> getSessionPermissions() {
|
||||
if (userContext.getUserPermissions() == null) {
|
||||
refreshSessionAuth();
|
||||
}
|
||||
@@ -222,12 +238,12 @@ public class WebSession extends BaseWebSession
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public synchronized SMController getSecurityController() {
|
||||
public SMController getSecurityController() {
|
||||
return userContext.getSecurityController();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public synchronized SMAdminController getAdminSecurityController() throws DBException {
|
||||
public SMAdminController getAdminSecurityController() throws DBException {
|
||||
if (!hasPermission(DBWConstants.PERMISSION_ADMIN)) {
|
||||
throw new DBException("Admin permissions required");
|
||||
}
|
||||
@@ -235,25 +251,67 @@ public class WebSession extends BaseWebSession
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public synchronized RMController getRmController() {
|
||||
public RMController getRmController() {
|
||||
return userContext.getRmController();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public synchronized DBFileController getFileController() {
|
||||
public DBFileController getFileController() {
|
||||
return userContext.getFileController();
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void refreshUserData() {
|
||||
super.refreshUserData();
|
||||
refreshSessionAuth();
|
||||
public void refreshUserData() {
|
||||
refreshUserPermissions();
|
||||
|
||||
initNavigatorModel();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void refreshUserPermissions() {
|
||||
super.refreshUserData();
|
||||
refreshSessionAuth();
|
||||
if (getUserId() == null && globalProject != null) {
|
||||
// refreshSessionAuth() updates accessible connections for named users only,
|
||||
// for anonymous sessions this was done by initNavigatorModel()
|
||||
globalProject.refreshAccessibleConnectionIds();
|
||||
}
|
||||
syncSessionProjects();
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds/removes only those session projects whose accessibility actually changed.
|
||||
* <p>
|
||||
* Unlike {@link #initNavigatorModel()} this keeps existing projects, and therefore
|
||||
* their live connections and SQL contexts, intact.
|
||||
*/
|
||||
private void syncSessionProjects() {
|
||||
if (getNavigatorModel() == null) {
|
||||
// model was never initialized for this session - nothing to sync
|
||||
return;
|
||||
}
|
||||
try {
|
||||
Map<String, RMProject> actualProjects = new LinkedHashMap<>();
|
||||
for (RMProject rmProject : getRmController().listAccessibleProjects()) {
|
||||
actualProjects.put(rmProject.getId(), rmProject);
|
||||
}
|
||||
for (WebSessionProjectImpl project : new ArrayList<>(getWorkspace().getProjects())) {
|
||||
if (actualProjects.remove(project.getId()) == null && !project.isInMemory()) {
|
||||
// in-memory (anonymous) projects are never listed by RM
|
||||
deleteSessionProject(project);
|
||||
}
|
||||
}
|
||||
for (RMProject rmProject : actualProjects.values()) {
|
||||
createWebProject(rmProject);
|
||||
}
|
||||
} catch (DBException e) {
|
||||
addSessionError(e);
|
||||
log.error("Error synchronizing accessible projects", e);
|
||||
}
|
||||
}
|
||||
|
||||
// Note: for admin use only
|
||||
public synchronized void resetUserState(boolean needResetUserCache) throws DBException {
|
||||
public void resetUserState(boolean needResetUserCache) throws DBException {
|
||||
clearAuthTokens();
|
||||
if (needResetUserCache) {
|
||||
try {
|
||||
@@ -267,7 +325,7 @@ public class WebSession extends BaseWebSession
|
||||
clearSessionContext();
|
||||
}
|
||||
|
||||
public synchronized void resetUserState() throws DBException {
|
||||
public void resetUserState() throws DBException {
|
||||
resetUserState(true);
|
||||
}
|
||||
|
||||
@@ -286,21 +344,25 @@ public class WebSession extends BaseWebSession
|
||||
}
|
||||
|
||||
private void initNavigatorModel() {
|
||||
navigatorModelLock.writeLock().lock();
|
||||
try {
|
||||
// Cleanup current data
|
||||
if (this.navigatorModel != null) {
|
||||
this.navigatorModel.dispose();
|
||||
this.navigatorModel = null;
|
||||
}
|
||||
this.globalProject = null;
|
||||
|
||||
// Cleanup current data
|
||||
if (this.navigatorModel != null) {
|
||||
this.navigatorModel.dispose();
|
||||
this.navigatorModel = null;
|
||||
loadProjects();
|
||||
|
||||
this.navigatorModel = new DBNModel(DBWorkbench.getPlatform(), getWorkspace());
|
||||
this.navigatorModel.setModelAuthContext(getWorkspace().getAuthContext());
|
||||
this.navigatorModel.initialize();
|
||||
|
||||
this.locale = Locale.getDefault().getLanguage();
|
||||
} finally {
|
||||
navigatorModelLock.writeLock().unlock();
|
||||
}
|
||||
this.globalProject = null;
|
||||
|
||||
loadProjects();
|
||||
|
||||
this.navigatorModel = new DBNModel(DBWorkbench.getPlatform(), getWorkspace().getProjects());
|
||||
this.navigatorModel.setModelAuthContext(getWorkspace().getAuthContext());
|
||||
this.navigatorModel.initialize();
|
||||
|
||||
this.locale = Locale.getDefault().getLanguage();
|
||||
}
|
||||
|
||||
private void loadProjects() {
|
||||
@@ -368,11 +430,16 @@ public class WebSession extends BaseWebSession
|
||||
}
|
||||
|
||||
private void resetNavigationModel() {
|
||||
getWorkspace().getProjects().forEach(WebSessionProjectImpl::dispose);
|
||||
navigatorModelLock.writeLock().lock();
|
||||
try {
|
||||
getWorkspace().getProjects().forEach(WebSessionProjectImpl::dispose);
|
||||
|
||||
if (this.navigatorModel != null) {
|
||||
this.navigatorModel.dispose();
|
||||
this.navigatorModel = null;
|
||||
if (this.navigatorModel != null) {
|
||||
this.navigatorModel.dispose();
|
||||
this.navigatorModel = null;
|
||||
}
|
||||
} finally {
|
||||
navigatorModelLock.writeLock().unlock();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -415,16 +482,45 @@ public class WebSession extends BaseWebSession
|
||||
|
||||
@Nullable
|
||||
public DBNModel getNavigatorModel() {
|
||||
return navigatorModel;
|
||||
navigatorModelLock.readLock().lock();
|
||||
try {
|
||||
return navigatorModel;
|
||||
} finally {
|
||||
navigatorModelLock.readLock().unlock();
|
||||
}
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public DBNModel getNavigatorModelOrThrow() throws DBWebException {
|
||||
if (navigatorModel != null) {
|
||||
return navigatorModel;
|
||||
navigatorModelLock.readLock().lock();
|
||||
try {
|
||||
if (navigatorModel != null) {
|
||||
return navigatorModel;
|
||||
}
|
||||
throw new DBWebException("Navigator model is not found in session");
|
||||
} finally {
|
||||
navigatorModelLock.readLock().unlock();
|
||||
}
|
||||
throw new DBWebException("Navigator model is not found in session");
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes the project's lazy database branch while preventing the navigator model from being replaced.
|
||||
*/
|
||||
public void initializeProjectNavigator(@NotNull WebSessionProjectImpl project) {
|
||||
navigatorModelLock.readLock().lock();
|
||||
try {
|
||||
if (navigatorModel == null) {
|
||||
return;
|
||||
}
|
||||
var projectNode = navigatorModel.getRoot().getProjectNode(project);
|
||||
if (projectNode != null) {
|
||||
projectNode.getDatabases();
|
||||
}
|
||||
} finally {
|
||||
navigatorModelLock.readLock().unlock();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns and clears progress messages
|
||||
*/
|
||||
@@ -437,7 +533,7 @@ public class WebSession extends BaseWebSession
|
||||
}
|
||||
}
|
||||
|
||||
public synchronized void updateInfo(boolean isOldHttpSessionUsed) {
|
||||
public void updateInfo(boolean isOldHttpSessionUsed) {
|
||||
log.trace("Update session lifetime " + getSessionId() + " for user " + getUserId());
|
||||
touchSession();
|
||||
if (isOldHttpSessionUsed) {
|
||||
@@ -469,7 +565,6 @@ public class WebSession extends BaseWebSession
|
||||
public void close() {
|
||||
try {
|
||||
resetNavigationModel();
|
||||
resetSessionCache();
|
||||
} catch (Throwable e) {
|
||||
log.error(e);
|
||||
}
|
||||
@@ -486,7 +581,6 @@ public class WebSession extends BaseWebSession
|
||||
public void close(boolean clearTokens, boolean sendSessionExpiredEvent) {
|
||||
try {
|
||||
resetNavigationModel();
|
||||
resetSessionCache();
|
||||
} catch (Throwable e) {
|
||||
log.error(e);
|
||||
}
|
||||
@@ -501,6 +595,7 @@ public class WebSession extends BaseWebSession
|
||||
super.close(clearTokens, sendSessionExpiredEvent);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private List<WebAuthInfo> clearAuthTokens() throws DBException {
|
||||
ArrayList<WebAuthInfo> tokensCopy;
|
||||
synchronized (authTokens) {
|
||||
@@ -585,11 +680,13 @@ public class WebSession extends BaseWebSession
|
||||
}
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public WebAsyncTaskInfo createAndRunAsyncTask(@NotNull String taskName, @NotNull WebAsyncTaskProcessor<?> runnable) {
|
||||
WebAsyncTaskInfo asyncTask = createAsyncTask(taskName);
|
||||
return runAsyncTask(asyncTask, runnable);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public WebAsyncTaskInfo runAsyncTask(@NotNull WebAsyncTaskInfo asyncTask, @NotNull WebAsyncTaskProcessor<?> runnable) {
|
||||
AbstractJob job = new AbstractCancelableJob(asyncTask.getName()) {
|
||||
@NotNull
|
||||
@@ -630,11 +727,11 @@ public class WebSession extends BaseWebSession
|
||||
return asyncTask;
|
||||
}
|
||||
|
||||
public void addSessionError(Throwable exception) {
|
||||
public void addSessionError(@NotNull Throwable exception) {
|
||||
addSessionMessage(new WebServerMessage(exception));
|
||||
}
|
||||
|
||||
public void addSessionMessage(WebServerMessage message) {
|
||||
public void addSessionMessage(@NotNull WebServerMessage message) {
|
||||
synchronized (sessionMessages) {
|
||||
sessionMessages.add(message);
|
||||
}
|
||||
@@ -645,14 +742,15 @@ public class WebSession extends BaseWebSession
|
||||
message.getMessage()));
|
||||
}
|
||||
|
||||
public void addInfoMessage(String message) {
|
||||
public void addInfoMessage(@NotNull String message) {
|
||||
addSessionMessage(new WebServerMessage(MessageType.INFO, message));
|
||||
}
|
||||
|
||||
public void addWarningMessage(String message) {
|
||||
public void addWarningMessage(@NotNull String message) {
|
||||
addSessionMessage(new WebServerMessage(MessageType.WARNING, message));
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public List<WebServerMessage> readLog(Integer maxEntries, Boolean clearLog) {
|
||||
synchronized (sessionMessages) {
|
||||
List<WebServerMessage> messages = new ArrayList<>();
|
||||
@@ -700,7 +798,7 @@ public class WebSession extends BaseWebSession
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@NotNull
|
||||
public List<WebAuthInfo> getAllAuthInfo() {
|
||||
synchronized (authTokens) {
|
||||
return new ArrayList<>(authTokens);
|
||||
@@ -752,7 +850,7 @@ public class WebSession extends BaseWebSession
|
||||
});
|
||||
}
|
||||
|
||||
private void removeAuthInfo(WebAuthInfo oldAuthInfo) {
|
||||
private void removeAuthInfo(@NotNull WebAuthInfo oldAuthInfo) {
|
||||
oldAuthInfo.closeAuth();
|
||||
synchronized (authTokens) {
|
||||
authTokens.remove(oldAuthInfo);
|
||||
@@ -778,6 +876,7 @@ public class WebSession extends BaseWebSession
|
||||
return oldInfo;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public List<DBACredentialsProvider> getContextCredentialsProviders() {
|
||||
return getAdapters(DBACredentialsProvider.class);
|
||||
}
|
||||
@@ -865,11 +964,11 @@ public class WebSession extends BaseWebSession
|
||||
return parameters;
|
||||
}
|
||||
|
||||
public synchronized void resetAuthToken() throws DBException {
|
||||
public void resetAuthToken() throws DBException {
|
||||
this.userContext.reset();
|
||||
}
|
||||
|
||||
public synchronized boolean updateSMSession(SMAuthInfo smAuthInfo) throws DBException {
|
||||
public boolean updateSMSession(SMAuthInfo smAuthInfo) throws DBException {
|
||||
boolean contextChanged = super.updateSMSession(smAuthInfo);
|
||||
if (contextChanged) {
|
||||
refreshUserData();
|
||||
@@ -919,8 +1018,13 @@ public class WebSession extends BaseWebSession
|
||||
*/
|
||||
public void addSessionProject(@NotNull WebSessionProjectImpl project) {
|
||||
getWorkspace().addProject(project);
|
||||
if (navigatorModel != null) {
|
||||
navigatorModel.getRoot().addProject(project, false);
|
||||
navigatorModelLock.readLock().lock();
|
||||
try {
|
||||
if (navigatorModel != null) {
|
||||
navigatorModel.getRoot().addProject(project, false);
|
||||
}
|
||||
} finally {
|
||||
navigatorModelLock.readLock().unlock();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -936,8 +1040,13 @@ public class WebSession extends BaseWebSession
|
||||
project.dispose();
|
||||
|
||||
getWorkspace().removeProject(project);
|
||||
if (navigatorModel != null) {
|
||||
navigatorModel.getRoot().removeProject(project);
|
||||
navigatorModelLock.readLock().lock();
|
||||
try {
|
||||
if (navigatorModel != null) {
|
||||
navigatorModel.getRoot().removeProject(project);
|
||||
}
|
||||
} finally {
|
||||
navigatorModelLock.readLock().unlock();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1001,7 +1110,6 @@ public class WebSession extends BaseWebSession
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public void handleTaskConfirmationWithParameters(@NotNull String taskId, @NotNull Map<String, Object> parameters) {
|
||||
String attributeName = getTaskConfirmationAttributeName(taskId);
|
||||
CompletableFuture<Map<String, Object>> confirmationFuture = getAttribute(attributeName);
|
||||
@@ -1013,6 +1121,16 @@ public class WebSession extends BaseWebSession
|
||||
}
|
||||
}
|
||||
|
||||
public void handleActionCancelledEvent(@NotNull String actionId) {
|
||||
Object attributeValue = getAttribute(actionId);
|
||||
if (!(attributeValue instanceof CompletableFuture<?> future)) {
|
||||
log.info("Action to be cancelled not found, skipping: " + actionId);
|
||||
return;
|
||||
}
|
||||
future.completeExceptionally(new DBWebException("Action cancelled by user: " + actionId));
|
||||
removeAttribute(actionId);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private String getTaskConfirmationAttributeName(@NotNull String taskId) {
|
||||
return WebSQLConstants.TASK_CONFIRMATION_ATTR_PREFIX + taskId;
|
||||
|
||||
+8
-1
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* DBeaver - Universal Database Manager
|
||||
* Copyright (C) 2010-2024 DBeaver Corp and others
|
||||
* Copyright (C) 2010-2026 DBeaver Corp and others
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -68,6 +68,13 @@ public class WebSessionEventsFilter {
|
||||
}
|
||||
}
|
||||
|
||||
public void migrateTo(@NotNull WebSessionEventsFilter target) {
|
||||
synchronized (target.subscribedProjectIds) {
|
||||
target.subscribedEventTopics.addAll(this.subscribedEventTopics);
|
||||
target.subscribedProjectIds.addAll(this.subscribedProjectIds);
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isEventAllowed(WSEvent event) {
|
||||
if (event.isForceProcessed()) {
|
||||
return true;
|
||||
|
||||
+21
-2
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* DBeaver - Universal Database Manager
|
||||
* Copyright (C) 2010-2024 DBeaver Corp and others
|
||||
* Copyright (C) 2010-2026 DBeaver Corp and others
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -17,6 +17,7 @@
|
||||
package io.cloudbeaver.model.session;
|
||||
|
||||
import io.cloudbeaver.WebSessionProjectImpl;
|
||||
import io.cloudbeaver.utils.ServletAppUtils;
|
||||
import org.jkiss.code.NotNull;
|
||||
import org.jkiss.code.Nullable;
|
||||
import org.jkiss.dbeaver.DBException;
|
||||
@@ -26,6 +27,7 @@ import org.jkiss.dbeaver.model.DBPImage;
|
||||
import org.jkiss.dbeaver.model.app.DBPPlatform;
|
||||
import org.jkiss.dbeaver.model.app.DBPProject;
|
||||
import org.jkiss.dbeaver.model.app.DBPWorkspace;
|
||||
import org.jkiss.dbeaver.model.fs.DBFFileSystemManager;
|
||||
import org.jkiss.dbeaver.model.impl.auth.SessionContextImpl;
|
||||
import org.jkiss.dbeaver.model.rm.RMUtils;
|
||||
import org.jkiss.dbeaver.runtime.DBWorkbench;
|
||||
@@ -45,6 +47,7 @@ public class WebSessionWorkspace implements DBPWorkspace {
|
||||
private final SessionContextImpl workspaceAuthContext;
|
||||
private final List<WebSessionProjectImpl> accessibleProjects = new ArrayList<>();
|
||||
private WebSessionProjectImpl activeProject;
|
||||
private DBFFileSystemManager fileSystemManager;
|
||||
|
||||
public WebSessionWorkspace(BaseWebSession session) {
|
||||
this.session = session;
|
||||
@@ -199,12 +202,28 @@ public class WebSessionWorkspace implements DBPWorkspace {
|
||||
|
||||
@Override
|
||||
public boolean hasRealmPermission(@NotNull String permission) {
|
||||
if (getWebSession() instanceof WebSession webSession) {
|
||||
return webSession.getSessionPermissions().contains(permission);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsRealmFeature(@NotNull String feature) {
|
||||
return false;
|
||||
return ServletAppUtils.getServletApplication().getAppConfiguration().isFeatureEnabled(feature);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public synchronized DBFFileSystemManager getFileSystemManager() {
|
||||
if (fileSystemManager == null) {
|
||||
synchronized (this) {
|
||||
if (fileSystemManager == null) {
|
||||
fileSystemManager = new DBFFileSystemManager(session.getWorkspace());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return fileSystemManager;
|
||||
}
|
||||
}
|
||||
|
||||
+18
-18
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* DBeaver - Universal Database Manager
|
||||
* Copyright (C) 2010-2024 DBeaver Corp and others
|
||||
* Copyright (C) 2010-2026 DBeaver Corp and others
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -53,9 +53,9 @@ public class WebUserContext implements SMCredentialsProvider {
|
||||
private final ServletApplication application;
|
||||
private final DBPWorkspace workspace;
|
||||
|
||||
private WebUser user;
|
||||
private Set<String> userPermissions;
|
||||
private SMCredentials smCredentials = null;
|
||||
private volatile WebUser user;
|
||||
private volatile Set<String> userPermissions;
|
||||
private volatile SMCredentials smCredentials = null;
|
||||
private String smSessionId;
|
||||
private String refreshToken;
|
||||
|
||||
@@ -87,7 +87,7 @@ public class WebUserContext implements SMCredentialsProvider {
|
||||
* @return - true if context changed
|
||||
* @throws DBException - if user already authorized and new token come from another user
|
||||
*/
|
||||
public synchronized boolean refresh(@NotNull SMAuthInfo smAuthInfo) throws DBException {
|
||||
public boolean refresh(@NotNull SMAuthInfo smAuthInfo) throws DBException {
|
||||
if (smAuthInfo.getAuthPermissions() == null && !isAuthorizedInSecurityManager()) {
|
||||
throw new DBCException("Required information about session permissions is missing");
|
||||
}
|
||||
@@ -175,56 +175,56 @@ public class WebUserContext implements SMCredentialsProvider {
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public synchronized SMController getSecurityController() {
|
||||
public SMController getSecurityController() {
|
||||
return securityController;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public synchronized SMCredentials getActiveUserCredentials() {
|
||||
public SMCredentials getActiveUserCredentials() {
|
||||
return smCredentials;
|
||||
}
|
||||
|
||||
public synchronized boolean isAuthorizedInSecurityManager() {
|
||||
public boolean isAuthorizedInSecurityManager() {
|
||||
return smCredentials != null;
|
||||
}
|
||||
|
||||
public synchronized boolean isNonAnonymousUserAuthorizedInSM() {
|
||||
public boolean isNonAnonymousUserAuthorizedInSM() {
|
||||
return isAuthorizedInSecurityManager() && getUser() != null;
|
||||
}
|
||||
|
||||
public synchronized boolean isAnonymousUserAuthorizedInSM() {
|
||||
public boolean isAnonymousUserAuthorizedInSM() {
|
||||
return isAuthorizedInSecurityManager() && getUser() == null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public synchronized WebUser getUser() {
|
||||
public WebUser getUser() {
|
||||
return user;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public synchronized String getUserId() {
|
||||
public String getUserId() {
|
||||
return user == null ? null : user.getUserId();
|
||||
}
|
||||
|
||||
protected synchronized void setUser(@Nullable WebUser user) {
|
||||
protected void setUser(@Nullable WebUser user) {
|
||||
this.user = user;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public synchronized SMAdminController getAdminSecurityController() {
|
||||
public SMAdminController getAdminSecurityController() {
|
||||
return adminSecurityController;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public synchronized Set<String> getUserPermissions() {
|
||||
public Set<String> getUserPermissions() {
|
||||
return userPermissions;
|
||||
}
|
||||
|
||||
/**
|
||||
* reread the current user's permissions
|
||||
*/
|
||||
public synchronized void refreshPermissions() throws DBException {
|
||||
public void refreshPermissions() throws DBException {
|
||||
if (isAuthorizedInSecurityManager()) {
|
||||
log.debug("refresh permissions " + getUserId() + " " + getSmSessionId());
|
||||
setUserPermissions(securityController.getTokenPermissions().getPermissions());
|
||||
@@ -245,8 +245,8 @@ public class WebUserContext implements SMCredentialsProvider {
|
||||
return secretController;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public synchronized String getSmSessionId() {
|
||||
@Nullable
|
||||
public String getSmSessionId() {
|
||||
return smSessionId;
|
||||
}
|
||||
|
||||
|
||||
@@ -114,4 +114,9 @@ public class WebUser {
|
||||
public String getDisableReason() {
|
||||
return user.getDisableReason();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public Instant getLastLoginTime() {
|
||||
return user.getLastLoginTime();
|
||||
}
|
||||
}
|
||||
|
||||
+12
-2
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* DBeaver - Universal Database Manager
|
||||
* Copyright (C) 2010-2025 DBeaver Corp and others
|
||||
* Copyright (C) 2010-2026 DBeaver Corp and others
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -31,6 +31,8 @@ import org.jkiss.dbeaver.model.preferences.DBPPropertyDescriptor;
|
||||
import org.jkiss.dbeaver.model.security.SMAuthCredentialsProfile;
|
||||
import org.jkiss.dbeaver.model.security.SMAuthProviderDescriptor;
|
||||
import org.jkiss.dbeaver.model.security.SMSubjectType;
|
||||
import org.jkiss.dbeaver.utils.GeneralUtils;
|
||||
import org.jkiss.dbeaver.utils.SystemVariablesResolver;
|
||||
import org.jkiss.utils.ArrayUtils;
|
||||
import org.jkiss.utils.CommonUtils;
|
||||
|
||||
@@ -57,6 +59,7 @@ public class WebAuthProviderDescriptor extends AbstractDescriptor {
|
||||
private final boolean isPrivate;
|
||||
private final boolean isAuthHidden;
|
||||
private final boolean isCaseInsensitive;
|
||||
private final boolean sameTabRedirectOnLogout;
|
||||
private final boolean serviceProvider;
|
||||
private final String[] requiredFeatures;
|
||||
private final boolean isRequired;
|
||||
@@ -73,6 +76,7 @@ public class WebAuthProviderDescriptor extends AbstractDescriptor {
|
||||
this.isRequired = CommonUtils.toBoolean(cfg.getAttribute(WebRegistryConstant.ATTR_REQUIRED));
|
||||
this.isAuthHidden = CommonUtils.toBoolean(cfg.getAttribute(WebRegistryConstant.ATTR_AUTH_HIDDEN));
|
||||
this.isCaseInsensitive = CommonUtils.toBoolean(cfg.getAttribute(WebRegistryConstant.ATTR_CASE_INSENSITIVE));
|
||||
this.sameTabRedirectOnLogout = CommonUtils.toBoolean(cfg.getAttribute(WebRegistryConstant.ATTR_SAME_TAB_LOGOUT_REDIRECT));
|
||||
this.serviceProvider = CommonUtils.toBoolean(cfg.getAttribute(WebRegistryConstant.ATTR_SERVICE_PROVIDER));
|
||||
|
||||
for (IConfigurationElement cfgElement : cfg.getChildren(WebRegistryConstant.TAG_CONFIGURATION)) {
|
||||
@@ -110,7 +114,9 @@ public class WebAuthProviderDescriptor extends AbstractDescriptor {
|
||||
}
|
||||
|
||||
public String getDescription() {
|
||||
return cfg.getAttribute(WebRegistryConstant.ATTR_DESCRIPTION);
|
||||
return GeneralUtils.replaceVariables(
|
||||
cfg.getAttribute(WebRegistryConstant.ATTR_DESCRIPTION), SystemVariablesResolver.INSTANCE
|
||||
);
|
||||
}
|
||||
|
||||
public DBPImage getIcon() {
|
||||
@@ -125,6 +131,10 @@ public class WebAuthProviderDescriptor extends AbstractDescriptor {
|
||||
return trusted;
|
||||
}
|
||||
|
||||
public boolean isSameTabRedirectOnLogout() {
|
||||
return sameTabRedirectOnLogout;
|
||||
}
|
||||
|
||||
public boolean isPrivate() {
|
||||
return isPrivate;
|
||||
}
|
||||
|
||||
+1
@@ -29,6 +29,7 @@ public class WebRegistryConstant {
|
||||
public static final String ATTR_REQUIRED = "required";
|
||||
public static final String ATTR_AUTH_HIDDEN = "authHidden";
|
||||
public static final String ATTR_CASE_INSENSITIVE = "caseInsensitive";
|
||||
public static final String ATTR_SAME_TAB_LOGOUT_REDIRECT = "sameTabRedirectOnLogout";
|
||||
public static final String ATTR_REQUIRED_FEATURES = "requiredFeatures";
|
||||
public static final String ATTR_CATEGORIES = "categories";
|
||||
public static final String ATTR_SERVICE_PROVIDER = "serviceProvider";
|
||||
|
||||
+2
-2
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* DBeaver - Universal Database Manager
|
||||
* Copyright (C) 2010-2025 DBeaver Corp and others
|
||||
* Copyright (C) 2010-2026 DBeaver Corp and others
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -76,7 +76,7 @@ public abstract class BaseServletPlatform extends BasePlatformImpl {
|
||||
// Remove temp folder
|
||||
if (tempFolder != null) {
|
||||
|
||||
if (!ContentUtils.deleteFileRecursive(tempFolder.toFile())) {
|
||||
if (!ContentUtils.deleteFileRecursive(tempFolder)) {
|
||||
log.warn("Can't delete temp folder '" + tempFolder.toAbsolutePath() + "'");
|
||||
}
|
||||
tempFolder = null;
|
||||
|
||||
@@ -28,49 +28,34 @@ public class CBConstants {
|
||||
public static final String RUNTIME_PRODUCT_CONFIG_FILE_NAME = ".product.runtime.conf";
|
||||
public static final String AUTO_CONFIG_FILE_NAME = ".cloudbeaver.auto.conf";
|
||||
|
||||
public static final String PARAM_SERVER_CONFIGURATION = "server";
|
||||
public static final String PARAM_SERVER_PORT = "serverPort";
|
||||
public static final String PARAM_SERVER_HOST = "serverHost";
|
||||
public static final String PARAM_SERVER_NAME = "serverName";
|
||||
public static final String PARAM_FORCE_HTTPS = "forceHttps";
|
||||
public static final String PARAM_BIND_SESSION_TO_IP = "bindSessionToIp";
|
||||
public static final String PARAM_SUPPORTED_HOSTS = "supportedHosts";
|
||||
public static final String PARAM_SSL_CONFIGURATION_PATH = "sslConfigurationPath";
|
||||
public static final String PARAM_CONTENT_ROOT = "contentRoot";
|
||||
public static final String PARAM_ROOT_URI = "rootURI";
|
||||
public static final String PARAM_SERVICES_URI = "serviceURI";
|
||||
public static final String PARAM_DRIVERS_LOCATION = "driversLocation";
|
||||
public static final String PARAM_WORKSPACE_LOCATION = "workspaceLocation";
|
||||
//legacy path to a product.conf file
|
||||
@Deprecated
|
||||
public static final String PARAM_PRODUCT_CONFIGURATION = "productConfiguration";
|
||||
public static final String PARAM_PRODUCT_SETTINGS = "productSettings";
|
||||
public static final String PARAM_EXTERNAL_PROPERTIES = "externalProperties";
|
||||
public static final String PARAM_STATIC_CONTENT = "staticContent";
|
||||
public static final String PARAM_RESOURCE_QUOTAS = "resourceQuotas";
|
||||
public static final String PARAM_RESOURCE_MANAGER_ENABLED = "resourceManagerEnabled";
|
||||
public static final String PARAM_SECRET_MANAGER_ENABLED = "secretManagerEnabled";
|
||||
public static final String PARAM_SHOW_READ_ONLY_CONN_INFO = "showReadOnlyConnectionInfo";
|
||||
public static final String PARAM_CONN_GRANT_ANON_ACCESS = "grantConnectionsAccessToAnonymousTeam";
|
||||
public static final String PARAM_AUTH_PROVIDERS = "authConfiguration";
|
||||
public static final String PARAM_DB_CONFIGURATION = "database";
|
||||
|
||||
public static final String PARAM_SESSION_EXPIRE_PERIOD = "expireSessionAfterPeriod";
|
||||
|
||||
public static final String PARAM_DEVEL_MODE = "develMode";
|
||||
public static final String PARAM_SECURITY_MANAGER = "enableSecurityManager";
|
||||
public static final String PARAM_SM_CONFIGURATION = "sm";
|
||||
public static final String PARAM_PASSWORD_POLICY_CONFIGURATION = "passwordPolicy";
|
||||
|
||||
public static final int DEFAULT_SERVER_PORT = 8080;
|
||||
//public static final String DEFAULT_SERVER_NAME = "CloudBeaver Web Server";
|
||||
|
||||
public static final String DEFAULT_CONTENT_ROOT = "/var/www/cloudbeaver";
|
||||
public static final String DEFAULT_ROOT_URI = "/";
|
||||
public static final String DEFAULT_SERVICES_URI = "/api/";
|
||||
|
||||
public static final String DEFAULT_DEPLOY_LOCATION = "/opt/cloudbeaver";
|
||||
public static final String DEFAULT_DRIVERS_LOCATION = DEFAULT_DEPLOY_LOCATION + "/drivers";
|
||||
public static final String DEFAULT_WORKSPACE_LOCATION = DEFAULT_DEPLOY_LOCATION + "/workspace";
|
||||
public static final String DEFAULT_PRODUCT_CONFIGURATION = "conf/product.conf";
|
||||
public static final String DEFAULT_DATASOURCE_PERMISSIONS_CONFIGURATION = "data-sources-permissions.json";
|
||||
public static final String DEFAULT_ADMIN_NAME = "cbadmin";
|
||||
@@ -83,14 +68,11 @@ public class CBConstants {
|
||||
public static final String VAR_HOST_DOCKER_INTERNAL = "host.docker.internal";
|
||||
|
||||
public static final String VAR_AUTO_CB_SERVER_NAME = "CB_SERVER_NAME";
|
||||
public static final String VAR_AUTO_CB_SERVER_URL = "CB_SERVER_URL";
|
||||
public static final String VAR_AUTO_CB_ADMIN_NAME = "CB_ADMIN_NAME";
|
||||
public static final String VAR_AUTO_CB_ADMIN_PASSWORD = "CB_ADMIN_PASSWORD";
|
||||
|
||||
public static final String CB_SESSION_COOKIE_NAME = "cb-session-id";
|
||||
|
||||
public static final String APPLICATION_JSON = "application/json";
|
||||
|
||||
public static final String ADMIN_AUTO_GRANT = "auto-grant";
|
||||
public static final String HOST_LOCALHOST = "localhost";
|
||||
public static final String HOST_127_0_0_1 = "127.0.0.1";
|
||||
@@ -100,4 +82,6 @@ public class CBConstants {
|
||||
public static final String DEFAULT_CLOUD_PROJECT_NAME = "GlobalConfiguration";
|
||||
|
||||
public static final String SECURED_VALUE = "******";
|
||||
public static final String PREF_DATA_EDITOR_EXPORT_DISABLED_OLD = "plugin.data-viewer.export.disabled";
|
||||
public static final String PREF_DATA_EDITOR_IMPORT_DISABLED_OLD = "plugin.data-import.disabled";
|
||||
}
|
||||
|
||||
+17
-3
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* DBeaver - Universal Database Manager
|
||||
* Copyright (C) 2010-2024 DBeaver Corp and others
|
||||
* Copyright (C) 2010-2026 DBeaver Corp and others
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -20,11 +20,14 @@ import io.cloudbeaver.WebProjectImpl;
|
||||
import io.cloudbeaver.model.app.ServletApplication;
|
||||
import org.jkiss.code.NotNull;
|
||||
import org.jkiss.code.Nullable;
|
||||
import org.jkiss.dbeaver.DBException;
|
||||
import org.jkiss.dbeaver.Log;
|
||||
import org.jkiss.dbeaver.model.app.DBPPlatform;
|
||||
import org.jkiss.dbeaver.model.app.DBPProject;
|
||||
import org.jkiss.dbeaver.model.auth.SMSession;
|
||||
import org.jkiss.dbeaver.model.impl.app.BaseProjectImpl;
|
||||
import org.jkiss.dbeaver.model.impl.app.BaseWorkspaceImpl;
|
||||
import org.jkiss.dbeaver.model.runtime.DBRProgressMonitor;
|
||||
import org.jkiss.utils.CommonUtils;
|
||||
|
||||
import java.io.IOException;
|
||||
@@ -51,12 +54,18 @@ public class ServerGlobalWorkspace extends BaseWorkspaceImpl {
|
||||
@NotNull DBPPlatform platform,
|
||||
@NotNull ServletApplication application
|
||||
) {
|
||||
super(platform, application.getWorkspaceDirectory());
|
||||
super(platform, application.getWorkspacePath());
|
||||
this.application = application;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public void initializeProjects() {
|
||||
protected SMSession acquireWorkspaceSession(@NotNull DBRProgressMonitor monitor) throws DBException {
|
||||
return new ServerGlobalWorkspaceSession(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initializeProjects() throws DBException {
|
||||
initializeWorkspaceSession();
|
||||
|
||||
// Load global project
|
||||
@@ -106,4 +115,9 @@ public class ServerGlobalWorkspace extends BaseWorkspaceImpl {
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsRealmFeature(@NotNull String feature) {
|
||||
return application.getAppConfiguration().isFeatureEnabled(feature);
|
||||
}
|
||||
}
|
||||
+36
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
* DBeaver - Universal Database Manager
|
||||
* Copyright (C) 2010-2026 DBeaver Corp and others
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package io.cloudbeaver.server;
|
||||
|
||||
import org.jkiss.code.NotNull;
|
||||
import org.jkiss.code.Nullable;
|
||||
import org.jkiss.dbeaver.model.app.DBPWorkspace;
|
||||
import org.jkiss.dbeaver.model.impl.app.LocalWorkspaceSession;
|
||||
import org.jkiss.dbeaver.model.secret.DBSSecretController;
|
||||
|
||||
public class ServerGlobalWorkspaceSession extends LocalWorkspaceSession {
|
||||
|
||||
public ServerGlobalWorkspaceSession(@NotNull DBPWorkspace workspace) {
|
||||
super(workspace);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public DBSSecretController getSecretController() {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
+2
-2
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* DBeaver - Universal Database Manager
|
||||
* Copyright (C) 2010-2025 DBeaver Corp and others
|
||||
* Copyright (C) 2010-2026 DBeaver Corp and others
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -30,7 +30,7 @@ public interface DBWServiceBindingServlet<APPLICATION extends ServletApplication
|
||||
return true;
|
||||
}
|
||||
|
||||
void addServlets(APPLICATION application, DBWServletContext servletContext) throws DBException;
|
||||
void addServlets(@NotNull APPLICATION application, @NotNull DBWServletContext servletContext) throws DBException;
|
||||
|
||||
@NotNull
|
||||
default List<String> getExcludedServletPaths(@NotNull APPLICATION application) {
|
||||
|
||||
+13
-4
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* DBeaver - Universal Database Manager
|
||||
* Copyright (C) 2010-2024 DBeaver Corp and others
|
||||
* Copyright (C) 2010-2026 DBeaver Corp and others
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -16,6 +16,7 @@
|
||||
*/
|
||||
package io.cloudbeaver.service.sql;
|
||||
|
||||
import org.jkiss.code.NotNull;
|
||||
import org.jkiss.dbeaver.model.data.DBDAttributeConstraint;
|
||||
import org.jkiss.dbeaver.model.gis.DBGeometry;
|
||||
import org.jkiss.utils.CommonUtils;
|
||||
@@ -27,6 +28,7 @@ import java.util.Map;
|
||||
*/
|
||||
public class WebSQLDataFilterConstraint {
|
||||
|
||||
private String attributeName;
|
||||
private Integer attributePosition;
|
||||
private Integer orderPosition;
|
||||
private Boolean orderAsc;
|
||||
@@ -38,8 +40,9 @@ public class WebSQLDataFilterConstraint {
|
||||
private WebSQLDataFilterConstraint() {
|
||||
}
|
||||
|
||||
public WebSQLDataFilterConstraint(Map<String, Object> map) {
|
||||
this.attributePosition = CommonUtils.toInt(map.get("attributePosition"));
|
||||
public WebSQLDataFilterConstraint(@NotNull Map<String, Object> map) {
|
||||
this.attributeName = CommonUtils.toString(map.get("attributeName"));
|
||||
this.attributePosition = CommonUtils.toInteger(map.get("attributePosition"), null);
|
||||
this.orderPosition = map.containsKey("orderPosition") ?
|
||||
CommonUtils.toInt(map.get("orderPosition")) + 1 : // Use position + 1 because 0 means no ordering (because of legacy compatibility)
|
||||
null;
|
||||
@@ -64,8 +67,10 @@ public class WebSQLDataFilterConstraint {
|
||||
|
||||
}
|
||||
|
||||
public static WebSQLDataFilterConstraint from(DBDAttributeConstraint constraint) {
|
||||
@NotNull
|
||||
public static WebSQLDataFilterConstraint from(@NotNull DBDAttributeConstraint constraint) {
|
||||
var webConstraint = new WebSQLDataFilterConstraint();
|
||||
webConstraint.attributeName = constraint.getAttributeName();
|
||||
webConstraint.orderPosition = constraint.getOrderPosition();
|
||||
webConstraint.orderAsc = !constraint.isOrderDescending();
|
||||
webConstraint.criteria = constraint.getCriteria();
|
||||
@@ -76,6 +81,10 @@ public class WebSQLDataFilterConstraint {
|
||||
return webConstraint;
|
||||
}
|
||||
|
||||
public String getAttributeName() {
|
||||
return attributeName;
|
||||
}
|
||||
|
||||
public Integer getAttributePosition() {
|
||||
return attributePosition;
|
||||
}
|
||||
|
||||
+43
-8
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* DBeaver - Universal Database Manager
|
||||
* Copyright (C) 2010-2024 DBeaver Corp and others
|
||||
* Copyright (C) 2010-2026 DBeaver Corp and others
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -40,6 +40,9 @@ public class WebSQLResultsInfo {
|
||||
@NotNull
|
||||
private final String id;
|
||||
private DBDAttributeBinding[] attributes;
|
||||
private DBDAttributeBinding documentAttribute;
|
||||
private String documentIdAttributeName;
|
||||
|
||||
// TODO: find a way to remove isSingleRow and use virtual keys for reading BLOB and string cell values.
|
||||
private boolean isSingleRow;
|
||||
private DBCTrace trace;
|
||||
@@ -62,14 +65,37 @@ public class WebSQLResultsInfo {
|
||||
return dataContainer;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public DBDAttributeBinding[] getAttributes() {
|
||||
return attributes;
|
||||
}
|
||||
|
||||
public void setAttributes(DBDAttributeBinding[] attributes) {
|
||||
public void setAttributes(@NotNull DBDAttributeBinding[] attributes) {
|
||||
this.attributes = attributes;
|
||||
}
|
||||
|
||||
public int getAttributePosition(@NotNull DBDAttributeBinding attribute) {
|
||||
for (int i = 0; i < attributes.length; i++) {
|
||||
if (attributes[i] == attribute) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
public int getDocumentIdAttributePosition(@NotNull DBDAttributeBinding documentAttribute) {
|
||||
if (documentIdAttributeName == null) {
|
||||
return -1;
|
||||
}
|
||||
for (int i = 0; i < attributes.length; i++) {
|
||||
DBDAttributeBinding attribute = attributes[i];
|
||||
if (attribute.getTopParent() == documentAttribute && documentIdAttributeName.equals(attribute.getName())) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
public String getQueryText() {
|
||||
return queryText;
|
||||
}
|
||||
@@ -122,13 +148,9 @@ public class WebSQLResultsInfo {
|
||||
return binding == null ? null : binding.getAttribute();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public DBSTypedObject getAttributeByPosition(int pos) {
|
||||
for (DBDAttributeBinding attr : attributes) {
|
||||
if (attr.getOrdinalPosition() == pos) {
|
||||
return attr;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
return pos >= 0 && pos < attributes.length ? attributes[pos] : null;
|
||||
}
|
||||
|
||||
public boolean canRefreshResults() {
|
||||
@@ -163,4 +185,17 @@ public class WebSQLResultsInfo {
|
||||
public void setDataFilter(@Nullable DBDDataFilter dataFilter) {
|
||||
this.dataFilter = dataFilter;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public DBDAttributeBinding getDocumentAttribute() {
|
||||
return documentAttribute;
|
||||
}
|
||||
|
||||
public void setDocumentAttribute(@Nullable DBDAttributeBinding documentAttribute) {
|
||||
this.documentAttribute = documentAttribute;
|
||||
}
|
||||
|
||||
public void setDocumentIdAttributeName(@Nullable String documentIdAttributeName) {
|
||||
this.documentIdAttributeName = documentIdAttributeName;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,6 +24,7 @@ import org.jkiss.code.Nullable;
|
||||
import org.jkiss.dbeaver.DBException;
|
||||
import org.jkiss.dbeaver.model.data.json.JSONUtils;
|
||||
import org.jkiss.dbeaver.utils.GeneralUtils;
|
||||
import org.jkiss.utils.HttpConstants;
|
||||
|
||||
import java.net.CookieManager;
|
||||
import java.net.URI;
|
||||
@@ -127,7 +128,7 @@ public class WebGQLClient {
|
||||
.uri(URI.create(apiUrl))
|
||||
.POST(HttpRequest.BodyPublishers.ofString(makeGQLRequest(query, variables)))
|
||||
.setHeader("TE-Client-Version", GeneralUtils.getMajorVersion())
|
||||
.header("Content-Type", "application/json");
|
||||
.header(HttpConstants.HEADER_CONTENT_TYPE, HttpConstants.CONTENT_TYPE_JSON);
|
||||
if (!requestHeaders.isEmpty()) {
|
||||
requestHeaders.forEach(requestBuilder::header);
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* DBeaver - Universal Database Manager
|
||||
* Copyright (C) 2010-2025 DBeaver Corp and others
|
||||
* Copyright (C) 2010-2026 DBeaver Corp and others
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -47,6 +47,8 @@ public class ServletAppUtils {
|
||||
private static final String HEADER_REFERER = "Referer";
|
||||
|
||||
private static final String HEADER_FORWARDED_SCHEME = "X-Forwarded-Scheme";
|
||||
private static final String HEADER_FORWARDED_PROTO = "X-Forwarded-Proto";
|
||||
private static final String HEADER_FORWARDED_PORT = "X-Forwarded-Port";
|
||||
private static final String HEADER_FORWARDED_HOST = "X-Forwarded-Host";
|
||||
private static final Set<Integer> DEFAULT_PORTS = Set.of(
|
||||
80,
|
||||
@@ -190,9 +192,16 @@ public class ServletAppUtils {
|
||||
@NotNull String serviceId,
|
||||
@NotNull String origin
|
||||
) throws DBException {
|
||||
String finalOrigin = webAuthApplication.modifyOrigin(origin);
|
||||
StringBuilder authUriBuilder = new StringBuilder(removeSideSlashes(finalOrigin));
|
||||
String finalOrigin = ServletAppUtils.substringBeforeRootURI(webAuthApplication.modifyOrigin(origin));
|
||||
String serviceUriSegment = removeSideSlashes(webAuthApplication.getAuthServiceUriSegment());
|
||||
String rootUri = removeSideSlashes(getServletApplication().getRootURI());
|
||||
if (finalOrigin.endsWith("/" + rootUri) && serviceUriSegment.startsWith(rootUri + "/")) {
|
||||
finalOrigin = finalOrigin.substring(0, finalOrigin.length() - rootUri.length());
|
||||
}
|
||||
StringBuilder authUriBuilder = new StringBuilder(removeSideSlashes(finalOrigin));
|
||||
if (CommonUtils.isNotEmpty(rootUri)) {
|
||||
authUriBuilder.append("/").append(rootUri);
|
||||
}
|
||||
if (CommonUtils.isNotEmpty(serviceUriSegment)) {
|
||||
authUriBuilder.append("/").append(serviceUriSegment);
|
||||
}
|
||||
@@ -226,6 +235,14 @@ public class ServletAppUtils {
|
||||
response.addCookie(sessionCookie);
|
||||
}
|
||||
|
||||
public static String getHeaderOrLegacy(HttpServletRequest request, String headerName, String legacyHeaderName) {
|
||||
String value = request.getHeader(headerName);
|
||||
if (CommonUtils.isEmpty(value)) {
|
||||
value = request.getHeader(legacyHeaderName);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
public static String getRequestCookie(HttpServletRequest request, String cookieName) {
|
||||
Cookie[] cookies = request.getCookies();
|
||||
if (cookies != null) {
|
||||
@@ -291,22 +308,42 @@ public class ServletAppUtils {
|
||||
}
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static String getHostOriginFromRequest(@NotNull HttpServletRequest request) {
|
||||
return substringBeforeRootURI(getOriginFromRequest(request));
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static String getOriginFromRequest(@NotNull HttpServletRequest request) {
|
||||
String origin = request.getHeader(HEADER_ORIGIN);
|
||||
if (log.isTraceEnabled()) {
|
||||
log.trace("Origin header: " + origin);
|
||||
}
|
||||
if ("null".equals(origin)) {
|
||||
//strange identity provider bug
|
||||
origin = null;
|
||||
}
|
||||
if (CommonUtils.isEmpty(origin)) {
|
||||
origin = request.getHeader(HEADER_X_ORIGIN);
|
||||
}
|
||||
if (CommonUtils.isEmpty(origin)) {
|
||||
origin = request.getHeader(HEADER_REFERER);
|
||||
if (log.isTraceEnabled()) {
|
||||
log.trace("X-Origin header: " + origin);
|
||||
}
|
||||
}
|
||||
String forwardedScheme = request.getHeader(HEADER_FORWARDED_SCHEME);
|
||||
if (CommonUtils.isEmpty(forwardedScheme)) {
|
||||
forwardedScheme = request.getHeader(HEADER_FORWARDED_PROTO);
|
||||
}
|
||||
String forwardedHost = request.getHeader(HEADER_FORWARDED_HOST);
|
||||
if (CommonUtils.isNotEmpty(forwardedScheme) && CommonUtils.isNotEmpty(forwardedHost)) {
|
||||
origin = forwardedScheme + "://" + forwardedHost;
|
||||
}
|
||||
if (CommonUtils.isEmpty(origin)) {
|
||||
if (log.isTraceEnabled()) {
|
||||
log.trace("forwarded origin: " + origin);
|
||||
}
|
||||
} else if (CommonUtils.isEmpty(origin)) {
|
||||
URI requestUrl = URI.create(request.getRequestURL().toString());
|
||||
if (log.isTraceEnabled()) {
|
||||
log.trace("Request URL: " + requestUrl);
|
||||
}
|
||||
origin = getRootUrlFromUri(requestUrl) + "/";
|
||||
}
|
||||
origin = removeSideSlashes(origin);
|
||||
@@ -318,13 +355,42 @@ public class ServletAppUtils {
|
||||
|
||||
origin = removeSideSlashes(origin);
|
||||
URI uri = URI.create(origin);
|
||||
if (DEFAULT_PORTS.contains(uri.getPort())) {
|
||||
int port = uri.getPort();
|
||||
if (CommonUtils.isNotEmpty(request.getHeader(HEADER_FORWARDED_PORT))) {
|
||||
try {
|
||||
port = Integer.parseInt(request.getHeader(HEADER_FORWARDED_PORT));
|
||||
} catch (NumberFormatException e) {
|
||||
log.error("Failed to parse port from header: " + request.getHeader(HEADER_FORWARDED_PORT), e);
|
||||
}
|
||||
}
|
||||
|
||||
String finalScheme = uri.getScheme();
|
||||
String finalHost = uri.getHost();
|
||||
int finalPort = port;
|
||||
boolean changed = false;
|
||||
if (CommonUtils.isNotEmpty(forwardedScheme) && !forwardedScheme.equals(finalScheme)) {
|
||||
finalScheme = forwardedScheme;
|
||||
changed = true;
|
||||
}
|
||||
if (CommonUtils.isNotEmpty(forwardedHost) && !forwardedHost.equals(finalHost)) {
|
||||
finalHost = forwardedHost;
|
||||
changed = true;
|
||||
}
|
||||
if (DEFAULT_PORTS.contains(port)) {
|
||||
finalPort = -1;
|
||||
changed = true;
|
||||
}
|
||||
if (log.isTraceEnabled()) {
|
||||
log.trace("Origin URI: " + origin);
|
||||
}
|
||||
|
||||
if (changed) {
|
||||
try {
|
||||
origin = new URI(
|
||||
uri.getScheme(),
|
||||
finalScheme,
|
||||
uri.getUserInfo(),
|
||||
uri.getHost(),
|
||||
-1,
|
||||
finalHost,
|
||||
finalPort,
|
||||
uri.getPath(),
|
||||
uri.getQuery(),
|
||||
uri.getFragment()
|
||||
|
||||
+35
-15
@@ -33,6 +33,7 @@ import org.jkiss.dbeaver.model.access.DBAAuthCredentials;
|
||||
import org.jkiss.dbeaver.model.app.DBPDataSourceRegistry;
|
||||
import org.jkiss.dbeaver.model.app.DBPProject;
|
||||
import org.jkiss.dbeaver.model.connection.DBPConnectionConfiguration;
|
||||
import org.jkiss.dbeaver.model.connection.DBPConnectionType;
|
||||
import org.jkiss.dbeaver.model.connection.DBPDataSourceProviderDescriptor;
|
||||
import org.jkiss.dbeaver.model.connection.DBPDriver;
|
||||
import org.jkiss.dbeaver.model.impl.auth.AuthModelDatabaseNativeCredentials;
|
||||
@@ -40,6 +41,7 @@ import org.jkiss.dbeaver.model.net.DBWHandlerConfiguration;
|
||||
import org.jkiss.dbeaver.model.net.ssh.SSHConstants;
|
||||
import org.jkiss.dbeaver.model.runtime.DBRProgressMonitor;
|
||||
import org.jkiss.dbeaver.model.websocket.event.datasource.WSDataSourceDisconnectEvent;
|
||||
import org.jkiss.dbeaver.registry.DataSourceProviderRegistry;
|
||||
import org.jkiss.dbeaver.registry.network.NetworkHandlerDescriptor;
|
||||
import org.jkiss.dbeaver.registry.network.NetworkHandlerRegistry;
|
||||
import org.jkiss.dbeaver.runtime.DBWorkbench;
|
||||
@@ -55,9 +57,12 @@ public class WebDataSourceUtils {
|
||||
private WebDataSourceUtils() {
|
||||
}
|
||||
|
||||
public static void saveCredentialsInDataSource(WebConnectionInfo webConnectionInfo, DBPDataSourceContainer dataSourceContainer, DBPConnectionConfiguration configuration) {
|
||||
public static void saveCredentialsInDataSource(
|
||||
@NotNull WebConnectionInfo webConnectionInfo,
|
||||
@NotNull DBPDataSourceContainer dataSourceContainer,
|
||||
@NotNull DBPConnectionConfiguration configuration
|
||||
) {
|
||||
// Properties passed from web
|
||||
// webConnectionInfo may be null in some cases (e.g. connection test when no actual connection exist yet)
|
||||
Map<String, Object> authProperties = webConnectionInfo.getSavedAuthProperties();
|
||||
if (authProperties != null) {
|
||||
authProperties.forEach((s, o) -> configuration.setAuthProperty(s, CommonUtils.toString(o)));
|
||||
@@ -162,18 +167,24 @@ public class WebDataSourceUtils {
|
||||
}
|
||||
|
||||
|
||||
public static boolean disconnectDataSource(@NotNull WebSession webSession, @NotNull DBPDataSourceContainer dataSource) {
|
||||
public static boolean disconnectDataSource(
|
||||
@NotNull WebSession webSession,
|
||||
@NotNull DBPDataSourceContainer dataSource,
|
||||
boolean sendDisconnectEvent
|
||||
) {
|
||||
if (dataSource.isConnected()) {
|
||||
try {
|
||||
dataSource.disconnect(webSession.getProgressMonitor());
|
||||
webSession.addSessionEvent(
|
||||
new WSDataSourceDisconnectEvent(
|
||||
dataSource.getProject().getId(),
|
||||
dataSource.getId(),
|
||||
webSession.getSessionId(),
|
||||
webSession.getUserId()
|
||||
)
|
||||
);
|
||||
if (sendDisconnectEvent) {
|
||||
webSession.addSessionEvent(
|
||||
new WSDataSourceDisconnectEvent(
|
||||
dataSource.getProject().getId(),
|
||||
dataSource.getId(),
|
||||
WebEventUtils.getSmSessionId(webSession),
|
||||
webSession.getUserId()
|
||||
)
|
||||
);
|
||||
}
|
||||
return true;
|
||||
} catch (DBException e) {
|
||||
log.error("Error closing connection", e);
|
||||
@@ -299,9 +310,7 @@ public class WebDataSourceUtils {
|
||||
if (config.getKeepAliveInterval() >= 0) {
|
||||
dsConfig.setKeepAliveInterval(config.getKeepAliveInterval());
|
||||
}
|
||||
if (config.isDefaultAutoCommit() != null) {
|
||||
dsConfig.getBootstrap().setDefaultAutoCommit(config.isDefaultAutoCommit());
|
||||
}
|
||||
dsConfig.getBootstrap().setDefaultAutoCommit(config.isDefaultAutoCommit());
|
||||
dsConfig.getBootstrap().setDefaultCatalogName(config.getDefaultCatalogName());
|
||||
dsConfig.getBootstrap().setDefaultSchemaName(config.getDefaultSchemaName());
|
||||
// Save provider props
|
||||
@@ -314,8 +323,19 @@ public class WebDataSourceUtils {
|
||||
if (config.getConfigurationType() != null) {
|
||||
dsConfig.setConfigurationType(config.getConfigurationType());
|
||||
}
|
||||
if (config.getConnectionType() != null) {
|
||||
DBPConnectionType connectionType = DataSourceProviderRegistry.getInstance().getConnectionType(config.getConnectionType(), null);
|
||||
if (connectionType != null) {
|
||||
dsConfig.setConnectionType(connectionType);
|
||||
}
|
||||
}
|
||||
|
||||
if (CommonUtils.isEmpty(config.getUrl())) {
|
||||
dsConfig.setUrl(driver.getConnectionURL(dsConfig));
|
||||
try {
|
||||
dsConfig.setUrl(driver.getConnectionURL(dsConfig));
|
||||
} catch (DBException e) {
|
||||
log.error("Error preparing connection URL", e);
|
||||
}
|
||||
}
|
||||
// Save network handlers
|
||||
if (config.getNetworkHandlersConfig() != null) {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* DBeaver - Universal Database Manager
|
||||
* Copyright (C) 2010-2024 DBeaver Corp and others
|
||||
* Copyright (C) 2010-2026 DBeaver Corp and others
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -17,119 +17,21 @@
|
||||
package io.cloudbeaver.utils;
|
||||
|
||||
import io.cloudbeaver.model.WebAsyncTaskInfo;
|
||||
import io.cloudbeaver.model.session.BaseWebSession;
|
||||
import io.cloudbeaver.model.session.WebSession;
|
||||
import io.cloudbeaver.websocket.event.task.WSSessionTaskInfoEvent;
|
||||
import org.jkiss.code.NotNull;
|
||||
import org.jkiss.code.Nullable;
|
||||
import org.jkiss.dbeaver.model.app.DBPProject;
|
||||
import org.jkiss.dbeaver.model.security.SMSubjectType;
|
||||
import org.jkiss.dbeaver.model.websocket.WSConstants;
|
||||
import org.jkiss.dbeaver.model.websocket.event.WSEvent;
|
||||
import org.jkiss.dbeaver.model.websocket.event.datasource.WSDataSourceEvent;
|
||||
import org.jkiss.dbeaver.model.websocket.event.datasource.WSDataSourceProperty;
|
||||
import org.jkiss.dbeaver.model.websocket.event.datasource.WSDatasourceFolderEvent;
|
||||
import org.jkiss.dbeaver.model.websocket.event.permissions.WSSubjectPermissionEvent;
|
||||
import org.jkiss.dbeaver.model.websocket.event.resource.WSResourceProperty;
|
||||
import org.jkiss.dbeaver.model.websocket.event.resource.WSResourceUpdatedEvent;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
public class WebEventUtils {
|
||||
/**
|
||||
* adds events of updated datasource to event controller
|
||||
*
|
||||
* @param project project of updated database
|
||||
* @param session web session
|
||||
* @param datasourceId id of datasource
|
||||
* @param eventAction type of event
|
||||
*/
|
||||
public static void addDataSourceUpdatedEvent(
|
||||
DBPProject project,
|
||||
WebSession session,
|
||||
String datasourceId,
|
||||
WSConstants.EventAction eventAction,
|
||||
WSDataSourceProperty property
|
||||
) {
|
||||
if (project == null) {
|
||||
return;
|
||||
}
|
||||
WSEvent event = null;
|
||||
switch (eventAction) {
|
||||
case CREATE:
|
||||
event = WSDataSourceEvent.create(
|
||||
session.getUserContext().getSmSessionId(),
|
||||
session.getUserId(),
|
||||
project.getId(),
|
||||
List.of(datasourceId),
|
||||
property
|
||||
);
|
||||
break;
|
||||
case DELETE:
|
||||
event = WSDataSourceEvent.delete(
|
||||
session.getUserContext().getSmSessionId(),
|
||||
session.getUserId(),
|
||||
project.getId(),
|
||||
List.of(datasourceId),
|
||||
property
|
||||
);
|
||||
break;
|
||||
case UPDATE:
|
||||
event = WSDataSourceEvent.update(
|
||||
session.getUserContext().getSmSessionId(),
|
||||
session.getUserId(),
|
||||
project.getId(),
|
||||
List.of(datasourceId),
|
||||
property
|
||||
);
|
||||
break;
|
||||
}
|
||||
if (event == null) {
|
||||
return;
|
||||
}
|
||||
ServletAppUtils.getServletApplication().getEventController().addEvent(event);
|
||||
}
|
||||
|
||||
public static void addNavigatorNodeUpdatedEvent(
|
||||
DBPProject project,
|
||||
WebSession session,
|
||||
String nodePath,
|
||||
WSConstants.EventAction eventAction
|
||||
) {
|
||||
if (project == null) {
|
||||
return;
|
||||
}
|
||||
WSEvent event = null;
|
||||
switch (eventAction) {
|
||||
case CREATE:
|
||||
event = WSDatasourceFolderEvent.create(
|
||||
session.getUserContext().getSmSessionId(),
|
||||
session.getUserId(),
|
||||
project.getId(),
|
||||
List.of(nodePath)
|
||||
);
|
||||
break;
|
||||
case DELETE:
|
||||
event = WSDatasourceFolderEvent.delete(
|
||||
session.getUserContext().getSmSessionId(),
|
||||
session.getUserId(),
|
||||
project.getId(),
|
||||
List.of(nodePath)
|
||||
);
|
||||
break;
|
||||
case UPDATE:
|
||||
event = WSDatasourceFolderEvent.update(
|
||||
session.getUserContext().getSmSessionId(),
|
||||
session.getUserId(),
|
||||
project.getId(),
|
||||
List.of(nodePath)
|
||||
);
|
||||
break;
|
||||
}
|
||||
if (event == null) {
|
||||
return;
|
||||
}
|
||||
ServletAppUtils.getServletApplication().getEventController().addEvent(event);
|
||||
}
|
||||
|
||||
public static void addRmResourceUpdatedEvent(
|
||||
String projectId,
|
||||
@@ -153,7 +55,7 @@ public class WebEventUtils {
|
||||
switch (eventAction) {
|
||||
case CREATE:
|
||||
event = WSResourceUpdatedEvent.create(
|
||||
session.getUserContext().getSmSessionId(),
|
||||
WebEventUtils.getSmSessionId(session),
|
||||
session.getUserId(),
|
||||
projectId,
|
||||
resourcePath,
|
||||
@@ -163,7 +65,7 @@ public class WebEventUtils {
|
||||
break;
|
||||
case DELETE:
|
||||
event = WSResourceUpdatedEvent.delete(
|
||||
session.getUserContext().getSmSessionId(),
|
||||
WebEventUtils.getSmSessionId(session),
|
||||
session.getUserId(),
|
||||
projectId,
|
||||
resourcePath,
|
||||
@@ -173,7 +75,7 @@ public class WebEventUtils {
|
||||
break;
|
||||
case UPDATE:
|
||||
event = WSResourceUpdatedEvent.update(
|
||||
session.getUserContext().getSmSessionId(),
|
||||
WebEventUtils.getSmSessionId(session),
|
||||
session.getUserId(),
|
||||
projectId,
|
||||
resourcePath,
|
||||
@@ -213,4 +115,12 @@ public class WebEventUtils {
|
||||
ServletAppUtils.getServletApplication().getEventController().addEvent(event);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static String getSmSessionId(@NotNull BaseWebSession webSession) {
|
||||
return webSession.getUserContext().getSmSessionId();
|
||||
}
|
||||
|
||||
public static boolean isSmSessionIdEquals(@NotNull BaseWebSession webSession, @Nullable String smSessionId) {
|
||||
return Objects.equals(getSmSessionId(webSession), smSessionId);
|
||||
}
|
||||
}
|
||||
+5
-1
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* DBeaver - Universal Database Manager
|
||||
* Copyright (C) 2010-2024 DBeaver Corp and others
|
||||
* Copyright (C) 2010-2026 DBeaver Corp and others
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -16,6 +16,8 @@
|
||||
*/
|
||||
package io.cloudbeaver.websocket;
|
||||
|
||||
import io.cloudbeaver.model.session.BaseWebSession;
|
||||
import org.jkiss.code.NotNull;
|
||||
import org.jkiss.dbeaver.DBException;
|
||||
import org.jkiss.dbeaver.model.websocket.event.WSEvent;
|
||||
|
||||
@@ -24,4 +26,6 @@ public interface CBWebSessionEventHandler {
|
||||
|
||||
void close();
|
||||
|
||||
void migrateToSession(@NotNull BaseWebSession newSession);
|
||||
|
||||
}
|
||||
|
||||
+40
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
* DBeaver - Universal Database Manager
|
||||
* Copyright (C) 2010-2026 DBeaver Corp and others
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package io.cloudbeaver.websocket.event.client;
|
||||
|
||||
import org.jkiss.code.NotNull;
|
||||
import org.jkiss.dbeaver.model.websocket.WSConstants;
|
||||
import org.jkiss.dbeaver.model.websocket.event.WSClientEvent;
|
||||
|
||||
public class WSSessionCancelActionClientEvent extends WSClientEvent {
|
||||
public static final String ID = "cb_client_cancel_action";
|
||||
|
||||
@NotNull
|
||||
private final String actionId;
|
||||
|
||||
public WSSessionCancelActionClientEvent(
|
||||
@NotNull String actionId
|
||||
) {
|
||||
super(ID, WSConstants.TOPIC_SESSION_ACTION);
|
||||
this.actionId = actionId;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public String getActionId() {
|
||||
return actionId;
|
||||
}
|
||||
}
|
||||
@@ -3,8 +3,8 @@ Bundle-ManifestVersion: 2
|
||||
Bundle-Vendor: DBeaver Corp
|
||||
Bundle-Name: Cloudbeaver Community Product
|
||||
Bundle-SymbolicName: io.cloudbeaver.product.ce;singleton:=true
|
||||
Bundle-Version: 26.0.0.qualifier
|
||||
Bundle-Release-Date: 20260302
|
||||
Bundle-Version: 26.2.1.qualifier
|
||||
Bundle-Release-Date: 20260921
|
||||
Bundle-RequiredExecutionEnvironment: JavaSE-21
|
||||
Bundle-ActivationPolicy: lazy
|
||||
Require-Bundle: io.cloudbeaver.server.ce
|
||||
|
||||
@@ -16,6 +16,10 @@
|
||||
</product>
|
||||
</extension>
|
||||
|
||||
<extension point="io.cloudbeaver.feature">
|
||||
<feature id="ai" label="AI Integration" description="Enables AI Chat in the application." enabledByDefault="true"/>
|
||||
</extension>
|
||||
|
||||
<extension point="org.jkiss.dbeaver.application">
|
||||
<application
|
||||
id="cloudbeaver-ce"
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
<relativePath>../</relativePath>
|
||||
</parent>
|
||||
<artifactId>io.cloudbeaver.product.ce</artifactId>
|
||||
<version>26.0.0-SNAPSHOT</version>
|
||||
<version>26.2.1-SNAPSHOT</version>
|
||||
<packaging>eclipse-plugin</packaging>
|
||||
|
||||
</project>
|
||||
|
||||
@@ -2,8 +2,8 @@ Manifest-Version: 1.0
|
||||
Bundle-ManifestVersion: 2
|
||||
Bundle-Name: Cloudbeaver Base JDBC drivers
|
||||
Bundle-SymbolicName: io.cloudbeaver.resources.drivers.base;singleton:=true
|
||||
Bundle-Version: 1.0.141.qualifier
|
||||
Bundle-Release-Date: 20260302
|
||||
Bundle-Version: 1.0.154.qualifier
|
||||
Bundle-Release-Date: 20260921
|
||||
Bundle-Vendor: DBeaver Corp
|
||||
Bundle-ActivationPolicy: lazy
|
||||
Automatic-Module-Name: io.cloudbeaver.resources.drivers.base
|
||||
|
||||
@@ -14,7 +14,9 @@
|
||||
<resource name="drivers/jaybird"/>
|
||||
<resource name="drivers/h2"/>
|
||||
<resource name="drivers/h2_v2"/>
|
||||
<resource name="drivers/h2_v3"/>
|
||||
<resource name="drivers/sqlite/xerial"/>
|
||||
<resource name="drivers/libsql"/>
|
||||
<resource name="drivers/mssql/new"/>
|
||||
<resource name="drivers/trino"/>
|
||||
<resource name="drivers/kyuubi"/>
|
||||
@@ -35,7 +37,9 @@
|
||||
<bundle id="drivers.firebird" label="Firebird drivers"/>
|
||||
<bundle id="drivers.h2" label="H2 drivers"/>
|
||||
<bundle id="drivers.h2_v2" label="H2 v2 drivers"/>
|
||||
<bundle id="drivers.h2_v3" label="H2 v3 drivers"/>
|
||||
<bundle id="drivers.sqlite.xerial" label="SQLite drivers"/>
|
||||
<bundle id="drivers.libsql" label="LibSQL drivers"/>
|
||||
<bundle id="drivers.mssql.new" label="SQL Server drivers"/>
|
||||
<bundle id="drivers.trino" label="Trino drivers"/>
|
||||
<bundle id="drivers.kyuubi" label="Apache Kyuubi drivers"/>
|
||||
@@ -55,13 +59,14 @@
|
||||
<driver id="clickhouse:com_clickhouse"/>
|
||||
<driver id="h2:h2_embedded"/>
|
||||
<driver id="h2:h2_embedded_v2"/>
|
||||
<driver id="h2:h2_embedded_v3"/>
|
||||
<driver id="sqlite:sqlite_jdbc"/>
|
||||
<driver id="sqlite:libsql_jdbc"/>
|
||||
<driver id="sqlserver:microsoft"/>
|
||||
<driver id="generic:trino_jdbc"/>
|
||||
<driver id="duckdb:duckdb_jdbc"/>
|
||||
<driver id="generic:kyuubi_hive"/>
|
||||
<driver id="databend:databend"/>
|
||||
<driver id="databend:databend_jdbc"/>
|
||||
</extension>
|
||||
|
||||
|
||||
|
||||
@@ -9,6 +9,6 @@
|
||||
<relativePath>../</relativePath>
|
||||
</parent>
|
||||
<artifactId>io.cloudbeaver.resources.drivers.base</artifactId>
|
||||
<version>1.0.141-SNAPSHOT</version>
|
||||
<version>1.0.154-SNAPSHOT</version>
|
||||
<packaging>eclipse-plugin</packaging>
|
||||
</project>
|
||||
|
||||
@@ -3,8 +3,8 @@ Bundle-ManifestVersion: 2
|
||||
Bundle-Vendor: DBeaver Corp
|
||||
Bundle-Name: CloudBeaver Community
|
||||
Bundle-SymbolicName: io.cloudbeaver.server.ce;singleton:=true
|
||||
Bundle-Version: 26.0.0.qualifier
|
||||
Bundle-Release-Date: 20260302
|
||||
Bundle-Version: 26.2.1.qualifier
|
||||
Bundle-Release-Date: 20260921
|
||||
Bundle-RequiredExecutionEnvironment: JavaSE-21
|
||||
Bundle-ActivationPolicy: lazy
|
||||
Bundle-Activator: io.cloudbeaver.server.CBPlatformActivator
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user