mirror of
https://github.com/dbeaver/cloudbeaver.git
synced 2026-10-16 14:50:15 +08:00
Compare commits
492
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
24ee5298f4 | ||
|
|
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 | ||
|
|
8a3f6455ec | ||
|
|
801139e3ff | ||
|
|
457909dd82 | ||
|
|
9f7e086369 | ||
|
|
6e8a09a49e | ||
|
|
8d11f94ad6 | ||
|
|
3b0cd66232 | ||
|
|
a60b7d4496 | ||
|
|
07a3a069fb | ||
|
|
4511208cad | ||
|
|
1ab8d15b00 | ||
|
|
ae331effde | ||
|
|
6c88030106 | ||
|
|
5e53341ba1 | ||
|
|
9338539564 | ||
|
|
af1aa83d8b | ||
|
|
32671ff239 | ||
|
|
f7c33a7f7e | ||
|
|
d70e4976e5 | ||
|
|
a0e6154a08 | ||
|
|
1b6e729506 | ||
|
|
3adb898f5a | ||
|
|
c886ee7bcd | ||
|
|
a5a9700ab9 | ||
|
|
2c38e75a99 | ||
|
|
12bc5ba5dc | ||
|
|
2719f53655 | ||
|
|
a2d336166b | ||
|
|
06da0705d2 | ||
|
|
058a341815 | ||
|
|
f5b0aae8bd | ||
|
|
789760aa9e | ||
|
|
fe77133226 | ||
|
|
8e237d1bbc | ||
|
|
0f3b8033df | ||
|
|
586d4154ab | ||
|
|
e0fe55c3bf | ||
|
|
9c406a4e1e | ||
|
|
05bf79e59c | ||
|
|
d013ece4f6 | ||
|
|
b0205e34c7 | ||
|
|
0b41ae89e8 | ||
|
|
b318c262b9 | ||
|
|
547831d060 | ||
|
|
93af9a8719 | ||
|
|
19125fed5e | ||
|
|
ea5fb47a56 | ||
|
|
89b4d1f970 | ||
|
|
0bd89236d2 | ||
|
|
abe0f6fb7d | ||
|
|
0b2b851772 | ||
|
|
dcb8e536b8 | ||
|
|
7881551821 | ||
|
|
cdc94dce5f | ||
|
|
70b67ce6f4 | ||
|
|
e570a05175 | ||
|
|
f82598ad66 | ||
|
|
d3b0b96bae | ||
|
|
ec9c2b895b | ||
|
|
be8744232b | ||
|
|
4088e7998e | ||
|
|
c540728fee | ||
|
|
84f0903fc7 | ||
|
|
89152a09a1 | ||
|
|
0bfdcf0886 | ||
|
|
d2d3ada58a | ||
|
|
a62478a574 | ||
|
|
980473918e | ||
|
|
26712e7bca | ||
|
|
9597e99bc9 | ||
|
|
7f92abd33e | ||
|
|
ad686faca8 | ||
|
|
c02411085e | ||
|
|
90fcbe6b7f | ||
|
|
1deffb984b | ||
|
|
bd52ee6583 | ||
|
|
013ad81581 | ||
|
|
4843002e0d | ||
|
|
9a05d0bf7f | ||
|
|
ecaa4934b1 | ||
|
|
8d89bc25ba | ||
|
|
40581e746b | ||
|
|
a9911d9297 | ||
|
|
a8d437dce6 | ||
|
|
bacd1bff91 | ||
|
|
1d9b0a2943 | ||
|
|
aa6a81428c | ||
|
|
b91ca06359 | ||
|
|
244479cd06 | ||
|
|
1c56d55462 | ||
|
|
be78d2fe92 | ||
|
|
4178826ff4 | ||
|
|
27ef1fdf73 | ||
|
|
5b0bedcf5e | ||
|
|
821805eba8 | ||
|
|
6531e45357 | ||
|
|
598fb92eaf | ||
|
|
71dda4aa45 | ||
|
|
cc1861c614 | ||
|
|
67c15af74d | ||
|
|
79194b2b50 | ||
|
|
673b824cb5 | ||
|
|
4e7555c419 | ||
|
|
9d0e75dba8 | ||
|
|
44cc1badf4 | ||
|
|
fdb4894ae4 | ||
|
|
a836412ee4 | ||
|
|
38b0246240 | ||
|
|
0bf769a662 | ||
|
|
0349bcee4c | ||
|
|
44b89296d2 | ||
|
|
b75d802883 | ||
|
|
9652520073 | ||
|
|
7e7d6634de | ||
|
|
c12561965f | ||
|
|
7a12ad4b91 | ||
|
|
c39de17df8 | ||
|
|
147edde923 | ||
|
|
aeaa864f57 | ||
|
|
bcda3e4312 | ||
|
|
72a007ceb6 | ||
|
|
e08ba6845e | ||
|
|
8f1fc1f36e | ||
|
|
b5cfcaef31 | ||
|
|
e9b1079f85 | ||
|
|
326514f914 | ||
|
|
67489a03b3 | ||
|
|
4ea0baf79b | ||
|
|
939f4d046a | ||
|
|
cd3cc2e92f | ||
|
|
7a8c4a141f | ||
|
|
5db7a0de5e | ||
|
|
15c61239ec | ||
|
|
164d155e7d | ||
|
|
821c9c8e56 | ||
|
|
b58e01794d | ||
|
|
fbc5f2d3ce | ||
|
|
ae9e87950a | ||
|
|
58db928e34 | ||
|
|
583519d9a7 | ||
|
|
a96862a444 | ||
|
|
b9d115ec33 | ||
|
|
78daa21c87 | ||
|
|
50090a3035 | ||
|
|
fc9b1d388f | ||
|
|
0c053efd68 |
@@ -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"
|
||||
}
|
||||
|
||||
@@ -1,17 +1,20 @@
|
||||
<img src="https://github.com/dbeaver/cloudbeaver/wiki/images/cloudbeaver-logo.png" align="right" width="250"/>
|
||||
<img src="https://github.com/dbeaver/cloudbeaver/wiki/images/cloudbeaver-logo.png" alt="CloudBeaver logo" align="right" width="250"/>
|
||||
|
||||
# CloudBeaver Community
|
||||
|
||||
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,42 +22,173 @@ 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.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.
|
||||
### 26.1.4 2026-08-03
|
||||
|
||||
[We are happy to reward](https://dbeaver.com/help-dbeaver/) our most active contributors every major sprint.
|
||||
- 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. Access it from the top toolbar. All AI features can be turned off in the Server configuration.
|
||||
- Added the option to configure AI profiles in the Administration panel, usable in the AI Chat and for the @ai command in the SQL Editor. Both OpenAI and Copilot engines can be utilized for configuration.
|
||||
- Connectivity:
|
||||
- Resolved the issue where the application could no longer connect to the database after running for an extended period.
|
||||
- Resolved the issue where user connection settings required re-login in a new session to apply their changes.
|
||||
- Accessibility:
|
||||
- Added a loader to the Save button in the Administration panel for visualizing the loading process.
|
||||
- Improved drag-and-drop in the Navigator Tree for objects by enlarging the auto-scroll trigger areas.
|
||||
- General:
|
||||
- Added the ability to export generated SQL to a .sql file from the Generate SQL dialog.
|
||||
- Improved "Open in SQL Editor" behavior by preserving the source table context for the Generate SQL dialog.
|
||||
- Fixed synchronization between instances eliminates the need to refresh to see the updated connection or folder in another instance.
|
||||
- Security:
|
||||
- Added a new "Auto-create users" setting for the reverse proxy provider, allowing new user creation on login. It is enabled by default.
|
||||
- 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 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.
|
||||
- Databases and drivers:
|
||||
- ClickHouse
|
||||
- The driver was updated to version 0.10.0.
|
||||
- Improved JSON display in the Value panel.
|
||||
- MySQL: Fixed support for comments starting with '#' symbol in the SQL Editor.
|
||||
- PostgreSQL:
|
||||
- The driver was updated to version 42.7.13.
|
||||
- Fixed an error that occurred when applying filters to database nodes.
|
||||
- Fixed generated calls for procedures with OUT parameters.
|
||||
|
||||
### 26.1.3 2026-07-20
|
||||
|
||||
- Data Editor:
|
||||
- Fixed copying of large JSON values in the Data Editor - now the full value can be copied and transferred.
|
||||
- Updated the Reference panel to show only relevant rows when a foreign key is null.
|
||||
- General:
|
||||
- Added connection types to visually distinguish database environments using background colors across the Database Navigator and application UI. Users with edit permissions can assign built-in environment types color-coded configurations.
|
||||
- Data Transfer:
|
||||
- Improved memory usage when importing large CSV files.
|
||||
- Fixed CSV export with multi-character quotes and improved quote/delimiter validation
|
||||
- Accessibility:
|
||||
- Enabled the use of the Enter or space keys to toggle checkboxes in the administration tabs and the metadata viewer.
|
||||
- Added the ability to expand and collapse code block elements, including JSON, using the keyboard.
|
||||
- Fixed shortcuts behavior for AZERTY keyboards. Now shortcuts like Ctrl/Cmd + Z work correctly.
|
||||
- Security:
|
||||
- Resolved an authorization vulnerability when testing connections by enforcing strict permission checks. This mitigation prevented unauthenticated users from exploiting JDBC requests to read arbitrary server files.
|
||||
- Resolved an LDAP injection vulnerability by properly escaping user input in the authentication username field, preventing attackers from manipulating search filters, bypassing access restrictions, and enumerating directory users.
|
||||
- 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.
|
||||
- Databases and drivers:
|
||||
- The Databend driver was updated to version 0.4.8.
|
||||
- Fixed an issue related to the display of metadata in the navigation tree for DB2, Firebird, and MySQL.
|
||||
|
||||
### 26.1.2 2026-07-06
|
||||
|
||||
- Data Editor:
|
||||
- Added the References panel showing related records from other tables connected by foreign keys. It helps to explore table relationships directly in the Data editor.
|
||||
- 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.
|
||||
- General:
|
||||
- Added the Last Login time to the user form and the user table in the administration part.
|
||||
- Fixed the bug with the inability to select entered text in the Find and Replace panel in editors.
|
||||
- Accessibility:
|
||||
- Added the ability to use the Enter or Space key to turn checkboxes on/off in the connection properties tab.
|
||||
- Security:
|
||||
- Fixed CVE-2026-55449. Resolved a local file disclosure vulnerability by enforcing strict validation of file identifiers and path containment during data updates.
|
||||
- Updated session handling to prevent session fixation vulnerabilities. The application generates new session identifiers upon login.
|
||||
- Fixed the high vulnerability (CVE-2026-12151) in the undici library. The library was updated to version 6.27.0.
|
||||
|
||||
### 26.1.1 2026-06-22
|
||||
|
||||
- General: Added a "Project Info" tab containing the project description to the project context menu.
|
||||
- Security:
|
||||
- Fixed a cross-site scripting vulnerability caused by unsanitized user input in the web interface.
|
||||
- Resolved a security flaw related to user-controlled file paths by adding strict validation to prevent unauthorized resource access.
|
||||
- Fixed the critical vulnerability (CVE-2026-9277) in the shell-quote library (updated to version 1.8.4).
|
||||
|
||||
### 26.1.0 2026-06-01
|
||||
|
||||
### Changes since 26.0.0:
|
||||
|
||||
- Administration:
|
||||
- Added support for custom startup arguments when running CloudBeaver in a container. Administrators can specify configuration file locations to adapt the application to their environment. (thanks to @MalteHei)
|
||||
- Updated the manifest.webmanifest file to generate dynamically based on the CLOUDBEAVER_ROOT_URI environment variable. (thanks to @houssemexo26)
|
||||
- Authorization:
|
||||
- Added HTTP header authentication support for GraphQL queries. Users can provide API tokens via Authorization or X-Api-Key headers, allowing external data tools to authenticate without modifying request bodies.
|
||||
- Fixed a bug in the authentication form: the existing password is now required for password changes.
|
||||
- SQL Editor:
|
||||
- Updated the SQL Editor to use real tab characters for indentation. The application displayed tabs as four-space gaps with a distinct visual marker.
|
||||
- Added an ability to view and edit scripts without connecting to the database.
|
||||
- Added the ability to open generated queries right from the Generate SQL window.
|
||||
- Added fuzzy search to SQL Editor auto-completion for object names. The feature allows users to find tables and containers even with typos or incorrect letter ordering.
|
||||
- Added the tooltip about quoting rules for the Bind variables parameters dialog.
|
||||
- Fixed keyword autocompletion for partially typed keywords to avoid redundant letters.
|
||||
- Data Editor:
|
||||
- Added the ability to copy-paste multiple cells at once. Pasted values will be distributed across selected cells.
|
||||
- Added an ability to add, duplicate, delete, or set values to NULL in multiple highlighted rows via the Data Editor context menu.
|
||||
- Added the Find and Replace functionality for the Data Editor with the ability to find data by matching case, whole word, or using regular expressions.
|
||||
- Data Editor started to keep the state of column configurations, such as filters, sorting, and ordering, after the reconnect, page refresh, and re-login.
|
||||
- Added key column mark for tables with unique keys in the data grid.
|
||||
- Added more settings to the Generated SQL window, including the ability to select fully qualified names for query or use compact SQL formatting.
|
||||
- Added a DDL generation option to the Data Editor and Result Set context menu.
|
||||
- Fixed copy and paste behavior for multiline and special characters in the Data Editor. Added visual glyphs to indicate line breaks and tabs in the data grid.
|
||||
- Fixed the ability to copy Boolean values in the clipboard in the Data Editor.
|
||||
- Fixed the ability to calculate row count for non-standard SELECT statements. (thanks to @fdcastel)
|
||||
- Navigator tree:
|
||||
- Reorganized the context menu on the connection level to make it more compact.
|
||||
- Added support for special symbols (pipe, comma, and asterisk) for the search field.
|
||||
- Removed the "Folders" setting from the settings list for the Navigator Tree.
|
||||
- Accessibility:
|
||||
- Added the Skip to content option for quick keyboard access to the Navigator Tree, editors, and shortcuts list tab to improve application accessibility.
|
||||
- Improved keyboard navigation for context menu and buttons for Data Editor, SQL Editor, and Navigator tree.
|
||||
- Added the Ctrl + / keyboard shortcut to open the context menu for selected cells in the Data Editor.
|
||||
- Fixed the ability to switch tabs in the Data Editor using the keyboard.
|
||||
- Fixed contrast for elements across different application parts in the light and dark themes to meet WCAG requirements.
|
||||
- General:
|
||||
- Marked icons for non-saved (temporary) scripts with the hourglass symbol to add visual distinction from the saved ones.
|
||||
- Added the ability to set to NULL any value in the Driver Properties.
|
||||
- Fixed custom driver properties display for the connection page.
|
||||
- Fixed data export in SQL format for values containing single quotes.
|
||||
- Databases and drivers:
|
||||
- ClickHouse:
|
||||
- Fixed the display of Array(JSON) types in the data grid.
|
||||
- Updated driver version to 0.9.7.
|
||||
- Fixed IP address display in the Data Editor by removing the leading slash.
|
||||
- Added support for the "prompt=false" connection property for DB2 for IBM i to turn off interactive prompts and prevent related errors.
|
||||
- DuckDB:
|
||||
- Fixed map rendering for GEOMETRY types with CRS parameters.
|
||||
- Fixed geometry type display for database versions > 1.5.
|
||||
- H2 database:
|
||||
- Added new embedded driver version 2.4.2.
|
||||
- Updated server driver to version 2.4.2.
|
||||
- Firebird: Expanded database-specific SQL dialect coverage for the SQL Editor. Added more keywords, built-in functions, and SQL generators.
|
||||
- MySQL: Fixed query boundaries recognition in the SQL Editor for scripts with the DELIMITER keyword.
|
||||
- PostgreSQL:
|
||||
- Updated driver to version 42.7.11
|
||||
- Fixed missing INOUT parameters in CHECK generation for procedures and functions.
|
||||
- Fixed handling of 24:xx values in time columns.
|
||||
- Fixed an error when connecting via URL if the database name contains a hyphen.
|
||||
- SQL Server:
|
||||
- Fixed DDL generation for external tables.
|
||||
- Fixed the database connectivity issue after the Java update.
|
||||
- Security:
|
||||
- Added an administrative setting to restrict SSH tunneling capabilities. Administrators can now limit tunnel configuration to authorized users, reducing the risk of unauthorized network access.
|
||||
- Fixed the critical vulnerability (CVE-2025-62718) in the axios library. The library was updated to version 1.15.0.
|
||||
- Fixed the high vulnerability (CVE-2026-4800) in the lodash library. The library was updated to version 4.18.0.
|
||||
- Fixed the high vulnerability (CVE-2026-39363) in the vite library. The library was updated to version 7.3.2.
|
||||
- Fixed the high vulnerability (CVE-2026-42035) in the axios library. The library was updated to version 1.15.1.
|
||||
- Fixed the high vulnerability (CVE-2026-3505) in the bcpg-jdk18on library. The library was updated to version 1.84.0.
|
||||
- Fixed the high vulnerability (CVE-2026-33671) in the picomatch library. The library was updated to version 2.3.2.
|
||||
- Fixed the high vulnerability (CVE-2026-33943) in the happy-dom library. The library was updated to version 20.8.8.
|
||||
- Fixed the high vulnerability (CVE-2025-45141) in the tar library. The library was updated to version 7.5.11.
|
||||
- Fixed the high vulnerability (CVE-2026-27148) in the storybook library. The library was updated to version 10.2.10.
|
||||
- Fixed the high vulnerability (CVE-2026-27606) in the rollup library. The library was updated to version 4.59.0.
|
||||
- Fixed the high vulnerability (CVE-2026-27904) in the minimatch library. The library was updated to version 3.1.3.
|
||||
- Fixed the high vulnerability (CVE-2026-27959) in the koa library. The library was updated to version 2.16.4.
|
||||
- Fixed the high vulnerability (CVE-2026-32141) in the flatted library. The library was updated to version 4.4.0.
|
||||
- Fixed the medium vulnerability (CVE-2026-41305) in the postcss library. The library was updated to version 8.5.14.
|
||||
|
||||
+1
-13
@@ -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.13.2</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.codehaus.mojo</groupId>
|
||||
<artifactId>exec-maven-plugin</artifactId>
|
||||
<version>3.1.0</version>
|
||||
<configuration>
|
||||
<mainClass>io.cloudbeaver.ConfigGenerator</mainClass>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<artifactId>maven-resources-plugin</artifactId>
|
||||
<version>3.3.1</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>copy-external-config</id>
|
||||
<phase>process-resources</phase>
|
||||
<goals>
|
||||
<goal>copy-resources</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<outputDirectory>${project.build.outputDirectory}/config</outputDirectory>
|
||||
<resources>
|
||||
<resource>
|
||||
<directory>${project.basedir}/../../config/template</directory>
|
||||
<includes>
|
||||
<include>cloudbeaver-base.conf</include>
|
||||
</includes>
|
||||
</resource>
|
||||
</resources>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</project>
|
||||
@@ -0,0 +1,235 @@
|
||||
/*
|
||||
* DBeaver - Universal Database Manager
|
||||
* Copyright (C) 2010-2026 DBeaver Corp and others
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package io.cloudbeaver;
|
||||
|
||||
import com.google.gson.*;
|
||||
import com.google.gson.reflect.TypeToken;
|
||||
|
||||
import java.io.Reader;
|
||||
import java.net.URL;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.*;
|
||||
|
||||
public class ConfigGenerator {
|
||||
|
||||
private static final Gson gson = new GsonBuilder()
|
||||
.setPrettyPrinting()
|
||||
.disableHtmlEscaping()
|
||||
.create();
|
||||
private static final String EMPTY_STRING = "''";
|
||||
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
String baseConfigPath = System.getProperty("config.base");
|
||||
if (baseConfigPath == null || baseConfigPath.isEmpty()) {
|
||||
URL resource = ConfigGenerator.class.getClassLoader().getResource("config/cloudbeaver-base.conf");
|
||||
if (resource == null) {
|
||||
System.out.println("Base config path is required");
|
||||
return;
|
||||
}
|
||||
baseConfigPath = resource.toURI().getPath();
|
||||
if (System.getProperty("os.name").toLowerCase().contains("win")) {
|
||||
// Fix for Windows in dev mode. Resource URI starts with / (e.g. /C:/some/folder)
|
||||
// And this breaks Java NIO
|
||||
if (baseConfigPath.startsWith("/")) {
|
||||
baseConfigPath = baseConfigPath.substring(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
String outputPath = System.getProperty("config.output");
|
||||
if (outputPath == null || outputPath.isEmpty()) {
|
||||
System.out.println("Output config path is required");
|
||||
return;
|
||||
}
|
||||
|
||||
String configPatches = System.getProperty("config.patches");
|
||||
|
||||
List<String> overrideConfigs = configPatches == null ? List.of() : Arrays.asList(configPatches.split(","));
|
||||
|
||||
Map<String, Object> config = readConfigurationFile(baseConfigPath);
|
||||
System.out.println("Base config loaded");
|
||||
|
||||
for (String overrideConfig : overrideConfigs) {
|
||||
if (Files.exists(Path.of(overrideConfig))) {
|
||||
System.out.println("Applying override config: " + overrideConfig);
|
||||
} else {
|
||||
System.out.println("Override config not found, skipping: " + overrideConfig);
|
||||
continue;
|
||||
}
|
||||
System.out.println("Processing override config: " + overrideConfig);
|
||||
Map<String, Object> overrideConfigMap = readConfigurationFile(overrideConfig);
|
||||
System.out.println("Override config loaded: " + overrideConfig);
|
||||
applyProductConfig(config, overrideConfigMap);
|
||||
System.out.println("Override config applied: " + overrideConfig);
|
||||
}
|
||||
|
||||
Map<String, Object> finalMap = convertToOutputFormat(config);
|
||||
System.out.println("Config converted to output format");
|
||||
|
||||
writeConfigurationFile(finalMap, outputPath);
|
||||
System.out.println("Config written to: " + outputPath);
|
||||
}
|
||||
|
||||
private static Map<String, Object> readConfigurationFile(String filePath) throws Exception {
|
||||
Path path = Path.of(filePath);
|
||||
if (!Files.exists(path)) {
|
||||
throw new IllegalArgumentException("Configuration file not found: " + filePath);
|
||||
}
|
||||
|
||||
try (Reader reader = Files.newBufferedReader(path)) {
|
||||
return gson.fromJson(reader, TypeToken.getParameterized(Map.class, String.class, Object.class).getType());
|
||||
}
|
||||
}
|
||||
|
||||
private static void applyProductConfig(Map<String, Object> baseConfig, Map<String, Object> productConfig) {
|
||||
if (productConfig.containsKey("add")) {
|
||||
Map<String, Object> add = (Map<String, Object>) productConfig.get("add");
|
||||
deepMergeMap(baseConfig, add);
|
||||
}
|
||||
|
||||
if (productConfig.containsKey("remove")) {
|
||||
Map<String, Object> remove = (Map<String, Object>) productConfig.get("remove");
|
||||
deepRemoveMap(baseConfig, remove);
|
||||
}
|
||||
}
|
||||
|
||||
private static void deepMergeMap(Map<String, Object> target, Map<String, Object> source) {
|
||||
for (Map.Entry<String, Object> entry : source.entrySet()) {
|
||||
Object sourceValue = entry.getValue();
|
||||
Object targetValue = target.get(entry.getKey());
|
||||
|
||||
if (sourceValue instanceof Map && targetValue instanceof Map) {
|
||||
deepMergeMap((Map<String, Object>) targetValue, (Map<String, Object>) sourceValue);
|
||||
} else {
|
||||
target.put(entry.getKey(), sourceValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void deepRemoveMap(Map<String, Object> target, Map<String, Object> source) {
|
||||
for (Map.Entry<String, Object> entry : source.entrySet()) {
|
||||
Object sourceValue = entry.getValue();
|
||||
Object targetValue = target.get(entry.getKey());
|
||||
|
||||
if (sourceValue instanceof Map && targetValue instanceof Map) {
|
||||
deepRemoveMap((Map<String, Object>) targetValue, (Map<String, Object>) sourceValue);
|
||||
} else {
|
||||
target.remove(entry.getKey());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void writeConfigurationFile(Map<String, Object> map, String filePath) throws Exception {
|
||||
JsonElement tree = gson.toJsonTree(map);
|
||||
StringBuilder sb = new StringBuilder();
|
||||
writeElement(tree, sb, 0);
|
||||
Files.write(Path.of(filePath), sb.toString().getBytes());
|
||||
}
|
||||
|
||||
private static void writeElement(JsonElement elem, StringBuilder sb, int count) {
|
||||
String indent = " ";
|
||||
if (elem.isJsonObject()) {
|
||||
sb.append("{\n");
|
||||
JsonObject obj = elem.getAsJsonObject();
|
||||
var iterator = obj.entrySet().iterator();
|
||||
|
||||
while (iterator.hasNext()) {
|
||||
var entry = iterator.next();
|
||||
sb.repeat(indent, count + 1).append(entry.getKey()).append(": ");
|
||||
writeElement(entry.getValue(), sb, count + 1);
|
||||
if (iterator.hasNext()) {
|
||||
sb.append(",");
|
||||
}
|
||||
sb.append("\n");
|
||||
}
|
||||
sb.repeat(indent, count).append("}");
|
||||
|
||||
} else if (elem.isJsonArray()) {
|
||||
sb.append("[\n");
|
||||
JsonArray array = elem.getAsJsonArray();
|
||||
for (int i = 0; i < array.size(); i++) {
|
||||
sb.repeat(indent, count + 1);
|
||||
writeElement(array.get(i), sb, count + 1);
|
||||
if (i < array.size() - 1) {
|
||||
sb.append(",");
|
||||
}
|
||||
sb.append("\n");
|
||||
}
|
||||
sb.repeat(indent, count).append("]");
|
||||
|
||||
} else {
|
||||
String valueStr = gson.toJson(elem).replace("\n", "\n" + indent);
|
||||
sb.append(valueStr);
|
||||
}
|
||||
}
|
||||
|
||||
private static Map<String, Object> convertToOutputFormat(Map<String, Object> map) {
|
||||
Map<String, Object> result = new LinkedHashMap<>();
|
||||
for (Map.Entry<String, Object> entry : map.entrySet()) {
|
||||
result.put(entry.getKey(), convertValue(entry.getValue()));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private static Object convertValue(Object value) {
|
||||
if (value instanceof Map) {
|
||||
Map<String, Object> map = (Map<String, Object>) value;
|
||||
if (!map.isEmpty() && map.containsKey("value")) {
|
||||
if (map.containsKey("env")) {
|
||||
String env = (String) map.get("env");
|
||||
Object defValue = map.get("value");
|
||||
return "${" + env + ":" + formatDefaultValue(defValue) + "}";
|
||||
} else {
|
||||
return convertValue(map.get("value"));
|
||||
}
|
||||
}
|
||||
Map<String, Object> result = new LinkedHashMap<>();
|
||||
for (Map.Entry<String, Object> entry : map.entrySet()) {
|
||||
result.put(entry.getKey(), convertValue(entry.getValue()));
|
||||
}
|
||||
return result;
|
||||
} else if (value instanceof List) {
|
||||
List<Object> list = (List<Object>) value;
|
||||
List<Object> result = new ArrayList<>();
|
||||
for (Object item : list) {
|
||||
result.add(convertValue(item));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
private static String formatDefaultValue(Object value) {
|
||||
return switch (value) {
|
||||
case null -> EMPTY_STRING;
|
||||
case String str -> str.isEmpty() ? EMPTY_STRING : str;
|
||||
case Boolean b -> value.toString();
|
||||
case Number number -> {
|
||||
if (number instanceof Double || number instanceof Float) {
|
||||
long longVal = number.longValue();
|
||||
if (longVal == number.doubleValue()) {
|
||||
yield String.valueOf(longVal);
|
||||
}
|
||||
}
|
||||
yield number.toString();
|
||||
}
|
||||
// TODO: handle list elements if needed
|
||||
default -> value.toString();
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -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.0.qualifier
|
||||
Bundle-Release-Date: 20251222
|
||||
Bundle-Version: 1.0.16.qualifier
|
||||
Bundle-Release-Date: 20260817
|
||||
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.0-SNAPSHOT</version>
|
||||
<version>1.0.16-SNAPSHOT</version>
|
||||
<packaging>eclipse-plugin</packaging>
|
||||
|
||||
</project>
|
||||
|
||||
+14
-16
@@ -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.
|
||||
@@ -18,39 +18,36 @@ 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;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected CloudBeaverTopLevelCommand createTopLevelCommand(
|
||||
@Nullable ApplicationInstanceController applicationInstanceController,
|
||||
@NotNull CommandLineContext context,
|
||||
@NotNull CLIContextImpl context,
|
||||
@NotNull CLIRunMeta runMeta
|
||||
) {
|
||||
if (authenticator != null) {
|
||||
context.setContextParameter(CLIConstants.CONTEXT_PARAM_AUTHENTICATOR, authenticator);
|
||||
}
|
||||
return new CloudBeaverTopLevelCommand(applicationInstanceController, context, runMeta);
|
||||
}
|
||||
|
||||
@@ -58,10 +55,11 @@ public class CloudBeaverCommandLine extends ApplicationCommandLine<ApplicationIn
|
||||
@Override
|
||||
protected CommandLine initCommandLine(
|
||||
@Nullable ApplicationInstanceController applicationInstanceController,
|
||||
@NotNull CommandLineContext context,
|
||||
@NotNull CLIRunMeta runMeta
|
||||
@NotNull CLIContextImpl context,
|
||||
@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",
|
||||
|
||||
+5
-3
@@ -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.
|
||||
@@ -19,13 +19,15 @@ package io.cloudbeaver.model.cli;
|
||||
import org.jkiss.code.NotNull;
|
||||
import org.jkiss.code.Nullable;
|
||||
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.CommandLineContext;
|
||||
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,
|
||||
@@ -35,7 +37,7 @@ public class CloudBeaverTopLevelCommand extends AbstractTopLevelCommand {
|
||||
|
||||
protected CloudBeaverTopLevelCommand(
|
||||
@Nullable ApplicationInstanceController controller,
|
||||
@NotNull CommandLineContext context,
|
||||
@NotNull CLIContextImpl context,
|
||||
@NotNull CLIRunMeta meta
|
||||
|
||||
) {
|
||||
|
||||
@@ -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.91.qualifier
|
||||
Bundle-Release-Date: 20251222
|
||||
Bundle-Version: 1.0.107.qualifier
|
||||
Bundle-Release-Date: 20260817
|
||||
Bundle-RequiredExecutionEnvironment: JavaSE-21
|
||||
Bundle-ActivationPolicy: lazy
|
||||
Require-Bundle: org.jkiss.dbeaver.data.gis;visibility:=reexport,
|
||||
@@ -14,9 +14,9 @@ Require-Bundle: org.jkiss.dbeaver.data.gis;visibility:=reexport,
|
||||
org.jkiss.dbeaver.model.nio;visibility:=reexport,
|
||||
org.jkiss.dbeaver.registry;visibility:=reexport,
|
||||
org.jkiss.dbeaver.net.ssh,
|
||||
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,
|
||||
@@ -28,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.91-SNAPSHOT</version>
|
||||
<version>1.0.107-SNAPSHOT</version>
|
||||
<packaging>eclipse-plugin</packaging>
|
||||
|
||||
</project>
|
||||
|
||||
@@ -146,6 +146,8 @@ 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();
|
||||
}
|
||||
if (CommonUtils.isEmpty(message)) {
|
||||
if (cause.getMessage() != null) {
|
||||
|
||||
+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.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.
|
||||
@@ -24,53 +24,46 @@ 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.DBPAdaptable;
|
||||
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.SMObjectType;
|
||||
import org.jkiss.dbeaver.model.security.SMControllerUtils;
|
||||
import org.jkiss.dbeaver.model.websocket.event.datasource.WSDataSourceEvent;
|
||||
import org.jkiss.dbeaver.model.websocket.event.datasource.WSDataSourceProperty;
|
||||
import org.jkiss.dbeaver.registry.DataSourceDescriptor;
|
||||
import org.jkiss.dbeaver.registry.DataSourceRegistry;
|
||||
import org.jkiss.dbeaver.registry.project.BaseProjectSettings;
|
||||
import org.jkiss.dbeaver.runtime.jobs.DisconnectJob;
|
||||
import org.jkiss.utils.CommonUtils;
|
||||
|
||||
import java.nio.file.Path;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.*;
|
||||
|
||||
public class WebSessionProjectImpl extends WebProjectImpl {
|
||||
public class WebSessionProjectImpl extends WebProjectImpl implements DBPAdaptable {
|
||||
private static final Log log = Log.getLog(WebSessionProjectImpl.class);
|
||||
|
||||
protected final WebSession webSession;
|
||||
private final Map<String, WebConnectionInfo> connections = new HashMap<>();
|
||||
private final Map<String, Object> projectSettings = new ConcurrentHashMap<>();
|
||||
private final BaseProjectSettings projectSettings;
|
||||
private boolean registryIsLoaded = false;
|
||||
|
||||
public WebSessionProjectImpl(
|
||||
@NotNull WebSession webSession,
|
||||
@NotNull RMProject project
|
||||
) {
|
||||
super(
|
||||
webSession.getWorkspace(),
|
||||
webSession.getRmController(),
|
||||
webSession.getSessionContext(),
|
||||
project,
|
||||
webSession.getUserPreferenceStore(),
|
||||
RMUtils.getProjectPath(project)
|
||||
);
|
||||
this.webSession = webSession;
|
||||
this(webSession, project, null);
|
||||
}
|
||||
|
||||
public WebSessionProjectImpl(
|
||||
@NotNull WebSession webSession,
|
||||
@NotNull RMProject project,
|
||||
@NotNull Path path
|
||||
@Nullable Path path
|
||||
) {
|
||||
super(
|
||||
webSession.getWorkspace(),
|
||||
@@ -78,17 +71,52 @@ public class WebSessionProjectImpl extends WebProjectImpl {
|
||||
webSession.getSessionContext(),
|
||||
project,
|
||||
webSession.getUserPreferenceStore(),
|
||||
path
|
||||
path == null ? RMUtils.getProjectPath(project) : path
|
||||
);
|
||||
this.webSession = webSession;
|
||||
this.projectSettings = new BaseProjectSettings(this) {
|
||||
@NotNull
|
||||
@Override
|
||||
protected Map<SMObjectType, Map<String, Map<String, String>>> loadAllProjectSettings() throws DBException {
|
||||
if (webSession.getUser() == null) {
|
||||
return new LinkedHashMap<>();
|
||||
}
|
||||
return SMControllerUtils.getObjectSettingsMap(WebSessionProjectImpl.this, webSession.getSecurityController());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void saveProjectSettings(
|
||||
@NotNull SMObjectType objectType,
|
||||
@NotNull String objectId,
|
||||
@NotNull Map<String, String> settings
|
||||
) throws DBException {
|
||||
if (webSession.getUserContext().isNonAnonymousUserAuthorizedInSM()) {
|
||||
webSession.getSecurityController().setObjectSettings(getId(), objectType, objectId, settings);
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@NotNull
|
||||
public BaseProjectSettings getProjectSettings() {
|
||||
return projectSettings;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public DBNModel getNavigatorModel() {
|
||||
return webSession.getNavigatorModel();
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> T getAdapter(@NotNull Class<T> adapter) {
|
||||
if (adapter == DBPObjectSettingsProvider.class) {
|
||||
return adapter.cast(projectSettings);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
protected DBPDataSourceRegistry createDataSourceRegistry() {
|
||||
@@ -190,6 +218,7 @@ public class WebSessionProjectImpl extends WebProjectImpl {
|
||||
*
|
||||
* @return connections from cache.
|
||||
*/
|
||||
@NotNull
|
||||
public List<WebConnectionInfo> getConnections() {
|
||||
if (!registryIsLoaded) {
|
||||
addDataSourcesToCache();
|
||||
@@ -223,14 +252,14 @@ public class WebSessionProjectImpl extends WebProjectImpl {
|
||||
}
|
||||
case WSDataSourceEvent.UPDATED -> {
|
||||
if (event.getProperty() == WSDataSourceProperty.CONFIGURATION) {
|
||||
WebDataSourceUtils.disconnectDataSource(webSession, ds);
|
||||
WebDataSourceUtils.disconnectDataSource(webSession, ds, true);
|
||||
}
|
||||
if (event.getProperty() != WSDataSourceProperty.INTERNAL) {
|
||||
sendDataSourceUpdatedEvent = true;
|
||||
}
|
||||
}
|
||||
case WSDataSourceEvent.DELETED -> {
|
||||
WebDataSourceUtils.disconnectDataSource(webSession, ds);
|
||||
WebDataSourceUtils.disconnectDataSource(webSession, ds, false);
|
||||
if (registry instanceof DBPDataSourceRegistryCache dsrc) {
|
||||
dsrc.removeDataSourceFromList(ds);
|
||||
}
|
||||
@@ -244,21 +273,6 @@ public class WebSessionProjectImpl extends WebProjectImpl {
|
||||
return sendDataSourceUpdatedEvent;
|
||||
}
|
||||
|
||||
// TODO: load project settings on project load
|
||||
public void refreshProjectSettings() throws DBException {
|
||||
if (webSession.getUser() == null) {
|
||||
projectSettings.clear();
|
||||
return;
|
||||
}
|
||||
Map<String, Object> loadedSettings = webSession.getSecurityController().getObjectSettings(
|
||||
getId(),
|
||||
SMObjectType.project,
|
||||
null
|
||||
);
|
||||
projectSettings.clear();
|
||||
projectSettings.putAll(loadedSettings);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public WebConnectionInfo createConnectionInfo(@NotNull DBPDataSourceContainer dataSourceDescriptor) {
|
||||
return new WebConnectionInfo(webSession, dataSourceDescriptor);
|
||||
@@ -331,19 +345,9 @@ public class WebSessionProjectImpl extends WebProjectImpl {
|
||||
@NotNull
|
||||
private WebConnectionInfo closeAndDeleteConnection(@NotNull WebConnectionInfo connectionInfo) throws DBWebException {
|
||||
DBPDataSourceContainer dataSourceContainer = connectionInfo.getDataSourceContainer();
|
||||
boolean disconnected = WebDataSourceUtils.disconnectDataSource(webSession, dataSourceContainer);
|
||||
WebDataSourceUtils.disconnectDataSource(webSession, dataSourceContainer, false);
|
||||
DBPDataSourceRegistry registry = getDataSourceRegistry();
|
||||
registry.removeDataSource(dataSourceContainer);
|
||||
try {
|
||||
registry.checkForErrors();
|
||||
} catch (DBException e) {
|
||||
try {
|
||||
registry.addDataSource(dataSourceContainer);
|
||||
} catch (DBException ex) {
|
||||
log.error("Error re-adding after delete attempt", e);
|
||||
}
|
||||
throw new DBWebException("Failed to delete connection", e);
|
||||
}
|
||||
removeConnection(dataSourceContainer);
|
||||
return connectionInfo;
|
||||
}
|
||||
@@ -353,6 +357,13 @@ public class WebSessionProjectImpl extends WebProjectImpl {
|
||||
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
|
||||
*/
|
||||
|
||||
+40
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
* DBeaver - Universal Database Manager
|
||||
* Copyright (C) 2010-2025 DBeaver Corp and others
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package io.cloudbeaver.model;
|
||||
|
||||
import io.cloudbeaver.model.session.WebSession;
|
||||
import io.cloudbeaver.utils.WebEventUtils;
|
||||
import org.jkiss.code.NotNull;
|
||||
import org.jkiss.dbeaver.DBException;
|
||||
import org.jkiss.dbeaver.model.runtime.AbstractJob;
|
||||
|
||||
public abstract class AbstractCancelableJob extends AbstractJob implements CustomCancelableJob {
|
||||
protected AbstractCancelableJob(@NotNull String name) {
|
||||
super(name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void cancelJob(@NotNull WebSession webSession, @NotNull WebAsyncTaskInfo taskInfo) {
|
||||
cancelJob(webSession, taskInfo, "Canceled by the user");
|
||||
}
|
||||
|
||||
public void cancelJob(@NotNull WebSession webSession, @NotNull WebAsyncTaskInfo taskInfo, @NotNull String errorMessage) {
|
||||
taskInfo.setRunning(false);
|
||||
taskInfo.setJobError(new DBException(errorMessage));
|
||||
WebEventUtils.sendAsyncTaskEvent(webSession, taskInfo);
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
+16
@@ -1,3 +1,19 @@
|
||||
/*
|
||||
* DBeaver - Universal Database Manager
|
||||
* Copyright (C) 2010-2025 DBeaver Corp and others
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package io.cloudbeaver.model;
|
||||
|
||||
import org.jkiss.code.NotNull;
|
||||
|
||||
+50
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
* DBeaver - Universal Database Manager
|
||||
* Copyright (C) 2010-2025 DBeaver Corp and others
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package io.cloudbeaver.model;
|
||||
|
||||
import org.jkiss.code.NotNull;
|
||||
|
||||
public class WebBasicEnumObjectInfo {
|
||||
|
||||
@NotNull
|
||||
private String displayName;
|
||||
@NotNull
|
||||
private String value;
|
||||
|
||||
public WebBasicEnumObjectInfo(@NotNull String displayName, @NotNull String value) {
|
||||
this.displayName = displayName;
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public String getDisplayName() {
|
||||
return displayName;
|
||||
}
|
||||
|
||||
public void setDisplayName(@NotNull String displayName) {
|
||||
this.displayName = displayName;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public String getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
public void setValue(@NotNull String value) {
|
||||
this.value = value;
|
||||
}
|
||||
}
|
||||
+148
-9
@@ -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;
|
||||
|
||||
@@ -42,7 +43,7 @@ public class WebConnectionConfig {
|
||||
private String databaseName;
|
||||
private String url;
|
||||
|
||||
private int keepAliveInterval;
|
||||
private Integer keepAliveInterval;
|
||||
|
||||
private String name;
|
||||
private String description;
|
||||
@@ -53,17 +54,20 @@ public class WebConnectionConfig {
|
||||
|
||||
private String authModelId;
|
||||
private Map<String, Object> credentials;
|
||||
private boolean saveCredentials;
|
||||
private boolean sharedCredentials;
|
||||
private Boolean saveCredentials;
|
||||
private Boolean sharedCredentials;
|
||||
private Map<String, Object> mainPropertyValues;
|
||||
private Map<String, Object> expertSettingsValues;
|
||||
private Map<String, Object> providerProperties;
|
||||
private List<WebNetworkHandlerConfigInput> networkHandlersConfig;
|
||||
private DBPDriverConfigurationType configurationType;
|
||||
private String selectedSecretId;
|
||||
private boolean defaultAutoCommit;
|
||||
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
|
||||
@@ -122,102 +135,178 @@ public class WebConnectionConfig {
|
||||
return connectionId;
|
||||
}
|
||||
|
||||
public void setConnectionId(String connectionId) {
|
||||
this.connectionId = connectionId;
|
||||
}
|
||||
|
||||
@Property
|
||||
public String getDriverId() {
|
||||
return driverId;
|
||||
}
|
||||
|
||||
public void setDriverId(String driverId) {
|
||||
this.driverId = driverId;
|
||||
}
|
||||
|
||||
@Property
|
||||
public boolean isReadOnly() {
|
||||
return readOnly;
|
||||
}
|
||||
|
||||
public void setReadOnly(boolean readOnly) {
|
||||
this.readOnly = readOnly;
|
||||
}
|
||||
|
||||
@Property
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
@Property
|
||||
public String getDescription() {
|
||||
return description;
|
||||
}
|
||||
|
||||
public void setDescription(String description) {
|
||||
this.description = description;
|
||||
}
|
||||
|
||||
@Property
|
||||
public String getFolder() {
|
||||
return folder;
|
||||
}
|
||||
|
||||
public void setFolder(String folder) {
|
||||
this.folder = folder;
|
||||
}
|
||||
|
||||
@Property
|
||||
public String getHost() {
|
||||
return host;
|
||||
}
|
||||
|
||||
public void setHost(String host) {
|
||||
this.host = host;
|
||||
}
|
||||
|
||||
@Property
|
||||
public String getPort() {
|
||||
return port;
|
||||
}
|
||||
|
||||
public void setPort(String port) {
|
||||
this.port = port;
|
||||
}
|
||||
|
||||
@Property
|
||||
public String getServerName() {
|
||||
return serverName;
|
||||
}
|
||||
|
||||
public void setServerName(String serverName) {
|
||||
this.serverName = serverName;
|
||||
}
|
||||
|
||||
@Property
|
||||
public String getDatabaseName() {
|
||||
return databaseName;
|
||||
}
|
||||
|
||||
public void setDatabaseName(String databaseName) {
|
||||
this.databaseName = databaseName;
|
||||
}
|
||||
|
||||
@Property
|
||||
public String getUrl() {
|
||||
return url;
|
||||
}
|
||||
|
||||
public void setUrl(String url) {
|
||||
this.url = url;
|
||||
}
|
||||
|
||||
@Property
|
||||
public Map<String, Object> getProperties() {
|
||||
return properties;
|
||||
}
|
||||
|
||||
public void setProperties(Map<String, Object> properties) {
|
||||
this.properties = properties;
|
||||
}
|
||||
|
||||
@Property
|
||||
public String getUserName() {
|
||||
return userName;
|
||||
}
|
||||
|
||||
public void setUserName(String userName) {
|
||||
this.userName = userName;
|
||||
}
|
||||
|
||||
@Property
|
||||
public String getUserPassword() {
|
||||
return userPassword;
|
||||
}
|
||||
|
||||
public void setUserPassword(String userPassword) {
|
||||
this.userPassword = userPassword;
|
||||
}
|
||||
|
||||
@Property
|
||||
public String getAuthModelId() {
|
||||
return authModelId;
|
||||
}
|
||||
|
||||
public void setAuthModelId(String authModelId) {
|
||||
this.authModelId = authModelId;
|
||||
}
|
||||
|
||||
@Property
|
||||
public DBPDriverConfigurationType getConfigurationType() {
|
||||
return configurationType;
|
||||
}
|
||||
|
||||
public void setConfigurationType(DBPDriverConfigurationType configurationType) {
|
||||
this.configurationType = configurationType;
|
||||
}
|
||||
|
||||
@Property
|
||||
public Map<String, Object> getCredentials() {
|
||||
return credentials;
|
||||
}
|
||||
|
||||
public void setCredentials(Map<String, Object> credentials) {
|
||||
this.credentials = credentials;
|
||||
}
|
||||
|
||||
public List<WebNetworkHandlerConfigInput> getNetworkHandlersConfig() {
|
||||
return networkHandlersConfig;
|
||||
}
|
||||
|
||||
public void setNetworkHandlersConfig(List<WebNetworkHandlerConfigInput> networkHandlersConfig) {
|
||||
this.networkHandlersConfig = networkHandlersConfig;
|
||||
}
|
||||
|
||||
@Property
|
||||
public boolean isSaveCredentials() {
|
||||
return saveCredentials;
|
||||
}
|
||||
|
||||
public void setSaveCredentials(boolean saveCredentials) {
|
||||
this.saveCredentials = saveCredentials;
|
||||
}
|
||||
|
||||
@Property
|
||||
public boolean isSharedCredentials() {
|
||||
return sharedCredentials;
|
||||
}
|
||||
|
||||
public void setSaveCredentials(boolean saveCredentials) {
|
||||
this.saveCredentials = saveCredentials;
|
||||
public void setSharedCredentials(boolean sharedCredentials) {
|
||||
this.sharedCredentials = sharedCredentials;
|
||||
}
|
||||
|
||||
@Property
|
||||
@@ -225,38 +314,88 @@ public class WebConnectionConfig {
|
||||
return mainPropertyValues;
|
||||
}
|
||||
|
||||
public void setMainPropertyValues(Map<String, Object> mainPropertyValues) {
|
||||
this.mainPropertyValues = mainPropertyValues;
|
||||
}
|
||||
|
||||
@Property
|
||||
public Map<String, Object> getExpertSettingsValues() {
|
||||
return expertSettingsValues;
|
||||
}
|
||||
|
||||
public void setExpertSettingsValues(Map<String, Object> expertSettingsValues) {
|
||||
this.expertSettingsValues = expertSettingsValues;
|
||||
}
|
||||
|
||||
@Property
|
||||
public Map<String, Object> getProviderProperties() {
|
||||
return providerProperties;
|
||||
}
|
||||
|
||||
public void setProviderProperties(Map<String, Object> providerProperties) {
|
||||
this.providerProperties = providerProperties;
|
||||
}
|
||||
|
||||
@Property
|
||||
public Integer getKeepAliveInterval() {
|
||||
return keepAliveInterval;
|
||||
}
|
||||
|
||||
public void setKeepAliveInterval(int keepAliveInterval) {
|
||||
this.keepAliveInterval = keepAliveInterval;
|
||||
}
|
||||
|
||||
@Property
|
||||
public Boolean isDefaultAutoCommit() {
|
||||
return defaultAutoCommit;
|
||||
}
|
||||
|
||||
public void setDefaultAutoCommit(boolean defaultAutoCommit) {
|
||||
this.defaultAutoCommit = defaultAutoCommit;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public String getSelectedSecretId() {
|
||||
return selectedSecretId;
|
||||
}
|
||||
|
||||
public void setSelectedSecretId(String selectedSecretId) {
|
||||
this.selectedSecretId = selectedSecretId;
|
||||
}
|
||||
|
||||
@Property
|
||||
public String getDefaultCatalogName() {
|
||||
return defaultCatalogName;
|
||||
}
|
||||
|
||||
public void setDefaultCatalogName(String defaultCatalogName) {
|
||||
this.defaultCatalogName = defaultCatalogName;
|
||||
}
|
||||
|
||||
@Property
|
||||
public String getDefaultSchemaName() {
|
||||
return defaultSchemaName;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
+47
-15
@@ -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 io.cloudbeaver.DBWebException;
|
||||
import io.cloudbeaver.WebProjectImpl;
|
||||
import io.cloudbeaver.model.app.BaseWebAppConfiguration;
|
||||
import io.cloudbeaver.model.session.WebSession;
|
||||
import io.cloudbeaver.server.CBConstants;
|
||||
import io.cloudbeaver.service.security.SMUtils;
|
||||
import io.cloudbeaver.service.sql.WebDataFormat;
|
||||
import io.cloudbeaver.utils.CBModelConstants;
|
||||
@@ -38,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;
|
||||
@@ -59,7 +63,6 @@ import java.util.stream.Collectors;
|
||||
public class WebConnectionInfo {
|
||||
|
||||
private static final Log log = Log.getLog(WebConnectionInfo.class);
|
||||
public static final String SECURED_VALUE = "********";
|
||||
|
||||
private static final String FEATURE_HAS_TOOLS = "hasTools";
|
||||
private static final String FEATURE_CONNECTED = "connected";
|
||||
@@ -127,7 +130,7 @@ public class WebConnectionInfo {
|
||||
if (canViewReadOnlyConnections()) {
|
||||
return dataSourceContainer.getConnectionConfiguration().getHostName();
|
||||
}
|
||||
return SECURED_VALUE;
|
||||
return CBConstants.SECURED_VALUE;
|
||||
}
|
||||
|
||||
@Property
|
||||
@@ -135,7 +138,7 @@ public class WebConnectionInfo {
|
||||
if (canViewReadOnlyConnections()) {
|
||||
return dataSourceContainer.getConnectionConfiguration().getHostPort();
|
||||
}
|
||||
return SECURED_VALUE;
|
||||
return CBConstants.SECURED_VALUE;
|
||||
}
|
||||
|
||||
@Property
|
||||
@@ -148,7 +151,7 @@ public class WebConnectionInfo {
|
||||
if (canViewReadOnlyConnections()) {
|
||||
return dataSourceContainer.getConnectionConfiguration().getDatabaseName();
|
||||
}
|
||||
return SECURED_VALUE;
|
||||
return CBConstants.SECURED_VALUE;
|
||||
}
|
||||
|
||||
@Property
|
||||
@@ -156,7 +159,7 @@ public class WebConnectionInfo {
|
||||
if (canViewReadOnlyConnections()) {
|
||||
return dataSourceContainer.getConnectionConfiguration().getUrl();
|
||||
}
|
||||
return SECURED_VALUE;
|
||||
return CBConstants.SECURED_VALUE;
|
||||
}
|
||||
|
||||
@Property
|
||||
@@ -211,7 +214,8 @@ public class WebConnectionInfo {
|
||||
|
||||
@Property
|
||||
public String getNodePath() {
|
||||
return DBNDataSource.makeDataSourceItemPath(dataSourceContainer);
|
||||
DBNDatabaseNode dsNode = DBNUtils.getNodeByObject(dataSourceContainer);
|
||||
return dsNode == null ? null : dsNode.getNodeUri();
|
||||
}
|
||||
|
||||
@Property
|
||||
@@ -287,6 +291,22 @@ public class WebConnectionInfo {
|
||||
return dataSourceContainer.getNavigatorSettings();
|
||||
}
|
||||
|
||||
@Property
|
||||
@NotNull
|
||||
public DBNBrowseSettings getDefaultNavigatorSettings() {
|
||||
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() {
|
||||
@@ -447,7 +467,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());
|
||||
@@ -521,12 +541,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
|
||||
@@ -566,10 +583,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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+5
-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.
|
||||
@@ -16,6 +16,7 @@
|
||||
*/
|
||||
package io.cloudbeaver.model;
|
||||
|
||||
import io.cloudbeaver.server.CBConstants;
|
||||
import org.jkiss.code.NotNull;
|
||||
import org.jkiss.dbeaver.model.net.DBWHandlerConfiguration;
|
||||
import org.jkiss.dbeaver.model.net.DBWHandlerType;
|
||||
@@ -88,7 +89,9 @@ public class WebNetworkHandlerConfig {
|
||||
DBPPropertyDescriptor[] descriptor = configuration.getHandlerDescriptor().getHandlerProperties();
|
||||
for (DBPPropertyDescriptor p : descriptor) {
|
||||
if (p.hasFeature(RegistryConstants.ATTR_PASSWORD)) {
|
||||
secureProperties.computeIfPresent(p.getId(), (k, v) -> CommonUtils.isEmpty(v) ? null : WebConnectionInfo.SECURED_VALUE);
|
||||
secureProperties.computeIfPresent(p.getId(), (k, v) ->
|
||||
CommonUtils.isEmpty(v) ? null : CBConstants.SECURED_VALUE
|
||||
);
|
||||
}
|
||||
}
|
||||
return secureProperties;
|
||||
|
||||
+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) {
|
||||
|
||||
@@ -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,11 +17,13 @@
|
||||
package io.cloudbeaver.model;
|
||||
|
||||
import io.cloudbeaver.model.session.WebSession;
|
||||
import io.cloudbeaver.server.CBConstants;
|
||||
import org.jkiss.code.NotNull;
|
||||
import org.jkiss.code.Nullable;
|
||||
import org.jkiss.dbeaver.DBException;
|
||||
import org.jkiss.dbeaver.model.*;
|
||||
import org.jkiss.dbeaver.model.connection.DBPDriverConfigurationType;
|
||||
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;
|
||||
@@ -30,6 +32,7 @@ import org.jkiss.dbeaver.model.preferences.DBPPropertyDescriptor;
|
||||
import org.jkiss.dbeaver.model.preferences.DBPPropertySource;
|
||||
import org.jkiss.dbeaver.registry.settings.ProductSettingDescriptor;
|
||||
import org.jkiss.dbeaver.runtime.properties.ObjectPropertyDescriptor;
|
||||
import org.jkiss.utils.ArrayUtils;
|
||||
import org.jkiss.utils.CommonUtils;
|
||||
|
||||
import java.lang.reflect.Array;
|
||||
@@ -39,63 +42,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();
|
||||
@@ -106,48 +104,59 @@ 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 Object getValue() throws DBException {
|
||||
Object value = propertySource == null ? null : propertySource.getPropertyValue(session.getProgressMonitor(), property.getId());
|
||||
if (property instanceof ObjectPropertyDescriptor) {
|
||||
ObjectPropertyDescriptor opd = (ObjectPropertyDescriptor) property;
|
||||
if (!showProtected && opd.isPassword() || opd.isHidden()) {
|
||||
if (value == null || value.toString().isEmpty()) {
|
||||
return "";
|
||||
}
|
||||
return "******";
|
||||
if (property instanceof ObjectPropertyDescriptor opd &&
|
||||
(opd.isPassword() || opd.isHidden())
|
||||
) {
|
||||
return maskValue(value);
|
||||
} else if (property instanceof PropertyDescriptor pd) {
|
||||
String[] features = pd.getFeatures();
|
||||
if (features != null && ArrayUtils.contains(features, DBConstants.PROP_FEATURE_PASSWORD)) {
|
||||
return maskValue(value);
|
||||
}
|
||||
}
|
||||
return value == null ? null : makePropertyValue(value);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Property
|
||||
public Object[] getValidValues() {
|
||||
if (property instanceof IPropertyValueListProvider) {
|
||||
@@ -160,17 +169,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) {
|
||||
@@ -178,7 +188,7 @@ public class WebPropertyInfo {
|
||||
.map(DBPDriverConfigurationType::toString)
|
||||
.toArray(String[]::new);
|
||||
}
|
||||
return supportedConfigurationTypes;
|
||||
return new String[0];
|
||||
}
|
||||
|
||||
@Property
|
||||
@@ -199,12 +209,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) {
|
||||
@@ -243,9 +255,11 @@ public class WebPropertyInfo {
|
||||
List<Object> result = new ArrayList<>();
|
||||
int length = Array.getLength(value);
|
||||
for (int i = 0; i < length; i++) {
|
||||
result.add(Array.get(value, i));
|
||||
result.add(makePropertyValue(Array.get(value, i)));
|
||||
}
|
||||
return result;
|
||||
} else if (value instanceof Enum<?> enumValue) {
|
||||
return new WebBasicEnumObjectInfo(enumValue.toString(), enumValue.name());
|
||||
}
|
||||
Class<?> dataType = property.getDataType();
|
||||
if (dataType == Boolean.class || dataType == Boolean.TYPE) {
|
||||
@@ -284,25 +298,19 @@ 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,
|
||||
READ_ONLY
|
||||
}
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static String maskValue(@Nullable Object value) {
|
||||
if (value == null || value.toString().isEmpty()) {
|
||||
return "";
|
||||
} else {
|
||||
return CBConstants.SECURED_VALUE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+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 "";
|
||||
}
|
||||
+13
-25
@@ -20,13 +20,11 @@ import com.google.gson.Gson;
|
||||
import com.google.gson.GsonBuilder;
|
||||
import org.jkiss.code.NotNull;
|
||||
import org.jkiss.code.Nullable;
|
||||
import org.jkiss.dbeaver.DBException;
|
||||
import org.jkiss.dbeaver.Log;
|
||||
import org.jkiss.dbeaver.registry.fs.FileSystemProviderRegistry;
|
||||
import org.jkiss.dbeaver.model.fs.DBFUtils;
|
||||
import org.jkiss.utils.CommonUtils;
|
||||
|
||||
import java.net.URI;
|
||||
import java.nio.file.FileSystem;
|
||||
import java.nio.file.FileSystems;
|
||||
import java.nio.file.Path;
|
||||
|
||||
/**
|
||||
@@ -65,30 +63,20 @@ public abstract class BaseServerConfigurationController<T extends ServletServerC
|
||||
if (CommonUtils.isEmpty(workspaceLocation)) {
|
||||
return defaultWorkspaceLocation;
|
||||
}
|
||||
URI workspaceUri = URI.create(workspaceLocation);
|
||||
if (workspaceUri.getScheme() == null) {
|
||||
// default filesystem
|
||||
return getHomeDirectory().resolve(workspaceLocation);
|
||||
} else {
|
||||
var externalFsProvider =
|
||||
FileSystemProviderRegistry.getInstance().getFileSystemProviderBySchema(workspaceUri.getScheme());
|
||||
if (externalFsProvider == null) {
|
||||
log.error("File system not found for scheme: " + workspaceUri.getScheme() + " default workspace " +
|
||||
"location will be used");
|
||||
try {
|
||||
Path externalFsPath = DBFUtils.getPathFromURI(workspaceLocation);
|
||||
if (externalFsPath == null) {
|
||||
log.warn("Failed to detect workspace path from URI: " + workspaceLocation +
|
||||
" default workspace location will be used");
|
||||
return defaultWorkspaceLocation;
|
||||
}
|
||||
ClassLoader fsClassloader = externalFsProvider.getInstance().getClass().getClassLoader();
|
||||
try (FileSystem externalFileSystem = FileSystems.newFileSystem(workspaceUri,
|
||||
System.getenv(),
|
||||
fsClassloader)) {
|
||||
log.info("Path from external filesystem used for workspace: " + workspaceUri);
|
||||
return externalFileSystem.provider().getPath(workspaceUri);
|
||||
} catch (Exception e) {
|
||||
log.error("Failed to initialize workspace path: " + workspaceUri + " default workspace " +
|
||||
"location will be used", e);
|
||||
}
|
||||
log.info("Path from external filesystem used for workspace: " + externalFsPath);
|
||||
return externalFsPath;
|
||||
} catch (DBException e) {
|
||||
log.error("Failed to detect workspace path from URI: " + workspaceLocation +
|
||||
" default workspace location will be used", e);
|
||||
return defaultWorkspaceLocation;
|
||||
}
|
||||
return defaultWorkspaceLocation;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
|
||||
+34
-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.
|
||||
@@ -17,6 +17,7 @@
|
||||
package io.cloudbeaver.model.app;
|
||||
|
||||
import io.cloudbeaver.model.log.SLF4JLogHandler;
|
||||
import io.cloudbeaver.registry.WebFeatureRegistry;
|
||||
import org.eclipse.core.runtime.Platform;
|
||||
import org.eclipse.equinox.app.IApplicationContext;
|
||||
import org.jkiss.code.NotNull;
|
||||
@@ -24,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;
|
||||
@@ -47,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";
|
||||
@@ -58,6 +61,7 @@ public abstract class BaseServletApplication extends BaseApplicationImpl impleme
|
||||
|
||||
private String instanceId;
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public RMController createResourceController(
|
||||
@NotNull SMCredentialsProvider credentialsProvider,
|
||||
@@ -72,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
|
||||
@@ -175,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);
|
||||
}
|
||||
@@ -201,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) {
|
||||
@@ -238,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() {
|
||||
@@ -277,4 +279,10 @@ public abstract class BaseServletApplication extends BaseApplicationImpl impleme
|
||||
public boolean isAnonymousAccessEnabled() {
|
||||
return getAppConfiguration().isAnonymousAccessEnabled();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public WebFeatureRegistry getFeatureRegistry() {
|
||||
return WebFeatureRegistry.getInstance();
|
||||
}
|
||||
}
|
||||
|
||||
+1
-2
@@ -17,7 +17,6 @@
|
||||
package io.cloudbeaver.model.app;
|
||||
|
||||
import io.cloudbeaver.DBWFeatureSet;
|
||||
import io.cloudbeaver.registry.WebFeatureRegistry;
|
||||
import io.cloudbeaver.utils.ServletAppUtils;
|
||||
import org.jkiss.code.NotNull;
|
||||
import org.jkiss.dbeaver.model.DBConstants;
|
||||
@@ -115,7 +114,7 @@ public abstract class BaseWebAppConfiguration implements ServletAppConfiguration
|
||||
public String[] getEnabledFeatures() {
|
||||
if (enabledFeatures == null) {
|
||||
// No config - enable all features (+backward compatibility)
|
||||
return WebFeatureRegistry.getInstance().getWebFeatures()
|
||||
return ServletAppUtils.getServletApplication().getFeatureRegistry().getWebFeatures()
|
||||
.stream().map(DBWFeatureSet::getId).toArray(String[]::new);
|
||||
}
|
||||
return enabledFeatures;
|
||||
|
||||
+8
-7
@@ -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.model.app;
|
||||
|
||||
import io.cloudbeaver.registry.WebFeatureRegistry;
|
||||
import org.jkiss.code.NotNull;
|
||||
import org.jkiss.code.Nullable;
|
||||
import org.jkiss.dbeaver.DBException;
|
||||
@@ -52,9 +53,6 @@ public interface ServletApplication extends DBPApplication {
|
||||
@NotNull
|
||||
Path getDataDirectory(boolean create);
|
||||
|
||||
@NotNull
|
||||
Path getWorkspaceDirectory();
|
||||
|
||||
@NotNull
|
||||
Path getHomeDirectory();
|
||||
|
||||
@@ -79,9 +77,6 @@ public interface ServletApplication extends DBPApplication {
|
||||
@NotNull
|
||||
DBFileController createFileController(@NotNull SMCredentialsProvider credentialsProvider);
|
||||
|
||||
@Nullable
|
||||
String getServerURL();
|
||||
|
||||
@NotNull
|
||||
default String getServicesURI() {
|
||||
return "/";
|
||||
@@ -116,4 +111,10 @@ public interface ServletApplication extends DBPApplication {
|
||||
}
|
||||
|
||||
boolean isAnonymousAccessEnabled();
|
||||
|
||||
/**
|
||||
* Web feature registry
|
||||
*/
|
||||
@NotNull
|
||||
WebFeatureRegistry getFeatureRegistry();
|
||||
}
|
||||
|
||||
+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
|
||||
|
||||
+2
-2
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* DBeaver - Universal Database Manager
|
||||
* Copyright (C) 2010-2024 DBeaver Corp and others
|
||||
* Copyright (C) 2010-2025 DBeaver Corp and others
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -28,7 +28,7 @@ import org.jkiss.dbeaver.model.runtime.DBRProgressMonitor;
|
||||
|
||||
import java.nio.file.Path;
|
||||
|
||||
public class FSUtils {
|
||||
public class WebFSUtils {
|
||||
@NotNull
|
||||
public static String makeUniqueFsId(@NotNull DBFVirtualFileSystem fileSystem) {
|
||||
return fileSystem.getType() + "://" + fileSystem.getId();
|
||||
-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 {
|
||||
|
||||
+31
-55
@@ -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,7 +16,6 @@
|
||||
*/
|
||||
package io.cloudbeaver.model.rm.local;
|
||||
|
||||
import io.cloudbeaver.BaseWebProjectImpl;
|
||||
import org.jkiss.code.NotNull;
|
||||
import org.jkiss.code.Nullable;
|
||||
import org.jkiss.dbeaver.DBException;
|
||||
@@ -27,8 +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.impl.auth.SessionContextImpl;
|
||||
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;
|
||||
@@ -51,25 +51,23 @@ public abstract class BaseLocalResourceController implements RMController {
|
||||
private static final Log log = Log.getLog(BaseLocalResourceController.class);
|
||||
|
||||
public static final String DEFAULT_CHANGE_ID = "0";
|
||||
private static final String FILE_REGEX = "(?U)[\\w.$()@/\\\\ -]+";
|
||||
private static final String PROJECT_REGEX = "(?U)[\\w.$()@ -]+"; // slash not allowed in project name
|
||||
|
||||
@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;
|
||||
}
|
||||
|
||||
@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;
|
||||
@@ -85,6 +83,7 @@ public abstract class BaseLocalResourceController implements RMController {
|
||||
return project;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public Object getProjectProperty(@NotNull String projectId, @NotNull String propName) throws DBException {
|
||||
var project = getWebProject(projectId, false);
|
||||
@@ -97,7 +96,7 @@ public abstract class BaseLocalResourceController implements RMController {
|
||||
public void setProjectProperty(
|
||||
@NotNull String projectId,
|
||||
@NotNull String propName,
|
||||
@NotNull Object propValue
|
||||
@Nullable Object propValue
|
||||
) throws DBException {
|
||||
RMLocalProject webProject = getWebProject(projectId, false);
|
||||
doFileWriteOperation(projectId, webProject.getMetadataFilePath(),
|
||||
@@ -110,8 +109,7 @@ public abstract class BaseLocalResourceController implements RMController {
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getProjectsDataSources(@NotNull String projectId, @Nullable String[] dataSourceIds)
|
||||
throws DBException {
|
||||
public String getProjectsDataSources(@NotNull String projectId, @Nullable String[] dataSourceIds) throws DBException {
|
||||
DBPProject projectMetadata = getWebProject(projectId, false);
|
||||
return doFileReadOperation(
|
||||
projectId,
|
||||
@@ -159,7 +157,7 @@ public abstract class BaseLocalResourceController implements RMController {
|
||||
@NotNull String configuration,
|
||||
@Nullable List<String> dataSourceIds
|
||||
) throws DBException {
|
||||
try (var lock = 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),
|
||||
@@ -190,7 +188,7 @@ public abstract class BaseLocalResourceController implements RMController {
|
||||
@NotNull String projectId,
|
||||
@NotNull String[] dataSourceIds
|
||||
) throws DBException {
|
||||
try (var projectLock = 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();
|
||||
@@ -215,7 +213,7 @@ public abstract class BaseLocalResourceController implements RMController {
|
||||
@NotNull String projectId,
|
||||
@NotNull String folderPath
|
||||
) throws DBException {
|
||||
try (var projectLock = 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),
|
||||
@@ -226,8 +224,10 @@ public abstract class BaseLocalResourceController implements RMController {
|
||||
GeneralUtils.validateResourceName(newName);
|
||||
var parent = result.getParent();
|
||||
var parentFolder = parent == null ? null : registry.getFolder(parent.toString().replace("\\", "/"));
|
||||
DBPDataSourceFolder newFolder = registry.addFolder(parentFolder, newName);
|
||||
((DataSourcePersistentRegistry) registry).saveDataSources();
|
||||
registry.addFolder(parentFolder, newName);
|
||||
if (registry instanceof DataSourcePersistentRegistry pr) {
|
||||
pr.saveDataSources();
|
||||
}
|
||||
registry.checkForErrors();
|
||||
return null;
|
||||
}
|
||||
@@ -241,21 +241,19 @@ public abstract class BaseLocalResourceController implements RMController {
|
||||
@NotNull String[] folderPaths,
|
||||
boolean dropContents
|
||||
) throws DBException {
|
||||
try (var projectLock = 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),
|
||||
() -> {
|
||||
DBPDataSourceRegistry registry = project.getDataSourceRegistry();
|
||||
for (String folderPath : folderPaths) {
|
||||
DBPDataSourceFolder folder = registry.getFolder(folderPath);
|
||||
if (folder != null) {
|
||||
log.debug("Deleting data source folder '" + folderPath + "' in project '" + projectId + "'");
|
||||
registry.removeFolder(folder, dropContents);
|
||||
} else {
|
||||
log.warn("Can not find folder by path [" + folderPath + "] for deletion");
|
||||
}
|
||||
log.debug("Deleting data source folder '" + folderPath + "' in project '" + projectId + "'");
|
||||
registry.removeFolder(folder, dropContents);
|
||||
}
|
||||
if (registry instanceof DataSourcePersistentRegistry pr) {
|
||||
pr.saveDataSources();
|
||||
}
|
||||
((DataSourcePersistentRegistry) registry).saveDataSources();
|
||||
registry.checkForErrors();
|
||||
return null;
|
||||
}
|
||||
@@ -269,7 +267,7 @@ public abstract class BaseLocalResourceController implements RMController {
|
||||
@NotNull String oldPath,
|
||||
@NotNull String newPath
|
||||
) throws DBException {
|
||||
try (var projectLock = 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),
|
||||
@@ -284,19 +282,19 @@ public abstract class BaseLocalResourceController implements RMController {
|
||||
}
|
||||
}
|
||||
|
||||
protected abstract RMLocalProject getWebProject(String projectId, boolean refresh) throws DBException;
|
||||
protected abstract RMLocalProject getWebProject(@NotNull String projectId, boolean refresh) throws DBException;
|
||||
|
||||
protected abstract <T> T doFileWriteOperation(String projectId, Path file, RMFileOperation<T> operation)
|
||||
protected abstract <T> T doFileWriteOperation(@NotNull String projectId, @NotNull Path file, @NotNull RMFileOperation<T> operation)
|
||||
throws DBException;
|
||||
|
||||
protected abstract <T> T doFileReadOperation(String projectId, Path file, RMFileOperation<T> operation)
|
||||
protected abstract <T> T doFileReadOperation(@NotNull String projectId, @NotNull Path file, @NotNull RMFileOperation<T> operation)
|
||||
throws DBException;
|
||||
|
||||
protected abstract <T> T doProjectOperation(String projectId, RMFileOperation<T> operation) throws DBException;
|
||||
protected abstract <T> T doProjectOperation(@NotNull String projectId, @NotNull RMFileOperation<T> operation) throws DBException;
|
||||
|
||||
protected abstract RMProject makeProjectFromId(String projectId, boolean loadPermissions) throws DBException;
|
||||
protected abstract RMProject makeProjectFromId(@NotNull String projectId, boolean loadPermissions) throws DBException;
|
||||
|
||||
protected void validateResourcePath(String resourcePath) throws DBException {
|
||||
protected void validateResourcePath(@NotNull String resourcePath) throws DBException {
|
||||
var fullPath = Paths.get(resourcePath);
|
||||
for (Path path : fullPath) {
|
||||
String fileName = IOUtils.getFileNameWithoutExtension(path);
|
||||
@@ -314,28 +312,6 @@ public abstract class BaseLocalResourceController implements RMController {
|
||||
}
|
||||
}
|
||||
|
||||
protected class InternalWebProjectImpl extends BaseWebProjectImpl {
|
||||
public InternalWebProjectImpl(
|
||||
@NotNull SessionContextImpl sessionContext,
|
||||
@NotNull RMProject rmProject,
|
||||
@NotNull Path projectPath
|
||||
) {
|
||||
super(
|
||||
BaseLocalResourceController.this.workspace,
|
||||
BaseLocalResourceController.this,
|
||||
sessionContext,
|
||||
rmProject,
|
||||
projectPath
|
||||
);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
protected DBPDataSourceRegistry createDataSourceRegistry() {
|
||||
return new DataSourceRegistry(this);
|
||||
}
|
||||
}
|
||||
|
||||
protected void fireRmResourceAddEvent(@NotNull String projectId, @NotNull String resourcePath) throws DBException {
|
||||
RMEventManager.fireEvent(
|
||||
new RMEvent(RMEvent.Action.RESOURCE_ADD,
|
||||
|
||||
+189
-86
@@ -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.SMController;
|
||||
import org.jkiss.dbeaver.model.security.SMObjectType;
|
||||
import org.jkiss.dbeaver.model.security.SMAdminController;
|
||||
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;
|
||||
@@ -78,20 +83,22 @@ public class LocalResourceController extends BaseLocalResourceController {
|
||||
private final Path userProjectsPath;
|
||||
private final Path sharedProjectsPath;
|
||||
private final String globalProjectName;
|
||||
private Supplier<SMController> smControllerSupplier;
|
||||
private Supplier<SMAdminController> smControllerSupplier;
|
||||
protected final List<RMFileOperationHandler> fileHandlers;
|
||||
|
||||
private final Map<String, RMLocalProject> projectRegistries = new LinkedHashMap<>();
|
||||
private final ProjectsMetadataInfo sharedProjectsMetadataInfo;
|
||||
|
||||
public LocalResourceController(
|
||||
DBPWorkspace workspace,
|
||||
SMCredentialsProvider credentialsProvider,
|
||||
Path rootPath,
|
||||
Path userProjectsPath,
|
||||
Path sharedProjectsPath,
|
||||
Supplier<SMController> smControllerSupplier
|
||||
@NotNull DBPWorkspace workspace,
|
||||
@NotNull SMCredentialsProvider credentialsProvider,
|
||||
@NotNull Path rootPath,
|
||||
@NotNull Path userProjectsPath,
|
||||
@NotNull Path sharedProjectsPath,
|
||||
@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;
|
||||
@@ -100,27 +107,34 @@ public class LocalResourceController extends BaseLocalResourceController {
|
||||
|
||||
this.globalProjectName = DBWorkbench.getPlatform().getApplication().getDefaultProjectName();
|
||||
this.fileHandlers = RMFileOperationHandlersRegistry.getInstance().getFileHandlers();
|
||||
this.sharedProjectsMetadataInfo = new ProjectsMetadataInfo(sharedProjectsPath, lockController);
|
||||
}
|
||||
|
||||
protected SMController getSecurityController() {
|
||||
@NotNull
|
||||
protected SMAdminController getSecurityController() {
|
||||
return smControllerSupplier.get();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private Path getGlobalProjectPath() {
|
||||
return globalProjectName == null ? null : this.rootPath.resolve(this.globalProjectName);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private Path getPrivateProjectPath() {
|
||||
SMCredentials activeUserCredentials = credentialsProvider.getActiveUserCredentials();
|
||||
String userId = activeUserCredentials == null ? null : activeUserCredentials.getUserId();
|
||||
return userId == null ? null : this.userProjectsPath.resolve(userId);
|
||||
}
|
||||
|
||||
protected RMLocalProject getWebProject(String projectId, boolean refresh) throws DBException {
|
||||
protected RMLocalProject getWebProject(@NotNull String projectId, boolean refresh) throws DBException {
|
||||
synchronized (projectRegistries) {
|
||||
RMLocalProject project = projectRegistries.get(projectId);
|
||||
if (project == null || refresh) {
|
||||
project = createWebProjectImpl(projectId, new SessionContextImpl(null));
|
||||
if (project.getProjectType() == RMProjectType.SHARED) {
|
||||
project.setProjectInfo(sharedProjectsMetadataInfo.getProjectInfo(projectId));
|
||||
}
|
||||
projectRegistries.put(projectId, project);
|
||||
}
|
||||
return project;
|
||||
@@ -167,11 +181,6 @@ public class LocalResourceController extends BaseLocalResourceController {
|
||||
projects.add(0, userProject);
|
||||
}
|
||||
}
|
||||
if (ServletAppUtils.getServletApplication().isMultiNode()) {
|
||||
for (RMProject rmProject : projects) {
|
||||
handleProjectOpened(rmProject.getId());
|
||||
}
|
||||
}
|
||||
|
||||
projects.sort(Comparator.comparing(RMProject::getDisplayName));
|
||||
return projects.toArray(new RMProject[0]);
|
||||
@@ -277,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)) {
|
||||
@@ -289,9 +298,6 @@ public class LocalResourceController extends BaseLocalResourceController {
|
||||
try {
|
||||
log.debug("Creating project '" + rmProject.getId() + "'");
|
||||
Files.createDirectories(projectPath);
|
||||
if (ServletAppUtils.getServletApplication().isMultiNode()) {
|
||||
createResourceTypeFolders(projectPath);
|
||||
}
|
||||
fireRmProjectAddEvent(rmProject);
|
||||
return rmProject;
|
||||
} catch (IOException e) {
|
||||
@@ -299,10 +305,11 @@ public class LocalResourceController extends BaseLocalResourceController {
|
||||
}
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public RMProject updateProject(@NotNull String projectId, @NotNull RMProjectInfo projectInfo) throws DBException {
|
||||
validateProjectName(projectId, projectInfo.getName());
|
||||
try (var projectLock = 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)) {
|
||||
@@ -312,6 +319,7 @@ public class LocalResourceController extends BaseLocalResourceController {
|
||||
throw new DBException("Project '" + projectId + "' is not shared");
|
||||
}
|
||||
project.updateProject(projectInfo.getName(), projectInfo.getDescription());
|
||||
sharedProjectsMetadataInfo.updateProjectInfo(projectId, projectInfo);
|
||||
return WebRMUtils.createRmProjectFromWebProject(project);
|
||||
}
|
||||
}
|
||||
@@ -328,26 +336,28 @@ public class LocalResourceController extends BaseLocalResourceController {
|
||||
|
||||
@Override
|
||||
public void deleteProject(@NotNull String projectId) throws DBException {
|
||||
try (var projectLock = lockController.lock(projectId, "deleteProject")) {
|
||||
RMProject project = makeProjectFromId(projectId, false);
|
||||
try (var ignoredLock = lockController.lock(LockTarget.of(projectId), LockOptions.of("deleteProject"))) {
|
||||
Path targetPath = getProjectPath(projectId);
|
||||
if (!Files.exists(targetPath)) {
|
||||
log.debug(MessageFormat.format("Project folder ''{0}'' is not found", projectId));
|
||||
log.error(MessageFormat.format("Project folder ''{0}'' is not found", projectId));
|
||||
return;
|
||||
}
|
||||
try {
|
||||
log.debug("Deleting project '" + projectId + "'");
|
||||
log.debug("Deleting project '" + projectId + "' in persistence storage");
|
||||
getSecurityController().deleteObject(projectId, projectId, SMObjectType.project);
|
||||
log.debug("Deleting project '" + projectId + "' folder");
|
||||
IOUtils.deleteDirectory(targetPath);
|
||||
getSecurityController().deleteAllObjectPermissions(projectId, SMObjectType.project);
|
||||
synchronized (projectRegistries) {
|
||||
projectRegistries.remove(projectId);
|
||||
}
|
||||
sharedProjectsMetadataInfo.updateProjectInfo(projectId, null);
|
||||
} catch (IOException e) {
|
||||
throw new DBException("Error deleting project '" + project.getName() + "'", e);
|
||||
throw new DBException("Error deleting project '" + projectId + "'", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public RMProject getProject(@NotNull String projectId, boolean readResources, boolean readProperties) throws DBException {
|
||||
RMProject project = makeProjectFromId(projectId, true);
|
||||
@@ -399,14 +409,28 @@ 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;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deleteProjectDataSources(@NotNull String projectId, @NotNull String[] dataSourceIds) throws DBException {
|
||||
super.deleteProjectDataSources(projectId, dataSourceIds);
|
||||
|
||||
log.debug("Delete datasources '" + Arrays.toString(dataSourceIds) + "' from security controller");
|
||||
for (String dsId : dataSourceIds) {
|
||||
try {
|
||||
getSecurityController().deleteObject(projectId, dsId, SMObjectType.datasource);
|
||||
} catch (DBException e) {
|
||||
log.error("Error deleting datasource '" + dsId + "' from database");
|
||||
}
|
||||
}
|
||||
if (credentialsProvider.getActiveUserCredentials() != null && dataSourceIds.length > 0) {
|
||||
ServletAppUtils.getServletApplication().getEventController().addEvent(
|
||||
WSDataSourceEvent.delete(
|
||||
@@ -484,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<>();
|
||||
@@ -595,7 +653,6 @@ public class LocalResourceController extends BaseLocalResourceController {
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@@ -614,7 +671,8 @@ public class LocalResourceController extends BaseLocalResourceController {
|
||||
}
|
||||
return doProjectOperation(projectId, () -> {
|
||||
try {
|
||||
Path folderPath = CommonUtils.isEmpty(folder) ?
|
||||
boolean projectFolder = CommonUtils.isEmpty(folder);
|
||||
Path folderPath = projectFolder ?
|
||||
projectPath :
|
||||
projectPath.resolve(folder);
|
||||
folderPath = folderPath.normalize();
|
||||
@@ -623,7 +681,7 @@ public class LocalResourceController extends BaseLocalResourceController {
|
||||
throw new DBException("Invalid folder path");
|
||||
}
|
||||
createFolder(folderPath);
|
||||
return readChildResources(projectId, folderPath, nameMask, readProperties, readHistory, recursive);
|
||||
return readChildResources(projectId, folderPath, nameMask, projectFolder, readProperties, readHistory, recursive);
|
||||
} catch (NoSuchFileException e) {
|
||||
throw new DBException("Invalid resource folder " + folder);
|
||||
} catch (IOException e) {
|
||||
@@ -637,29 +695,55 @@ public class LocalResourceController extends BaseLocalResourceController {
|
||||
@NotNull String projectId,
|
||||
@NotNull Path folderPath,
|
||||
@Nullable String nameMask,
|
||||
boolean projectFolder,
|
||||
boolean readProperties,
|
||||
boolean readHistory,
|
||||
boolean recursive
|
||||
) throws IOException {
|
||||
try (Stream<Path> files = Files.list(folderPath)) {
|
||||
return files.filter(path -> {
|
||||
Set<RMResource> children = files.filter(path -> {
|
||||
String fileName = path.getFileName().toString();
|
||||
return (nameMask == null || nameMask.equals(fileName)) && !fileName.startsWith(".");
|
||||
}) // skip hidden files
|
||||
.sorted(Comparator.comparing(path -> path.getFileName().toString(), String.CASE_INSENSITIVE_ORDER))
|
||||
.map((Path path) -> makeResourceFromPath(projectId, path, nameMask, readProperties, readHistory, recursive))
|
||||
.filter(Objects::nonNull)
|
||||
.collect(Collectors.toSet());
|
||||
if (ServletAppUtils.getServletApplication().isMultiNode() && projectFolder) {
|
||||
addResourceTypeFolders(folderPath, children);
|
||||
}
|
||||
return children.stream()
|
||||
.sorted(Comparator.comparing(RMResource::getName, String.CASE_INSENSITIVE_ORDER))
|
||||
.toArray(RMResource[]::new);
|
||||
}
|
||||
}
|
||||
|
||||
private void addResourceTypeFolders(@NotNull Path projectPath, @NotNull Set<RMResource> children) {
|
||||
Set<String> folderNames = children.stream()
|
||||
.filter(RMResource::isFolder)
|
||||
.map(RMResource::getName)
|
||||
.collect(Collectors.toSet());
|
||||
// fake resources for default resource type folders
|
||||
for (ResourceTypeDescriptor type : ResourceTypeRegistry.getInstance().getResourceTypes()) {
|
||||
String defaultRoot = type.getDefaultRoot(null);
|
||||
if (CommonUtils.isEmpty(defaultRoot)) {
|
||||
continue;
|
||||
}
|
||||
Path resourceFolder = projectPath.resolve(defaultRoot);
|
||||
if (!folderNames.contains(resourceFolder.getFileName().toString())) {
|
||||
RMResource resource = new RMResource(resourceFolder.getFileName().toString());
|
||||
resource.setFolder(true);
|
||||
children.add(resource);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String createResource(
|
||||
@NotNull String projectId,
|
||||
@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)) {
|
||||
@@ -691,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()) {
|
||||
@@ -763,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" : ""));
|
||||
}
|
||||
@@ -851,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()
|
||||
@@ -896,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(),
|
||||
@@ -917,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(),
|
||||
@@ -944,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) {
|
||||
@@ -954,7 +1054,7 @@ public class LocalResourceController extends BaseLocalResourceController {
|
||||
}
|
||||
|
||||
@Nullable
|
||||
protected RMProject makeProjectFromId(String projectId, boolean loadPermissions) throws DBException {
|
||||
protected RMProject makeProjectFromId(@NotNull String projectId, boolean loadPermissions) throws DBException {
|
||||
var projectName = WebRMUtils.parseProjectName(projectId);
|
||||
var projectPath = getProjectPath(projectId);
|
||||
if (!Files.exists(projectPath)) {
|
||||
@@ -993,29 +1093,13 @@ public class LocalResourceController extends BaseLocalResourceController {
|
||||
.toArray(String[]::new);
|
||||
|
||||
RMLocalProject webProject = new RMLocalProject(workspace, new SessionContextImpl(null), path, type);
|
||||
if (type == RMProjectType.SHARED) {
|
||||
webProject.setProjectInfo(sharedProjectsMetadataInfo.getProjectInfo(webProject.getId()));
|
||||
}
|
||||
return createRmProjectFromWebProject(path, webProject, allProjectPermissions);
|
||||
}
|
||||
|
||||
private void createResourceTypeFolders(Path path) {
|
||||
// FIXME: do not create folders by force!!!
|
||||
var resourceTypes = ResourceTypeRegistry.getInstance().getResourceTypes();
|
||||
for (var resourceType : resourceTypes) {
|
||||
var defaultRoot = resourceType.getDefaultRoot(null);
|
||||
if (defaultRoot == null) {
|
||||
continue;
|
||||
}
|
||||
var typeFolder = path.resolve(defaultRoot);
|
||||
try {
|
||||
if (!Files.exists(typeFolder)) {
|
||||
createFolder(typeFolder);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.error("Resource folder " + typeFolder + " is not created", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected <T> T doProjectOperation(String projectId, RMFileOperation<T> operation) throws DBException {
|
||||
protected <T> T doProjectOperation(@NotNull String projectId, @NotNull RMFileOperation<T> operation) throws DBException {
|
||||
for (RMFileOperationHandler fileHandler : fileHandlers) {
|
||||
try {
|
||||
fileHandler.projectOpened(projectId);
|
||||
@@ -1033,7 +1117,7 @@ public class LocalResourceController extends BaseLocalResourceController {
|
||||
return operation.doOperation();
|
||||
}
|
||||
|
||||
protected <T> T doFileReadOperation(String projectId, Path file, RMFileOperation<T> operation) throws DBException {
|
||||
protected <T> T doFileReadOperation(@NotNull String projectId, @NotNull Path file, @NotNull RMFileOperation<T> operation) throws DBException {
|
||||
for (RMFileOperationHandler fileHandler : fileHandlers) {
|
||||
try {
|
||||
fileHandler.beforeFileRead(projectId, file);
|
||||
@@ -1052,7 +1136,11 @@ public class LocalResourceController extends BaseLocalResourceController {
|
||||
return operation.doOperation();
|
||||
}
|
||||
|
||||
protected <T> T doFileWriteOperation(String projectId, Path file, RMFileOperation<T> operation) throws DBException {
|
||||
protected <T> T doFileWriteOperation(
|
||||
@NotNull String projectId,
|
||||
@NotNull Path file,
|
||||
@NotNull RMFileOperation<T> operation
|
||||
) throws DBException {
|
||||
for (RMFileOperationHandler fileHandler : fileHandlers) {
|
||||
fileHandler.beforeFileChange(projectId, file);
|
||||
}
|
||||
@@ -1071,10 +1159,11 @@ public class LocalResourceController extends BaseLocalResourceController {
|
||||
return result;
|
||||
}
|
||||
|
||||
protected Path getProjectPath(String projectId) throws DBException {
|
||||
protected Path getProjectPath(@NotNull String projectId) throws DBException {
|
||||
RMProjectName project = WebRMUtils.parseProjectName(projectId);
|
||||
RMProjectType type = project.getType();
|
||||
String projectName = project.getName();
|
||||
GeneralUtils.validateResourceNameUnconditionally(projectName);
|
||||
switch (type) {
|
||||
case GLOBAL:
|
||||
if (!projectName.equals(globalProjectName)) {
|
||||
@@ -1162,7 +1251,7 @@ public class LocalResourceController extends BaseLocalResourceController {
|
||||
|
||||
if (recursive && resource.isFolder()) {
|
||||
try {
|
||||
resource.setChildren(readChildResources(projectId, path, nameMask, readProperties, readHistory, true));
|
||||
resource.setChildren(readChildResources(projectId, path, nameMask, false, readProperties, readHistory, true));
|
||||
} catch (IOException e) {
|
||||
log.error(e);
|
||||
}
|
||||
@@ -1176,16 +1265,14 @@ public class LocalResourceController extends BaseLocalResourceController {
|
||||
return getProjectPath(projectId).toAbsolutePath().relativize(path).toString().replace('\\', IPath.SEPARATOR);
|
||||
}
|
||||
|
||||
protected void handleProjectOpened(String projectId) throws DBException {
|
||||
createResourceTypeFolders(getProjectPath(projectId));
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static Builder builder(
|
||||
SMCredentialsProvider credentialsProvider,
|
||||
DBPWorkspace workspace,
|
||||
Supplier<SMController> 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
|
||||
@@ -1195,16 +1282,19 @@ public class LocalResourceController extends BaseLocalResourceController {
|
||||
|
||||
public static class Builder {
|
||||
protected final SMCredentialsProvider credentialsProvider;
|
||||
protected final Supplier<SMController> smController;
|
||||
protected final Supplier<SMAdminController> smController;
|
||||
protected final DBPWorkspace workspace;
|
||||
|
||||
protected Path rootPath;
|
||||
protected Path userProjectsPath;
|
||||
protected Path sharedProjectsPath;
|
||||
protected LockManager lockController;
|
||||
|
||||
protected Builder(
|
||||
DBPWorkspace workspace, SMCredentialsProvider credentialsProvider,
|
||||
Supplier<SMController> smControllerSupplier
|
||||
@NotNull DBPWorkspace workspace,
|
||||
@NotNull SMCredentialsProvider credentialsProvider,
|
||||
@NotNull LockManager lockController,
|
||||
@NotNull Supplier<SMAdminController> smControllerSupplier
|
||||
) {
|
||||
this.workspace = workspace;
|
||||
this.credentialsProvider = credentialsProvider;
|
||||
@@ -1212,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
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+152
@@ -0,0 +1,152 @@
|
||||
/*
|
||||
* DBeaver - Universal Database Manager
|
||||
* Copyright (C) 2010-2025 DBeaver Corp and others
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package io.cloudbeaver.model.rm.local;
|
||||
|
||||
import com.google.gson.reflect.TypeToken;
|
||||
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.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;
|
||||
import org.jkiss.dbeaver.model.rm.RMUtils;
|
||||
import org.jkiss.utils.CommonUtils;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
public class ProjectsMetadataInfo {
|
||||
private static final Log log = Log.getLog(ProjectsMetadataInfo.class);
|
||||
private static final String PROJECTS_INFO_FILE_NAME = "projects-info.json";
|
||||
|
||||
private final Map<String, RMProjectInfo> projectsInfo = new LinkedHashMap<>();
|
||||
private final Path projectsPath;
|
||||
private final LockManager lockController;
|
||||
|
||||
|
||||
public ProjectsMetadataInfo(@NotNull Path projectsPath, @NotNull LockManager lockController) throws DBException {
|
||||
this.projectsPath = projectsPath;
|
||||
this.lockController = lockController;
|
||||
readProjectInfos(projectsPath);
|
||||
}
|
||||
|
||||
public RMProjectInfo getProjectInfo(@NotNull String projectId) {
|
||||
return projectsInfo.computeIfAbsent(projectId, key -> new RMProjectInfo());
|
||||
}
|
||||
|
||||
public void updateProjectInfo(@NotNull String projectId, @Nullable RMProjectInfo projectInfo) {
|
||||
if (projectInfo != null) {
|
||||
projectsInfo.put(projectId, projectInfo);
|
||||
} else {
|
||||
projectsInfo.remove(projectId);
|
||||
}
|
||||
saveProjectsInfo();
|
||||
}
|
||||
|
||||
private void readProjectInfos(@NotNull Path path) {
|
||||
if (!Files.exists(path)) {
|
||||
return;
|
||||
}
|
||||
Path projectInfoFile = path.resolve(PROJECTS_INFO_FILE_NAME);
|
||||
if (!Files.exists(projectInfoFile)) {
|
||||
loadAndMigrateProjectDataToCommonFile(path);
|
||||
saveProjectsInfo();
|
||||
return;
|
||||
}
|
||||
readProjectInfosFromFile(projectInfoFile);
|
||||
}
|
||||
|
||||
private void readProjectInfosFromFile(@NotNull Path projectInfoFile) {
|
||||
try {
|
||||
log.info("Reading project metadata information");
|
||||
String content = Files.readString(projectInfoFile, StandardCharsets.UTF_8);
|
||||
Map<String, RMProjectInfo> loaded = JSONUtils.GSON.fromJson(
|
||||
content,
|
||||
TypeToken.getParameterized(Map.class, String.class, RMProjectInfo.class).getType()
|
||||
);
|
||||
projectsInfo.clear();
|
||||
if (loaded != null) {
|
||||
projectsInfo.putAll(loaded);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
log.error("Error reading existing " + PROJECTS_INFO_FILE_NAME, e);
|
||||
}
|
||||
}
|
||||
|
||||
private void loadAndMigrateProjectDataToCommonFile(@NotNull Path path) {
|
||||
log.info("Migrating shared project information to the common place");
|
||||
Map<String, RMProjectInfo> infos = new LinkedHashMap<>();
|
||||
try (Stream<Path> stream = Files.list(path)) {
|
||||
stream.filter(Files::isDirectory).forEach(projectDir -> {
|
||||
RMProjectInfo info = getProjectInfoFromProjectSettings(projectDir);
|
||||
String projectId = RMUtils.makeProjectIdFromPath(projectDir, RMProjectType.SHARED);
|
||||
infos.put(projectId, info);
|
||||
});
|
||||
} catch (IOException e) {
|
||||
log.error("Error listing shared projects path", e);
|
||||
}
|
||||
log.info("Migration for project information completed");
|
||||
projectsInfo.clear();
|
||||
projectsInfo.putAll(infos);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private RMProjectInfo getProjectInfoFromProjectSettings(@NotNull Path projectPath) {
|
||||
String name = null;
|
||||
String description = null;
|
||||
|
||||
Path settings = projectPath.resolve(DBPProject.METADATA_FOLDER).resolve(BaseProjectImpl.SETTINGS_STORAGE_FILE);
|
||||
if (Files.exists(settings) && Files.isRegularFile(settings)) {
|
||||
try {
|
||||
String json = Files.readString(settings, StandardCharsets.UTF_8);
|
||||
Map<String, Object> map = JSONUtils.GSON.fromJson(json, JSONUtils.MAP_TYPE_TOKEN);
|
||||
name = JSONUtils.getString(map, BaseProjectImpl.PROP_PROJECT_NAME);
|
||||
description = JSONUtils.getString(map, BaseProjectImpl.PROP_PROJECT_DESCRIPTION);
|
||||
} catch (IOException e) {
|
||||
log.warn("Failed to read project settings for " + projectPath + ": " + e.getMessage());
|
||||
} catch (Exception e) {
|
||||
log.warn("Failed to parse project settings for " + projectPath + ": " + e.getMessage());
|
||||
}
|
||||
}
|
||||
RMProjectInfo info = new RMProjectInfo();
|
||||
info.setName(CommonUtils.isEmpty(name) ? projectPath.getFileName().toString() : name);
|
||||
info.setDescription(description);
|
||||
return info;
|
||||
}
|
||||
|
||||
private void 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) {
|
||||
log.error("Error writing " + PROJECTS_INFO_FILE_NAME, e);
|
||||
} catch (DBException e) {
|
||||
log.error("Error locking file " + PROJECTS_INFO_FILE_NAME + ": " + e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
+37
@@ -18,8 +18,10 @@ package io.cloudbeaver.model.rm.local;
|
||||
|
||||
import org.jkiss.code.NotNull;
|
||||
import org.jkiss.code.Nullable;
|
||||
import org.jkiss.dbeaver.DBException;
|
||||
import org.jkiss.dbeaver.model.app.DBPWorkspace;
|
||||
import org.jkiss.dbeaver.model.auth.SMSessionContext;
|
||||
import org.jkiss.dbeaver.model.rm.RMProjectInfo;
|
||||
import org.jkiss.dbeaver.model.rm.RMProjectType;
|
||||
import org.jkiss.dbeaver.model.rm.RMUtils;
|
||||
import org.jkiss.dbeaver.registry.project.LocalProjectImpl;
|
||||
@@ -29,6 +31,8 @@ import java.nio.file.Path;
|
||||
public class RMLocalProject extends LocalProjectImpl {
|
||||
@NotNull
|
||||
private final RMProjectType projectType;
|
||||
@Nullable
|
||||
private RMProjectInfo projectInfo;
|
||||
|
||||
public RMLocalProject(
|
||||
@NotNull DBPWorkspace workspace,
|
||||
@@ -51,6 +55,39 @@ public class RMLocalProject extends LocalProjectImpl {
|
||||
return RMUtils.makeProjectIdFromPath(projectPath, projectType);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public String getDescription() {
|
||||
if (projectInfo == null) {
|
||||
return null;
|
||||
}
|
||||
return projectInfo.getDescription();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public String getName() {
|
||||
if (projectInfo == null || projectInfo.getName() == null) {
|
||||
return projectPath.getFileName().toString();
|
||||
}
|
||||
return projectInfo.getName();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public RMProjectType getProjectType() {
|
||||
return projectType;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateProject(@Nullable String newName, @Nullable String description) throws DBException {
|
||||
this.projectInfo = new RMProjectInfo();
|
||||
projectInfo.setName(newName);
|
||||
projectInfo.setDescription(description);
|
||||
}
|
||||
|
||||
public void setProjectInfo(@NotNull RMProjectInfo projectInfo) {
|
||||
this.projectInfo = projectInfo;
|
||||
}
|
||||
|
||||
public boolean canUpdateProjectName() {
|
||||
return RMProjectType.SHARED.equals(projectType);
|
||||
|
||||
+37
-15
@@ -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.
|
||||
@@ -21,7 +21,6 @@ import io.cloudbeaver.model.app.ServletApplication;
|
||||
import io.cloudbeaver.model.app.ServletAuthApplication;
|
||||
import io.cloudbeaver.websocket.CBWebSessionEventHandler;
|
||||
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.auth.SMAuthInfo;
|
||||
@@ -56,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();
|
||||
@@ -103,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) {
|
||||
@@ -117,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()) {
|
||||
@@ -151,8 +167,8 @@ public abstract class BaseWebSession extends AbstractSessionPersistent {
|
||||
return workspace.getAuthContext();
|
||||
}
|
||||
|
||||
protected void clearSessionContext() {
|
||||
this.workspace.getAuthContext().clear();
|
||||
protected synchronized void clearSessionContext() {
|
||||
this.workspace.getAuthContext().clearContext(false);
|
||||
this.workspace.getAuthContext().addSession(this);
|
||||
}
|
||||
|
||||
@@ -177,27 +193,33 @@ public abstract class BaseWebSession extends AbstractSessionPersistent {
|
||||
return lastAccessTime;
|
||||
}
|
||||
|
||||
public synchronized void touchSession() {
|
||||
@NotNull
|
||||
public SessionType getSessionType() {
|
||||
return sessionType;
|
||||
}
|
||||
|
||||
public void touchSession() {
|
||||
this.lastAccessTime = System.currentTimeMillis();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public synchronized WebUserContext getUserContext() {
|
||||
public WebUserContext getUserContext() {
|
||||
return userContext;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
super.close();
|
||||
cleanUpSession(true);
|
||||
super.close();
|
||||
}
|
||||
|
||||
public void close(boolean clearTokens, boolean sendSessionExpiredEvent) {
|
||||
cleanUpSession(sendSessionExpiredEvent);
|
||||
super.close();
|
||||
}
|
||||
|
||||
private void cleanUpSession(boolean sendSessionExpiredEvent) {
|
||||
application.getEventController().addEvent(new WSEventDeleteTempFile(getSessionId()));
|
||||
application.getEventController().addEvent(new WSEventDeleteTempFile(getUserContext().getSmSessionId()));
|
||||
synchronized (sessionEventHandlers) {
|
||||
var sessionExpiredEvent = new WSSessionExpiredEvent();
|
||||
for (CBWebSessionEventHandler sessionEventHandler : sessionEventHandlers) {
|
||||
@@ -227,7 +249,7 @@ public abstract class BaseWebSession extends AbstractSessionPersistent {
|
||||
this.eventsFilter = eventsFilter;
|
||||
}
|
||||
|
||||
public boolean isProjectAccessible(String projectId) {
|
||||
public boolean isProjectAccessible(@NotNull String projectId) {
|
||||
return userContext.getAccessibleProjectIds().contains(projectId);
|
||||
}
|
||||
|
||||
@@ -240,11 +262,11 @@ public abstract class BaseWebSession extends AbstractSessionPersistent {
|
||||
}
|
||||
|
||||
|
||||
public void removeSessionProject(@Nullable String projectId) throws DBException {
|
||||
public void removeSessionProject(@NotNull String projectId) throws DBException {
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
+150
-132
@@ -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,10 +17,7 @@
|
||||
package io.cloudbeaver.model.session;
|
||||
|
||||
import io.cloudbeaver.*;
|
||||
import io.cloudbeaver.model.CustomCancelableJob;
|
||||
import io.cloudbeaver.model.WebAsyncTaskInfo;
|
||||
import io.cloudbeaver.model.WebConnectionInfo;
|
||||
import io.cloudbeaver.model.WebServerMessage;
|
||||
import io.cloudbeaver.model.*;
|
||||
import io.cloudbeaver.model.app.ServletApplication;
|
||||
import io.cloudbeaver.model.app.ServletAuthApplication;
|
||||
import io.cloudbeaver.model.session.monitor.TaskProgressMonitor;
|
||||
@@ -30,7 +27,6 @@ import io.cloudbeaver.service.sql.WebSQLConstants;
|
||||
import io.cloudbeaver.utils.CBModelConstants;
|
||||
import io.cloudbeaver.utils.WebDataSourceUtils;
|
||||
import io.cloudbeaver.utils.WebEventUtils;
|
||||
import org.eclipse.core.runtime.IAdaptable;
|
||||
import org.eclipse.core.runtime.IStatus;
|
||||
import org.eclipse.core.runtime.Status;
|
||||
import org.jkiss.code.NotNull;
|
||||
@@ -38,6 +34,7 @@ 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.DBPAdaptable;
|
||||
import org.jkiss.dbeaver.model.DBPDataSourceContainer;
|
||||
import org.jkiss.dbeaver.model.DBPEventListener;
|
||||
import org.jkiss.dbeaver.model.access.DBAAuthCredentials;
|
||||
@@ -67,7 +64,8 @@ import java.time.Instant;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.function.Function;
|
||||
import java.util.concurrent.locks.ReadWriteLock;
|
||||
import java.util.concurrent.locks.ReentrantReadWriteLock;
|
||||
import java.util.stream.Collectors;
|
||||
/**
|
||||
* Web session.
|
||||
@@ -75,7 +73,7 @@ import java.util.stream.Collectors;
|
||||
*/
|
||||
//TODO: split to authenticated and non authenticated context
|
||||
public class WebSession extends BaseWebSession
|
||||
implements SMSessionWithAuth, SMCredentialsProvider, DBACredentialsProvider, IAdaptable {
|
||||
implements SMSessionWithAuth, SMCredentialsProvider, DBACredentialsProvider, DBPAdaptable {
|
||||
|
||||
private static final Log log = Log.getLog(WebSession.class);
|
||||
|
||||
@@ -84,6 +82,7 @@ public class WebSession extends BaseWebSession
|
||||
private static final AtomicInteger TASK_ID = new AtomicInteger();
|
||||
|
||||
public static String RUNTIME_PARAM_AUTH_INFOS = "auth-infos";
|
||||
public static String RUNTIME_PARAM_CLIENT_ORIGIN = "client-origin";
|
||||
private final AtomicInteger taskCount = new AtomicInteger();
|
||||
|
||||
private final String lastRemoteAddr;
|
||||
@@ -91,17 +90,18 @@ public class WebSession extends BaseWebSession
|
||||
|
||||
private String locale;
|
||||
private boolean cacheExpired;
|
||||
private String clientOrigin;
|
||||
|
||||
protected WebSessionGlobalProjectImpl globalProject;
|
||||
private final List<WebServerMessage> sessionMessages = new ArrayList<>();
|
||||
|
||||
private final Map<String, WebAsyncTaskInfo> asyncTasks = new HashMap<>();
|
||||
private final Map<String, Function<Object, Object>> attributeDisposers = new HashMap<>();
|
||||
|
||||
// Map of auth tokens. Key is authentication provider
|
||||
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);
|
||||
@@ -116,7 +116,8 @@ public class WebSession extends BaseWebSession
|
||||
CommonUtils.toString(requestInfo.getLocale()),
|
||||
application,
|
||||
sessionHandlers,
|
||||
requestInfo.getLastRemoteAddress()
|
||||
requestInfo.getLastRemoteAddress(),
|
||||
requestInfo.getSessionType()
|
||||
);
|
||||
updateSessionParameters(requestInfo);
|
||||
}
|
||||
@@ -128,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");
|
||||
}
|
||||
@@ -188,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();
|
||||
@@ -204,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();
|
||||
}
|
||||
@@ -225,28 +238,30 @@ 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");
|
||||
}
|
||||
return userContext.getAdminSecurityController();
|
||||
}
|
||||
|
||||
public synchronized RMController getRmController() {
|
||||
@NotNull
|
||||
public RMController getRmController() {
|
||||
return userContext.getRmController();
|
||||
}
|
||||
|
||||
public synchronized DBFileController getFileController() {
|
||||
@NotNull
|
||||
public DBFileController getFileController() {
|
||||
return userContext.getFileController();
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void refreshUserData() {
|
||||
public void refreshUserData() {
|
||||
super.refreshUserData();
|
||||
refreshSessionAuth();
|
||||
|
||||
@@ -254,7 +269,7 @@ public class WebSession extends BaseWebSession
|
||||
}
|
||||
|
||||
// Note: for admin use only
|
||||
public synchronized void resetUserState(boolean needResetUserCache) throws DBException {
|
||||
public void resetUserState(boolean needResetUserCache) throws DBException {
|
||||
clearAuthTokens();
|
||||
if (needResetUserCache) {
|
||||
try {
|
||||
@@ -268,7 +283,7 @@ public class WebSession extends BaseWebSession
|
||||
clearSessionContext();
|
||||
}
|
||||
|
||||
public synchronized void resetUserState() throws DBException {
|
||||
public void resetUserState() throws DBException {
|
||||
resetUserState(true);
|
||||
}
|
||||
|
||||
@@ -277,22 +292,35 @@ public class WebSession extends BaseWebSession
|
||||
return connectListener;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public String getClientOrigin() {
|
||||
return clientOrigin;
|
||||
}
|
||||
|
||||
public void updateClientOrigin(@NotNull String originFromRequest) {
|
||||
this.clientOrigin = originFromRequest;
|
||||
}
|
||||
|
||||
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() {
|
||||
@@ -323,7 +351,8 @@ public class WebSession extends BaseWebSession
|
||||
}
|
||||
}
|
||||
|
||||
private WebSessionProjectImpl createWebProject(RMProject project) throws DBException {
|
||||
@NotNull
|
||||
private WebSessionProjectImpl createWebProject(@NotNull RMProject project) throws DBException {
|
||||
WebSessionProjectImpl sessionProject;
|
||||
if (project.isGlobal()) {
|
||||
sessionProject = createGlobalProject(project);
|
||||
@@ -338,13 +367,10 @@ public class WebSession extends BaseWebSession
|
||||
if (!project.isShared() || application.isConfigurationMode()) {
|
||||
getWorkspace().setActiveProject(sessionProject);
|
||||
}
|
||||
log.info(String.format(
|
||||
"Project created: [ID=%s, Name=%s, Type=%s, Creator=%s]",
|
||||
project.getId(), project.getName(), project.getType(), project.getCreator()
|
||||
));
|
||||
return sessionProject;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
protected WebSessionProjectImpl createSessionProject(@NotNull RMProject project) throws DBException {
|
||||
return new WebSessionProjectImpl(this, project, getProjectPath(project));
|
||||
}
|
||||
@@ -354,32 +380,24 @@ public class WebSession extends BaseWebSession
|
||||
return RMUtils.getProjectPath(project);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
protected WebSessionProjectImpl createGlobalProject(RMProject project) {
|
||||
globalProject = new WebSessionGlobalProjectImpl(this, project);
|
||||
globalProject.refreshAccessibleConnectionIds();
|
||||
return globalProject;
|
||||
}
|
||||
|
||||
private void resetSessionCache() throws DBCException {
|
||||
// Clear attributes
|
||||
synchronized (attributes) {
|
||||
for (Map.Entry<String, Function<Object, Object>> attrDisposer : attributeDisposers.entrySet()) {
|
||||
Object attrValue = attributes.get(attrDisposer.getKey());
|
||||
attrDisposer.getValue().apply(attrValue);
|
||||
}
|
||||
attributeDisposers.clear();
|
||||
// Remove all non-persistent attributes
|
||||
attributes.entrySet().removeIf(
|
||||
entry -> !(entry.getValue() instanceof PersistentAttribute));
|
||||
}
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -422,15 +440,25 @@ public class WebSession extends BaseWebSession
|
||||
|
||||
@Nullable
|
||||
public DBNModel getNavigatorModel() {
|
||||
return navigatorModel;
|
||||
navigatorModelLock.readLock().lock();
|
||||
try {
|
||||
return navigatorModel;
|
||||
} finally {
|
||||
navigatorModelLock.readLock().unlock();
|
||||
}
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public DBNModel getNavigatorModelOrThrow() throws DBWebException {
|
||||
if (navigatorModel != null) {
|
||||
return navigatorModel;
|
||||
navigatorModelLock.readLock().lock();
|
||||
try {
|
||||
if (navigatorModel != null) {
|
||||
return navigatorModel;
|
||||
}
|
||||
throw new DBWebException("Navigator model is not found in session");
|
||||
} finally {
|
||||
navigatorModelLock.readLock().unlock();
|
||||
}
|
||||
throw new DBWebException("Navigator model is not found in session");
|
||||
}
|
||||
/**
|
||||
* Returns and clears progress messages
|
||||
@@ -444,7 +472,7 @@ public class WebSession extends BaseWebSession
|
||||
}
|
||||
}
|
||||
|
||||
public synchronized void updateInfo(boolean isOldHttpSessionUsed) {
|
||||
public void updateInfo(boolean isOldHttpSessionUsed) {
|
||||
log.trace("Update session lifetime " + getSessionId() + " for user " + getUserId());
|
||||
touchSession();
|
||||
if (isOldHttpSessionUsed) {
|
||||
@@ -476,7 +504,6 @@ public class WebSession extends BaseWebSession
|
||||
public void close() {
|
||||
try {
|
||||
resetNavigationModel();
|
||||
resetSessionCache();
|
||||
} catch (Throwable e) {
|
||||
log.error(e);
|
||||
}
|
||||
@@ -493,7 +520,6 @@ public class WebSession extends BaseWebSession
|
||||
public void close(boolean clearTokens, boolean sendSessionExpiredEvent) {
|
||||
try {
|
||||
resetNavigationModel();
|
||||
resetSessionCache();
|
||||
} catch (Throwable e) {
|
||||
log.error(e);
|
||||
}
|
||||
@@ -508,6 +534,7 @@ public class WebSession extends BaseWebSession
|
||||
super.close(clearTokens, sendSessionExpiredEvent);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private List<WebAuthInfo> clearAuthTokens() throws DBException {
|
||||
ArrayList<WebAuthInfo> tokensCopy;
|
||||
synchronized (authTokens) {
|
||||
@@ -592,13 +619,15 @@ 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 AbstractJob(asyncTask.getName()) {
|
||||
AbstractJob job = new AbstractCancelableJob(asyncTask.getName()) {
|
||||
@NotNull
|
||||
@Override
|
||||
protected IStatus run(@NotNull DBRProgressMonitor monitor) {
|
||||
@@ -637,11 +666,11 @@ public class WebSession extends BaseWebSession
|
||||
return asyncTask;
|
||||
}
|
||||
|
||||
public void addSessionError(Throwable exception) {
|
||||
public void addSessionError(@NotNull Throwable exception) {
|
||||
addSessionMessage(new WebServerMessage(exception));
|
||||
}
|
||||
|
||||
public void addSessionMessage(WebServerMessage message) {
|
||||
public void addSessionMessage(@NotNull WebServerMessage message) {
|
||||
synchronized (sessionMessages) {
|
||||
sessionMessages.add(message);
|
||||
}
|
||||
@@ -652,14 +681,15 @@ public class WebSession extends BaseWebSession
|
||||
message.getMessage()));
|
||||
}
|
||||
|
||||
public void addInfoMessage(String message) {
|
||||
public void addInfoMessage(@NotNull String message) {
|
||||
addSessionMessage(new WebServerMessage(MessageType.INFO, message));
|
||||
}
|
||||
|
||||
public void addWarningMessage(String message) {
|
||||
public void addWarningMessage(@NotNull String message) {
|
||||
addSessionMessage(new WebServerMessage(MessageType.WARNING, message));
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public List<WebServerMessage> readLog(Integer maxEntries, Boolean clearLog) {
|
||||
synchronized (sessionMessages) {
|
||||
List<WebServerMessage> messages = new ArrayList<>();
|
||||
@@ -679,42 +709,6 @@ public class WebSession extends BaseWebSession
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> T getAttribute(String name) {
|
||||
synchronized (attributes) {
|
||||
Object value = attributes.get(name);
|
||||
if (value instanceof PersistentAttribute persistentAttribute) {
|
||||
value = persistentAttribute.value();
|
||||
}
|
||||
return (T) value;
|
||||
}
|
||||
}
|
||||
|
||||
public void setAttribute(String name, Object value, boolean persistent) {
|
||||
synchronized (attributes) {
|
||||
attributes.put(name, persistent ? new PersistentAttribute(value) : value);
|
||||
}
|
||||
}
|
||||
|
||||
public <T> T getAttribute(String name, Function<T, T> creator, Function<T, T> disposer) {
|
||||
synchronized (attributes) {
|
||||
Object value = attributes.get(name);
|
||||
if (value instanceof PersistentAttribute persistentAttribute) {
|
||||
value = persistentAttribute.value();
|
||||
}
|
||||
if (value == null) {
|
||||
value = creator.apply(null);
|
||||
if (value != null) {
|
||||
attributes.put(name, value);
|
||||
if (disposer != null) {
|
||||
attributeDisposers.put(name, (Function<Object, Object>) disposer);
|
||||
}
|
||||
}
|
||||
}
|
||||
return (T) value;
|
||||
}
|
||||
}
|
||||
|
||||
@Property
|
||||
public Map<String, Object> getActionParameters() {
|
||||
WebActionParameters action = WebActionParameters.fromSession(this, true);
|
||||
@@ -743,7 +737,7 @@ public class WebSession extends BaseWebSession
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@NotNull
|
||||
public List<WebAuthInfo> getAllAuthInfo() {
|
||||
synchronized (authTokens) {
|
||||
return new ArrayList<>(authTokens);
|
||||
@@ -795,7 +789,7 @@ public class WebSession extends BaseWebSession
|
||||
});
|
||||
}
|
||||
|
||||
private void removeAuthInfo(WebAuthInfo oldAuthInfo) {
|
||||
private void removeAuthInfo(@NotNull WebAuthInfo oldAuthInfo) {
|
||||
oldAuthInfo.closeAuth();
|
||||
synchronized (authTokens) {
|
||||
authTokens.remove(oldAuthInfo);
|
||||
@@ -821,6 +815,7 @@ public class WebSession extends BaseWebSession
|
||||
return oldInfo;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public List<DBACredentialsProvider> getContextCredentialsProviders() {
|
||||
return getAdapters(DBACredentialsProvider.class);
|
||||
}
|
||||
@@ -839,6 +834,7 @@ public class WebSession extends BaseWebSession
|
||||
contextCredentialsProvider.provideAuthParameters(monitor, dataSourceContainer, configuration);
|
||||
}
|
||||
configuration.setRuntimeAttribute(RUNTIME_PARAM_AUTH_INFOS, getAllAuthInfo());
|
||||
configuration.setRuntimeAttribute(RUNTIME_PARAM_CLIENT_ORIGIN, this.clientOrigin);
|
||||
|
||||
WebSessionProjectImpl project = getProjectById(dataSourceContainer.getProject().getId());
|
||||
if (project != null) {
|
||||
@@ -869,7 +865,7 @@ public class WebSession extends BaseWebSession
|
||||
|
||||
// May be called to extract auth information from session
|
||||
@Override
|
||||
public <T> T getAdapter(Class<T> adapter) {
|
||||
public <T> T getAdapter(@NotNull Class<T> adapter) {
|
||||
synchronized (authTokens) {
|
||||
for (WebAuthInfo authInfo : authTokens) {
|
||||
if (isAuthInfoInstanceOf(authInfo, adapter)) {
|
||||
@@ -907,11 +903,11 @@ public class WebSession extends BaseWebSession
|
||||
return parameters;
|
||||
}
|
||||
|
||||
public synchronized void resetAuthToken() throws DBException {
|
||||
public void resetAuthToken() throws DBException {
|
||||
this.userContext.reset();
|
||||
}
|
||||
|
||||
public synchronized boolean updateSMSession(SMAuthInfo smAuthInfo) throws DBException {
|
||||
public boolean updateSMSession(SMAuthInfo smAuthInfo) throws DBException {
|
||||
boolean contextChanged = super.updateSMSession(smAuthInfo);
|
||||
if (contextChanged) {
|
||||
refreshUserData();
|
||||
@@ -930,7 +926,7 @@ public class WebSession extends BaseWebSession
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public WebSessionProjectImpl getProjectById(@Nullable String projectId) {
|
||||
public WebSessionProjectImpl getProjectById(@NotNull String projectId) {
|
||||
return getWorkspace().getProjectById(projectId);
|
||||
}
|
||||
|
||||
@@ -939,6 +935,7 @@ public class WebSession extends BaseWebSession
|
||||
*
|
||||
* @throws DBWebException if project with provided id is not found.
|
||||
*/
|
||||
@NotNull
|
||||
public WebSessionProjectImpl getAccessibleProjectById(@Nullable String projectId) throws DBWebException {
|
||||
WebSessionProjectImpl project = null;
|
||||
if (projectId != null) {
|
||||
@@ -950,6 +947,7 @@ public class WebSession extends BaseWebSession
|
||||
return project;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public List<WebSessionProjectImpl> getAccessibleProjects() {
|
||||
return getWorkspace().getProjects();
|
||||
}
|
||||
@@ -959,26 +957,35 @@ 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();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes project from session cache and navigator tree.
|
||||
*/
|
||||
public void deleteSessionProject(@Nullable WebSessionProjectImpl project) {
|
||||
if (project != null) {
|
||||
RMProject rmProject = project.getRMProject();
|
||||
log.info(String.format(
|
||||
"Project deleted: [ID=%s, Name=%s, Type=%s, Creator=%s]",
|
||||
rmProject.getId(), rmProject.getName(), rmProject.getType(), rmProject.getCreator()
|
||||
));
|
||||
project.dispose();
|
||||
}
|
||||
public void deleteSessionProject(@NotNull WebSessionProjectImpl project) {
|
||||
RMProject rmProject = project.getRMProject();
|
||||
log.info(String.format(
|
||||
"Project deleted: [ID=%s, Name=%s, Type=%s, Creator=%s]",
|
||||
rmProject.getId(), rmProject.getName(), rmProject.getType(), rmProject.getCreator()
|
||||
));
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -986,6 +993,10 @@ public class WebSession extends BaseWebSession
|
||||
public void addSessionProject(@NotNull String projectId) throws DBException {
|
||||
super.addSessionProject(projectId);
|
||||
var rmProject = getRmController().getProject(projectId, false, false);
|
||||
if (rmProject == null) {
|
||||
log.error("RM project '" + projectId + "' not found");
|
||||
return;
|
||||
}
|
||||
createWebProject(rmProject);
|
||||
}
|
||||
|
||||
@@ -1000,7 +1011,7 @@ public class WebSession extends BaseWebSession
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeSessionProject(@Nullable String projectId) throws DBException {
|
||||
public void removeSessionProject(@NotNull String projectId) throws DBException {
|
||||
super.removeSessionProject(projectId);
|
||||
var project = getProjectById(projectId);
|
||||
if (project == null) {
|
||||
@@ -1038,7 +1049,6 @@ public class WebSession extends BaseWebSession
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public void handleTaskConfirmationWithParameters(@NotNull String taskId, @NotNull Map<String, Object> parameters) {
|
||||
String attributeName = getTaskConfirmationAttributeName(taskId);
|
||||
CompletableFuture<Map<String, Object>> confirmationFuture = getAttribute(attributeName);
|
||||
@@ -1050,6 +1060,16 @@ public class WebSession extends BaseWebSession
|
||||
}
|
||||
}
|
||||
|
||||
public void handleActionCancelledEvent(@NotNull String actionId) {
|
||||
Object attributeValue = getAttribute(actionId);
|
||||
if (!(attributeValue instanceof CompletableFuture<?> future)) {
|
||||
log.info("Action to be cancelled not found, skipping: " + actionId);
|
||||
return;
|
||||
}
|
||||
future.completeExceptionally(new DBWebException("Action cancelled by user: " + actionId));
|
||||
removeAttribute(actionId);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private String getTaskConfirmationAttributeName(@NotNull String taskId) {
|
||||
return WebSQLConstants.TASK_CONFIRMATION_ATTR_PREFIX + taskId;
|
||||
@@ -1071,6 +1091,4 @@ public class WebSession extends BaseWebSession
|
||||
return true;
|
||||
}
|
||||
|
||||
private record PersistentAttribute(Object value) {
|
||||
}
|
||||
}
|
||||
|
||||
+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;
|
||||
|
||||
+75
-24
@@ -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,12 +17,17 @@
|
||||
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;
|
||||
import org.jkiss.dbeaver.Log;
|
||||
import org.jkiss.dbeaver.model.DBPAdaptable;
|
||||
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;
|
||||
@@ -36,11 +41,13 @@ import java.util.List;
|
||||
* Web workspace
|
||||
*/
|
||||
public class WebSessionWorkspace implements DBPWorkspace {
|
||||
private static final Log log = Log.getLog(WebSessionWorkspace.class);
|
||||
|
||||
private final BaseWebSession session;
|
||||
private final SessionContextImpl workspaceAuthContext;
|
||||
private final List<WebSessionProjectImpl> accessibleProjects = new ArrayList<>();
|
||||
private WebSessionProjectImpl activeProject;
|
||||
private DBFFileSystemManager fileSystemManager;
|
||||
|
||||
public WebSessionWorkspace(BaseWebSession session) {
|
||||
this.session = session;
|
||||
@@ -87,7 +94,9 @@ public class WebSessionWorkspace implements DBPWorkspace {
|
||||
@NotNull
|
||||
@Override
|
||||
public List<WebSessionProjectImpl> getProjects() {
|
||||
return accessibleProjects;
|
||||
synchronized (accessibleProjects) {
|
||||
return new ArrayList<>(accessibleProjects);
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@@ -99,26 +108,30 @@ public class WebSessionWorkspace implements DBPWorkspace {
|
||||
@Nullable
|
||||
@Override
|
||||
public WebSessionProjectImpl getProject(@NotNull String projectName) {
|
||||
for (WebSessionProjectImpl project : accessibleProjects) {
|
||||
if (project.getName().equals(projectName)) {
|
||||
return project;
|
||||
synchronized (accessibleProjects) {
|
||||
for (WebSessionProjectImpl project : accessibleProjects) {
|
||||
if (project.getName().equals(projectName)) {
|
||||
return project;
|
||||
}
|
||||
}
|
||||
}
|
||||
log.error("Project name '" + projectName + "' not found in session workspace");
|
||||
return null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public WebSessionProjectImpl getProjectById(@Nullable String projectId) {
|
||||
if (projectId == null) {
|
||||
return activeProject;
|
||||
}
|
||||
for (WebSessionProjectImpl project : accessibleProjects) {
|
||||
if (project.getId().equals(projectId)) {
|
||||
return project;
|
||||
public WebSessionProjectImpl getProjectById(@NotNull String projectId) {
|
||||
synchronized (accessibleProjects) {
|
||||
for (WebSessionProjectImpl project : accessibleProjects) {
|
||||
if (project.getId().equals(projectId)) {
|
||||
return project;
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
log.error("Project ID '" + projectId + "' not found in session workspace");
|
||||
// FIXME: return null here
|
||||
return activeProject;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@@ -143,36 +156,74 @@ public class WebSessionWorkspace implements DBPWorkspace {
|
||||
return null;
|
||||
}
|
||||
|
||||
public void setActiveProject(WebSessionProjectImpl activeProject) {
|
||||
@NotNull
|
||||
@Override
|
||||
public WebSessionProjectImpl createProject(@NotNull String name, @Nullable String description) throws DBException {
|
||||
throw new DBException("Project creation is not supported in web session workspace");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deleteProject(@NotNull DBPProject project) throws DBException {
|
||||
throw new DBException("Project removing is not supported in web session workspace");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void renameProject(@NotNull DBPProject project, @NotNull String newName) throws DBException {
|
||||
throw new DBException("Project renaming is not supported in web session workspace");
|
||||
}
|
||||
|
||||
public void setActiveProject(@NotNull WebSessionProjectImpl activeProject) {
|
||||
this.activeProject = activeProject;
|
||||
}
|
||||
|
||||
void addProject(WebSessionProjectImpl project) {
|
||||
accessibleProjects.add(project);
|
||||
void addProject(@NotNull WebSessionProjectImpl project) {
|
||||
synchronized (accessibleProjects) {
|
||||
accessibleProjects.add(project);
|
||||
}
|
||||
}
|
||||
|
||||
void removeProject(WebSessionProjectImpl project) {
|
||||
accessibleProjects.remove(project);
|
||||
void removeProject(@NotNull WebSessionProjectImpl project) {
|
||||
synchronized (accessibleProjects) {
|
||||
accessibleProjects.remove(project);
|
||||
}
|
||||
}
|
||||
|
||||
void clearProjects() {
|
||||
if (!this.accessibleProjects.isEmpty()) {
|
||||
for (WebSessionProjectImpl project : accessibleProjects) {
|
||||
project.dispose();
|
||||
synchronized (accessibleProjects) {
|
||||
if (!this.accessibleProjects.isEmpty()) {
|
||||
for (WebSessionProjectImpl project : accessibleProjects) {
|
||||
project.dispose();
|
||||
}
|
||||
this.activeProject = null;
|
||||
this.accessibleProjects.clear();
|
||||
}
|
||||
this.activeProject = null;
|
||||
this.accessibleProjects.clear();
|
||||
}
|
||||
}
|
||||
|
||||
@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;
|
||||
}
|
||||
}
|
||||
|
||||
+24
-17
@@ -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,53 +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;
|
||||
}
|
||||
|
||||
public synchronized String getUserId() {
|
||||
@Nullable
|
||||
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;
|
||||
}
|
||||
|
||||
public synchronized SMAdminController getAdminSecurityController() {
|
||||
@NotNull
|
||||
public SMAdminController getAdminSecurityController() {
|
||||
return adminSecurityController;
|
||||
}
|
||||
|
||||
public synchronized Set<String> getUserPermissions() {
|
||||
@Nullable
|
||||
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());
|
||||
@@ -234,6 +237,7 @@ public class WebUserContext implements SMCredentialsProvider {
|
||||
this.userPermissions = permissions;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public DBSSecretController getSecretController() throws DBException {
|
||||
if (this.securityController == null) {
|
||||
this.secretController = application.getSecretController(this, workspace.getAuthContext());
|
||||
@@ -241,7 +245,8 @@ public class WebUserContext implements SMCredentialsProvider {
|
||||
return secretController;
|
||||
}
|
||||
|
||||
public synchronized String getSmSessionId() {
|
||||
@Nullable
|
||||
public String getSmSessionId() {
|
||||
return smSessionId;
|
||||
}
|
||||
|
||||
@@ -255,6 +260,7 @@ public class WebUserContext implements SMCredentialsProvider {
|
||||
return rmController;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public DBFileController getFileController() {
|
||||
return fileController;
|
||||
}
|
||||
@@ -268,6 +274,7 @@ public class WebUserContext implements SMCredentialsProvider {
|
||||
this.refreshToken = refreshToken;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public WebSessionPreferenceStore getPreferenceStore() {
|
||||
return preferenceStore;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
+4
-2
@@ -38,8 +38,7 @@ public class WebFeatureDescriptor extends AbstractContextDescriptor implements D
|
||||
private final DBPImage icon;
|
||||
private final boolean enabledByDefault;
|
||||
|
||||
public WebFeatureDescriptor(IConfigurationElement config)
|
||||
{
|
||||
public WebFeatureDescriptor(@NotNull IConfigurationElement config) {
|
||||
super(config);
|
||||
this.id = config.getAttribute("id");
|
||||
this.label = config.getAttribute("label");
|
||||
@@ -77,4 +76,7 @@ public class WebFeatureDescriptor extends AbstractContextDescriptor implements D
|
||||
return enabledByDefault;
|
||||
}
|
||||
|
||||
public boolean isApplicable() {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
+15
-8
@@ -20,10 +20,12 @@ import io.cloudbeaver.DBWFeatureSet;
|
||||
import org.eclipse.core.runtime.IConfigurationElement;
|
||||
import org.eclipse.core.runtime.IExtensionRegistry;
|
||||
import org.eclipse.core.runtime.Platform;
|
||||
import org.jkiss.code.NotNull;
|
||||
import org.jkiss.dbeaver.Log;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class WebFeatureRegistry {
|
||||
|
||||
@@ -33,7 +35,7 @@ public class WebFeatureRegistry {
|
||||
|
||||
private static WebFeatureRegistry instance = null;
|
||||
|
||||
public synchronized static WebFeatureRegistry getInstance() {
|
||||
public static synchronized WebFeatureRegistry getInstance() {
|
||||
if (instance == null) {
|
||||
instance = new WebFeatureRegistry();
|
||||
instance.loadExtensions(Platform.getExtensionRegistry());
|
||||
@@ -41,23 +43,28 @@ public class WebFeatureRegistry {
|
||||
return instance;
|
||||
}
|
||||
|
||||
private final List<DBWFeatureSet> webFeatures = new ArrayList<>();
|
||||
private final List<WebFeatureDescriptor> webFeatures = new ArrayList<>();
|
||||
|
||||
private WebFeatureRegistry() {
|
||||
}
|
||||
|
||||
private void loadExtensions(IExtensionRegistry registry) {
|
||||
protected void loadExtensions(@NotNull IExtensionRegistry registry) {
|
||||
IConfigurationElement[] extConfigs = registry.getConfigurationElementsFor(WebFeatureDescriptor.EXTENSION_ID);
|
||||
for (IConfigurationElement ext : extConfigs) {
|
||||
if (TAG_FEATURE.equals(ext.getName())) {
|
||||
this.webFeatures.add(
|
||||
new WebFeatureDescriptor(ext));
|
||||
this.webFeatures.add(createFeatureDescriptor(ext));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@NotNull
|
||||
protected WebFeatureDescriptor createFeatureDescriptor(@NotNull IConfigurationElement ext) {
|
||||
return new WebFeatureDescriptor(ext);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public List<DBWFeatureSet> getWebFeatures() {
|
||||
return webFeatures;
|
||||
return webFeatures.stream()
|
||||
.filter(WebFeatureDescriptor::isApplicable)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+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";
|
||||
|
||||
+4
-3
@@ -41,7 +41,7 @@ public abstract class BaseServletPlatform extends BasePlatformImpl {
|
||||
if (tempFolder == null) {
|
||||
synchronized (this) {
|
||||
if (tempFolder == null) {
|
||||
initTempFolder(monitor);
|
||||
initTempFolder();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -59,9 +59,8 @@ public abstract class BaseServletPlatform extends BasePlatformImpl {
|
||||
@NotNull
|
||||
public abstract ServletApplication getApplication();
|
||||
|
||||
private void initTempFolder(@NotNull DBRProgressMonitor monitor) {
|
||||
private void initTempFolder() {
|
||||
// Make temp folder
|
||||
monitor.subTask("Create temp folder");
|
||||
String sysTempFolder = System.getProperty(StandardConstants.ENV_TMP_DIR);
|
||||
if (CommonUtils.isNotEmpty(sysTempFolder)) {
|
||||
tempFolder = Path.of(sysTempFolder).resolve(BASE_TEMP_DIR).resolve(DBWConstants.WORK_DATA_FOLDER_NAME);
|
||||
@@ -69,9 +68,11 @@ public abstract class BaseServletPlatform extends BasePlatformImpl {
|
||||
//we do not use workspace because it can be in external file system
|
||||
tempFolder = getApplication().getHomeDirectory().resolve(DBWConstants.WORK_DATA_FOLDER_NAME);
|
||||
}
|
||||
log.debug("Temp folder: " + tempFolder);
|
||||
}
|
||||
|
||||
public synchronized void dispose() {
|
||||
super.dispose();
|
||||
// Remove temp folder
|
||||
if (tempFolder != null) {
|
||||
|
||||
|
||||
@@ -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.
|
||||
@@ -28,50 +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_SERVER_URL = "serverURL";
|
||||
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";
|
||||
@@ -84,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";
|
||||
@@ -99,4 +80,8 @@ public class CBConstants {
|
||||
public static final String BIND_SESSION_ENABLE = "enable";
|
||||
public static final String BIND_SESSION_DISABLE = "disable";
|
||||
public static final String DEFAULT_CLOUD_PROJECT_NAME = "GlobalConfiguration";
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
/*
|
||||
* DBeaver - Universal Database Manager
|
||||
* Copyright (C) 2010-2025 DBeaver Corp and others
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package io.cloudbeaver.service.sql;
|
||||
|
||||
import org.jkiss.code.NotNull;
|
||||
import org.jkiss.code.Nullable;
|
||||
|
||||
public record WebSQLQueryParameterRecord(@NotNull String name, @Nullable String value) {
|
||||
}
|
||||
@@ -18,18 +18,20 @@ package io.cloudbeaver.test;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.GsonBuilder;
|
||||
import io.cloudbeaver.server.CBConstants;
|
||||
import org.jkiss.code.NotNull;
|
||||
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;
|
||||
import java.net.http.HttpClient;
|
||||
import java.net.http.HttpRequest;
|
||||
import java.net.http.HttpResponse;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
@@ -56,10 +58,13 @@ public class WebGQLClient {
|
||||
@NotNull
|
||||
private final HttpClient httpClient;
|
||||
@NotNull
|
||||
private final CookieManager cookieManager;
|
||||
@NotNull
|
||||
private final String apiUrl;
|
||||
|
||||
public WebGQLClient(@NotNull HttpClient httpClient, @NotNull String apiUrl) {
|
||||
public WebGQLClient(@NotNull HttpClient httpClient, @NotNull CookieManager cookieManager, @NotNull String apiUrl) {
|
||||
this.httpClient = httpClient;
|
||||
this.cookieManager = cookieManager;
|
||||
this.apiUrl = apiUrl;
|
||||
}
|
||||
|
||||
@@ -70,9 +75,18 @@ public class WebGQLClient {
|
||||
* @param variables GraphQL query variables
|
||||
* @return GraphQL response
|
||||
*/
|
||||
@NotNull
|
||||
@Nullable
|
||||
public <T> T sendQuery(@NotNull String query, @Nullable Map<String, Object> variables) throws Exception {
|
||||
return sendQueryWithHeaders(query, variables, List.of());
|
||||
return sendQueryWithHeaders(query, variables, Map.of());
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public Map<String, Object> sendQueryAndGetHeaders(
|
||||
@NotNull String query,
|
||||
@Nullable Map<String, Object> variables,
|
||||
@NotNull Map<String, String> requestHeaders
|
||||
) throws Exception {
|
||||
return executeGQLRequest(query, variables, requestHeaders);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -87,15 +101,36 @@ public class WebGQLClient {
|
||||
public <T> T sendQueryWithHeaders(
|
||||
@NotNull String query,
|
||||
@Nullable Map<String, Object> variables,
|
||||
@NotNull List<String> headers
|
||||
@NotNull Map<String, String> headers
|
||||
) throws Exception {
|
||||
Map<String, Object> parsed = executeGQLRequest(query, variables, headers);
|
||||
// graphql response will be in "data" key
|
||||
return (T) parsed.get("data");
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private Map<String, Object> executeGQLRequest(
|
||||
@NotNull String query,
|
||||
@Nullable Map<String, Object> variables,
|
||||
@NotNull Map<String, String> requestHeaders
|
||||
) throws Exception {
|
||||
return executeGQLRequest(query, variables, requestHeaders, "result");
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public Map<String, Object> executeGQLRequest(
|
||||
@NotNull String query,
|
||||
@Nullable Map<String, Object> variables,
|
||||
@NotNull Map<String, String> requestHeaders,
|
||||
@NotNull String resultName
|
||||
) throws Exception {
|
||||
HttpRequest.Builder requestBuilder = HttpRequest.newBuilder()
|
||||
.uri(URI.create(apiUrl))
|
||||
.POST(HttpRequest.BodyPublishers.ofString(makeGQLRequest(query, variables)))
|
||||
.setHeader("TE-Client-Version", GeneralUtils.getMajorVersion())
|
||||
.header("Content-Type", "application/json");
|
||||
if (!headers.isEmpty()) {
|
||||
requestBuilder.headers(headers.toArray(String[]::new));
|
||||
.header(HttpConstants.HEADER_CONTENT_TYPE, HttpConstants.CONTENT_TYPE_JSON);
|
||||
if (!requestHeaders.isEmpty()) {
|
||||
requestHeaders.forEach(requestBuilder::header);
|
||||
}
|
||||
HttpRequest request = requestBuilder.build();
|
||||
HttpResponse<String> response = httpClient.send(request, HttpResponse.BodyHandlers.ofString());
|
||||
@@ -107,8 +142,10 @@ public class WebGQLClient {
|
||||
String message = JSONUtils.getString(JSONUtils.getObjectList(body, "errors").get(0), "message");
|
||||
throw new DBException(message);
|
||||
}
|
||||
// graphql response will be in "data" key
|
||||
return (T) JSONUtils.getObject(body, "data").get("result");
|
||||
Map<String, Object> parsed = new LinkedHashMap<>();
|
||||
parsed.put("data", JSONUtils.getObject(body, "data").get(resultName));
|
||||
parsed.put("headers", response.headers().map());
|
||||
return parsed;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@@ -121,4 +158,13 @@ public class WebGQLClient {
|
||||
|
||||
return gson.toJson(request, Map.class);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public String getSessionIdCookie() {
|
||||
return cookieManager.getCookieStore().getCookies().stream()
|
||||
.filter(cookie -> cookie.getName().equals(CBConstants.CB_SESSION_COOKIE_NAME))
|
||||
.findFirst()
|
||||
.get()
|
||||
.getValue();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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()
|
||||
|
||||
+38
-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.
|
||||
@@ -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,10 +41,11 @@ 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.DataSourceUtils;
|
||||
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;
|
||||
import org.jkiss.dbeaver.utils.PropertySerializationUtils;
|
||||
import org.jkiss.utils.CommonUtils;
|
||||
|
||||
import java.util.*;
|
||||
@@ -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);
|
||||
@@ -216,7 +227,7 @@ public class WebDataSourceUtils {
|
||||
if (properties == null) {
|
||||
return;
|
||||
}
|
||||
DataSourceUtils.updateCredentialsFromProperties(progressMonitor, credentials, properties);
|
||||
PropertySerializationUtils.updateCredentialsFromProperties(progressMonitor, credentials, properties);
|
||||
}
|
||||
|
||||
public static void saveAuthProperties(
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user