mirror of
https://github.com/dbeaver/cloudbeaver.git
synced 2026-10-16 14:50:15 +08:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5a8660a37f | ||
|
|
1ca883bf64 | ||
|
|
e3608a2504 | ||
|
|
b3c1cebd69 | ||
|
|
0610759ac2 | ||
|
|
ff770a1270 | ||
|
|
4c7b9fca95 | ||
|
|
f98b9aa989 | ||
|
|
1ecdced23e | ||
|
|
cb60358ae6 | ||
|
|
0f9e7b650c | ||
|
|
b2eab058c9 | ||
|
|
4f0fa25d83 | ||
|
|
a3bdfb68fe | ||
|
|
584dd1ce4f | ||
|
|
5255ee96db | ||
|
|
e68c44ed73 | ||
|
|
e4f01a3fa8 | ||
|
|
a5c9073e6f | ||
|
|
b7d32ad676 | ||
|
|
a520aa1f20 | ||
|
|
8e4c0c11a4 | ||
|
|
72b3838fa9 | ||
|
|
df9de51bda | ||
|
|
390f53f36b | ||
|
|
d3cf9e3938 | ||
|
|
97dc309bcb | ||
|
|
6c609d7f62 | ||
|
|
f67835d2d2 | ||
|
|
98197c8829 | ||
|
|
997936328e | ||
|
|
f7832b56fa | ||
|
|
81ce6b6ac7 | ||
|
|
518410c2e8 | ||
|
|
4645a8e3d1 | ||
|
|
be59a4dab9 | ||
|
|
3a92d7d23d | ||
|
|
d44e6b4e7a | ||
|
|
d87115d753 | ||
|
|
245d49b892 | ||
|
|
3831abc880 | ||
|
|
e818f1658c | ||
|
|
739a6f7f11 | ||
|
|
ab985673b0 | ||
|
|
abf3e7247c | ||
|
|
49f321462e | ||
|
|
d0a820d27b | ||
|
|
bd9a18bc50 | ||
|
|
3fa1130608 | ||
|
|
fe2c7b76be | ||
|
|
4344dce700 | ||
|
|
dc402f1940 | ||
|
|
bdcda6a1fb | ||
|
|
baef7fbae6 | ||
|
|
b52c2788b1 | ||
|
|
48b9326336 | ||
|
|
37d76c428d | ||
|
|
01bcf9479d | ||
|
|
31355b81f8 | ||
|
|
226762c5f9 | ||
|
|
6b58613c83 | ||
|
|
e3f04bcbb0 | ||
|
|
c5d0e4bade | ||
|
|
45335f5dd7 | ||
|
|
ff4c453f8d | ||
|
|
be5bb95154 | ||
|
|
ece5e8f3f1 | ||
|
|
9dcd287c61 | ||
|
|
97b43c044f | ||
|
|
4550a71298 | ||
|
|
ca4fe0b96d | ||
|
|
66c1f94464 | ||
|
|
a9c2d8830a | ||
|
|
1f8dd642bc | ||
|
|
c874b4b440 | ||
|
|
c31826f830 | ||
|
|
e00ca87364 | ||
|
|
e56247b837 | ||
|
|
4e25a42b1c | ||
|
|
0d41713c2d | ||
|
|
c58e62c90e | ||
|
|
6da5f3b0e4 | ||
|
|
713082215f | ||
|
|
571d6b7c0a | ||
|
|
a2d9fddc5e | ||
|
|
2e71cfdf87 | ||
|
|
c4975475a1 | ||
|
|
0d941b61c8 | ||
|
|
3b0e31614c | ||
|
|
542217cc16 | ||
|
|
75b293d6a9 | ||
|
|
ad27eec4a7 | ||
|
|
55ab736104 | ||
|
|
9384877bb0 | ||
|
|
63c7c6feb1 | ||
|
|
6eaafd6e2a | ||
|
|
0a88cbdb50 | ||
|
|
1a434adb3d | ||
|
|
8a685ef47e | ||
|
|
a981d37a2d | ||
|
|
cba58e0c15 | ||
|
|
edc15b664f | ||
|
|
39dfa3e05d | ||
|
|
320d7e84ee | ||
|
|
7ea8befbe4 | ||
|
|
d8ade2a09c | ||
|
|
5030094f1f | ||
|
|
4a04023062 | ||
|
|
751a9c7893 | ||
|
|
006dd209ee | ||
|
|
4e0c23ef30 | ||
|
|
da6a84c15a | ||
|
|
ff32a5a577 | ||
|
|
f479d8410a | ||
|
|
d3054e26ce | ||
|
|
32baba6434 | ||
|
|
00f45ae455 | ||
|
|
beed8ff9ae | ||
|
|
cbc98c5722 | ||
|
|
45266974ab | ||
|
|
232dfb4ed4 | ||
|
|
a94ca5e635 | ||
|
|
0767d0d3a1 | ||
|
|
a21ebf837c | ||
|
|
f1507c3a7d | ||
|
|
773edfc9c0 | ||
|
|
68abf6051a | ||
|
|
8d5a61e04e | ||
|
|
4d060ed746 | ||
|
|
534b02d5ee | ||
|
|
7a3b17081c | ||
|
|
15430d5c62 | ||
|
|
619d4754cb | ||
|
|
5389d4a5c6 | ||
|
|
b2d7f12a11 | ||
|
|
8d8ac0b5df | ||
|
|
6affba2d40 | ||
|
|
7769e1c7bf | ||
|
|
3a889769b4 | ||
|
|
b8968ab349 | ||
|
|
f0156ae069 | ||
|
|
a81b81ff2d | ||
|
|
65e1b25519 | ||
|
|
d2b72d3617 | ||
|
|
3fa9f5276f | ||
|
|
8b1a75eef6 | ||
|
|
7acaf2668d | ||
|
|
9839a8fc26 | ||
|
|
fb6a3c4c69 | ||
|
|
daf3811b5c | ||
|
|
d84fa05f58 | ||
|
|
2704db7bc9 | ||
|
|
18c2d7bb02 | ||
|
|
f2239f596c | ||
|
|
00611a0f26 | ||
|
|
fe71b00ba0 | ||
|
|
20b94f06f6 | ||
|
|
cd4e97b4d9 | ||
|
|
d4c7dd9676 | ||
|
|
fa6476e0d5 | ||
|
|
32fcddf535 | ||
|
|
7682304a25 | ||
|
|
acdb139739 | ||
|
|
15cb96a68f | ||
|
|
35668e69b4 | ||
|
|
aa1edf0e29 | ||
|
|
d6d99f5e9c | ||
|
|
f2896b8b90 | ||
|
|
30c4020790 | ||
|
|
038f03823c | ||
|
|
5206680590 | ||
|
|
f736dd4b46 | ||
|
|
11e320e225 | ||
|
|
439d2505cc | ||
|
|
5f3405f7bf | ||
|
|
11507c362d | ||
|
|
31dd99c573 | ||
|
|
9443351204 | ||
|
|
79050cddcf | ||
|
|
4e6119d835 | ||
|
|
a4de35c8c6 | ||
|
|
07f8fb6081 | ||
|
|
77b2601523 | ||
|
|
3983595b5f | ||
|
|
d6febf4c48 | ||
|
|
f771757f11 | ||
|
|
d01d862e3e | ||
|
|
451a866149 | ||
|
|
1a06b4e17b | ||
|
|
c55ad1bac9 | ||
|
|
3aa9b80e56 | ||
|
|
617f3b45d7 | ||
|
|
af67b02d9e | ||
|
|
4f4c637866 | ||
|
|
5433262127 | ||
|
|
7b5855e5b8 | ||
|
|
79080444fd | ||
|
|
044cafae24 | ||
|
|
8c51b22d81 | ||
|
|
1ef7a4570d | ||
|
|
40569b708a | ||
|
|
8e67a473d1 | ||
|
|
5df9b187b4 | ||
|
|
17a7e0c39d | ||
|
|
292acd5d51 | ||
|
|
421549c08a | ||
|
|
e9b6f6e888 | ||
|
|
b205876ff4 | ||
|
|
028b04b3b2 | ||
|
|
32624147f8 | ||
|
|
0d11f60a58 | ||
|
|
f3bc8a28ec | ||
|
|
623f4458d9 | ||
|
|
eaeec9f1be | ||
|
|
dd501d0716 | ||
|
|
1d2e1cd823 | ||
|
|
b73dbe684b | ||
|
|
9b3098d68f | ||
|
|
069218cb5e | ||
|
|
29ec2e09b7 | ||
|
|
5b077f3189 | ||
|
|
73b5e8fc59 | ||
|
|
1073335af5 | ||
|
|
d5cdb8fb43 | ||
|
|
752f913db0 | ||
|
|
0d665a6b7b | ||
|
|
84cbf143ae | ||
|
|
f4a62bbca8 | ||
|
|
d93d7748cf | ||
|
|
d2f1f29e80 | ||
|
|
b7add23754 | ||
|
|
07fc1f88b7 | ||
|
|
c79a9a8efa | ||
|
|
e785d98c9b | ||
|
|
45c201c465 | ||
|
|
664864d478 | ||
|
|
3cdcb07646 | ||
|
|
e33eb9fd60 | ||
|
|
8fba6107b2 | ||
|
|
f41effd655 | ||
|
|
9b34a496dc | ||
|
|
93c9cdd23d | ||
|
|
c7e6cf9d95 | ||
|
|
27b0180107 | ||
|
|
f073954a32 | ||
|
|
e5ed5c8682 | ||
|
|
90cd350267 | ||
|
|
1360631ab4 | ||
|
|
dda35760eb | ||
|
|
1086151bcb | ||
|
|
e7c066e592 | ||
|
|
5aae41d1fe | ||
|
|
4ad921a9c8 | ||
|
|
632c93f246 | ||
|
|
4e4b817a38 | ||
|
|
b097b1147a | ||
|
|
d00f08b9d0 | ||
|
|
9d0ecce84f | ||
|
|
7550915e9c | ||
|
|
78c19187de | ||
|
|
fc592908c2 | ||
|
|
ba0cd2d3fa | ||
|
|
872c226522 | ||
|
|
ab94dd837b | ||
|
|
dce23485a7 | ||
|
|
17510f2de1 | ||
|
|
98d171e503 | ||
|
|
736d91fbe3 | ||
|
|
c0b71a431a | ||
|
|
d8585e6a37 | ||
|
|
6668197625 | ||
|
|
b3f6eb929b | ||
|
|
9f885eac34 | ||
|
|
5945bc1823 | ||
|
|
e6d3b2af7f | ||
|
|
0c981a67f4 | ||
|
|
a571eeef8e | ||
|
|
3642bbfa41 | ||
|
|
ba48b282fd | ||
|
|
a8e8f7ef33 | ||
|
|
7af7fafb42 | ||
|
|
9b43e2f158 | ||
|
|
c78adb94a2 | ||
|
|
8292eb3ac9 | ||
|
|
10a3dcc1e5 | ||
|
|
796afa3d17 | ||
|
|
d4739a0dba | ||
|
|
65718f681c | ||
|
|
32b38d9732 | ||
|
|
d9e6dcfd02 | ||
|
|
cd10ed2ea1 | ||
|
|
f3b9034855 |
@@ -0,0 +1,76 @@
|
||||
name: frontend-cache
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: ["devel"]
|
||||
paths:
|
||||
- "webapp/**"
|
||||
|
||||
# Allows you to run this workflow manually from the Actions tab
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
build_and_test:
|
||||
runs-on: ubuntu-latest
|
||||
defaults:
|
||||
run:
|
||||
working-directory: ./webapp
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: "18"
|
||||
|
||||
- name: Get yarn cache directory path
|
||||
id: yarn-cache-dir-path
|
||||
run: echo "dir=$(yarn cache dir)" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: restore yarn cache
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: ${{ steps.yarn-cache-dir-path.outputs.dir }}
|
||||
key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-yarn-
|
||||
|
||||
- name: restore node_modules
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: "**/node_modules"
|
||||
key: ${{ runner.os }}-node_modules-${{ hashFiles('**/yarn.lock') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-node_modules-
|
||||
|
||||
- name: restore typescript cache
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: "**/packages/*/dist"
|
||||
key: ${{ runner.os }}-dist-${{ hashFiles('**/yarn.lock') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-dist-
|
||||
|
||||
- name: yarn install
|
||||
uses: borales/actions-yarn@v4
|
||||
with:
|
||||
dir: webapp
|
||||
cmd: install
|
||||
|
||||
- name: yarn lerna bootstrap
|
||||
uses: borales/actions-yarn@v4
|
||||
with:
|
||||
dir: webapp
|
||||
cmd: lerna bootstrap
|
||||
|
||||
- name: build
|
||||
uses: borales/actions-yarn@v4
|
||||
with:
|
||||
dir: webapp/packages/product-default
|
||||
cmd: build # build tyescript source code
|
||||
|
||||
- name: test
|
||||
uses: borales/actions-yarn@v4
|
||||
with:
|
||||
dir: webapp
|
||||
cmd: lerna run test # build packages that have test script
|
||||
@@ -0,0 +1,29 @@
|
||||
name: cleanup caches by a branch
|
||||
on:
|
||||
pull_request:
|
||||
types:
|
||||
- closed
|
||||
|
||||
jobs:
|
||||
cleanup:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Cleanup
|
||||
run: |
|
||||
gh extension install actions/gh-actions-cache
|
||||
|
||||
echo "Fetching list of cache key"
|
||||
cacheKeysForPR=$(gh actions-cache list -R $REPO -B $BRANCH -L 100 | cut -f 1 )
|
||||
|
||||
## Setting this to not fail the workflow while deleting cache keys.
|
||||
set +e
|
||||
echo "Deleting caches..."
|
||||
for cacheKey in $cacheKeysForPR
|
||||
do
|
||||
gh actions-cache delete $cacheKey -R $REPO -B $BRANCH --confirm
|
||||
done
|
||||
echo "Done"
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
REPO: ${{ github.repository }}
|
||||
BRANCH: refs/pull/${{ github.event.pull_request.number }}/merge
|
||||
@@ -0,0 +1,80 @@
|
||||
name: frontend
|
||||
|
||||
on:
|
||||
# Triggers the workflow on push or pull request events but only for the "devel" branch
|
||||
pull_request:
|
||||
branches: ["devel"]
|
||||
paths:
|
||||
- "webapp/**" # monitor for the frontend changes
|
||||
|
||||
# Allows you to run this workflow manually from the Actions tab
|
||||
workflow_dispatch:
|
||||
|
||||
# Allows you to reuse workflows by referencing their YAML files
|
||||
workflow_call:
|
||||
|
||||
jobs:
|
||||
build_and_test:
|
||||
runs-on: ubuntu-latest
|
||||
defaults:
|
||||
run:
|
||||
working-directory: ./webapp
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: "18"
|
||||
|
||||
- name: Get yarn cache directory path
|
||||
id: yarn-cache-dir-path
|
||||
run: echo "dir=$(yarn cache dir)" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: restore yarn cache
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: ${{ steps.yarn-cache-dir-path.outputs.dir }}
|
||||
key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-yarn-
|
||||
|
||||
- name: restore node_modules
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: "**/node_modules"
|
||||
key: ${{ runner.os }}-node_modules-${{ hashFiles('**/yarn.lock') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-node_modules-
|
||||
|
||||
- name: restore typescript cache
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: "**/packages/*/dist"
|
||||
key: ${{ runner.os }}-dist-${{ hashFiles('**/yarn.lock') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-dist-
|
||||
|
||||
- name: yarn install
|
||||
uses: borales/actions-yarn@v4
|
||||
with:
|
||||
dir: webapp
|
||||
cmd: install
|
||||
|
||||
- name: yarn lerna bootstrap
|
||||
uses: borales/actions-yarn@v4
|
||||
with:
|
||||
dir: webapp
|
||||
cmd: lerna bootstrap
|
||||
|
||||
- name: build
|
||||
uses: borales/actions-yarn@v4
|
||||
with:
|
||||
dir: webapp/packages/product-default
|
||||
cmd: bundle
|
||||
|
||||
- name: test
|
||||
uses: borales/actions-yarn@v4
|
||||
with:
|
||||
dir: webapp
|
||||
cmd: lerna run test
|
||||
@@ -1,32 +0,0 @@
|
||||
name: Run Jest Tests
|
||||
|
||||
# Controls when the workflow will run
|
||||
on:
|
||||
# Triggers the workflow on push or pull request events but only for the "devel" branch
|
||||
pull_request:
|
||||
branches: [ "devel" ]
|
||||
paths:
|
||||
- 'webapp/**' # monitor for the frontend changes
|
||||
|
||||
# Allows you to run this workflow manually from the Actions tab
|
||||
workflow_dispatch:
|
||||
|
||||
# A workflow run is made up of one or more jobs that can run sequentially or in parallel
|
||||
jobs:
|
||||
# This workflow contains a single job called "build"
|
||||
run-jest-tests:
|
||||
# The type of runner that the job will run on
|
||||
runs-on: ubuntu-latest
|
||||
defaults:
|
||||
run:
|
||||
working-directory: ./webapp
|
||||
# Steps represent a sequence of tasks that will be executed as part of the job
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: '18'
|
||||
- run: npm install yarn -g
|
||||
- run: yarn
|
||||
- run: yarn lerna bootstrap
|
||||
- run: yarn lerna run test
|
||||
@@ -1,4 +1,4 @@
|
||||
name: "Commit Message Validation"
|
||||
name: validation
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
@@ -11,7 +11,7 @@ on:
|
||||
- edited
|
||||
- ready_for_review
|
||||
- labeled
|
||||
|
||||
|
||||
jobs:
|
||||
validate-commit-message:
|
||||
uses: dbeaver/dbeaver/.github/workflows/reused-commit-msgs-validator.yml@devel
|
||||
commit-message:
|
||||
uses: dbeaver/dbeaver/.github/workflows/reused-commit-msgs-validator.yml@devel
|
||||
Vendored
+2
-1
@@ -19,6 +19,7 @@
|
||||
"VisualStudioExptTeam.vscodeintellicode",
|
||||
"yzhang.markdown-all-in-one",
|
||||
"GraphQL.vscode-graphql-syntax",
|
||||
"mrmlnc.vscode-json5"
|
||||
"mrmlnc.vscode-json5",
|
||||
"esbenp.prettier-vscode"
|
||||
]
|
||||
}
|
||||
|
||||
Vendored
+22
-9
@@ -4,9 +4,19 @@
|
||||
{
|
||||
"type": "chrome",
|
||||
"request": "launch",
|
||||
"name": "Debug CE",
|
||||
"name": "Chrome",
|
||||
"url": "http://127.0.0.1:3100",
|
||||
"webRoot": "${workspaceFolder}/webapp/packages/product-default"
|
||||
"webRoot": "${workspaceFolder}/..",
|
||||
"sourceMaps": true,
|
||||
"sourceMapPathOverrides": {
|
||||
"webpack:///*": "${workspaceFolder}/../*"
|
||||
},
|
||||
"skipFiles": [
|
||||
"<node_internals>/**",
|
||||
"**/node_modules/**",
|
||||
"${workspaceFolder}/webapp/**/node_modules/**/*.js",
|
||||
"${workspaceFolder}/webapp/**/dist/**/*.js"
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "java",
|
||||
@@ -14,21 +24,21 @@
|
||||
"cwd": "${workspaceFolder}/../opt/cbce",
|
||||
"request": "launch",
|
||||
"mainClass": "org.eclipse.equinox.launcher.Main",
|
||||
"classPaths": [
|
||||
"${env:DBEAVER_DEVEL_ECLIPSE_PATH}/plugins/org.eclipse.equinox.launcher_1.6.400.v20210924-0641.jar"
|
||||
],
|
||||
"windows": {
|
||||
"type": "java",
|
||||
"name": "CloudBeaver CE",
|
||||
"request": "launch",
|
||||
"mainClass": "org.eclipse.equinox.launcher.Main",
|
||||
"classPaths": [
|
||||
"${workspaceFolder}/../eclipse/eclipse/plugins/org.eclipse.equinox.launcher_1.6.500.v20230717-2134.jar"
|
||||
],
|
||||
"args": [
|
||||
"-product",
|
||||
"io.cloudbeaver.product.ce.product",
|
||||
"-configuration",
|
||||
"file:${env:DBEAVER_DEVEL_ECLIPSE_WORKSPACE_PATH}/.metadata/.plugins/org.eclipse.pde.core/CloudbeaverServer.product/",
|
||||
"file:${workspaceFolder}/../eclipse/workspace/.metadata/.plugins/org.eclipse.pde.core/CloudbeaverServer.product/",
|
||||
"-dev",
|
||||
"file:${env:DBEAVER_DEVEL_ECLIPSE_WORKSPACE_PATH}/.metadata/.plugins/org.eclipse.pde.core/CloudbeaverServer.product/dev.properties",
|
||||
"file:${workspaceFolder}/../eclipse/workspace/.metadata/.plugins/org.eclipse.pde.core/CloudbeaverServer.product/dev.properties",
|
||||
"-os",
|
||||
"win32",
|
||||
"-ws",
|
||||
@@ -54,13 +64,16 @@
|
||||
"name": "CloudBeaver CE",
|
||||
"request": "launch",
|
||||
"mainClass": "org.eclipse.equinox.launcher.Main",
|
||||
"classPaths": [
|
||||
"${workspaceFolder}/../eclipse/Eclipse.app/Contents/Eclipse/plugins/org.eclipse.equinox.launcher_1.6.500.v20230717-2134.jar"
|
||||
],
|
||||
"args": [
|
||||
"-product",
|
||||
"io.cloudbeaver.product.ce.product",
|
||||
"-configuration",
|
||||
"file:${env:DBEAVER_DEVEL_ECLIPSE_WORKSPACE_PATH}/.metadata/.plugins/org.eclipse.pde.core/CloudbeaverServer.product/",
|
||||
"file:${workspaceFolder}/../eclipse/workspace/.metadata/.plugins/org.eclipse.pde.core/CloudbeaverServer.product/",
|
||||
"-dev",
|
||||
"file:${env:DBEAVER_DEVEL_ECLIPSE_WORKSPACE_PATH}/.metadata/.plugins/org.eclipse.pde.core/CloudbeaverServer.product/dev.properties",
|
||||
"file:${workspaceFolder}/../eclipse/workspace/.metadata/.plugins/org.eclipse.pde.core/CloudbeaverServer.product/dev.properties",
|
||||
"-os",
|
||||
"macosx",
|
||||
"-ws",
|
||||
|
||||
Vendored
+2
-1
@@ -1,6 +1,7 @@
|
||||
{
|
||||
"editor.tabSize": 2,
|
||||
"editor.formatOnSave": true,
|
||||
"editor.linkedEditing": true,
|
||||
|
||||
"task.autoDetect": "off",
|
||||
|
||||
@@ -8,6 +9,7 @@
|
||||
"java.jdt.ls.vmargs": "-XX:+UseParallelGC -XX:GCTimeRatio=4 -XX:AdaptiveSizePolicyWeight=90 -Dsun.zip.disableMemoryMapping=true -Xmx16G -Xms100m -Xlog:disable",
|
||||
"java.compile.nullAnalysis.mode": "automatic",
|
||||
|
||||
"typescript.preferences.useAliasesForRenames": false,
|
||||
"typescript.preferences.importModuleSpecifier": "relative",
|
||||
"typescript.preferences.includePackageJsonAutoImports": "on",
|
||||
"typescript.preferences.quoteStyle": "single",
|
||||
@@ -20,7 +22,6 @@
|
||||
"typescript",
|
||||
"typescriptreact"
|
||||
],
|
||||
"eslint.packageManager": "yarn",
|
||||
"eslint.workingDirectories": ["webapp"],
|
||||
|
||||
"editor.defaultFormatter": "esbenp.prettier-vscode",
|
||||
|
||||
Vendored
+14
-37
@@ -13,21 +13,7 @@
|
||||
},
|
||||
"tasks": [
|
||||
{
|
||||
"label": "Generate dev proprties for CBCE",
|
||||
"type": "shell",
|
||||
"command": "mvn",
|
||||
"args": [
|
||||
"package",
|
||||
"-q",
|
||||
"exec:java",
|
||||
"-Dexec.args=-productFile ${workspaceFolder}/server/product/web-server/CloudbeaverServer.product -projectsFolder ${workspaceFolder}/../ -eclipse ${env:DBEAVER_DEVEL_ECLIPSE_PATH} -output ${env:DBEAVER_DEVEL_ECLIPSE_WORKSPACE_PATH}/.metadata/.plugins/org.eclipse.pde.core/CloudbeaverServer.product"
|
||||
],
|
||||
"options": {
|
||||
"cwd": "${workspaceFolder}/../dbeaver-packaging/tools/eclipse-plugins-resolver"
|
||||
}
|
||||
},
|
||||
{
|
||||
"label": "Run Tests",
|
||||
"label": "Run Tests CE",
|
||||
"type": "shell",
|
||||
"command": "yarn",
|
||||
"group": "test",
|
||||
@@ -35,25 +21,27 @@
|
||||
},
|
||||
{
|
||||
"label": "Run DevServer CE",
|
||||
"group": "build",
|
||||
"isBackground": true,
|
||||
"type": "shell",
|
||||
"command": "yarn",
|
||||
"args": [
|
||||
"dev",
|
||||
"--",
|
||||
"--env",
|
||||
"server=${input:stage.ce}",
|
||||
"--host",
|
||||
"0.0.0.0"
|
||||
],
|
||||
"isBackground": true,
|
||||
"args": ["dev"],
|
||||
"options": {
|
||||
"env": {
|
||||
"server": "${input:stage.ce}"
|
||||
},
|
||||
"cwd": "${workspaceFolder}/webapp/packages/product-default"
|
||||
}
|
||||
},
|
||||
{
|
||||
"label": "Build CE",
|
||||
"type": "shell",
|
||||
"command": "./build-sqlite.bat",
|
||||
"windows": {
|
||||
"command": "./build-sqlite.bat"
|
||||
},
|
||||
"osx": {
|
||||
"command": "./build-sqlite.sh"
|
||||
},
|
||||
"options": {
|
||||
"cwd": "${workspaceFolder}/deploy"
|
||||
},
|
||||
@@ -65,19 +53,8 @@
|
||||
{
|
||||
"label": "Yarn Install",
|
||||
"type": "shell",
|
||||
"command": "yarn",
|
||||
"command": "yarn install; yarn lerna bootstrap",
|
||||
"args": []
|
||||
},
|
||||
{
|
||||
"label": "Lerna Bootstrap",
|
||||
"type": "shell",
|
||||
"command": "yarn",
|
||||
"args": ["lerna", "bootstrap"]
|
||||
},
|
||||
{
|
||||
"label": "Bootstrap",
|
||||
"dependsOrder": "sequence",
|
||||
"dependsOn": ["Yarn Install", "Lerna Bootstrap"]
|
||||
}
|
||||
],
|
||||
"inputs": [
|
||||
|
||||
@@ -22,27 +22,57 @@ You can see live demo of CloudBeaver here: https://demo.cloudbeaver.io
|
||||
|
||||
## Changelog
|
||||
|
||||
### CloudBeaver 23.1.0 - 2023-06-05
|
||||
### CloudBeaver 23.2.3 - 2023-10-23
|
||||
|
||||
Changes since 23.0.0
|
||||
- The SSL option is available for establishing a connection in SQL Server;
|
||||
- Added the ability to edit binary values in a table;
|
||||
- Different bug fixes and enhancements have been made.
|
||||
|
||||
- Data viewer:
|
||||
- New grouping panel menu was added in the Data Viewer. This panel extracts unique values from the database column for count. Users can drag and drop the column to the grouping panel and get the results immediately. Sorting, filtering and exporting of the results are available on the Grouping panel.
|
||||
### CloudBeaver 23.2.2 - 2023-10-09
|
||||
|
||||
- The 'Save credentials' checkbox has been removed from a template creating form as credentials are not stored in templates;
|
||||
- Different bug fixes and enhancements have been made.
|
||||
|
||||
### CloudBeaver 23.2.1 - 2023-09-25
|
||||
|
||||
- The Output tab has been implemented, which includes warnings, info and notices generated by the database when executing user queries;
|
||||
- Scrollbars have been made theme-independent, resolving interface issues with light theme;
|
||||
- Fixed an issue in the SQL editor where it was not possible to switch the active schema when working with Oracle databases;
|
||||
- Different bug fixes and enhancements have been made.
|
||||
|
||||
|
||||
23.2.0 - 2023-09-04
|
||||
|
||||
- Performance:
|
||||
- Pagination has been added to the Navigation tree and metadata viewer, allowing working with more than 1000 database items;
|
||||
- Access Management:
|
||||
- Access permissions can be specified for pre-configured connections via the configuration file;
|
||||
- Reverse Proxy authentication now includes the option to provide users' First name and Last name;
|
||||
- Authorization:
|
||||
- The SSL option is now available for establishing connections in MySQL, PostgreSQL, Clickhouse;
|
||||
- Connections:
|
||||
- Connections are consistently displayed now when they are pre-configured into the workspace in the Global Configuration json file
|
||||
- Navigation tree:
|
||||
- The filters for the Navigation tree now allow hiding and showing schemas within the interface.
|
||||
- The search request considers file names and excludes the .sql file extension for now;
|
||||
- Data Editor:
|
||||
- The ability to rearrange columns in the Data Editor has been added;
|
||||
- The ability to use custom functions in the Grouping panel has been added;
|
||||
- Administration:
|
||||
- New Settings panel displays the product configuration settings such as Minimum fetch size, Maximum fetch size, and Default fetch size from the Data Editor;
|
||||
- "Connection Management" tab has been renamed to "Connection Templates" - now only connection templates are displayed;
|
||||
- SQL Editor:
|
||||
- We improved the performance of the SQL-editor - as a result, handling scripts with up to 10 000 lines does not present any challenges;
|
||||
- In the SQL-editor, pressing Tab/Space followed by Enter now causes the cursor to move to a new line;
|
||||
- In the SQL editor, when the cursor goes back on the query, the previous hints are displayed;
|
||||
- Error when running SQL with semicolon has been fixed.
|
||||
- Connections:
|
||||
- If there is an error in saving the data, the tab for the chosen connection dialog will stay open to allow corrections;
|
||||
- The URL-configuration for PostgreSQL now correctly displays only a single database.
|
||||
- Users can simultaneously edit resources for now, allowing them to work together;
|
||||
- Support for displaying tables with nested arrays of objects has been added;
|
||||
- The ability to compress export files allowed to increase in download speed, especially for larger files;
|
||||
- UX in the search bar has been improved - users can delete a query or request by clicking on the cross icon;
|
||||
- Driver management:
|
||||
- The CE version now offers the updated sqlite-jdbc driver, version 3.41.2;
|
||||
- CloudBeaver has the option to connect to H2 database version 2;
|
||||
- The internal CloudBeaver database is upgraded to the newest H2 version 2 to avoid vulnerability issues. The database will be safely upgraded automatically for the servers with default configurations. You can perform this upgrade manually if you have a custom configuration for this database in your infrastructure.
|
||||
- Connections:
|
||||
- Option to increase the maximum size of text files displayed in the value panel (using the sqlTextPreviewMaxLength parameter) has been added;
|
||||
- Support for custom logging configuration has been added. An external configuration file can be used instead of the default configuration.
|
||||
- Custom settings have been added to the interface for the following dialogue connections: SQLite, DB2, SQL Server, MySQL, Oracle and PostgreSQL;
|
||||
- Trino driver has been updated;
|
||||
- Security:
|
||||
- Password autofill functionality was removed;
|
||||
- The "eye" icon for password fields has been removed - the passwords entered into fields will not be displayed in the interface for now;
|
||||
- Many small bug fixes, enhancements, and improvements have been made
|
||||
|
||||
|
||||
### Old CloudBeaver releases
|
||||
|
||||
+6
-6
@@ -5,13 +5,13 @@
|
||||
Use this section to tell people about which versions of your project are
|
||||
currently being supported with security updates.
|
||||
|
||||
| Version | Supported |
|
||||
| ------- | ------------------|
|
||||
| 21.x | yes |
|
||||
| 22.0 | yes |
|
||||
| Version | Supported |
|
||||
| ------- | --------- |
|
||||
| 22.x | yes |
|
||||
| 23.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 devops@dbeaver.com.
|
||||
You will receive a response from us within 48 hours.
|
||||
If the issue is confirmed, we will release a patch as soon as possible depending on complexity but historically within a few days.
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
<dependency>
|
||||
<groupId>com.h2database</groupId>
|
||||
<artifactId>h2</artifactId>
|
||||
<version>1.4.199</version>
|
||||
<version>2.2.220</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
<build>
|
||||
|
||||
@@ -6,6 +6,8 @@
|
||||
contentRoot: "web",
|
||||
driversLocation: "drivers",
|
||||
|
||||
sslConfigurationPath:"${CLOUDBEAVER_SSL_CONF_PATH:workspace/.data/ssl-config.xml}",
|
||||
|
||||
rootURI: "/",
|
||||
serviceURI: "/api/",
|
||||
|
||||
|
||||
Regular → Executable
+1
-1
@@ -57,7 +57,7 @@ cd ..\cloudbeaver\webapp
|
||||
|
||||
call yarn
|
||||
call yarn lerna bootstrap
|
||||
call yarn lerna run build --no-bail --stream --scope=@cloudbeaver/product-default &::-- -- --env source-map
|
||||
call yarn lerna run bundle --no-bail --stream --scope=@cloudbeaver/product-default &::-- -- --env source-map
|
||||
|
||||
cd ..\deploy
|
||||
|
||||
|
||||
+1
-1
@@ -57,7 +57,7 @@ cd ../cloudbeaver/webapp
|
||||
|
||||
yarn
|
||||
yarn lerna run bootstrap
|
||||
yarn lerna run build --no-bail --stream --scope=@cloudbeaver/product-default #-- -- --env source-map
|
||||
yarn lerna run bundle --no-bail --stream --scope=@cloudbeaver/product-default #-- -- --env source-map
|
||||
if [[ "$?" -ne 0 ]] ; then
|
||||
echo 'Application build failed'; exit $rc
|
||||
fi
|
||||
|
||||
@@ -3,6 +3,6 @@ FROM eclipse-temurin:17.0.4.1_1-jre-focal
|
||||
COPY cloudbeaver /opt/cloudbeaver
|
||||
|
||||
EXPOSE 8978
|
||||
|
||||
RUN find /opt/cloudbeaver -type d -exec chmod 775 {} \;
|
||||
WORKDIR /opt/cloudbeaver/
|
||||
ENTRYPOINT ["./run-server.sh"]
|
||||
|
||||
@@ -10,4 +10,38 @@ IF NOT EXIST workspace\.metadata (
|
||||
)
|
||||
)
|
||||
|
||||
java %JAVA_OPTS% --add-modules=ALL-SYSTEM --add-opens=java.base/java.io=ALL-UNNAMED --add-opens=java.base/java.lang=ALL-UNNAMED --add-opens=java.base/java.lang.reflect=ALL-UNNAMED --add-opens=java.base/java.net=ALL-UNNAMED --add-opens=java.base/java.nio=ALL-UNNAMED --add-opens=java.base/java.nio.charset=ALL-UNNAMED --add-opens=java.base/java.text=ALL-UNNAMED --add-opens=java.base/java.time=ALL-UNNAMED --add-opens=java.base/java.util=ALL-UNNAMED --add-opens=java.base/java.util.concurrent=ALL-UNNAMED --add-opens=java.base/java.util.concurrent.atomic=ALL-UNNAMED --add-opens=java.base/jdk.internal.vm=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 -jar %launcherJar% -product io.cloudbeaver.product.ce.product -web-config conf/cloudbeaver.conf -nl en -registryMultiLanguage
|
||||
SET VMARGS_OPTS = ""
|
||||
If Not Defined JAVA_OPTS (
|
||||
SET VMARGS_OPTS = "-Xmx2048M"
|
||||
)
|
||||
|
||||
java %JAVA_OPTS% ^
|
||||
-Dfile.encoding=UTF-8 ^
|
||||
--add-modules=ALL-SYSTEM ^
|
||||
--add-opens=java.base/java.io=ALL-UNNAMED ^
|
||||
--add-opens=java.base/java.lang=ALL-UNNAMED ^
|
||||
--add-opens=java.base/java.lang.reflect=ALL-UNNAMED ^
|
||||
--add-opens=java.base/java.net=ALL-UNNAMED ^
|
||||
--add-opens=java.base/java.nio=ALL-UNNAMED ^
|
||||
--add-opens=java.base/java.nio.charset=ALL-UNNAMED ^
|
||||
--add-opens=java.base/java.text=ALL-UNNAMED ^
|
||||
--add-opens=java.base/java.time=ALL-UNNAMED ^
|
||||
--add-opens=java.base/java.util=ALL-UNNAMED ^
|
||||
--add-opens=java.base/java.util.concurrent=ALL-UNNAMED ^
|
||||
--add-opens=java.base/java.util.concurrent.atomic=ALL-UNNAMED ^
|
||||
--add-opens=java.base/jdk.internal.vm=ALL-UNNAMED ^
|
||||
--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 ^
|
||||
--add-opens=java.base/java.util.concurrent.atomic=ALL-UNNAMED ^
|
||||
--add-opens=java.sql/java.sql=ALL-UNNAMED ^
|
||||
-jar %launcherJar% ^
|
||||
-product io.cloudbeaver.product.ce.product ^
|
||||
-data %workspacePath% ^
|
||||
-web-config conf/cloudbeaver.conf ^
|
||||
-nl en ^
|
||||
-registryMultiLanguage
|
||||
|
||||
@@ -4,6 +4,37 @@ launcherJar=( server/plugins/org.eclipse.equinox.launcher*.jar )
|
||||
|
||||
echo "Starting Cloudbeaver Server"
|
||||
|
||||
[ ! -d "workspace/.metadata" ] && mkdir -p workspace/.metadata && mkdir -p workspace/GlobalConfiguration/.dbeaver && [ ! -f "workspace/GlobalConfiguration/.dbeaver/data-sources.json" ] && cp conf/initial-data-sources.conf workspace/GlobalConfiguration/.dbeaver/data-sources.json
|
||||
[ ! -d "workspace/.metadata" ] && mkdir -p workspace/.metadata \
|
||||
&& mkdir -p workspace/GlobalConfiguration/.dbeaver \
|
||||
&& [ ! -f "workspace/GlobalConfiguration/.dbeaver/data-sources.json" ] \
|
||||
&& cp conf/initial-data-sources.conf workspace/GlobalConfiguration/.dbeaver/data-sources.json
|
||||
|
||||
java ${JAVA_OPTS} --add-modules=ALL-SYSTEM --add-opens=java.base/java.io=ALL-UNNAMED --add-opens=java.base/java.lang=ALL-UNNAMED --add-opens=java.base/java.lang.reflect=ALL-UNNAMED --add-opens=java.base/java.net=ALL-UNNAMED --add-opens=java.base/java.nio=ALL-UNNAMED --add-opens=java.base/java.nio.charset=ALL-UNNAMED --add-opens=java.base/java.text=ALL-UNNAMED --add-opens=java.base/java.time=ALL-UNNAMED --add-opens=java.base/java.util=ALL-UNNAMED --add-opens=java.base/java.util.concurrent=ALL-UNNAMED --add-opens=java.base/java.util.concurrent.atomic=ALL-UNNAMED --add-opens=java.base/jdk.internal.vm=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 -jar ${launcherJar} -product io.cloudbeaver.product.ce.product -web-config conf/cloudbeaver.conf -nl en -registryMultiLanguage
|
||||
java ${JAVA_OPTS} \
|
||||
-Dfile.encoding=UTF-8 \
|
||||
--add-modules=ALL-SYSTEM \
|
||||
--add-opens=java.base/java.io=ALL-UNNAMED \
|
||||
--add-opens=java.base/java.lang=ALL-UNNAMED \
|
||||
--add-opens=java.base/java.lang.reflect=ALL-UNNAMED \
|
||||
--add-opens=java.base/java.net=ALL-UNNAMED \
|
||||
--add-opens=java.base/java.nio=ALL-UNNAMED \
|
||||
--add-opens=java.base/java.nio.charset=ALL-UNNAMED \
|
||||
--add-opens=java.base/java.text=ALL-UNNAMED \
|
||||
--add-opens=java.base/java.time=ALL-UNNAMED \
|
||||
--add-opens=java.base/java.util=ALL-UNNAMED \
|
||||
--add-opens=java.base/java.util.concurrent=ALL-UNNAMED \
|
||||
--add-opens=java.base/java.util.concurrent.atomic=ALL-UNNAMED \
|
||||
--add-opens=java.base/jdk.internal.vm=ALL-UNNAMED \
|
||||
--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 \
|
||||
--add-opens=java.base/java.util.concurrent.atomic=ALL-UNNAMED \
|
||||
--add-opens=java.sql/java.sql=ALL-UNNAMED \
|
||||
-jar ${launcherJar} \
|
||||
-product io.cloudbeaver.product.ce.product \
|
||||
-web-config conf/cloudbeaver.conf \
|
||||
-nl en \
|
||||
-registryMultiLanguage
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
<?pde version="3.8"?>
|
||||
<target name="org.jkiss.tp.cloudbeaver">
|
||||
<locations>
|
||||
<location path="${env_var:DBEAVER_DEVEL_ECLIPSE_PATH}/plugins" type="Directory"/>
|
||||
<location path="../eclipse/Eclipse.app/Contents/Eclipse/plugins" type="Directory"/>
|
||||
<location path="../eclipse/eclipse/plugins" type="Directory"/>
|
||||
</locations>
|
||||
</target>
|
||||
@@ -0,0 +1,9 @@
|
||||
projects:
|
||||
schema:
|
||||
include: ./**/*.graphqls
|
||||
schema: "gql.ce"
|
||||
extensions:
|
||||
endpoints:
|
||||
Default GraphQL Endpoint:
|
||||
url: http://localhost:8080/api/gql
|
||||
introspect: false
|
||||
@@ -1,33 +1,37 @@
|
||||
Manifest-Version: 1.0
|
||||
Bundle-ManifestVersion: 2
|
||||
Bundle-Vendor: %Bundle-Vendor
|
||||
Bundle-Name: %Bundle-Name
|
||||
Bundle-Vendor: DBeaver Corp
|
||||
Bundle-Name: Cloudbeaver Web Model
|
||||
Bundle-SymbolicName: io.cloudbeaver.model;singleton:=true
|
||||
Bundle-Version: 1.0.31.qualifier
|
||||
Bundle-Release-Date: 20230626
|
||||
Bundle-RequiredExecutionEnvironment: JavaSE-11
|
||||
Bundle-Version: 1.0.40.qualifier
|
||||
Bundle-Release-Date: 20231106
|
||||
Bundle-RequiredExecutionEnvironment: JavaSE-17
|
||||
Bundle-ActivationPolicy: lazy
|
||||
Bundle-ClassPath: .
|
||||
Require-Bundle: org.jkiss.dbeaver.data.gis;visibility:=reexport,
|
||||
Require-Bundle: org.jkiss.dbeaver.data.gis;visibility:=reexport,
|
||||
org.jkiss.dbeaver.model;visibility:=reexport,
|
||||
org.jkiss.dbeaver.model.sm;visibility:=reexport,
|
||||
org.jkiss.dbeaver.model.event;visibility:=reexport,
|
||||
org.jkiss.dbeaver.model.nio;visibility:=reexport,
|
||||
org.jkiss.dbeaver.registry;visibility:=reexport,
|
||||
org.jkiss.dbeaver.net.ssh,
|
||||
org.jkiss.bundle.graphql.java;visibility:=reexport,
|
||||
org.jkiss.bundle.apache.dbcp,
|
||||
com.google.gson;visibility:=reexport,
|
||||
jakarta.servlet-api;visibility:=reexport
|
||||
jakarta.servlet-api;bundle-version="[4.0.0,4.0.0]";visibility:=reexport
|
||||
Export-Package: io.cloudbeaver,
|
||||
io.cloudbeaver.auth,
|
||||
io.cloudbeaver.auth.provider,
|
||||
io.cloudbeaver.auth.provider.local,
|
||||
io.cloudbeaver.auth.provisioning,
|
||||
io.cloudbeaver.websocket,
|
||||
io.cloudbeaver.model,
|
||||
io.cloudbeaver.model.app,
|
||||
io.cloudbeaver.model.fs,
|
||||
io.cloudbeaver.model.rm,
|
||||
io.cloudbeaver.model.rm.local,
|
||||
io.cloudbeaver.model.rm.lock,
|
||||
io.cloudbeaver.model.rm.fs.nio,
|
||||
io.cloudbeaver.model.session,
|
||||
io.cloudbeaver.model.user,
|
||||
io.cloudbeaver.registry,
|
||||
|
||||
@@ -1,2 +0,0 @@
|
||||
Bundle-Vendor = DBeaver Corp
|
||||
Bundle-Name = Cloudbeaver Web Model
|
||||
@@ -3,5 +3,4 @@ output.. = target/classes/
|
||||
bin.includes = .,\
|
||||
META-INF/,\
|
||||
plugin.xml,\
|
||||
schema/,\
|
||||
OSGI-INF/
|
||||
schema/
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
<relativePath>../</relativePath>
|
||||
</parent>
|
||||
<artifactId>io.cloudbeaver.model</artifactId>
|
||||
<version>1.0.31-SNAPSHOT</version>
|
||||
<version>1.0.40-SNAPSHOT</version>
|
||||
<packaging>eclipse-plugin</packaging>
|
||||
|
||||
</project>
|
||||
|
||||
@@ -16,14 +16,15 @@
|
||||
*/
|
||||
package io.cloudbeaver;
|
||||
|
||||
import io.cloudbeaver.model.rm.RMUtils;
|
||||
import org.eclipse.core.resources.IProject;
|
||||
import org.jkiss.code.NotNull;
|
||||
import org.jkiss.code.Nullable;
|
||||
import org.jkiss.dbeaver.model.app.DBPWorkspace;
|
||||
import org.jkiss.dbeaver.model.auth.SMSessionContext;
|
||||
import org.jkiss.dbeaver.model.rm.RMController;
|
||||
import org.jkiss.dbeaver.model.rm.RMControllerProvider;
|
||||
import org.jkiss.dbeaver.model.rm.RMProject;
|
||||
import org.jkiss.dbeaver.model.rm.RMUtils;
|
||||
import org.jkiss.dbeaver.registry.BaseProjectImpl;
|
||||
import org.jkiss.utils.CommonUtils;
|
||||
import org.jkiss.utils.Pair;
|
||||
@@ -31,7 +32,7 @@ import org.jkiss.utils.Pair;
|
||||
import java.nio.file.Path;
|
||||
import java.util.Collection;
|
||||
|
||||
public class BaseWebProjectImpl extends BaseProjectImpl {
|
||||
public class BaseWebProjectImpl extends BaseProjectImpl implements RMControllerProvider {
|
||||
|
||||
@NotNull
|
||||
private final RMProject project;
|
||||
@@ -61,6 +62,12 @@ public class BaseWebProjectImpl extends BaseProjectImpl {
|
||||
return resourceController;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public RMProject getRMProject() {
|
||||
return project;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isVirtual() {
|
||||
return true;
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
package io.cloudbeaver;
|
||||
|
||||
import org.jkiss.dbeaver.model.access.DBAPermissionRealm;
|
||||
import org.jkiss.dbeaver.model.rm.RMConstants;
|
||||
|
||||
/**
|
||||
* General constants
|
||||
@@ -25,7 +26,7 @@ public class DBWConstants {
|
||||
|
||||
public static final String PERMISSION_ADMIN = DBAPermissionRealm.PERMISSION_ADMIN;
|
||||
|
||||
public static final String PERMISSION_CONFIGURATION_MANAGER = "configuration-manager";
|
||||
public static final String PERMISSION_CONFIGURATION_MANAGER = RMConstants.PERMISSION_CONFIGURATION_MANAGER;
|
||||
public static final String PERMISSION_PRIVATE_PROJECT_ACCESS = "private-project-access";
|
||||
|
||||
public static final String PERMISSION_EDIT_STRUCTURE = "edit-meta";
|
||||
@@ -33,9 +34,6 @@ public class DBWConstants {
|
||||
|
||||
public static final String STATE_ATTR_SIGN_IN_STATE = "state.signin";
|
||||
|
||||
public static final String USER_PROJECTS_FOLDER = "user-projects";
|
||||
public static final String SHARED_PROJECTS_FOLDER = "shared-projects";
|
||||
|
||||
public enum SignInState {
|
||||
GLOBAL,
|
||||
EMBEDDED
|
||||
|
||||
@@ -144,14 +144,18 @@ public class DBWebException extends DBException implements GraphQLError {
|
||||
}
|
||||
|
||||
private static String makeMessage(String message, Throwable cause) {
|
||||
if (CommonUtils.isEmpty(message)) {
|
||||
if (cause != null) {
|
||||
if (cause.getMessage() != null) {
|
||||
return cause.getMessage();
|
||||
}
|
||||
return cause.getClass().getName();
|
||||
if (cause == null) {
|
||||
if (CommonUtils.isEmpty(message)) {
|
||||
return "Unknown internal error";
|
||||
} else {
|
||||
return message;
|
||||
}
|
||||
return "Unknown internal error";
|
||||
}
|
||||
if (CommonUtils.isEmpty(message)) {
|
||||
if (cause.getMessage() != null) {
|
||||
return cause.getMessage();
|
||||
}
|
||||
return cause.getClass().getName();
|
||||
}
|
||||
if (CommonUtils.equalObjects(message, cause.getMessage())) {
|
||||
return message;
|
||||
|
||||
+2
-2
@@ -208,8 +208,8 @@ public class WebDataSourceRegistryProxy implements DBPDataSourceRegistry, DataSo
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public DBWNetworkProfile getNetworkProfile(String name) {
|
||||
return dataSourceRegistry.getNetworkProfile(name);
|
||||
public DBWNetworkProfile getNetworkProfile(String source, String name) {
|
||||
return dataSourceRegistry.getNetworkProfile(source, name);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
/*
|
||||
* DBeaver - Universal Database Manager
|
||||
* Copyright (C) 2010-2023 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 org.jkiss.dbeaver.model.DBPPage;
|
||||
import org.jkiss.dbeaver.model.data.json.JSONUtils;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
public class WebPage extends DBPPage {
|
||||
public WebPage(Map<String, Object> params) {
|
||||
super(JSONUtils.getInteger(params, "offset"), JSONUtils.getInteger(params, "limit"));
|
||||
}
|
||||
}
|
||||
@@ -27,7 +27,7 @@ import org.jkiss.dbeaver.model.rm.RMProject;
|
||||
import org.jkiss.dbeaver.registry.rm.DataSourceRegistryRM;
|
||||
import org.jkiss.dbeaver.runtime.DBWorkbench;
|
||||
|
||||
public class WebProjectImpl extends BaseWebProjectImpl {
|
||||
public abstract class WebProjectImpl extends BaseWebProjectImpl {
|
||||
private static final Log log = Log.getLog(WebProjectImpl.class);
|
||||
|
||||
public WebProjectImpl(
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
/*
|
||||
* DBeaver - Universal Database Manager
|
||||
* Copyright (C) 2010-2023 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 io.cloudbeaver.model.session.WebSession;
|
||||
import org.jkiss.code.NotNull;
|
||||
import org.jkiss.code.Nullable;
|
||||
import org.jkiss.dbeaver.model.navigator.DBNModel;
|
||||
import org.jkiss.dbeaver.model.rm.RMProject;
|
||||
|
||||
public class WebSessionProjectImpl extends WebProjectImpl {
|
||||
|
||||
private final WebSession webSession;
|
||||
|
||||
public WebSessionProjectImpl(@NotNull WebSession webSession, @NotNull RMProject project, @NotNull DataSourceFilter dataSourceFilter) {
|
||||
super(webSession.getWorkspace(), webSession.getRmController(), webSession.getSessionContext(), project, dataSourceFilter);
|
||||
this.webSession = webSession;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public DBNModel getNavigatorModel() {
|
||||
return webSession.getNavigatorModel();
|
||||
}
|
||||
}
|
||||
+2
-1
@@ -19,6 +19,7 @@ package io.cloudbeaver.auth;
|
||||
import org.jkiss.code.NotNull;
|
||||
import org.jkiss.dbeaver.DBException;
|
||||
import org.jkiss.dbeaver.model.runtime.DBRProgressMonitor;
|
||||
import org.jkiss.dbeaver.model.security.SMAuthProviderCustomConfiguration;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
@@ -31,7 +32,7 @@ public interface SMAuthProviderAssigner {
|
||||
@NotNull
|
||||
SMAutoAssign detectAutoAssignments(
|
||||
@NotNull DBRProgressMonitor monitor,
|
||||
@NotNull Map<String, Object> providerConfig,
|
||||
@NotNull SMAuthProviderCustomConfiguration providerConfig,
|
||||
@NotNull Map<String, Object> authParameters
|
||||
) throws DBException;
|
||||
|
||||
|
||||
+3
-2
@@ -26,6 +26,7 @@ import org.jkiss.dbeaver.model.DBPObject;
|
||||
import org.jkiss.dbeaver.model.auth.SMAuthProvider;
|
||||
import org.jkiss.dbeaver.model.auth.SMSession;
|
||||
import org.jkiss.dbeaver.model.runtime.DBRProgressMonitor;
|
||||
import org.jkiss.dbeaver.model.security.SMAuthProviderCustomConfiguration;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
@@ -41,7 +42,7 @@ public interface SMAuthProviderExternal<AUTH_SESSION extends SMSession> extends
|
||||
@NotNull
|
||||
default Map<String, Object> authExternalUser(
|
||||
@NotNull DBRProgressMonitor monitor,
|
||||
@NotNull Map<String, Object> providerConfig, // Auth provider configuration (e.g. 3rd party auth server address)
|
||||
@Nullable SMAuthProviderCustomConfiguration providerConfig, // Auth provider configuration (e.g. 3rd party auth server address)
|
||||
@NotNull Map<String, Object> authParameters // Passed auth parameters (e.g. user name or password)
|
||||
) throws DBException {
|
||||
return authParameters;
|
||||
@@ -51,7 +52,7 @@ public interface SMAuthProviderExternal<AUTH_SESSION extends SMSession> extends
|
||||
@NotNull
|
||||
DBWUserIdentity getUserIdentity(
|
||||
@NotNull DBRProgressMonitor monitor,
|
||||
@NotNull Map<String, Object> providerConfig,
|
||||
@Nullable SMAuthProviderCustomConfiguration customConfiguration,
|
||||
@NotNull Map<String, Object> authParameters) throws DBException;
|
||||
|
||||
/**
|
||||
|
||||
+34
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
* DBeaver - Universal Database Manager
|
||||
* Copyright (C) 2010-2023 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.auth.provisioning;
|
||||
|
||||
import io.cloudbeaver.model.session.WebSession;
|
||||
import org.jkiss.code.NotNull;
|
||||
import org.jkiss.dbeaver.DBException;
|
||||
import org.jkiss.dbeaver.model.security.SMAuthProviderCustomConfiguration;
|
||||
import org.jkiss.dbeaver.model.security.user.SMUserProvisioning;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public interface SMProvisioner {
|
||||
@NotNull
|
||||
List<SMUserProvisioning> listExternalUsers(
|
||||
@NotNull WebSession webSession,
|
||||
@NotNull SMAuthProviderCustomConfiguration customConfiguration,
|
||||
@NotNull SMProvisioningFilter filter
|
||||
) throws DBException;
|
||||
}
|
||||
+28
@@ -0,0 +1,28 @@
|
||||
package io.cloudbeaver.auth.provisioning;
|
||||
|
||||
import org.jkiss.code.Nullable;
|
||||
import org.jkiss.dbeaver.model.data.json.JSONUtils;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
public class SMProvisioningFilter {
|
||||
@Nullable
|
||||
private final Integer offset;
|
||||
@Nullable
|
||||
private final Integer limit;
|
||||
|
||||
public SMProvisioningFilter(Map<String, Object> params) {
|
||||
this.offset = JSONUtils.getInteger(params, "offset");
|
||||
this.limit = JSONUtils.getInteger(params, "limit");
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public Integer getOffset() {
|
||||
return offset;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public Integer getLimit() {
|
||||
return limit;
|
||||
}
|
||||
}
|
||||
+29
-4
@@ -25,6 +25,7 @@ import io.cloudbeaver.utils.CBModelConstants;
|
||||
import io.cloudbeaver.utils.WebAppUtils;
|
||||
import io.cloudbeaver.utils.WebCommonUtils;
|
||||
import org.jkiss.dbeaver.DBException;
|
||||
import org.jkiss.dbeaver.Log;
|
||||
import org.jkiss.dbeaver.model.DBConstants;
|
||||
import org.jkiss.dbeaver.model.DBPDataSource;
|
||||
import org.jkiss.dbeaver.model.DBPDataSourceContainer;
|
||||
@@ -40,6 +41,7 @@ import org.jkiss.dbeaver.model.navigator.DBNDataSource;
|
||||
import org.jkiss.dbeaver.model.preferences.DBPPropertyDescriptor;
|
||||
import org.jkiss.dbeaver.model.preferences.DBPPropertySource;
|
||||
import org.jkiss.dbeaver.model.rm.RMProjectPermission;
|
||||
import org.jkiss.dbeaver.model.runtime.DBRRunnableParametrized;
|
||||
import org.jkiss.dbeaver.runtime.DBWorkbench;
|
||||
import org.jkiss.utils.CommonUtils;
|
||||
|
||||
@@ -52,7 +54,8 @@ import java.util.stream.Collectors;
|
||||
*/
|
||||
public class WebConnectionInfo {
|
||||
|
||||
private static final String SECURED_VALUE = "********";
|
||||
private static final Log log = Log.getLog(WebConnectionInfo.class);
|
||||
public static final String SECURED_VALUE = "********";
|
||||
private final WebSession session;
|
||||
private final DBPDataSourceContainer dataSourceContainer;
|
||||
private WebServerError connectError;
|
||||
@@ -63,6 +66,7 @@ public class WebConnectionInfo {
|
||||
|
||||
private transient Map<String, Object> savedAuthProperties;
|
||||
private transient List<WebNetworkHandlerConfigInput> savedNetworkCredentials;
|
||||
private transient List<DBRRunnableParametrized<WebConnectionInfo>> closeListeners = null;
|
||||
|
||||
public WebConnectionInfo(WebSession session, DBPDataSourceContainer ds) {
|
||||
this.session = session;
|
||||
@@ -322,9 +326,8 @@ public class WebConnectionInfo {
|
||||
return new WebPropertyInfo[0];
|
||||
}
|
||||
|
||||
// Fill session and user provided credentials
|
||||
// Fill user provided credentials
|
||||
DBPConnectionConfiguration configWithAuth = new DBPConnectionConfiguration(dataSourceContainer.getConnectionConfiguration());
|
||||
session.provideAuthParameters(session.getProgressMonitor(), dataSourceContainer, configWithAuth);
|
||||
|
||||
// show all properties if it is a manual connection
|
||||
Predicate<DBPPropertyDescriptor> predicate = CommonUtils.isEmpty(getRequiredAuth())
|
||||
@@ -362,9 +365,23 @@ public class WebConnectionInfo {
|
||||
this.savedNetworkCredentials = networkCredentials;
|
||||
}
|
||||
|
||||
public void clearSavedCredentials() {
|
||||
public void clearCache() {
|
||||
this.savedAuthProperties = null;
|
||||
this.savedNetworkCredentials = null;
|
||||
this.fireCloseListeners();
|
||||
}
|
||||
|
||||
public void fireCloseListeners() {
|
||||
if (closeListeners != null) {
|
||||
for (DBRRunnableParametrized<WebConnectionInfo> listener : closeListeners) {
|
||||
try {
|
||||
listener.run(this);
|
||||
} catch (Exception e) {
|
||||
log.debug(e);
|
||||
}
|
||||
}
|
||||
closeListeners = null;
|
||||
}
|
||||
}
|
||||
|
||||
@Property
|
||||
@@ -418,4 +435,12 @@ public class WebConnectionInfo {
|
||||
return appConfig.isShowReadOnlyConnectionInfo();
|
||||
|
||||
}
|
||||
|
||||
public void addCloseListener(DBRRunnableParametrized<WebConnectionInfo> listener) {
|
||||
if (closeListeners == null) {
|
||||
closeListeners = new ArrayList<>();
|
||||
}
|
||||
closeListeners.add(listener);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+21
-1
@@ -18,10 +18,17 @@ package io.cloudbeaver.model;
|
||||
|
||||
import org.jkiss.code.NotNull;
|
||||
import org.jkiss.dbeaver.model.net.DBWHandlerConfiguration;
|
||||
import org.jkiss.dbeaver.model.net.DBWHandlerDescriptor;
|
||||
import org.jkiss.dbeaver.model.net.DBWHandlerType;
|
||||
import org.jkiss.dbeaver.model.net.ssh.SSHConstants;
|
||||
import org.jkiss.dbeaver.model.preferences.DBPPropertyDescriptor;
|
||||
import org.jkiss.dbeaver.model.preferences.DBPPropertySource;
|
||||
import org.jkiss.dbeaver.model.runtime.VoidProgressMonitor;
|
||||
import org.jkiss.dbeaver.registry.RegistryConstants;
|
||||
import org.jkiss.dbeaver.runtime.properties.PropertySourceMap;
|
||||
import org.jkiss.utils.CommonUtils;
|
||||
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
@@ -52,6 +59,7 @@ public class WebNetworkHandlerConfig {
|
||||
return configuration.isEnabled();
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public SSHConstants.AuthType getAuthType() {
|
||||
return CommonUtils.valueOf(SSHConstants.AuthType.class, configuration.getStringProperty(SSHConstants.PROP_AUTH_TYPE), SSHConstants.AuthType.PASSWORD);
|
||||
}
|
||||
@@ -64,7 +72,7 @@ public class WebNetworkHandlerConfig {
|
||||
return CommonUtils.isEmpty(configuration.getPassword()) ? null : "";
|
||||
}
|
||||
|
||||
|
||||
@Deprecated // use secure properties
|
||||
public String getKey() {
|
||||
return CommonUtils.isEmpty(configuration.getSecureProperty(SSHConstants.PROP_KEY_VALUE)) ? null : "";
|
||||
}
|
||||
@@ -78,4 +86,16 @@ public class WebNetworkHandlerConfig {
|
||||
return configuration.getProperties();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public Map<String, String> getSecureProperties() {
|
||||
Map<String, String> secureProperties = new LinkedHashMap<>(configuration.getSecureProperties());
|
||||
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);
|
||||
}
|
||||
}
|
||||
return secureProperties;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+14
@@ -20,6 +20,7 @@ import org.jkiss.dbeaver.model.data.json.JSONUtils;
|
||||
import org.jkiss.dbeaver.model.net.DBWHandlerType;
|
||||
import org.jkiss.utils.CommonUtils;
|
||||
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
@@ -61,6 +62,7 @@ public class WebNetworkHandlerConfigInput {
|
||||
return JSONUtils.getString(cfg, "password");
|
||||
}
|
||||
|
||||
@Deprecated // use secured properties
|
||||
public String getKey() {
|
||||
return JSONUtils.getString(cfg, "key");
|
||||
}
|
||||
@@ -77,4 +79,16 @@ public class WebNetworkHandlerConfigInput {
|
||||
return JSONUtils.getObjectOrNull(cfg, "properties");
|
||||
}
|
||||
|
||||
public Map<String, String> getSecureProperties() {
|
||||
var secureProperties = JSONUtils.getObjectOrNull(cfg, "secureProperties");
|
||||
if (secureProperties == null) {
|
||||
return null;
|
||||
}
|
||||
var result = new LinkedHashMap<String, String>();
|
||||
for (Map.Entry<String, Object> property : secureProperties.entrySet()) {
|
||||
result.put(property.getKey(), CommonUtils.toString(property.getValue()));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -88,6 +88,11 @@ public class WebPropertyInfo {
|
||||
}
|
||||
}
|
||||
|
||||
@Property
|
||||
public String getHint() {
|
||||
return property.getHint();
|
||||
}
|
||||
|
||||
@Property
|
||||
public int getOrder() {
|
||||
return property instanceof ObjectPropertyDescriptor ? ((ObjectPropertyDescriptor) property).getOrderNumber() : -1;
|
||||
@@ -116,7 +121,8 @@ public class WebPropertyInfo {
|
||||
|
||||
@Property
|
||||
public Object getDefaultValue() throws DBException {
|
||||
return property.getDefaultValue();
|
||||
var defaultValue = property.getDefaultValue();
|
||||
return defaultValue == null ? getValue() : defaultValue;
|
||||
}
|
||||
|
||||
@Property
|
||||
|
||||
@@ -17,26 +17,17 @@
|
||||
package io.cloudbeaver.model;
|
||||
|
||||
import io.cloudbeaver.utils.CBModelConstants;
|
||||
import org.jkiss.dbeaver.model.websocket.event.MessageType;
|
||||
|
||||
import java.io.PrintWriter;
|
||||
import java.io.StringWriter;
|
||||
import java.time.Instant;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.ZoneId;
|
||||
|
||||
/**
|
||||
* Web server message
|
||||
*/
|
||||
public class WebServerMessage {
|
||||
|
||||
|
||||
public enum MessageType {
|
||||
DEBUG,
|
||||
INFO,
|
||||
WARNING,
|
||||
ERROR
|
||||
}
|
||||
|
||||
private final MessageType type;
|
||||
private final Instant time;
|
||||
private final String message;
|
||||
|
||||
+7
-6
@@ -18,6 +18,7 @@ package io.cloudbeaver.model.app;
|
||||
|
||||
import io.cloudbeaver.DataSourceFilter;
|
||||
import io.cloudbeaver.WebProjectImpl;
|
||||
import io.cloudbeaver.WebSessionProjectImpl;
|
||||
import io.cloudbeaver.model.log.SLF4JLogHandler;
|
||||
import io.cloudbeaver.model.session.WebSession;
|
||||
import io.cloudbeaver.server.WebGlobalWorkspace;
|
||||
@@ -69,8 +70,10 @@ public abstract class BaseWebApplication extends BaseApplicationImpl implements
|
||||
}
|
||||
|
||||
@Override
|
||||
public RMController createResourceController(@NotNull SMCredentialsProvider credentialsProvider,
|
||||
@NotNull DBPWorkspace workspace) throws DBException {
|
||||
public RMController createResourceController(
|
||||
@NotNull SMCredentialsProvider credentialsProvider,
|
||||
@NotNull DBPWorkspace workspace
|
||||
) throws DBException {
|
||||
throw new IllegalStateException("Resource controller is not supported by " + getClass().getSimpleName());
|
||||
}
|
||||
|
||||
@@ -173,10 +176,8 @@ public abstract class BaseWebApplication extends BaseApplicationImpl implements
|
||||
@NotNull RMProject project,
|
||||
@NotNull DataSourceFilter dataSourceFilter
|
||||
) {
|
||||
return new WebProjectImpl(
|
||||
webSession.getWorkspace(),
|
||||
webSession.getRmController(),
|
||||
webSession.getSessionContext(),
|
||||
return new WebSessionProjectImpl(
|
||||
webSession,
|
||||
project,
|
||||
dataSourceFilter
|
||||
);
|
||||
|
||||
+6
-3
@@ -53,7 +53,8 @@ public interface WebApplication extends DBPApplication {
|
||||
WebProjectImpl createProjectImpl(
|
||||
@NotNull WebSession webSession,
|
||||
@NotNull RMProject project,
|
||||
@NotNull DataSourceFilter dataSourceFilter);
|
||||
@NotNull DataSourceFilter dataSourceFilter
|
||||
);
|
||||
|
||||
SMController createSecurityController(@NotNull SMCredentialsProvider credentialsProvider) throws DBException;
|
||||
|
||||
@@ -61,8 +62,10 @@ public interface WebApplication extends DBPApplication {
|
||||
|
||||
DBSSecretController getSecretController(@NotNull SMCredentialsProvider credentialsProvider) throws DBException;
|
||||
|
||||
RMController createResourceController(@NotNull SMCredentialsProvider credentialsProvider,
|
||||
@NotNull DBPWorkspace workspace) throws DBException;
|
||||
RMController createResourceController(
|
||||
@NotNull SMCredentialsProvider credentialsProvider,
|
||||
@NotNull DBPWorkspace workspace
|
||||
) throws DBException;
|
||||
|
||||
DBFileController createFileController(@NotNull SMCredentialsProvider credentialsProvider);
|
||||
|
||||
|
||||
+2
@@ -16,6 +16,7 @@
|
||||
*/
|
||||
package io.cloudbeaver.model.app;
|
||||
|
||||
import org.jkiss.code.Nullable;
|
||||
import org.jkiss.dbeaver.model.security.SMAuthProviderCustomConfiguration;
|
||||
|
||||
import java.util.Set;
|
||||
@@ -33,5 +34,6 @@ public interface WebAuthConfiguration {
|
||||
|
||||
Set<SMAuthProviderCustomConfiguration> getAuthCustomConfigurations();
|
||||
|
||||
@Nullable
|
||||
SMAuthProviderCustomConfiguration getAuthProviderConfiguration(String configId);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
* DBeaver - Universal Database Manager
|
||||
* Copyright (C) 2010-2023 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.fs;
|
||||
|
||||
import org.jkiss.code.NotNull;
|
||||
import org.jkiss.dbeaver.model.fs.DBFVirtualFileSystem;
|
||||
|
||||
public class FSUtils {
|
||||
@NotNull
|
||||
public static String makeUniqueFsId(@NotNull DBFVirtualFileSystem fileSystem) {
|
||||
return fileSystem.getType() + ":" + fileSystem.getId();
|
||||
}
|
||||
}
|
||||
+2
-2
@@ -19,7 +19,7 @@ public abstract class DBNAbstractResourceManagerNode extends DBNNode implements
|
||||
super(parentNode);
|
||||
}
|
||||
|
||||
public void removeChildResourceNode(@NotNull Queue<RMResource> resourcePath) {
|
||||
public void removeChildResourceNode(@NotNull Queue<String> resourcePath) {
|
||||
if (children == null || resourcePath.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
@@ -49,7 +49,7 @@ public abstract class DBNAbstractResourceManagerNode extends DBNNode implements
|
||||
return;
|
||||
}
|
||||
var expectedResource = resourcePath.poll();
|
||||
var node = RMNavigatorUtils.findResourceNode(children, expectedResource);
|
||||
var node = RMNavigatorUtils.findResourceNode(children, expectedResource.getName());
|
||||
if (node == null) { // we are in expected parent node
|
||||
DBNResourceManagerResource newResourceNode = new DBNResourceManagerResource(this, expectedResource);
|
||||
children = ArrayUtils.add(DBNResourceManagerResource.class, children, newResourceNode);
|
||||
|
||||
+1
-1
@@ -101,7 +101,7 @@ public class DBNResourceManagerProject extends DBNAbstractResourceManagerNode {
|
||||
return children;
|
||||
}
|
||||
|
||||
public RMController getResourceController() {
|
||||
protected RMController getResourceController() {
|
||||
return ((DBNResourceManagerRoot) getParentNode()).getResourceController();
|
||||
}
|
||||
|
||||
|
||||
+31
-6
@@ -20,6 +20,7 @@ package io.cloudbeaver.model.rm;
|
||||
import io.cloudbeaver.model.session.WebSession;
|
||||
import org.jkiss.code.NotNull;
|
||||
import org.jkiss.dbeaver.DBException;
|
||||
import org.jkiss.dbeaver.Log;
|
||||
import org.jkiss.dbeaver.model.DBPHiddenObject;
|
||||
import org.jkiss.dbeaver.model.DBPImage;
|
||||
import org.jkiss.dbeaver.model.app.DBPProject;
|
||||
@@ -29,11 +30,14 @@ import org.jkiss.dbeaver.model.navigator.DBNRoot;
|
||||
import org.jkiss.dbeaver.model.rm.*;
|
||||
import org.jkiss.dbeaver.model.runtime.DBRProgressMonitor;
|
||||
import org.jkiss.utils.ArrayUtils;
|
||||
import org.jkiss.utils.CommonUtils;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
public class DBNResourceManagerRoot extends DBNNode implements DBPHiddenObject, RMEventListener {
|
||||
|
||||
private static final Log log = Log.getLog(DBNResourceManagerRoot.class);
|
||||
|
||||
private DBNResourceManagerProject[] projects;
|
||||
private RMController resourceController;
|
||||
|
||||
@@ -74,6 +78,9 @@ public class DBNResourceManagerRoot extends DBNNode implements DBPHiddenObject,
|
||||
public DBNResourceManagerProject[] getChildren(DBRProgressMonitor monitor) throws DBException {
|
||||
if (projects == null) {
|
||||
List<? extends DBPProject> projectList = getParentNode().getModel().getModelProjects();
|
||||
if (CommonUtils.isEmpty(projectList)) {
|
||||
return new DBNResourceManagerProject[0];
|
||||
}
|
||||
SMSession session = null;
|
||||
for (DBPProject project : projectList) {
|
||||
session = getParentNode().getModel().getModelAuthContext().getSpaceSession(monitor, project.getWorkspace(), false);
|
||||
@@ -123,10 +130,10 @@ public class DBNResourceManagerRoot extends DBNNode implements DBPHiddenObject,
|
||||
var action = event.getAction();
|
||||
switch (action) {
|
||||
case RESOURCE_DELETE:
|
||||
deleteResourceNode(event.getProject(), event.getResourceTree());
|
||||
deleteResourceNode(event.getProject(), event.getResourcePath());
|
||||
break;
|
||||
case RESOURCE_ADD:
|
||||
addResourceNode(event.getProject(), event.getResourceTree());
|
||||
addResourceNode(event.getProject(), event.getResourcePath());
|
||||
break;
|
||||
case PROJECT_ADD:
|
||||
addProjectNode(event.getProject());
|
||||
@@ -134,9 +141,19 @@ public class DBNResourceManagerRoot extends DBNNode implements DBPHiddenObject,
|
||||
}
|
||||
}
|
||||
|
||||
private void deleteResourceNode(RMProject project, List<RMResource> resourcePath) {
|
||||
@Override
|
||||
protected void dispose(boolean reflect) {
|
||||
RMEventManager.removeEventListener(this);
|
||||
super.dispose(reflect);
|
||||
}
|
||||
|
||||
private void deleteResourceNode(RMProject project, String resourcePath) {
|
||||
if (resourcePath == null) {
|
||||
return;
|
||||
}
|
||||
var projectNode = getProjectNode(project);
|
||||
projectNode.ifPresent(dbnResourceManagerProject -> dbnResourceManagerProject.removeChildResourceNode(new ArrayDeque<>(resourcePath)));
|
||||
var rmResourcePath = Arrays.asList(resourcePath.split("/"));
|
||||
projectNode.ifPresent(dbnResourceManagerProject -> dbnResourceManagerProject.removeChildResourceNode(new ArrayDeque<>(rmResourcePath)));
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@@ -146,9 +163,17 @@ public class DBNResourceManagerRoot extends DBNNode implements DBPHiddenObject,
|
||||
.findFirst();
|
||||
}
|
||||
|
||||
private void addResourceNode(RMProject project, List<RMResource> resourcePath) {
|
||||
private void addResourceNode(RMProject project, String resourcePath) {
|
||||
if (resourcePath == null) {
|
||||
return;
|
||||
}
|
||||
var projectNode = getProjectNode(project);
|
||||
projectNode.ifPresent(dbnResourceManagerProject -> dbnResourceManagerProject.addChildResourceNode(new ArrayDeque<>(resourcePath)));
|
||||
try {
|
||||
var rmResourcePath = Arrays.asList(getResourceController().getResourcePath(project.getId(), resourcePath));
|
||||
projectNode.ifPresent(dbnResourceManagerProject -> dbnResourceManagerProject.addChildResourceNode(new ArrayDeque<>(rmResourcePath)));
|
||||
} catch (DBException e) {
|
||||
log.error("Cannot add new node to resource manager tree", e);
|
||||
}
|
||||
}
|
||||
|
||||
private void addProjectNode(RMProject project) {
|
||||
|
||||
+2
-2
@@ -3,11 +3,11 @@ package io.cloudbeaver.model.rm;
|
||||
import org.jkiss.dbeaver.model.rm.RMResource;
|
||||
|
||||
class RMNavigatorUtils {
|
||||
public static DBNResourceManagerResource findResourceNode(DBNResourceManagerResource[] resourceNodes, RMResource expectedResource) {
|
||||
public static DBNResourceManagerResource findResourceNode(DBNResourceManagerResource[] resourceNodes, String expectedResource) {
|
||||
DBNResourceManagerResource node = null;
|
||||
for (DBNResourceManagerResource resourceNode : resourceNodes) {
|
||||
var nodeResource = resourceNode.getResource();
|
||||
if (nodeResource.getName().equals(expectedResource.getName()) && nodeResource.isFolder() == expectedResource.isFolder()) {
|
||||
if (nodeResource.getName().equals(expectedResource)) {
|
||||
node = resourceNode;
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -1,103 +0,0 @@
|
||||
/*
|
||||
* DBeaver - Universal Database Manager
|
||||
* Copyright (C) 2010-2023 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;
|
||||
|
||||
import io.cloudbeaver.DBWConstants;
|
||||
import io.cloudbeaver.model.app.BaseWebApplication;
|
||||
import org.jkiss.code.NotNull;
|
||||
import org.jkiss.dbeaver.DBException;
|
||||
import org.jkiss.dbeaver.model.rm.RMProject;
|
||||
import org.jkiss.dbeaver.model.rm.RMProjectPermission;
|
||||
import org.jkiss.dbeaver.model.rm.RMProjectType;
|
||||
import org.jkiss.dbeaver.runtime.DBWorkbench;
|
||||
import org.jkiss.utils.CommonUtils;
|
||||
|
||||
import java.nio.file.Path;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class RMUtils {
|
||||
|
||||
public static Path getRootPath() {
|
||||
return DBWorkbench.getPlatform().getWorkspace().getAbsolutePath();
|
||||
}
|
||||
|
||||
public static Path getUserProjectsPath() {
|
||||
return getRootPath().resolve(DBWConstants.USER_PROJECTS_FOLDER);
|
||||
}
|
||||
|
||||
public static Path getSharedProjectsPath() {
|
||||
return getRootPath().resolve(DBWConstants.SHARED_PROJECTS_FOLDER);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static Path getProjectPath(RMProject project) {
|
||||
switch (project.getType()) {
|
||||
case GLOBAL:
|
||||
return getRootPath().resolve(BaseWebApplication.getInstance().getDefaultProjectName());
|
||||
case SHARED:
|
||||
return getSharedProjectsPath().resolve(project.getName());
|
||||
default:
|
||||
return getUserProjectsPath().resolve(project.getName());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Different types of project have different location on the workspace. Method returns path to the project.
|
||||
*
|
||||
* @param projectId project name or other identifier
|
||||
* @return path to the project based on the name of projects and prefixes that it contains
|
||||
*/
|
||||
@NotNull
|
||||
public static Path getProjectPathByName(@NotNull String projectId) throws DBException {
|
||||
int divPos = projectId.indexOf("_");
|
||||
if (divPos <= 0) {
|
||||
throw new DBException("Bad project ID");
|
||||
}
|
||||
|
||||
String prefix = projectId.substring(0, divPos);
|
||||
String projectName = projectId.substring(divPos + 1);
|
||||
switch (RMProjectType.getByPrefix(prefix)) {
|
||||
case GLOBAL:
|
||||
String defaultProjectName = BaseWebApplication.getInstance().getDefaultProjectName();
|
||||
if (CommonUtils.isEmpty(defaultProjectName)) {
|
||||
throw new DBException("Global projects are not supported");
|
||||
}
|
||||
return getRootPath().resolve(defaultProjectName);
|
||||
case SHARED:
|
||||
return getSharedProjectsPath().resolve(projectName);
|
||||
default:
|
||||
return getUserProjectsPath().resolve(projectName);
|
||||
}
|
||||
}
|
||||
|
||||
public static Set<String> parseProjectPermissions(Set<String> permissions) {
|
||||
return permissions.stream()
|
||||
.map(RMProjectPermission::fromPermission).filter(Objects::nonNull)
|
||||
.flatMap(permission -> permission.getAllPermissions().stream())
|
||||
.collect(Collectors.toSet());
|
||||
}
|
||||
|
||||
public static RMProject createAnonymousProject() {
|
||||
RMProject project = new RMProject("anonymous");
|
||||
project.setId("anonymous");
|
||||
project.setType(RMProjectType.USER);
|
||||
project.setProjectPermissions(RMProjectPermission.DATA_SOURCES_EDIT.getAllPermissions().toArray(new String[0]));
|
||||
return project;
|
||||
}
|
||||
}
|
||||
+120
-40
@@ -19,7 +19,6 @@ package io.cloudbeaver.model.rm.local;
|
||||
import io.cloudbeaver.BaseWebProjectImpl;
|
||||
import io.cloudbeaver.DBWConstants;
|
||||
import io.cloudbeaver.model.app.WebApplication;
|
||||
import io.cloudbeaver.model.rm.RMUtils;
|
||||
import io.cloudbeaver.model.rm.lock.RMFileLockController;
|
||||
import io.cloudbeaver.service.security.SMUtils;
|
||||
import io.cloudbeaver.service.sql.WebSQLConstants;
|
||||
@@ -42,8 +41,11 @@ import org.jkiss.dbeaver.model.impl.auth.SessionContextImpl;
|
||||
import org.jkiss.dbeaver.model.rm.*;
|
||||
import org.jkiss.dbeaver.model.runtime.VoidProgressMonitor;
|
||||
import org.jkiss.dbeaver.model.security.SMController;
|
||||
import org.jkiss.dbeaver.model.security.SMObjects;
|
||||
import org.jkiss.dbeaver.model.security.SMObjectType;
|
||||
import org.jkiss.dbeaver.model.sql.DBQuotaException;
|
||||
import org.jkiss.dbeaver.model.websocket.event.MessageType;
|
||||
import org.jkiss.dbeaver.model.websocket.event.WSEventType;
|
||||
import org.jkiss.dbeaver.model.websocket.event.WSSessionLogUpdatedEvent;
|
||||
import org.jkiss.dbeaver.registry.*;
|
||||
import org.jkiss.dbeaver.runtime.DBWorkbench;
|
||||
import org.jkiss.utils.ArrayUtils;
|
||||
@@ -71,19 +73,18 @@ public class LocalResourceController implements RMController {
|
||||
|
||||
private static final String FILE_REGEX = "(?U)[\\w.$()@/\\\\ -]+";
|
||||
private static final String PROJECT_REGEX = "(?U)[\\w.$()@ -]+"; // slash not allowed in project name
|
||||
|
||||
public static final String DEFAULT_CHANGE_ID = "0";
|
||||
|
||||
private final DBPWorkspace workspace;
|
||||
private final SMCredentialsProvider credentialsProvider;
|
||||
protected final SMCredentialsProvider credentialsProvider;
|
||||
|
||||
private final Path rootPath;
|
||||
private final Path userProjectsPath;
|
||||
private final Path sharedProjectsPath;
|
||||
private final String globalProjectName;
|
||||
private Supplier<SMController> smControllerSupplier;
|
||||
private final RMFileLockController lockController;
|
||||
private final List<RMFileOperationHandler> fileHandlers;
|
||||
protected final RMFileLockController lockController;
|
||||
protected final List<RMFileOperationHandler> fileHandlers;
|
||||
|
||||
private final Map<String, BaseWebProjectImpl> projectRegistries = new LinkedHashMap<>();
|
||||
|
||||
@@ -121,7 +122,7 @@ public class LocalResourceController implements RMController {
|
||||
return userId == null ? null : this.userProjectsPath.resolve(userId);
|
||||
}
|
||||
|
||||
private BaseWebProjectImpl getWebProject(String projectId, boolean refresh) throws DBException {
|
||||
protected BaseWebProjectImpl getWebProject(String projectId, boolean refresh) throws DBException {
|
||||
synchronized (projectRegistries) {
|
||||
BaseWebProjectImpl project = projectRegistries.get(projectId);
|
||||
if (project == null || refresh) {
|
||||
@@ -185,7 +186,7 @@ public class LocalResourceController implements RMController {
|
||||
if (credentialsProvider.hasPermission(DBWConstants.PERMISSION_ADMIN) || credentialsProvider.hasPermission(RMConstants.PERMISSION_RM_ADMIN)) {
|
||||
return new ArrayList<>(Arrays.asList(listAllSharedProjects()));
|
||||
}
|
||||
var accessibleSharedProjects = getSecurityController().getAllAvailableObjectsPermissions(SMObjects.PROJECT);
|
||||
var accessibleSharedProjects = getSecurityController().getAllAvailableObjectsPermissions(SMObjectType.project);
|
||||
|
||||
return accessibleSharedProjects
|
||||
.stream()
|
||||
@@ -240,7 +241,7 @@ public class LocalResourceController implements RMController {
|
||||
}
|
||||
String[] permissions = getSecurityController().getObjectPermissions(activeUserCreds.getUserId(),
|
||||
projectId,
|
||||
SMObjects.PROJECT
|
||||
SMObjectType.project
|
||||
).getPermissions();
|
||||
return Arrays.stream(permissions).map(RMProjectPermission::fromPermission).collect(Collectors.toSet());
|
||||
}
|
||||
@@ -290,6 +291,7 @@ public class LocalResourceController implements RMController {
|
||||
throw new DBException("Project '" + name + "' not created");
|
||||
}
|
||||
try {
|
||||
log.debug("Creating project '" + project.getId() + "'");
|
||||
Files.createDirectories(projectPath);
|
||||
if (WebAppUtils.getWebApplication().isMultiNode()) {
|
||||
createResourceTypeFolders(projectPath);
|
||||
@@ -310,8 +312,12 @@ public class LocalResourceController implements RMController {
|
||||
throw new DBException("Project '" + project.getName() + "' doesn't exists");
|
||||
}
|
||||
try {
|
||||
log.debug("Deleting project '" + projectId + "'");
|
||||
IOUtils.deleteDirectory(targetPath);
|
||||
getSecurityController().deleteAllObjectPermissions(projectId, SMObjects.PROJECT);
|
||||
getSecurityController().deleteAllObjectPermissions(projectId, SMObjectType.project);
|
||||
synchronized (projectRegistries) {
|
||||
projectRegistries.remove(projectId);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
throw new DBException("Error deleting project '" + project.getName() + "'", e);
|
||||
}
|
||||
@@ -321,6 +327,9 @@ public class LocalResourceController implements RMController {
|
||||
@Override
|
||||
public RMProject getProject(@NotNull String projectId, boolean readResources, boolean readProperties) throws DBException {
|
||||
RMProject project = makeProjectFromId(projectId, true);
|
||||
if (project == null) {
|
||||
return null;
|
||||
}
|
||||
if (readResources) {
|
||||
doProjectOperation(projectId, () -> {
|
||||
project.setChildren(
|
||||
@@ -347,6 +356,7 @@ public class LocalResourceController implements RMController {
|
||||
BaseWebProjectImpl webProject = getWebProject(projectId, false);
|
||||
doFileWriteOperation(projectId, webProject.getMetadataFilePath(),
|
||||
() -> {
|
||||
log.debug("Updating value for property '" + propName + "' in project '" + projectId + "'");
|
||||
webProject.setProjectProperty(propName, propValue);
|
||||
return null;
|
||||
}
|
||||
@@ -356,12 +366,13 @@ public class LocalResourceController implements RMController {
|
||||
@Override
|
||||
public String getProjectsDataSources(@NotNull String projectId, @Nullable String[] dataSourceIds) throws DBException {
|
||||
DBPProject projectMetadata = getWebProject(projectId, false);
|
||||
DBPDataSourceRegistry registry = projectMetadata.getDataSourceRegistry();
|
||||
registry.checkForErrors();
|
||||
return doFileReadOperation(
|
||||
projectId,
|
||||
projectMetadata.getMetadataFolder(false),
|
||||
() -> {
|
||||
DBPDataSourceRegistry registry = projectMetadata.getDataSourceRegistry();
|
||||
registry.refreshConfig();
|
||||
registry.checkForErrors();
|
||||
DataSourceConfigurationManagerBuffer buffer = new DataSourceConfigurationManagerBuffer();
|
||||
Predicate<DBPDataSourceContainer> filter = null;
|
||||
if (!ArrayUtils.isEmpty(dataSourceIds)) {
|
||||
@@ -405,6 +416,7 @@ public class LocalResourceController implements RMController {
|
||||
false
|
||||
);
|
||||
registry.checkForErrors();
|
||||
log.debug("Save data sources configuration in project '" + projectId + "'");
|
||||
((DataSourcePersistentRegistry) registry).saveDataSources();
|
||||
registry.checkForErrors();
|
||||
return configChanged;
|
||||
@@ -424,6 +436,7 @@ public class LocalResourceController implements RMController {
|
||||
DBPDataSourceContainer dataSource = registry.getDataSource(dataSourceId);
|
||||
|
||||
if (dataSource != null) {
|
||||
log.debug("Deleting data source '" + dataSourceId + "' in project '" + projectId + "'");
|
||||
registry.removeDataSource(dataSource);
|
||||
} else {
|
||||
log.warn("Could not find datasource " + dataSourceId + " for deletion");
|
||||
@@ -440,6 +453,7 @@ public class LocalResourceController implements RMController {
|
||||
@NotNull String folderPath) throws DBException {
|
||||
try (var projectLock = lockController.lockProject(projectId, "createDatasourceFolder")) {
|
||||
DBPProject project = getWebProject(projectId, false);
|
||||
log.debug("Creating data source folder '" + folderPath + "' in project '" + projectId + "'");
|
||||
doFileWriteOperation(projectId, project.getMetadataFolder(false),
|
||||
() -> {
|
||||
DBPDataSourceRegistry registry = project.getDataSourceRegistry();
|
||||
@@ -469,6 +483,7 @@ public class LocalResourceController implements RMController {
|
||||
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");
|
||||
@@ -489,6 +504,7 @@ public class LocalResourceController implements RMController {
|
||||
) throws DBException {
|
||||
try (var projectLock = lockController.lockProject(projectId, "createDatasourceFolder")) {
|
||||
DBPProject project = getWebProject(projectId, false);
|
||||
log.debug("Moving data source folder from '" + oldPath + "' to '" + newPath + "' in project '" + projectId + "'");
|
||||
doFileWriteOperation(projectId, project.getMetadataFolder(false),
|
||||
() -> {
|
||||
DBPDataSourceRegistry registry = project.getDataSourceRegistry();
|
||||
@@ -567,6 +583,7 @@ public class LocalResourceController implements RMController {
|
||||
if (Files.exists(targetPath)) {
|
||||
throw new DBException("Resource '" + resourcePath + "' already exists");
|
||||
}
|
||||
log.debug("Creating resource '" + resourcePath + "' in project '" + projectId + "'");
|
||||
createFolder(targetPath.getParent());
|
||||
doFileWriteOperation(projectId, targetPath, () -> {
|
||||
try {
|
||||
@@ -596,10 +613,10 @@ public class LocalResourceController implements RMController {
|
||||
var normalizedOldResourcePath = CommonUtils.normalizeResourcePath(oldResourcePath);
|
||||
var normalizedNewResourcePath = CommonUtils.normalizeResourcePath(newResourcePath);
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug("Moving resource from '" + normalizedOldResourcePath + "' to '" + normalizedNewResourcePath + "'");
|
||||
log.debug("Moving resource from '" + normalizedOldResourcePath + "' to '" + normalizedNewResourcePath +
|
||||
"' in project '" + projectId + "'");
|
||||
}
|
||||
Path oldTargetPath = getTargetPath(projectId, normalizedOldResourcePath);
|
||||
List<RMResource> rmOldResourcePath = makeResourcePath(projectId, oldTargetPath, false);
|
||||
|
||||
doFileWriteOperation(projectId, oldTargetPath, () -> {
|
||||
if (!Files.exists(oldTargetPath)) {
|
||||
@@ -622,7 +639,7 @@ public class LocalResourceController implements RMController {
|
||||
return null;
|
||||
});
|
||||
|
||||
fireRmResourceDeleteEvent(projectId, rmOldResourcePath);
|
||||
fireRmResourceDeleteEvent(projectId, normalizedOldResourcePath);
|
||||
fireRmResourceAddEvent(projectId, normalizedNewResourcePath);
|
||||
}
|
||||
|
||||
@@ -636,10 +653,10 @@ public class LocalResourceController implements RMController {
|
||||
* Gathers the old-new properties paths pairs and updates properties via BaseProjectImpl#moveResourcePropertiesBatch()
|
||||
*/
|
||||
private void movePropertiesRecursive(
|
||||
@NotNull String projectId,
|
||||
@NotNull Path rootResourcePath,
|
||||
@NotNull String oldRootPropertiesPath,
|
||||
@NotNull String newRootPropertiesPath
|
||||
@NotNull String projectId,
|
||||
@NotNull Path rootResourcePath,
|
||||
@NotNull String oldRootPropertiesPath,
|
||||
@NotNull String newRootPropertiesPath
|
||||
) throws IOException, DBException {
|
||||
var project = getWebProject(projectId, false);
|
||||
var projectPath = getProjectPath(projectId);
|
||||
@@ -660,11 +677,10 @@ public class LocalResourceController implements RMController {
|
||||
public void deleteResource(@NotNull String projectId, @NotNull String resourcePath, boolean recursive) throws DBException {
|
||||
try (var projectLock = lockController.lockProject(projectId, "deleteResource")) {
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug("Removing resource from '" + resourcePath + "'" + (recursive ? " recursive" : ""));
|
||||
log.debug("Removing resource from '" + resourcePath + "' in project '" + projectId + "'" + (recursive ? " recursive" : ""));
|
||||
}
|
||||
validateResourcePath(resourcePath);
|
||||
Path targetPath = getTargetPath(projectId, resourcePath);
|
||||
List<RMResource> rmResourcePath = makeResourcePath(projectId, targetPath, recursive);
|
||||
doFileWriteOperation(projectId, targetPath, () -> {
|
||||
if (!Files.exists(targetPath)) {
|
||||
throw new DBException("Resource '" + resourcePath + "' doesn't exists");
|
||||
@@ -697,7 +713,7 @@ public class LocalResourceController implements RMController {
|
||||
});
|
||||
|
||||
log.debug("Fire resource delete event");
|
||||
fireRmResourceDeleteEvent(projectId, rmResourcePath);
|
||||
fireRmResourceDeleteEvent(projectId, resourcePath);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -717,6 +733,12 @@ public class LocalResourceController implements RMController {
|
||||
return makeResourcePath(projectId, getTargetPath(projectId, resourcePath), false).toArray(RMResource[]::new);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public RMResource getResource(@NotNull String projectId, @NotNull String resourcePath) throws DBException {
|
||||
return makeResourceFromPath(projectId, getTargetPath(projectId, resourcePath), null, false, false, false);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public byte[] getResourceContents(@NotNull String projectId, @NotNull String resourcePath) throws DBException {
|
||||
@@ -757,15 +779,16 @@ public class LocalResourceController implements RMController {
|
||||
}
|
||||
Path targetPath = getTargetPath(projectId, resourcePath);
|
||||
if (!forceOverwrite && Files.exists(targetPath)) {
|
||||
throw new DBException("Resource '" + resourcePath + "' exists");
|
||||
throw new DBException("Resource '" + IOUtils.getFileNameWithoutExtension(targetPath) + "' already exists");
|
||||
}
|
||||
|
||||
doFileWriteOperation(projectId, targetPath, () -> {
|
||||
createFolder(targetPath.getParent());
|
||||
try {
|
||||
log.debug("Writing data to resource '" + targetPath + " in project " + projectId + "'");
|
||||
Files.write(targetPath, data);
|
||||
} catch (IOException e) {
|
||||
throw new DBException("Error reading resource '" + resourcePath + "'", e);
|
||||
throw new DBException("Error writing resource '" + resourcePath + "'", e);
|
||||
}
|
||||
return null;
|
||||
});
|
||||
@@ -777,7 +800,7 @@ public class LocalResourceController implements RMController {
|
||||
return DEFAULT_CHANGE_ID;
|
||||
}
|
||||
|
||||
private void createFolder(Path targetPath) throws DBException {
|
||||
protected void createFolder(Path targetPath) throws DBException {
|
||||
if (!Files.exists(targetPath)) {
|
||||
try {
|
||||
Files.createDirectories(targetPath);
|
||||
@@ -800,6 +823,7 @@ public class LocalResourceController implements RMController {
|
||||
BaseWebProjectImpl webProject = getWebProject(projectId, false);
|
||||
doFileWriteOperation(projectId, webProject.getMetadataFilePath(),
|
||||
() -> {
|
||||
log.debug("Updating resource property '" + propertyName + "' in project '" + projectId + "'");
|
||||
webProject.setResourceProperty(resourcePath, propertyName, propertyValue);
|
||||
return null;
|
||||
}
|
||||
@@ -853,9 +877,21 @@ public class LocalResourceController implements RMController {
|
||||
return type.getPrefix() + "_" + projectName;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private RMProject makeProjectFromId(String projectId, boolean loadPermissions) throws DBException {
|
||||
var projectName = parseProjectName(projectId);
|
||||
var projectPath = getProjectPath(projectId);
|
||||
if (!Files.exists(projectPath)) {
|
||||
if (isPrivateProject(projectId) && isProjectOwner(projectId)) {
|
||||
try {
|
||||
Files.createDirectories(projectPath);
|
||||
} catch (Exception e) {
|
||||
throw new DBException("Failed to create project " + projectId + ": " + e.getMessage(), e);
|
||||
}
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
Set<RMProjectPermission> permissions = Set.of();
|
||||
if (loadPermissions && credentialsProvider.getActiveUserCredentials() != null) {
|
||||
permissions = getProjectPermissions(projectId, projectName.getType());
|
||||
@@ -923,35 +959,65 @@ public class LocalResourceController implements RMController {
|
||||
}
|
||||
}
|
||||
|
||||
private <T> T doProjectOperation(String projectId, RMFileOperation<T> operation) throws DBException {
|
||||
var projectPath = getProjectPath(projectId);
|
||||
protected <T> T doProjectOperation(String projectId, RMFileOperation<T> operation) throws DBException {
|
||||
for (RMFileOperationHandler fileHandler : fileHandlers) {
|
||||
fileHandler.projectOpened(projectPath);
|
||||
try {
|
||||
fileHandler.projectOpened(projectId);
|
||||
} catch (Exception e) {
|
||||
if (credentialsProvider.getActiveUserCredentials() != null) {
|
||||
WebAppUtils.getWebApplication().getEventController().addEvent(
|
||||
new WSSessionLogUpdatedEvent(
|
||||
WSEventType.SESSION_LOG_UPDATED,
|
||||
credentialsProvider.getActiveUserCredentials().getSmSessionId(),
|
||||
credentialsProvider.getActiveUserCredentials().getUserId(),
|
||||
MessageType.ERROR,
|
||||
e.getMessage()));
|
||||
}
|
||||
}
|
||||
}
|
||||
return operation.doOperation();
|
||||
}
|
||||
|
||||
private <T> T doFileReadOperation(String projectId, Path file, RMFileOperation<T> operation) throws DBException {
|
||||
var projectPath = getProjectPath(projectId);
|
||||
protected <T> T doFileReadOperation(String projectId, Path file, RMFileOperation<T> operation) throws DBException {
|
||||
for (RMFileOperationHandler fileHandler : fileHandlers) {
|
||||
fileHandler.beforeFileRead(projectPath, file);
|
||||
try {
|
||||
fileHandler.beforeFileRead(projectId, file);
|
||||
} catch (Exception e) {
|
||||
if (credentialsProvider.getActiveUserCredentials() != null) {
|
||||
WebAppUtils.getWebApplication().getEventController().addEvent(
|
||||
new WSSessionLogUpdatedEvent(
|
||||
WSEventType.SESSION_LOG_UPDATED,
|
||||
credentialsProvider.getActiveUserCredentials().getSmSessionId(),
|
||||
credentialsProvider.getActiveUserCredentials().getUserId(),
|
||||
MessageType.ERROR,
|
||||
e.getMessage()));
|
||||
}
|
||||
log.error("Error before file reading", e);
|
||||
}
|
||||
}
|
||||
return operation.doOperation();
|
||||
}
|
||||
|
||||
private <T> T doFileWriteOperation(String projectId, Path file, RMFileOperation<T> operation) throws DBException {
|
||||
var projectPath = getProjectPath(projectId);
|
||||
protected <T> T doFileWriteOperation(String projectId, Path file, RMFileOperation<T> operation) throws DBException {
|
||||
for (RMFileOperationHandler fileHandler : fileHandlers) {
|
||||
fileHandler.beforeFileChange(projectPath, file);
|
||||
fileHandler.beforeFileChange(projectId, file);
|
||||
}
|
||||
T result;
|
||||
try {
|
||||
result = operation.doOperation();
|
||||
} catch (Exception e) {
|
||||
for (RMFileOperationHandler fileHandler : fileHandlers) {
|
||||
fileHandler.handleFileChangeException(projectId, file, e);
|
||||
}
|
||||
throw e;
|
||||
}
|
||||
var result = operation.doOperation();
|
||||
for (RMFileOperationHandler fileHandler : fileHandlers) {
|
||||
fileHandler.afterFileChange(projectPath, file, credentialsProvider.getActiveUserCredentials());
|
||||
fileHandler.afterFileChange(projectId, file, credentialsProvider.getActiveUserCredentials());
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private Path getProjectPath(String projectId) throws DBException {
|
||||
protected Path getProjectPath(String projectId) throws DBException {
|
||||
RMProjectName project = parseProjectName(projectId);
|
||||
RMProjectType type = project.getType();
|
||||
String projectName = project.getName();
|
||||
@@ -1060,11 +1126,11 @@ public class LocalResourceController implements RMController {
|
||||
RMEventManager.fireEvent(
|
||||
new RMEvent(RMEvent.Action.RESOURCE_ADD,
|
||||
getProject(projectId, false, false),
|
||||
Arrays.asList(getResourcePath(projectId, resourcePath)))
|
||||
resourcePath)
|
||||
);
|
||||
}
|
||||
|
||||
private void fireRmResourceDeleteEvent(@NotNull String projectId, @NotNull List<RMResource> resourcePath) throws DBException {
|
||||
private void fireRmResourceDeleteEvent(@NotNull String projectId, @NotNull String resourcePath) throws DBException {
|
||||
RMEventManager.fireEvent(
|
||||
new RMEvent(RMEvent.Action.RESOURCE_DELETE,
|
||||
makeProjectFromId(projectId, false),
|
||||
@@ -1135,6 +1201,7 @@ public class LocalResourceController implements RMController {
|
||||
public static class RMProjectName {
|
||||
String prefix;
|
||||
String name;
|
||||
|
||||
private RMProjectName(String prefix, String name) {
|
||||
this.prefix = prefix;
|
||||
this.name = name;
|
||||
@@ -1179,9 +1246,22 @@ public class LocalResourceController implements RMController {
|
||||
return RMProjectType.GLOBAL.getPrefix().equals(rmProjectName.getPrefix());
|
||||
}
|
||||
|
||||
public static boolean isPrivateProject(String projectId, String userId) {
|
||||
public static boolean isPrivateProject(String projectId) {
|
||||
RMProjectName rmProjectName = parseProjectNameUnsafe(projectId);
|
||||
return RMProjectType.USER.getPrefix().equals(rmProjectName.getPrefix());
|
||||
}
|
||||
|
||||
private boolean isProjectOwner(String projectId) {
|
||||
var activeUserCredentials = credentialsProvider.getActiveUserCredentials();
|
||||
var userId = activeUserCredentials == null ? null : activeUserCredentials.getUserId();
|
||||
return isProjectOwner(projectId, userId);
|
||||
}
|
||||
|
||||
public static boolean isProjectOwner(String projectId, String userId) {
|
||||
RMProjectName rmProjectName = parseProjectNameUnsafe(projectId);
|
||||
return RMProjectType.USER.getPrefix().equals(rmProjectName.getPrefix()) &&
|
||||
rmProjectName.name.equals(userId);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
+13
-4
@@ -25,24 +25,33 @@ import java.nio.file.Path;
|
||||
|
||||
public interface RMFileOperationHandler {
|
||||
|
||||
void projectOpened(@NotNull Path projectPath) throws DBException;
|
||||
void projectOpened(@NotNull String projectId) throws DBException;
|
||||
|
||||
/**
|
||||
* Executed before reading file
|
||||
*/
|
||||
void beforeFileRead(@NotNull Path projectPath, @NotNull Path filePath) throws DBException;
|
||||
void beforeFileRead(@NotNull String projectId, @NotNull Path filePath) throws DBException;
|
||||
|
||||
/**
|
||||
* Executed before file modification
|
||||
*/
|
||||
void beforeFileChange(@NotNull Path projectPath, @NotNull Path filePath) throws DBException;
|
||||
void beforeFileChange(@NotNull String projectId, @NotNull Path filePath) throws DBException;
|
||||
|
||||
/**
|
||||
* Executed after file modification
|
||||
*/
|
||||
void afterFileChange(
|
||||
@NotNull Path projectPath,
|
||||
@NotNull String projectId,
|
||||
@NotNull Path filePath,
|
||||
@Nullable SMCredentials smCredentials
|
||||
) throws DBException;
|
||||
|
||||
/**
|
||||
* Executed if something goes wrong in file modification
|
||||
*/
|
||||
void handleFileChangeException(
|
||||
@NotNull String projectId,
|
||||
@NotNull Path filePath,
|
||||
@NotNull Throwable cause
|
||||
) throws DBException;
|
||||
}
|
||||
|
||||
+21
-1
@@ -18,6 +18,7 @@ package io.cloudbeaver.model.rm.lock;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import io.cloudbeaver.model.app.WebApplication;
|
||||
import org.jkiss.code.NotNull;
|
||||
import org.jkiss.code.Nullable;
|
||||
import org.jkiss.dbeaver.DBException;
|
||||
import org.jkiss.dbeaver.Log;
|
||||
@@ -71,7 +72,8 @@ public class RMFileLockController {
|
||||
* @param operationName - executed operation name
|
||||
* @return - lock
|
||||
*/
|
||||
public RMLock lockProject(String projectId, String operationName) throws DBException {
|
||||
@NotNull
|
||||
public RMLock lockProject(@NotNull String projectId,@NotNull String operationName) throws DBException {
|
||||
synchronized (RMFileLockController.class) {
|
||||
try {
|
||||
createLockFolderIfNeeded();
|
||||
@@ -91,6 +93,24 @@ public class RMFileLockController {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* if the project is already locked, the operation will be executed as a child of the first lock,
|
||||
* otherwise it creates its own lock.
|
||||
*
|
||||
* @param projectId - project to be locked
|
||||
* @param operationName - executed operation name
|
||||
* @return - lock
|
||||
*/
|
||||
@Nullable
|
||||
public RMLock lockIfNotLocked(@NotNull String projectId, @NotNull String operationName) throws DBException {
|
||||
synchronized (RMFileLockController.class) {
|
||||
if (isProjectLocked(projectId)) {
|
||||
return null;
|
||||
}
|
||||
return lockProject(projectId, operationName);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check that project locked
|
||||
*/
|
||||
|
||||
+18
-2
@@ -16,6 +16,7 @@
|
||||
*/
|
||||
package io.cloudbeaver.model.session;
|
||||
|
||||
import io.cloudbeaver.model.WebServerMessage;
|
||||
import io.cloudbeaver.model.app.WebApplication;
|
||||
import io.cloudbeaver.model.app.WebAuthApplication;
|
||||
import io.cloudbeaver.websocket.CBWebSessionEventHandler;
|
||||
@@ -29,6 +30,7 @@ import org.jkiss.dbeaver.model.auth.SMSessionContext;
|
||||
import org.jkiss.dbeaver.model.auth.impl.AbstractSessionPersistent;
|
||||
import org.jkiss.dbeaver.model.meta.Property;
|
||||
import org.jkiss.dbeaver.model.websocket.event.WSEvent;
|
||||
import org.jkiss.dbeaver.model.websocket.event.WSEventDeleteTempFile;
|
||||
import org.jkiss.dbeaver.model.websocket.event.session.WSSessionExpiredEvent;
|
||||
|
||||
import java.time.Instant;
|
||||
@@ -82,7 +84,7 @@ public abstract class BaseWebSession extends AbstractSessionPersistent {
|
||||
synchronized (sessionEventHandlers) {
|
||||
for (CBWebSessionEventHandler eventHandler : sessionEventHandlers) {
|
||||
try {
|
||||
eventHandler.handeWebSessionEvent(event);
|
||||
eventHandler.handleWebSessionEvent(event);
|
||||
} catch (DBException e) {
|
||||
log.error(e.getMessage(), e);
|
||||
addSessionError(e);
|
||||
@@ -109,6 +111,16 @@ public abstract class BaseWebSession extends AbstractSessionPersistent {
|
||||
return userContext.refresh(smAuthInfo);
|
||||
}
|
||||
|
||||
public synchronized void refreshUserData() {
|
||||
try {
|
||||
userContext.refreshPermissions();
|
||||
userContext.refreshAccessibleProjects();
|
||||
} catch (DBException e) {
|
||||
addSessionError(e);
|
||||
log.error("Error refreshing accessible projects", e);
|
||||
}
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public SMAuthSpace getSessionSpace() {
|
||||
@@ -155,10 +167,12 @@ public abstract class BaseWebSession extends AbstractSessionPersistent {
|
||||
public void close() {
|
||||
super.close();
|
||||
var sessionExpiredEvent = new WSSessionExpiredEvent();
|
||||
application.getEventController().addEvent(sessionExpiredEvent);
|
||||
application.getEventController().addEvent(new WSEventDeleteTempFile(getSessionId()));
|
||||
synchronized (sessionEventHandlers) {
|
||||
for (CBWebSessionEventHandler sessionEventHandler : sessionEventHandlers) {
|
||||
try {
|
||||
sessionEventHandler.handeWebSessionEvent(sessionExpiredEvent);
|
||||
sessionEventHandler.handleWebSessionEvent(sessionExpiredEvent);
|
||||
} catch (DBException e) {
|
||||
log.warn("Failed to send session expiration event", e);
|
||||
}
|
||||
@@ -191,6 +205,8 @@ public abstract class BaseWebSession extends AbstractSessionPersistent {
|
||||
userContext.getAccessibleProjectIds().remove(projectId);
|
||||
}
|
||||
|
||||
public abstract void addSessionMessage(WebServerMessage message);
|
||||
|
||||
@Property
|
||||
public boolean isValid() {
|
||||
return getRemainingTime() > 0;
|
||||
|
||||
+6
@@ -16,6 +16,7 @@
|
||||
*/
|
||||
package io.cloudbeaver.model.session;
|
||||
|
||||
import io.cloudbeaver.model.WebServerMessage;
|
||||
import io.cloudbeaver.model.app.WebAuthApplication;
|
||||
import org.jkiss.code.NotNull;
|
||||
import org.jkiss.dbeaver.DBException;
|
||||
@@ -37,6 +38,11 @@ public class WebHeadlessSession extends BaseWebSession {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addSessionMessage(WebServerMessage message) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public SMSessionPrincipal getSessionPrincipal() {
|
||||
return null;
|
||||
|
||||
+60
-11
@@ -27,11 +27,11 @@ import io.cloudbeaver.model.WebAsyncTaskInfo;
|
||||
import io.cloudbeaver.model.WebConnectionInfo;
|
||||
import io.cloudbeaver.model.WebServerMessage;
|
||||
import io.cloudbeaver.model.app.WebAuthApplication;
|
||||
import io.cloudbeaver.model.rm.RMUtils;
|
||||
import io.cloudbeaver.model.user.WebUser;
|
||||
import io.cloudbeaver.service.DBWSessionHandler;
|
||||
import io.cloudbeaver.service.sql.WebSQLConstants;
|
||||
import io.cloudbeaver.utils.CBModelConstants;
|
||||
import io.cloudbeaver.utils.WebAppUtils;
|
||||
import io.cloudbeaver.utils.WebDataSourceUtils;
|
||||
import org.eclipse.core.runtime.IAdaptable;
|
||||
import org.eclipse.core.runtime.IStatus;
|
||||
@@ -42,6 +42,7 @@ import org.jkiss.dbeaver.DBException;
|
||||
import org.jkiss.dbeaver.Log;
|
||||
import org.jkiss.dbeaver.model.DBFileController;
|
||||
import org.jkiss.dbeaver.model.DBPDataSourceContainer;
|
||||
import org.jkiss.dbeaver.model.DBPEvent;
|
||||
import org.jkiss.dbeaver.model.access.DBAAuthCredentials;
|
||||
import org.jkiss.dbeaver.model.access.DBACredentialsProvider;
|
||||
import org.jkiss.dbeaver.model.app.DBPDataSourceRegistry;
|
||||
@@ -50,12 +51,14 @@ import org.jkiss.dbeaver.model.app.DBPProject;
|
||||
import org.jkiss.dbeaver.model.auth.*;
|
||||
import org.jkiss.dbeaver.model.connection.DBPConnectionConfiguration;
|
||||
import org.jkiss.dbeaver.model.exec.DBCException;
|
||||
import org.jkiss.dbeaver.model.fs.DBFFileSystemManager;
|
||||
import org.jkiss.dbeaver.model.meta.Association;
|
||||
import org.jkiss.dbeaver.model.meta.Property;
|
||||
import org.jkiss.dbeaver.model.navigator.DBNModel;
|
||||
import org.jkiss.dbeaver.model.rm.RMController;
|
||||
import org.jkiss.dbeaver.model.rm.RMProject;
|
||||
import org.jkiss.dbeaver.model.rm.RMProjectType;
|
||||
import org.jkiss.dbeaver.model.rm.RMUtils;
|
||||
import org.jkiss.dbeaver.model.runtime.AbstractJob;
|
||||
import org.jkiss.dbeaver.model.runtime.BaseProgressMonitor;
|
||||
import org.jkiss.dbeaver.model.runtime.DBRProgressMonitor;
|
||||
@@ -63,9 +66,10 @@ import org.jkiss.dbeaver.model.runtime.ProxyProgressMonitor;
|
||||
import org.jkiss.dbeaver.model.security.SMAdminController;
|
||||
import org.jkiss.dbeaver.model.security.SMConstants;
|
||||
import org.jkiss.dbeaver.model.security.SMController;
|
||||
import org.jkiss.dbeaver.model.security.SMObjects;
|
||||
import org.jkiss.dbeaver.model.security.SMObjectType;
|
||||
import org.jkiss.dbeaver.model.security.user.SMObjectPermissions;
|
||||
import org.jkiss.dbeaver.model.sql.DBQuotaException;
|
||||
import org.jkiss.dbeaver.model.websocket.event.MessageType;
|
||||
import org.jkiss.dbeaver.model.websocket.event.WSEventType;
|
||||
import org.jkiss.dbeaver.model.websocket.event.WSSessionLogUpdatedEvent;
|
||||
import org.jkiss.dbeaver.registry.DataSourceDescriptor;
|
||||
@@ -82,7 +86,6 @@ import java.util.*;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* Web session.
|
||||
* Is the main source of data in web application
|
||||
@@ -95,7 +98,6 @@ public class WebSession extends BaseWebSession
|
||||
public static final SMSessionType CB_SESSION_TYPE = new SMSessionType("CloudBeaver");
|
||||
private static final String WEB_SESSION_AUTH_CONTEXT_TYPE = "web-session";
|
||||
private static final String ATTR_LOCALE = "locale";
|
||||
|
||||
private static final AtomicInteger TASK_ID = new AtomicInteger();
|
||||
|
||||
private final AtomicInteger taskCount = new AtomicInteger();
|
||||
@@ -232,7 +234,9 @@ public class WebSession extends BaseWebSession
|
||||
return userContext.getFileController();
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void refreshUserData() {
|
||||
super.refreshUserData();
|
||||
refreshSessionAuth();
|
||||
|
||||
initNavigatorModel();
|
||||
@@ -398,7 +402,7 @@ public class WebSession extends BaseWebSession
|
||||
private Set<String> readAccessibleConnectionIds() {
|
||||
try {
|
||||
return getSecurityController()
|
||||
.getAllAvailableObjectsPermissions(SMObjects.DATASOURCE)
|
||||
.getAllAvailableObjectsPermissions(SMObjectType.datasource)
|
||||
.stream()
|
||||
.map(SMObjectPermissions::getObjectId)
|
||||
.collect(Collectors.toSet());
|
||||
@@ -461,12 +465,36 @@ public class WebSession extends BaseWebSession
|
||||
this.accessibleConnectionIds = readAccessibleConnectionIds();
|
||||
}
|
||||
|
||||
public synchronized void addAccessibleConnectionToCache(@NotNull String dsId) {
|
||||
this.accessibleConnectionIds.add(dsId);
|
||||
var registry = getProjectById(WebAppUtils.getGlobalProjectId()).getDataSourceRegistry();
|
||||
var dataSource = registry.getDataSource(dsId);
|
||||
if (dataSource != null) {
|
||||
connections.put(dsId, new WebConnectionInfo(this, dataSource));
|
||||
// reflect changes is navigator model
|
||||
registry.notifyDataSourceListeners(new DBPEvent(DBPEvent.Action.OBJECT_ADD, dataSource, true));
|
||||
}
|
||||
}
|
||||
|
||||
public synchronized void removeAccessibleConnectionFromCache(@NotNull String dsId) {
|
||||
var registry = getProjectById(WebAppUtils.getGlobalProjectId()).getDataSourceRegistry();
|
||||
var dataSource = registry.getDataSource(dsId);
|
||||
if (dataSource != null) {
|
||||
this.accessibleConnectionIds.remove(dsId);
|
||||
connections.remove(dsId);
|
||||
// reflect changes is navigator model
|
||||
registry.notifyDataSourceListeners(new DBPEvent(DBPEvent.Action.OBJECT_REMOVE, dataSource));
|
||||
dataSource.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
private synchronized void authAsAnonymousUser() throws DBException {
|
||||
if (!application.getAppConfiguration().isAnonymousAccessEnabled()) {
|
||||
return;
|
||||
}
|
||||
SMAuthInfo authInfo = getSecurityController().authenticateAnonymousUser(this.id, getSessionParameters(), CB_SESSION_TYPE);
|
||||
updateSMSession(authInfo);
|
||||
notifySessionAuthChange();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@@ -568,6 +596,7 @@ public class WebSession extends BaseWebSession
|
||||
}
|
||||
|
||||
public void removeConnection(WebConnectionInfo connectionInfo) {
|
||||
connectionInfo.clearCache();
|
||||
synchronized (connections) {
|
||||
connections.remove(connectionInfo.getId());
|
||||
}
|
||||
@@ -587,7 +616,6 @@ public class WebSession extends BaseWebSession
|
||||
log.error("Error closing web session tokens");
|
||||
}
|
||||
this.userContext.setUser(null);
|
||||
|
||||
super.close();
|
||||
}
|
||||
|
||||
@@ -697,15 +725,20 @@ public class WebSession extends BaseWebSession
|
||||
synchronized (sessionMessages) {
|
||||
sessionMessages.add(message);
|
||||
}
|
||||
addSessionEvent(new WSSessionLogUpdatedEvent());
|
||||
addSessionEvent(new WSSessionLogUpdatedEvent(
|
||||
WSEventType.SESSION_LOG_UPDATED,
|
||||
this.userContext.getSmSessionId(),
|
||||
this.userContext.getUserId(),
|
||||
MessageType.ERROR,
|
||||
message.getMessage()));
|
||||
}
|
||||
|
||||
public void addInfoMessage(String message) {
|
||||
addSessionMessage(new WebServerMessage(WebServerMessage.MessageType.INFO, message));
|
||||
addSessionMessage(new WebServerMessage(MessageType.INFO, message));
|
||||
}
|
||||
|
||||
public void addWarningMessage(String message) {
|
||||
addSessionMessage(new WebServerMessage(WebServerMessage.MessageType.WARNING, message));
|
||||
addSessionMessage(new WebServerMessage(MessageType.WARNING, message));
|
||||
}
|
||||
|
||||
public List<WebServerMessage> readLog(Integer maxEntries, Boolean clearLog) {
|
||||
@@ -866,7 +899,11 @@ public class WebSession extends BaseWebSession
|
||||
// Auth credentials provider
|
||||
// Adds auth properties passed from web (by user)
|
||||
@Override
|
||||
public boolean provideAuthParameters(@NotNull DBRProgressMonitor monitor, @NotNull DBPDataSourceContainer dataSourceContainer, @NotNull DBPConnectionConfiguration configuration) {
|
||||
public boolean provideAuthParameters(
|
||||
@NotNull DBRProgressMonitor monitor,
|
||||
@NotNull DBPDataSourceContainer dataSourceContainer,
|
||||
@NotNull DBPConnectionConfiguration configuration
|
||||
) {
|
||||
try {
|
||||
// Properties from nested auth sessions
|
||||
for (DBACredentialsProvider contextCredentialsProvider : getContextCredentialsProviders()) {
|
||||
@@ -984,7 +1021,10 @@ public class WebSession extends BaseWebSession
|
||||
}
|
||||
|
||||
public void deleteSessionProject(@Nullable WebProjectImpl project) {
|
||||
getWorkspace().remoteProject((WebProjectImpl) project);
|
||||
if (project != null) {
|
||||
project.dispose();
|
||||
}
|
||||
getWorkspace().removeProject(project);
|
||||
if (navigatorModel != null) {
|
||||
navigatorModel.getRoot().removeProject(project);
|
||||
}
|
||||
@@ -1011,6 +1051,15 @@ public class WebSession extends BaseWebSession
|
||||
}
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public DBFFileSystemManager getFileSystemManager(String projectId) throws DBException {
|
||||
var project = getProjectById(projectId);
|
||||
if (project == null) {
|
||||
throw new DBException("Project not found: " + projectId);
|
||||
}
|
||||
return project.getFileSystemManager();
|
||||
}
|
||||
|
||||
private class SessionProgressMonitor extends BaseProgressMonitor {
|
||||
@Override
|
||||
public void beginTask(String name, int totalWork) {
|
||||
|
||||
+4
-13
@@ -17,7 +17,6 @@
|
||||
|
||||
package io.cloudbeaver.model.session;
|
||||
|
||||
import io.cloudbeaver.DBWConstants;
|
||||
import io.cloudbeaver.DBWUserIdentity;
|
||||
import io.cloudbeaver.DBWebException;
|
||||
import io.cloudbeaver.auth.SMAuthProviderExternal;
|
||||
@@ -38,7 +37,6 @@ import org.jkiss.utils.CommonUtils;
|
||||
|
||||
import java.time.OffsetDateTime;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@@ -103,7 +101,6 @@ public class WebSessionAuthProcessor {
|
||||
String userId = curUser.getUserId();
|
||||
|
||||
var securityController = webSession.getSecurityController();
|
||||
Map<String, Object> providerConfig = Collections.emptyMap();
|
||||
var newAuthInfos = new ArrayList<WebAuthInfo>();
|
||||
for (Map.Entry<SMAuthConfigurationReference, Object> entry : authInfo.getAuthData().entrySet()) {
|
||||
SMAuthConfigurationReference authConfiguration = entry.getKey();
|
||||
@@ -115,15 +112,6 @@ public class WebSessionAuthProcessor {
|
||||
SMAuthProviderExternal<?> authProviderExternal = authProviderInstance instanceof SMAuthProviderExternal<?> ?
|
||||
(SMAuthProviderExternal<?>) authProviderInstance : null;
|
||||
|
||||
boolean providerDisabled = !isProviderEnabled(providerId);
|
||||
if (configMode || webSession.hasPermission(DBWConstants.PERMISSION_ADMIN)) {
|
||||
// 1. Admin can authorize in any providers
|
||||
// 2. When it authorizes in non-local provider for the first time we force linkUser flag
|
||||
if (providerDisabled && webSession.getUser() != null) {
|
||||
linkWithActiveUser = true;
|
||||
}
|
||||
}
|
||||
|
||||
SMSession authSession;
|
||||
|
||||
if (authProviderExternal != null && !configMode && !alreadyLoggedIn) {
|
||||
@@ -138,7 +126,10 @@ public class WebSessionAuthProcessor {
|
||||
}
|
||||
|
||||
DBWUserIdentity userIdentity = null;
|
||||
|
||||
var providerConfigId = authConfiguration.getAuthProviderConfigurationId();
|
||||
var providerConfig = WebAppUtils.getWebAuthApplication()
|
||||
.getAuthConfiguration()
|
||||
.getAuthProviderConfiguration(providerConfigId);
|
||||
if (authProviderExternal != null) {
|
||||
try {
|
||||
userIdentity =
|
||||
|
||||
+3
-3
@@ -17,7 +17,6 @@
|
||||
package io.cloudbeaver.model.session;
|
||||
|
||||
import io.cloudbeaver.WebProjectImpl;
|
||||
import io.cloudbeaver.model.rm.RMUtils;
|
||||
import org.jkiss.code.NotNull;
|
||||
import org.jkiss.code.Nullable;
|
||||
import org.jkiss.dbeaver.model.app.DBPPlatform;
|
||||
@@ -25,6 +24,7 @@ import org.jkiss.dbeaver.model.app.DBPProject;
|
||||
import org.jkiss.dbeaver.model.app.DBPWorkspace;
|
||||
import org.jkiss.dbeaver.model.auth.SMSessionContext;
|
||||
import org.jkiss.dbeaver.model.impl.auth.SessionContextImpl;
|
||||
import org.jkiss.dbeaver.model.rm.RMUtils;
|
||||
import org.jkiss.dbeaver.runtime.DBWorkbench;
|
||||
import org.jkiss.utils.CommonUtils;
|
||||
|
||||
@@ -92,7 +92,7 @@ public class WebSessionWorkspace implements DBPWorkspace {
|
||||
}
|
||||
|
||||
@Override
|
||||
public DBPProject getProject(@NotNull String projectName) {
|
||||
public WebProjectImpl getProject(@NotNull String projectName) {
|
||||
for (WebProjectImpl project : accessibleProjects) {
|
||||
if (project.getName().equals(projectName)) {
|
||||
return project;
|
||||
@@ -140,7 +140,7 @@ public class WebSessionWorkspace implements DBPWorkspace {
|
||||
accessibleProjects.add(project);
|
||||
}
|
||||
|
||||
void remoteProject(WebProjectImpl project) {
|
||||
void removeProject(WebProjectImpl project) {
|
||||
accessibleProjects.remove(project);
|
||||
}
|
||||
|
||||
|
||||
+25
-15
@@ -80,16 +80,21 @@ public class WebUserContext implements SMCredentialsProvider {
|
||||
* refresh context state based on new token from security manager
|
||||
*
|
||||
* @param smAuthInfo - auth info from security manager
|
||||
* @return - true if context changed
|
||||
* @throws DBException - if user already authorized and new token come from another user
|
||||
*/
|
||||
public synchronized boolean refresh(SMAuthInfo smAuthInfo) throws DBException {
|
||||
if (smAuthInfo.getAuthStatus() != SMAuthStatus.SUCCESS || smAuthInfo.getSmAccessToken() == null) {
|
||||
throw new DBCException("Authorization did not complete successfully");
|
||||
}
|
||||
if (smAuthInfo.getAuthPermissions() == null) {
|
||||
if (smAuthInfo.getAuthPermissions() == null && !isAuthorizedInSecurityManager()) {
|
||||
throw new DBCException("Required information about session permissions is missing");
|
||||
}
|
||||
return refresh(smAuthInfo.getSmAccessToken(), smAuthInfo.getSmRefreshToken(), smAuthInfo.getAuthPermissions());
|
||||
boolean sessionChanged = !CommonUtils.equalObjects(smSessionId, smAuthInfo.getAuthPermissions().getSessionId());
|
||||
if (smAuthInfo.getAuthStatus() != SMAuthStatus.SUCCESS || (sessionChanged && smAuthInfo.getSmAccessToken() == null)) {
|
||||
throw new DBCException("Authorization did not complete successfully");
|
||||
}
|
||||
if (sessionChanged) {
|
||||
return refresh(smAuthInfo.getSmAccessToken(), smAuthInfo.getSmRefreshToken(), smAuthInfo.getAuthPermissions());
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public synchronized boolean refresh(
|
||||
@@ -108,30 +113,30 @@ public class WebUserContext implements SMCredentialsProvider {
|
||||
smAuthPermissions.getSessionId(),
|
||||
smAuthPermissions.getPermissions()
|
||||
);
|
||||
this.refreshToken = smRefreshToken;
|
||||
setRefreshToken(smRefreshToken);
|
||||
setUserPermissions(smAuthPermissions.getPermissions());
|
||||
this.securityController = application.createSecurityController(this);
|
||||
this.adminSecurityController = application.getAdminSecurityController(this);
|
||||
this.secretController = application.getSecretController(this);
|
||||
this.rmController = application.createResourceController(this, workspace);
|
||||
this.fileController = application.createFileController(this);
|
||||
if (isSessionChanged) {
|
||||
this.smSessionId = smAuthPermissions.getSessionId();
|
||||
setUser(smAuthPermissions.getUserId() == null ? null : new WebUser(securityController.getCurrentUser()));
|
||||
this.accessibleProjectIds.clear();
|
||||
this.accessibleProjectIds.addAll(
|
||||
Arrays.stream(rmController.listAccessibleProjects()).map(RMProject::getId).collect(Collectors.toSet())
|
||||
);
|
||||
refreshAccessibleProjects();
|
||||
}
|
||||
return isSessionChanged;
|
||||
}
|
||||
|
||||
public synchronized void refreshAccessibleProjects() throws DBException {
|
||||
this.accessibleProjectIds.clear();
|
||||
this.accessibleProjectIds.addAll(
|
||||
Arrays.stream(rmController.listAccessibleProjects()).map(RMProject::getId).collect(Collectors.toSet())
|
||||
);
|
||||
}
|
||||
|
||||
public synchronized void refreshSMSession() throws DBException {
|
||||
if (smCredentials == null || refreshToken == null) {
|
||||
return;
|
||||
}
|
||||
var newTokens = securityController.refreshSession(refreshToken);
|
||||
this.refreshToken = newTokens.getSmRefreshToken();
|
||||
setRefreshToken(newTokens.getSmRefreshToken());
|
||||
this.smCredentials = new SMCredentials(
|
||||
newTokens.getSmAccessToken(),
|
||||
smCredentials.getUserId(),
|
||||
@@ -204,6 +209,7 @@ public class WebUserContext implements SMCredentialsProvider {
|
||||
*/
|
||||
public synchronized void refreshPermissions() throws DBException {
|
||||
if (isAuthorizedInSecurityManager()) {
|
||||
log.debug("refresh permissions " + getUserId() + " " + getSmSessionId());
|
||||
setUserPermissions(securityController.getTokenPermissions().getPermissions());
|
||||
} else {
|
||||
setUserPermissions(getDefaultPermissions());
|
||||
@@ -238,4 +244,8 @@ public class WebUserContext implements SMCredentialsProvider {
|
||||
public Set<String> getAccessibleProjectIds() {
|
||||
return accessibleProjectIds;
|
||||
}
|
||||
|
||||
private void setRefreshToken(@Nullable String refreshToken) {
|
||||
this.refreshToken = refreshToken;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,16 +15,25 @@ import org.jkiss.dbeaver.model.security.exception.SMRefreshTokenExpiredException
|
||||
import java.util.ArrayList;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
public class SMUtils {
|
||||
public static boolean isAdmin(SMCredentialsProvider webSession) {
|
||||
return webSession.hasPermission(DBWConstants.PERMISSION_ADMIN);
|
||||
}
|
||||
|
||||
public static boolean isAdmin(@NotNull Set<String> permissions) {
|
||||
return permissions.contains(DBWConstants.PERMISSION_ADMIN);
|
||||
}
|
||||
|
||||
public static boolean isRMAdmin(SMCredentialsProvider webSession) {
|
||||
return isAdmin(webSession) || webSession.hasPermission(RMConstants.PERMISSION_RM_ADMIN);
|
||||
}
|
||||
|
||||
public static boolean isRMAdmin(@NotNull Set<String> permissions) {
|
||||
return isAdmin(permissions) || permissions.contains(RMConstants.PERMISSION_RM_ADMIN);
|
||||
}
|
||||
|
||||
public static boolean hasProjectPermission(
|
||||
SMCredentialsProvider credentialsProvider,
|
||||
RMProject project,
|
||||
|
||||
+14
@@ -26,7 +26,9 @@ import org.jkiss.dbeaver.model.struct.DBSAttributeBase;
|
||||
import org.jkiss.dbeaver.model.struct.DBSDataContainer;
|
||||
import org.jkiss.dbeaver.model.struct.DBSTypedObject;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Web query results info.
|
||||
@@ -82,6 +84,18 @@ public class WebSQLResultsInfo {
|
||||
return null;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public Set<DBDRowIdentifier> getRowIdentifiers() {
|
||||
Set<DBDRowIdentifier> rowIdentifiers = new HashSet<>();
|
||||
for (DBDAttributeBinding column : attributes) {
|
||||
DBDRowIdentifier rowIdentifier = column.getRowIdentifier();
|
||||
if (rowIdentifier != null) {
|
||||
rowIdentifiers.add(rowIdentifier);
|
||||
}
|
||||
}
|
||||
return rowIdentifiers;
|
||||
}
|
||||
|
||||
public DBSAttributeBase getAttribute(String attributeName) {
|
||||
DBPDataSource dataSource = dataContainer.getDataSource();
|
||||
|
||||
|
||||
@@ -16,9 +16,12 @@
|
||||
*/
|
||||
package io.cloudbeaver.utils;
|
||||
|
||||
import io.cloudbeaver.DBWebException;
|
||||
import io.cloudbeaver.WebProjectImpl;
|
||||
import io.cloudbeaver.auth.NoAuthCredentialsProvider;
|
||||
import io.cloudbeaver.model.app.WebApplication;
|
||||
import io.cloudbeaver.model.app.WebAuthApplication;
|
||||
import io.cloudbeaver.model.session.WebSession;
|
||||
import org.jkiss.code.NotNull;
|
||||
import org.jkiss.code.Nullable;
|
||||
import org.jkiss.dbeaver.DBException;
|
||||
@@ -54,6 +57,14 @@ public class WebAppUtils {
|
||||
return (WebApplication) DBWorkbench.getPlatform().getApplication();
|
||||
}
|
||||
|
||||
public static WebAuthApplication getWebAuthApplication() throws DBException {
|
||||
WebApplication application = getWebApplication();
|
||||
if (!WebAuthApplication.class.isAssignableFrom(application.getClass())) {
|
||||
throw new DBException("The current application doesn't contain authorization configuration");
|
||||
}
|
||||
return (WebAuthApplication) application;
|
||||
}
|
||||
|
||||
public static SMAuthenticationManager getAuthManager(WebApplication application) throws DBException {
|
||||
var smController = application.createSecurityController(new NoAuthCredentialsProvider());
|
||||
if (!SMAuthenticationManager.class.isAssignableFrom(smController.getClass())) {
|
||||
@@ -144,12 +155,7 @@ public class WebAppUtils {
|
||||
|
||||
@NotNull
|
||||
public static StringBuilder getAuthApiPrefix(String serviceId) throws DBException {
|
||||
WebApplication application = getWebApplication();
|
||||
if (!WebAuthApplication.class.isAssignableFrom(application.getClass())) {
|
||||
throw new DBException("The current application doesn't contain authorization configuration");
|
||||
}
|
||||
WebAuthApplication webAuthApplication = (WebAuthApplication) application;
|
||||
return getAuthApiPrefix(webAuthApplication, serviceId);
|
||||
return getAuthApiPrefix(getWebAuthApplication(), serviceId);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@@ -206,4 +212,12 @@ public class WebAppUtils {
|
||||
return RMProjectType.GLOBAL.getPrefix() + "_" + globalConfigurationName;
|
||||
}
|
||||
|
||||
public static WebProjectImpl getProjectById(WebSession webSession, String projectId) throws DBWebException {
|
||||
WebProjectImpl project = webSession.getProjectById(projectId);
|
||||
if (project == null) {
|
||||
throw new DBWebException("Project '" + projectId + "' not found");
|
||||
}
|
||||
return project;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+17
-2
@@ -88,11 +88,25 @@ public class WebDataSourceUtils {
|
||||
if (cfgInput.getPassword() != null) {
|
||||
handlerConfig.setPassword(cfgInput.getPassword());
|
||||
}
|
||||
if (cfgInput.getKey() != null) {
|
||||
setSecureProperties(handlerConfig, cfgInput, true);
|
||||
if (cfgInput.getKey() != null) { // backward compatibility
|
||||
handlerConfig.setSecureProperty(SSHConstants.PROP_KEY_VALUE, cfgInput.getKey());
|
||||
}
|
||||
}
|
||||
|
||||
private static void setSecureProperties(DBWHandlerConfiguration handlerConfig, WebNetworkHandlerConfigInput cfgInput, boolean ignoreNulls) {
|
||||
var secureProperties = cfgInput.getSecureProperties();
|
||||
if (secureProperties == null) {
|
||||
return;
|
||||
}
|
||||
for (var pr : secureProperties.entrySet()) {
|
||||
if (ignoreNulls && pr.getValue() == null) {
|
||||
continue;
|
||||
}
|
||||
handlerConfig.setSecureProperty(pr.getKey(), pr.getValue());
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static DBPDataSourceContainer getLocalOrGlobalDataSource(
|
||||
WebApplication application, WebSession webSession, @Nullable String projectId, String connectionId
|
||||
@@ -121,7 +135,8 @@ public class WebDataSourceUtils {
|
||||
public static void updateHandlerCredentials(DBWHandlerConfiguration handlerCfg, WebNetworkHandlerConfigInput webConfig) {
|
||||
handlerCfg.setUserName(webConfig.getUserName());
|
||||
handlerCfg.setPassword(webConfig.getPassword());
|
||||
handlerCfg.setSecureProperty(SSHConstants.PROP_KEY_VALUE, webConfig.getKey());
|
||||
setSecureProperties(handlerCfg, webConfig, false);
|
||||
handlerCfg.setSecureProperty(SSHConstants.PROP_KEY_VALUE, webConfig.getKey()); // backward compatibility
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -130,18 +130,16 @@ public class WebEventUtils {
|
||||
String projectId,
|
||||
WebSession session,
|
||||
String resourcePath,
|
||||
RMResource[] resourceParsedPath,
|
||||
WSConstants.EventAction eventAction,
|
||||
WSResourceProperty property
|
||||
) {
|
||||
addRmResourceUpdatedEvent(projectId, session, resourcePath, resourceParsedPath, eventAction, property, null);
|
||||
addRmResourceUpdatedEvent(projectId, session, resourcePath, eventAction, property, null);
|
||||
}
|
||||
|
||||
public static void addRmResourceUpdatedEvent(
|
||||
String projectId,
|
||||
WebSession session,
|
||||
String resourcePath,
|
||||
RMResource[] resourceParsedPath,
|
||||
WSConstants.EventAction eventAction,
|
||||
WSResourceProperty property,
|
||||
String details
|
||||
@@ -154,7 +152,6 @@ public class WebEventUtils {
|
||||
session.getUserId(),
|
||||
projectId,
|
||||
resourcePath,
|
||||
resourceParsedPath,
|
||||
property,
|
||||
details
|
||||
);
|
||||
@@ -165,7 +162,6 @@ public class WebEventUtils {
|
||||
session.getUserId(),
|
||||
projectId,
|
||||
resourcePath,
|
||||
resourceParsedPath,
|
||||
property,
|
||||
details
|
||||
);
|
||||
@@ -176,7 +172,6 @@ public class WebEventUtils {
|
||||
session.getUserId(),
|
||||
projectId,
|
||||
resourcePath,
|
||||
resourceParsedPath,
|
||||
property,
|
||||
details
|
||||
);
|
||||
|
||||
+2
-1
@@ -20,7 +20,8 @@ import org.jkiss.dbeaver.DBException;
|
||||
import org.jkiss.dbeaver.model.websocket.event.WSEvent;
|
||||
|
||||
public interface CBWebSessionEventHandler {
|
||||
void handeWebSessionEvent(WSEvent event) throws DBException;
|
||||
void handleWebSessionEvent(WSEvent event) throws DBException;
|
||||
|
||||
void close();
|
||||
|
||||
}
|
||||
|
||||
@@ -1,13 +1,12 @@
|
||||
Manifest-Version: 1.0
|
||||
Bundle-ManifestVersion: 2
|
||||
Bundle-Vendor: %Bundle-Vendor
|
||||
Bundle-Name: %Bundle-Name
|
||||
Bundle-Vendor: DBeaver Corp
|
||||
Bundle-Name: Cloudbeaver Community Product
|
||||
Bundle-SymbolicName: io.cloudbeaver.product.ce;singleton:=true
|
||||
Bundle-Version: 23.1.1.qualifier
|
||||
Bundle-Release-Date: 20230626
|
||||
Bundle-RequiredExecutionEnvironment: JavaSE-11
|
||||
Bundle-Version: 23.2.4.qualifier
|
||||
Bundle-Release-Date: 20231106
|
||||
Bundle-RequiredExecutionEnvironment: JavaSE-17
|
||||
Bundle-ActivationPolicy: lazy
|
||||
Bundle-ClassPath: .
|
||||
Require-Bundle: io.cloudbeaver.server
|
||||
Bundle-Localization: OSGI-INF/l10n/bundle
|
||||
Automatic-Module-Name: io.cloudbeaver.product.ce
|
||||
|
||||
@@ -1,2 +0,0 @@
|
||||
Bundle-Vendor = DBeaver Corp
|
||||
Bundle-Name = Cloudbeaver CE bundle
|
||||
@@ -2,5 +2,4 @@ source.. = src/
|
||||
output.. = target/classes/
|
||||
bin.includes = .,\
|
||||
META-INF/,\
|
||||
OSGI-INF/,\
|
||||
plugin.xml
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
<relativePath>../</relativePath>
|
||||
</parent>
|
||||
<artifactId>io.cloudbeaver.product.ce</artifactId>
|
||||
<version>23.1.1-SNAPSHOT</version>
|
||||
<version>23.2.4-SNAPSHOT</version>
|
||||
<packaging>eclipse-plugin</packaging>
|
||||
|
||||
</project>
|
||||
|
||||
@@ -2,8 +2,8 @@ Manifest-Version: 1.0
|
||||
Bundle-ManifestVersion: 2
|
||||
Bundle-Name: Base JDBC drivers
|
||||
Bundle-SymbolicName: io.cloudbeaver.resources.drivers.base;singleton:=true
|
||||
Bundle-Version: 1.0.76.qualifier
|
||||
Bundle-Release-Date: 20230626
|
||||
Bundle-Version: 1.0.85.qualifier
|
||||
Bundle-Release-Date: 20231106
|
||||
Bundle-Vendor: DBeaver Corp
|
||||
Bundle-ActivationPolicy: lazy
|
||||
Automatic-Module-Name: io.cloudbeaver.resources.drivers.base
|
||||
|
||||
@@ -9,6 +9,6 @@
|
||||
<relativePath>../</relativePath>
|
||||
</parent>
|
||||
<artifactId>io.cloudbeaver.resources.drivers.base</artifactId>
|
||||
<version>1.0.76-SNAPSHOT</version>
|
||||
<version>1.0.85-SNAPSHOT</version>
|
||||
<packaging>eclipse-plugin</packaging>
|
||||
</project>
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
Manifest-Version: 1.0
|
||||
Bundle-ManifestVersion: 2
|
||||
Bundle-Vendor: %Bundle-Vendor
|
||||
Bundle-Name: %Bundle-Name
|
||||
Bundle-Vendor: DBeaver Corp
|
||||
Bundle-Name: Cloudbeaver Web Server
|
||||
Bundle-SymbolicName: io.cloudbeaver.server;singleton:=true
|
||||
Bundle-Version: 23.1.1.qualifier
|
||||
Bundle-Release-Date: 20230626
|
||||
Bundle-RequiredExecutionEnvironment: JavaSE-11
|
||||
Bundle-Version: 23.2.4.qualifier
|
||||
Bundle-Release-Date: 20231106
|
||||
Bundle-RequiredExecutionEnvironment: JavaSE-17
|
||||
Bundle-ActivationPolicy: lazy
|
||||
Bundle-Activator: io.cloudbeaver.server.CBPlatformActivator
|
||||
Bundle-ClassPath: .
|
||||
@@ -34,5 +34,4 @@ Export-Package: io.cloudbeaver,
|
||||
io.cloudbeaver.service.session,
|
||||
io.cloudbeaver.service.sql
|
||||
Import-Package: org.slf4j
|
||||
Bundle-Localization: OSGI-INF/l10n/bundle
|
||||
Automatic-Module-Name: io.cloudbeaver.server
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
Bundle-Vendor = DBeaver Corp
|
||||
Bundle-Name = Cloudbeaver Web Server
|
||||
|
||||
@@ -2,7 +2,6 @@ source.. = src/
|
||||
output.. = target/classes/
|
||||
bin.includes = .,\
|
||||
META-INF/,\
|
||||
OSGI-INF/,\
|
||||
schema/,\
|
||||
static/,\
|
||||
plugin.xml
|
||||
|
||||
@@ -57,8 +57,11 @@
|
||||
<eventHandler class="io.cloudbeaver.server.events.WSRmResourceUpdatedEventHandlerImpl">
|
||||
<topic id="cb_scripts"/>
|
||||
</eventHandler>
|
||||
<eventHandler class="io.cloudbeaver.server.events.WSPermissionUpdatedEventHandler">
|
||||
<topic id="cb_permissions"/>
|
||||
<eventHandler class="io.cloudbeaver.server.events.WSObjectPermissionUpdatedEventHandler">
|
||||
<topic id="cb_object_permissions"/>
|
||||
</eventHandler>
|
||||
<eventHandler class="io.cloudbeaver.server.events.WSSubjectPermissionUpdatedEventHandler">
|
||||
<topic id="cb_subject_permissions"/>
|
||||
</eventHandler>
|
||||
<eventHandler class="io.cloudbeaver.server.events.WSProjectUpdatedEventHandler">
|
||||
<topic id="cb_projects"/>
|
||||
@@ -66,6 +69,15 @@
|
||||
<eventHandler class="io.cloudbeaver.server.events.WSUserSecretEventHandlerImpl">
|
||||
<topic id="cb_user_secret"/>
|
||||
</eventHandler>
|
||||
<eventHandler class="io.cloudbeaver.server.events.WSLogEventHandler">
|
||||
<topic id="cb_session_log"/>
|
||||
</eventHandler>
|
||||
<eventHandler class="io.cloudbeaver.server.events.WSEventHandlerWorkspaceConfigUpdate">
|
||||
<topic id="cb_workspace_configuration"/>
|
||||
</eventHandler>
|
||||
<eventHandler class="io.cloudbeaver.server.events.WSDeleteTempFileHandler">
|
||||
<topic id="cb_delete_temp_folder"/>
|
||||
</eventHandler>
|
||||
</extension>
|
||||
|
||||
</plugin>
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
<relativePath>../</relativePath>
|
||||
</parent>
|
||||
<artifactId>io.cloudbeaver.server</artifactId>
|
||||
<version>23.1.1-SNAPSHOT</version>
|
||||
<version>23.2.4-SNAPSHOT</version>
|
||||
<packaging>eclipse-plugin</packaging>
|
||||
|
||||
</project>
|
||||
|
||||
@@ -3,6 +3,13 @@ scalar Object
|
||||
# Date/Time
|
||||
scalar DateTime
|
||||
|
||||
input PageInput {
|
||||
limit: Int
|
||||
offset: Int
|
||||
}
|
||||
|
||||
directive @since(version: String!) repeatable on OBJECT|SCALAR|QUERY|MUTATION|FIELD|VARIABLE_DEFINITION|OBJECT|FIELD_DEFINITION|ARGUMENT_DEFINITION|INTERFACE|ENUM|ENUM_VALUE|INPUT_OBJECT|INPUT_FIELD_DEFINITION
|
||||
|
||||
type Query
|
||||
|
||||
type Mutation
|
||||
|
||||
@@ -12,6 +12,8 @@ type ObjectPropertyInfo {
|
||||
displayName: String
|
||||
# Property description
|
||||
description: String
|
||||
# Property hint
|
||||
hint: String @since(version: "23.2.3")
|
||||
# Property category (may be used if object has a lot of properties)
|
||||
category: String
|
||||
# Property data type (int, String, etc)
|
||||
@@ -117,6 +119,7 @@ type ServerConfig {
|
||||
hostName: String! @deprecated # use container id instead
|
||||
containerId: String!
|
||||
defaultAuthRole: String
|
||||
defaultUserTeam: String # [23.2.2]
|
||||
|
||||
anonymousAccessEnabled: Boolean!
|
||||
supportsCustomConnections: Boolean!
|
||||
@@ -282,15 +285,17 @@ type NetworkHandlerDescriptor {
|
||||
properties: [ObjectPropertyInfo!]!
|
||||
}
|
||||
|
||||
# SSH network handler config. Name without prefix only for backward compatibility
|
||||
type NetworkHandlerConfig {
|
||||
id: ID!
|
||||
enabled: Boolean!
|
||||
authType: NetworkHandlerAuthType!
|
||||
authType: NetworkHandlerAuthType! @deprecated # use properties
|
||||
userName: String
|
||||
password: String
|
||||
key: String
|
||||
key: String @deprecated # use secured properties
|
||||
savePassword: Boolean!
|
||||
properties: Object!
|
||||
secureProperties: Object!
|
||||
}
|
||||
|
||||
# Connection instance
|
||||
@@ -426,12 +431,13 @@ input NavigatorSettingsInput {
|
||||
input NetworkHandlerConfigInput {
|
||||
id: ID!
|
||||
enabled: Boolean
|
||||
authType: NetworkHandlerAuthType
|
||||
authType: NetworkHandlerAuthType @deprecated # use properties
|
||||
userName: String
|
||||
password: String
|
||||
key: String
|
||||
key: String @deprecated # use secured properties
|
||||
savePassword: Boolean
|
||||
properties: Object
|
||||
secureProperties: Object
|
||||
}
|
||||
|
||||
# Configuration of particular connection. Used for new connection create. Includes auth info
|
||||
|
||||
@@ -25,7 +25,12 @@ enum CBServerEventId {
|
||||
cb_rm_resource_deleted,
|
||||
|
||||
cb_rm_project_added,
|
||||
cb_rm_project_removed
|
||||
cb_rm_project_removed,
|
||||
|
||||
cb_object_permissions_updated,
|
||||
cb_subject_permissions_updated,
|
||||
|
||||
cb_database_output_log_updated
|
||||
}
|
||||
|
||||
# Events sent by client
|
||||
@@ -43,7 +48,11 @@ enum CBEventTopic {
|
||||
cb_datasource,
|
||||
cb_datasource_folder,
|
||||
cb_scripts,
|
||||
cb_projects
|
||||
cb_projects,
|
||||
cb_object_permissions,
|
||||
cb_subject_permissions,
|
||||
cb_database_output_log,
|
||||
cb_delete_temp_folder
|
||||
}
|
||||
|
||||
# Base server event interface
|
||||
@@ -122,6 +131,12 @@ type CBProjectUpdateEvent implements CBServerEvent {
|
||||
projectId: String!
|
||||
}
|
||||
|
||||
# Subject permission update event
|
||||
type CBSubjectPermissionUpdateEvent implements CBServerEvent {
|
||||
id: CBServerEventId!
|
||||
topicId: CBEventTopic
|
||||
}
|
||||
|
||||
# Topic subscription event
|
||||
type CBTopicEvent implements CBClientEvent {
|
||||
id: CBClientEventId!
|
||||
@@ -142,6 +157,22 @@ type CBProjectsActiveEvent implements CBClientEvent {
|
||||
projectIds: [String!]! # list of active projects
|
||||
}
|
||||
|
||||
# Database output log event
|
||||
type CBDatabaseOutputLogEvent implements CBServerEvent {
|
||||
id: CBServerEventId!
|
||||
topicId: CBEventTopic
|
||||
contextId: String!
|
||||
messages: [WSOutputLogInfo!]!
|
||||
eventTimestamp: Int!
|
||||
}
|
||||
|
||||
# Define the type for WSOutputLogInfo
|
||||
type WSOutputLogInfo {
|
||||
severity: String
|
||||
message: String
|
||||
# Add more fields as needed
|
||||
}
|
||||
|
||||
extend type Query {
|
||||
emptyEvent: Boolean
|
||||
}
|
||||
|
||||
@@ -59,7 +59,9 @@ type NavigatorNodeInfo {
|
||||
# Node human readable name
|
||||
name: String
|
||||
#Node full name
|
||||
fullName: String
|
||||
fullName: String @deprecated # Use name parameter (23.2.0)
|
||||
#Node plain name (23.2.0)
|
||||
plainName: String
|
||||
# Node icon path
|
||||
icon: String
|
||||
# Node description
|
||||
@@ -67,13 +69,17 @@ type NavigatorNodeInfo {
|
||||
# Node type
|
||||
nodeType: String
|
||||
# Can this property have child nodes?
|
||||
hasChildren: Boolean
|
||||
hasChildren: Boolean!
|
||||
# Project id of the node
|
||||
projectId: String
|
||||
|
||||
# Associated object. Maybe null for non-database objects
|
||||
object: DatabaseObjectInfo
|
||||
|
||||
# Associated object. Return value depends on the node type - connectionId for connection node, resource path for resource node, etc.
|
||||
# null - if node currently not support this property
|
||||
objectId: String @since(version: "23.2.4")
|
||||
|
||||
# Supported features: item, container, leaf
|
||||
# canDelete, canRename
|
||||
features: [ String! ]
|
||||
@@ -82,9 +88,18 @@ type NavigatorNodeInfo {
|
||||
# If is different than properties. It doesn't perform any expensive operation and doesn't require authentication.
|
||||
nodeDetails: [ ObjectPropertyInfo! ]
|
||||
|
||||
folder: Boolean
|
||||
inline: Boolean
|
||||
navigable: Boolean
|
||||
folder: Boolean!
|
||||
inline: Boolean!
|
||||
navigable: Boolean!
|
||||
filtered: Boolean!
|
||||
|
||||
# Reads node filter. Expensive invocation, read only when it is really needed
|
||||
filter: NavigatorNodeFilter
|
||||
}
|
||||
|
||||
type NavigatorNodeFilter {
|
||||
include: [String!]
|
||||
exclude: [String!]
|
||||
}
|
||||
|
||||
type DatabaseCatalog {
|
||||
@@ -93,6 +108,7 @@ type DatabaseCatalog {
|
||||
}
|
||||
|
||||
type DatabaseStructContainers {
|
||||
parentNode: NavigatorNodeInfo
|
||||
catalogList: [ DatabaseCatalog! ]!
|
||||
schemaList: [ NavigatorNodeInfo! ]!
|
||||
supportsCatalogChange: Boolean!
|
||||
@@ -133,6 +149,10 @@ extend type Mutation {
|
||||
navDeleteNodes( nodePaths: [ID!]! ): Int
|
||||
|
||||
# Moves nodes with specified IDs to the connection folder
|
||||
navMoveNodesToFolder( nodePaths: [ID!]!, folderPath: ID!): Boolean
|
||||
navMoveNodesToFolder( nodePaths: [ID!]!, folderPath: ID!): Boolean!
|
||||
|
||||
# Sets filter for the folder node. If both include and exclude are null then filter is removed.
|
||||
# Node must be refreshed after applying filters. Node children can be changed
|
||||
navSetFolderFilter( nodePath: ID!, include: [String!], exclude: [String!]): Boolean!
|
||||
|
||||
}
|
||||
@@ -268,7 +268,9 @@ extend type Query {
|
||||
contextId: ID!,
|
||||
connectionId: ID!,
|
||||
resultsId: ID!,
|
||||
columnNames: [String!]
|
||||
columnNames: [String!],
|
||||
functions: [String!],
|
||||
showDuplicatesOnly: Boolean
|
||||
): String!
|
||||
}
|
||||
|
||||
@@ -287,7 +289,8 @@ extend type Mutation {
|
||||
sql: String!,
|
||||
resultId: ID,
|
||||
filter: SQLDataFilter,
|
||||
dataFormat: ResultDataFormat # requested data format. May be ignored by server
|
||||
dataFormat: ResultDataFormat, # requested data format. May be ignored by server
|
||||
readLogs: Boolean # added 23.2.1
|
||||
): AsyncTaskInfo!
|
||||
|
||||
# Read data from table
|
||||
|
||||
@@ -222,6 +222,17 @@ public class WebServiceUtils extends WebCommonUtils {
|
||||
@Nullable Map<String, Object> authProperties,
|
||||
boolean saveCredentials,
|
||||
boolean sharedCredentials
|
||||
) {
|
||||
saveAuthProperties(dataSourceContainer, configuration, authProperties, saveCredentials, sharedCredentials, false);
|
||||
}
|
||||
|
||||
public static void saveAuthProperties(
|
||||
@NotNull DBPDataSourceContainer dataSourceContainer,
|
||||
@NotNull DBPConnectionConfiguration configuration,
|
||||
@Nullable Map<String, Object> authProperties,
|
||||
boolean saveCredentials,
|
||||
boolean sharedCredentials,
|
||||
boolean isTest
|
||||
) {
|
||||
dataSourceContainer.setSavePassword(saveCredentials);
|
||||
dataSourceContainer.setSharedCredentials(sharedCredentials);
|
||||
@@ -242,12 +253,14 @@ public class WebServiceUtils extends WebCommonUtils {
|
||||
// Read save credentials
|
||||
DBAAuthCredentials credentials = configuration.getAuthModel().loadCredentials(dataSourceContainer, configuration);
|
||||
|
||||
var currentAuthProps = new HashMap<String, String>();
|
||||
for (Map.Entry<String, Object> stringObjectEntry : authProperties.entrySet()) {
|
||||
var value = stringObjectEntry.getValue() == null ? null : stringObjectEntry.getValue().toString();
|
||||
currentAuthProps.put(stringObjectEntry.getKey(), value);
|
||||
if (isTest) {
|
||||
var currentAuthProps = new HashMap<String, String>();
|
||||
for (Map.Entry<String, Object> stringObjectEntry : authProperties.entrySet()) {
|
||||
var value = stringObjectEntry.getValue() == null ? null : stringObjectEntry.getValue().toString();
|
||||
currentAuthProps.put(stringObjectEntry.getKey(), value);
|
||||
}
|
||||
configuration.setAuthProperties(currentAuthProps);
|
||||
}
|
||||
configuration.setAuthProperties(currentAuthProps);
|
||||
if (!authProperties.isEmpty()) {
|
||||
|
||||
// Make new Gson parser with type adapters to deserialize into existing credentials
|
||||
@@ -264,25 +277,6 @@ public class WebServiceUtils extends WebCommonUtils {
|
||||
}
|
||||
}
|
||||
|
||||
public static void updateConnectionFromConfig(DBPDataSourceContainer dataSource, WebConnectionConfig config) throws DBWebException {
|
||||
setConnectionConfiguration(dataSource.getDriver(), dataSource.getConnectionConfiguration(), config);
|
||||
dataSource.setName(config.getName());
|
||||
dataSource.setDescription(config.getDescription());
|
||||
if (config.getFolder() != null) {
|
||||
dataSource.setFolder(dataSource.getRegistry().getFolder(config.getFolder()));
|
||||
} else {
|
||||
dataSource.setFolder(null);
|
||||
}
|
||||
getGlobalDataSourceRegistry().getAllFolders().clear();
|
||||
saveAuthProperties(
|
||||
dataSource,
|
||||
dataSource.getConnectionConfiguration(),
|
||||
config.getCredentials(),
|
||||
config.isSaveCredentials(),
|
||||
config.isSharedCredentials()
|
||||
);
|
||||
}
|
||||
|
||||
public static DBNBrowseSettings parseNavigatorSettings(Map<String, Object> settingsMap) {
|
||||
return gson.fromJson(
|
||||
gson.toJsonTree(settingsMap), DataSourceNavigatorSettings.class);
|
||||
|
||||
+8
-1
@@ -19,12 +19,15 @@ package io.cloudbeaver.model;
|
||||
import io.cloudbeaver.DBWebException;
|
||||
import io.cloudbeaver.WebServiceUtils;
|
||||
import io.cloudbeaver.model.session.WebSession;
|
||||
import io.cloudbeaver.utils.WebCommonUtils;
|
||||
import org.jkiss.dbeaver.model.connection.DBPAuthModelDescriptor;
|
||||
import org.jkiss.dbeaver.model.meta.Property;
|
||||
import org.jkiss.dbeaver.model.preferences.DBPPropertyDescriptor;
|
||||
import org.jkiss.dbeaver.model.preferences.DBPPropertySource;
|
||||
import org.jkiss.utils.CommonUtils;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
/**
|
||||
* WebDatabaseAuthModel
|
||||
@@ -73,8 +76,12 @@ public class WebDatabaseAuthModel {
|
||||
public WebPropertyInfo[] getProperties() throws DBWebException {
|
||||
|
||||
DBPPropertySource credentialsSource = model.createCredentialsSource(null, null);
|
||||
Predicate<DBPPropertyDescriptor> predicate = CommonUtils.isEmpty(getRequiredAuth())
|
||||
? p -> true
|
||||
: p -> WebCommonUtils.isAuthPropertyApplicable(p, webSession.getContextCredentialsProviders());
|
||||
|
||||
return Arrays.stream(credentialsSource.getProperties())
|
||||
.filter(p -> WebServiceUtils.isAuthPropertyApplicable(p, webSession.getContextCredentialsProviders()))
|
||||
.filter(predicate)
|
||||
.map(p -> new WebPropertyInfo(webSession, p, credentialsSource)).toArray(WebPropertyInfo[]::new);
|
||||
}
|
||||
|
||||
|
||||
+3
-4
@@ -213,6 +213,7 @@ public class WebDatabaseDriverInfo {
|
||||
return new String[0];
|
||||
}
|
||||
return NetworkHandlerRegistry.getInstance().getDescriptors(driver).stream()
|
||||
.filter(h -> !h.isDesktopHandler())
|
||||
.map(NetworkHandlerDescriptor::getId).toArray(String[]::new);
|
||||
}
|
||||
|
||||
@@ -240,10 +241,8 @@ public class WebDatabaseDriverInfo {
|
||||
|
||||
@Property
|
||||
public boolean getRequiresServerName() {
|
||||
if (driver.getSampleURL() == null) {
|
||||
return false;
|
||||
}
|
||||
return driver.getSampleURL().contains(URL_SERVER_FIELD);
|
||||
return driver.getSupportedPageFields().contains(DBConstants.PROP_SERVER) ||
|
||||
(driver.getSampleURL() != null && driver.getSampleURL().contains(URL_SERVER_FIELD));
|
||||
}
|
||||
|
||||
@Property
|
||||
|
||||
@@ -212,4 +212,9 @@ public class WebServerConfig {
|
||||
public String getDefaultAuthRole() {
|
||||
return application.getDefaultAuthRole();
|
||||
}
|
||||
|
||||
@Property
|
||||
public String getDefaultUserTeam() {
|
||||
return application.getAppConfiguration().getDefaultUserTeam();
|
||||
}
|
||||
}
|
||||
|
||||
+10
-1
@@ -17,7 +17,8 @@
|
||||
package io.cloudbeaver.model.user;
|
||||
|
||||
import io.cloudbeaver.WebServiceUtils;
|
||||
import io.cloudbeaver.model.app.WebApplication;
|
||||
import io.cloudbeaver.auth.SMAuthProviderFederated;
|
||||
import io.cloudbeaver.auth.provisioning.SMProvisioner;
|
||||
import io.cloudbeaver.registry.WebAuthProviderConfiguration;
|
||||
import io.cloudbeaver.registry.WebAuthProviderDescriptor;
|
||||
import io.cloudbeaver.server.CBApplication;
|
||||
@@ -70,6 +71,10 @@ public class WebAuthProviderInfo {
|
||||
return descriptor.isConfigurable();
|
||||
}
|
||||
|
||||
public boolean isFederated() {
|
||||
return descriptor.getInstance() instanceof SMAuthProviderFederated;
|
||||
}
|
||||
|
||||
public boolean isTrusted() {
|
||||
return descriptor.isTrusted();
|
||||
}
|
||||
@@ -78,6 +83,10 @@ public class WebAuthProviderInfo {
|
||||
return descriptor.isPrivate();
|
||||
}
|
||||
|
||||
public boolean isSupportProvisioning() {
|
||||
return descriptor.getInstance() instanceof SMProvisioner;
|
||||
}
|
||||
|
||||
public List<WebAuthProviderConfiguration> getConfigurations() {
|
||||
List<WebAuthProviderConfiguration> result = new ArrayList<>();
|
||||
for (SMAuthProviderCustomConfiguration cfg : CBApplication.getInstance().getAppConfiguration().getAuthCustomConfigurations()) {
|
||||
|
||||
+158
-41
@@ -16,6 +16,7 @@
|
||||
*/
|
||||
package io.cloudbeaver.server;
|
||||
|
||||
import org.jkiss.dbeaver.model.auth.AuthInfo;
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.GsonBuilder;
|
||||
import com.google.gson.InstanceCreator;
|
||||
@@ -24,7 +25,6 @@ import io.cloudbeaver.auth.CBAuthConstants;
|
||||
import io.cloudbeaver.model.app.BaseWebApplication;
|
||||
import io.cloudbeaver.model.app.WebAuthApplication;
|
||||
import io.cloudbeaver.model.app.WebAuthConfiguration;
|
||||
import io.cloudbeaver.model.session.WebAuthInfo;
|
||||
import io.cloudbeaver.registry.WebDriverRegistry;
|
||||
import io.cloudbeaver.registry.WebServiceRegistry;
|
||||
import io.cloudbeaver.server.jetty.CBJettyServer;
|
||||
@@ -33,6 +33,7 @@ import io.cloudbeaver.service.DBWServiceServerConfigurator;
|
||||
import io.cloudbeaver.service.security.SMControllerConfiguration;
|
||||
import io.cloudbeaver.service.session.WebSessionManager;
|
||||
import io.cloudbeaver.utils.WebAppUtils;
|
||||
import io.cloudbeaver.utils.WebDataSourceUtils;
|
||||
import org.eclipse.core.runtime.Platform;
|
||||
import org.eclipse.osgi.service.datalocation.Location;
|
||||
import org.jkiss.code.NotNull;
|
||||
@@ -49,7 +50,7 @@ import org.jkiss.dbeaver.model.runtime.DBRProgressMonitor;
|
||||
import org.jkiss.dbeaver.model.security.SMAdminController;
|
||||
import org.jkiss.dbeaver.model.security.SMAuthProviderCustomConfiguration;
|
||||
import org.jkiss.dbeaver.model.security.SMConstants;
|
||||
import org.jkiss.dbeaver.model.security.SMObjects;
|
||||
import org.jkiss.dbeaver.model.security.SMObjectType;
|
||||
import org.jkiss.dbeaver.model.websocket.event.WSEventController;
|
||||
import org.jkiss.dbeaver.model.websocket.event.WSServerConfigurationChangedEvent;
|
||||
import org.jkiss.dbeaver.registry.BaseApplicationImpl;
|
||||
@@ -107,6 +108,7 @@ public abstract class CBApplication extends BaseWebApplication implements WebAut
|
||||
protected int serverPort = CBConstants.DEFAULT_SERVER_PORT;
|
||||
private String serverHost = null;
|
||||
private String serverName = null;
|
||||
private String sslConfigurationPath = null;
|
||||
private String contentRoot = CBConstants.DEFAULT_CONTENT_ROOT;
|
||||
private String rootURI = CBConstants.DEFAULT_ROOT_URI;
|
||||
private String servicesURI = CBConstants.DEFAULT_SERVICES_URI;
|
||||
@@ -257,11 +259,13 @@ public abstract class CBApplication extends BaseWebApplication implements WebAut
|
||||
if (CommonUtils.isEmpty(localHostAddress)) {
|
||||
localHostAddress = System.getProperty(CBConstants.VAR_CB_LOCAL_HOST_ADDR);
|
||||
}
|
||||
if (CommonUtils.isEmpty(localHostAddress) || "127.0.0.1".equals(localHostAddress) || "::0".equals(localHostAddress)) {
|
||||
if (CommonUtils.isEmpty(localHostAddress) || "127.0.0.1".equals(localHostAddress) || "::0".equals(
|
||||
localHostAddress)) {
|
||||
localHostAddress = "localhost";
|
||||
}
|
||||
|
||||
final Runtime runtime = Runtime.getRuntime();
|
||||
initializeAdditionalConfiguration();
|
||||
|
||||
Location instanceLoc = Platform.getInstanceLocation();
|
||||
try {
|
||||
@@ -278,8 +282,10 @@ public abstract class CBApplication extends BaseWebApplication implements WebAut
|
||||
}
|
||||
|
||||
log.debug(GeneralUtils.getProductName() + " " + GeneralUtils.getProductVersion() + " is starting"); //$NON-NLS-1$
|
||||
log.debug("\tOS: " + System.getProperty(StandardConstants.ENV_OS_NAME) + " " + System.getProperty(StandardConstants.ENV_OS_VERSION) + " (" + System.getProperty(StandardConstants.ENV_OS_ARCH) + ")");
|
||||
log.debug("\tJava version: " + System.getProperty(StandardConstants.ENV_JAVA_VERSION) + " by " + System.getProperty(StandardConstants.ENV_JAVA_VENDOR) + " (" + System.getProperty(StandardConstants.ENV_JAVA_ARCH) + "bit)");
|
||||
log.debug("\tOS: " + System.getProperty(StandardConstants.ENV_OS_NAME) + " " + System.getProperty(
|
||||
StandardConstants.ENV_OS_VERSION) + " (" + System.getProperty(StandardConstants.ENV_OS_ARCH) + ")");
|
||||
log.debug("\tJava version: " + System.getProperty(StandardConstants.ENV_JAVA_VERSION) + " by " + System.getProperty(
|
||||
StandardConstants.ENV_JAVA_VENDOR) + " (" + System.getProperty(StandardConstants.ENV_JAVA_ARCH) + "bit)");
|
||||
log.debug("\tInstall path: '" + SystemVariablesResolver.getInstallPath() + "'"); //$NON-NLS-1$ //$NON-NLS-2$
|
||||
log.debug("\tGlobal workspace: '" + instanceLoc.getURL() + "'"); //$NON-NLS-1$ //$NON-NLS-2$
|
||||
log.debug("\tMemory available " + (runtime.totalMemory() / (1024 * 1024)) + "Mb/" + (runtime.maxMemory() / (1024 * 1024)) + "Mb");
|
||||
@@ -309,7 +315,8 @@ public abstract class CBApplication extends BaseWebApplication implements WebAut
|
||||
}
|
||||
{
|
||||
// Perform services initialization
|
||||
for (DBWServiceInitializer wsi : WebServiceRegistry.getInstance().getWebServices(DBWServiceInitializer.class)) {
|
||||
for (DBWServiceInitializer wsi : WebServiceRegistry.getInstance()
|
||||
.getWebServices(DBWServiceInitializer.class)) {
|
||||
try {
|
||||
wsi.initializeService(this);
|
||||
} catch (Exception e) {
|
||||
@@ -319,14 +326,6 @@ public abstract class CBApplication extends BaseWebApplication implements WebAut
|
||||
|
||||
}
|
||||
|
||||
{
|
||||
try {
|
||||
initializeSecurityController();
|
||||
} catch (Exception e) {
|
||||
log.error("Error initializing database", e);
|
||||
return;
|
||||
}
|
||||
}
|
||||
try {
|
||||
initializeServer();
|
||||
} catch (DBException e) {
|
||||
@@ -334,11 +333,22 @@ public abstract class CBApplication extends BaseWebApplication implements WebAut
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
initializeSecurityController();
|
||||
} catch (Exception e) {
|
||||
log.error("Error initializing database", e);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
if (configurationMode) {
|
||||
// Try to configure automatically
|
||||
performAutoConfiguration(configPath.toFile().getParentFile());
|
||||
} else if (appConfiguration.isGrantConnectionsAccessToAnonymousTeam() && !isMultiNode()) {
|
||||
grantAnonymousAccessToConnections(appConfiguration, "auto-grant");
|
||||
} else if (!isMultiNode()) {
|
||||
if (appConfiguration.isGrantConnectionsAccessToAnonymousTeam()) {
|
||||
grantAnonymousAccessToConnections(appConfiguration, CBConstants.ADMIN_AUTO_GRANT);
|
||||
}
|
||||
grantPermissionsToConnections();
|
||||
}
|
||||
|
||||
if (enableSecurityManager) {
|
||||
@@ -360,6 +370,10 @@ public abstract class CBApplication extends BaseWebApplication implements WebAut
|
||||
return;
|
||||
}
|
||||
|
||||
protected void initializeAdditionalConfiguration() {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures server automatically.
|
||||
* Called on startup
|
||||
@@ -372,7 +386,8 @@ public abstract class CBApplication extends BaseWebApplication implements WebAut
|
||||
String autoAdminName = System.getenv(CBConstants.VAR_AUTO_CB_ADMIN_NAME);
|
||||
String autoAdminPassword = System.getenv(CBConstants.VAR_AUTO_CB_ADMIN_PASSWORD);
|
||||
|
||||
if (CommonUtils.isEmpty(autoServerName) || CommonUtils.isEmpty(autoAdminName) || CommonUtils.isEmpty(autoAdminPassword)) {
|
||||
if (CommonUtils.isEmpty(autoServerName) || CommonUtils.isEmpty(autoAdminName) || CommonUtils.isEmpty(
|
||||
autoAdminPassword)) {
|
||||
// Try to load from auto config file
|
||||
if (configPath.exists()) {
|
||||
File autoConfigFile = new File(configPath, CBConstants.AUTO_CONFIG_FILE_NAME);
|
||||
@@ -386,13 +401,15 @@ public abstract class CBApplication extends BaseWebApplication implements WebAut
|
||||
autoAdminName = autoProps.getProperty(CBConstants.VAR_AUTO_CB_ADMIN_NAME);
|
||||
autoAdminPassword = autoProps.getProperty(CBConstants.VAR_AUTO_CB_ADMIN_PASSWORD);
|
||||
} catch (IOException e) {
|
||||
log.error("Error loading auto configuration file '" + autoConfigFile.getAbsolutePath() + "'", e);
|
||||
log.error("Error loading auto configuration file '" + autoConfigFile.getAbsolutePath() + "'",
|
||||
e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (CommonUtils.isEmpty(autoServerName) || CommonUtils.isEmpty(autoAdminName) || CommonUtils.isEmpty(autoAdminPassword)) {
|
||||
if (CommonUtils.isEmpty(autoServerName) || CommonUtils.isEmpty(autoAdminName) || CommonUtils.isEmpty(
|
||||
autoAdminPassword)) {
|
||||
log.info("No auto configuration was found. Server must be configured manually");
|
||||
return;
|
||||
}
|
||||
@@ -488,6 +505,7 @@ public abstract class CBApplication extends BaseWebApplication implements WebAut
|
||||
protected Path loadServerConfiguration() throws DBException {
|
||||
Path path = super.loadServerConfiguration();
|
||||
|
||||
|
||||
File runtimeConfigFile = getRuntimeAppConfigFile();
|
||||
if (runtimeConfigFile.exists()) {
|
||||
log.debug("Runtime configuration [" + runtimeConfigFile.getAbsolutePath() + "]");
|
||||
@@ -507,7 +525,9 @@ public abstract class CBApplication extends BaseWebApplication implements WebAut
|
||||
parseConfiguration(configPath.toFile());
|
||||
}
|
||||
// Set default preferences
|
||||
PrefUtils.setDefaultPreferenceValue(ModelPreferences.getPreferences(), ModelPreferences.UI_DRIVERS_HOME, getDriversLocation());
|
||||
PrefUtils.setDefaultPreferenceValue(ModelPreferences.getPreferences(),
|
||||
ModelPreferences.UI_DRIVERS_HOME,
|
||||
getDriversLocation());
|
||||
}
|
||||
|
||||
private void parseConfiguration(File configFile) throws DBException {
|
||||
@@ -535,6 +555,7 @@ public abstract class CBApplication extends BaseWebApplication implements WebAut
|
||||
}
|
||||
|
||||
serverName = JSONUtils.getString(serverConfig, CBConstants.PARAM_SERVER_NAME, serverName);
|
||||
sslConfigurationPath = JSONUtils.getString(serverConfig, CBConstants.PARAM_SSL_CONFIGURATION_PATH, sslConfigurationPath);
|
||||
contentRoot = WebAppUtils.getRelativePath(
|
||||
JSONUtils.getString(serverConfig, CBConstants.PARAM_CONTENT_ROOT, contentRoot), homeFolder);
|
||||
rootURI = readRootUri(serverConfig);
|
||||
@@ -544,10 +565,14 @@ public abstract class CBApplication extends BaseWebApplication implements WebAut
|
||||
workspaceLocation = WebAppUtils.getRelativePath(
|
||||
JSONUtils.getString(serverConfig, CBConstants.PARAM_WORKSPACE_LOCATION, workspaceLocation), homeFolder);
|
||||
|
||||
maxSessionIdleTime = JSONUtils.getLong(serverConfig, CBConstants.PARAM_SESSION_EXPIRE_PERIOD, maxSessionIdleTime);
|
||||
maxSessionIdleTime = JSONUtils.getLong(serverConfig,
|
||||
CBConstants.PARAM_SESSION_EXPIRE_PERIOD,
|
||||
maxSessionIdleTime);
|
||||
|
||||
develMode = JSONUtils.getBoolean(serverConfig, CBConstants.PARAM_DEVEL_MODE, develMode);
|
||||
enableSecurityManager = JSONUtils.getBoolean(serverConfig, CBConstants.PARAM_SECURITY_MANAGER, enableSecurityManager);
|
||||
enableSecurityManager = JSONUtils.getBoolean(serverConfig,
|
||||
CBConstants.PARAM_SECURITY_MANAGER,
|
||||
enableSecurityManager);
|
||||
//SM config
|
||||
gson.fromJson(
|
||||
gson.toJsonTree(JSONUtils.getObject(serverConfig, CBConstants.PARAM_SM_CONFIGURATION)),
|
||||
@@ -629,7 +654,8 @@ public abstract class CBApplication extends BaseWebApplication implements WebAut
|
||||
|
||||
}
|
||||
|
||||
protected void readProductConfiguration(Map<String, Object> serverConfig, Gson gson, String homeFolder) throws DBException {
|
||||
protected void readProductConfiguration(Map<String, Object> serverConfig, Gson gson, String homeFolder)
|
||||
throws DBException {
|
||||
String productConfigPath = WebAppUtils.getRelativePath(
|
||||
JSONUtils.getString(
|
||||
serverConfig,
|
||||
@@ -645,7 +671,8 @@ public abstract class CBApplication extends BaseWebApplication implements WebAut
|
||||
log.error("Product configuration file not found (" + productConfigFile.getAbsolutePath() + "'");
|
||||
} else {
|
||||
log.debug("Load product configuration from '" + productConfigFile.getAbsolutePath() + "'");
|
||||
try (Reader reader = new InputStreamReader(new FileInputStream(productConfigFile), StandardCharsets.UTF_8)) {
|
||||
try (Reader reader = new InputStreamReader(new FileInputStream(productConfigFile),
|
||||
StandardCharsets.UTF_8)) {
|
||||
productConfiguration.putAll(JSONUtils.parseMap(gson, reader));
|
||||
} catch (Exception e) {
|
||||
throw new DBException("Error reading product configuration", e);
|
||||
@@ -665,6 +692,22 @@ public abstract class CBApplication extends BaseWebApplication implements WebAut
|
||||
}
|
||||
}
|
||||
|
||||
protected Map<String, Object> readConnectionsPermissionsConfiguration(Path parentPath) {
|
||||
String permissionsConfigPath = WebAppUtils.getRelativePath(CBConstants.DEFAULT_DATASOURCE_PERMISSIONS_CONFIGURATION,
|
||||
parentPath);
|
||||
File permissionsConfigFile = new File(permissionsConfigPath);
|
||||
if (permissionsConfigFile.exists()) {
|
||||
log.debug("Load permissions configuration from '" + permissionsConfigFile.getAbsolutePath() + "'");
|
||||
try (Reader reader = new InputStreamReader(new FileInputStream(permissionsConfigFile),
|
||||
StandardCharsets.UTF_8)) {
|
||||
return JSONUtils.parseMap(getGson(), reader);
|
||||
} catch (Exception e) {
|
||||
log.error("Error reading permissions configuration", e);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
protected Map<String, Object> readConfiguration(File configFile) throws DBException {
|
||||
Map<String, Object> configProps = new LinkedHashMap<>();
|
||||
if (configFile.exists()) {
|
||||
@@ -702,7 +745,7 @@ public abstract class CBApplication extends BaseWebApplication implements WebAut
|
||||
public Map<String, Object> readConfigurationFile(File configFile) throws DBException {
|
||||
try (Reader reader = new InputStreamReader(new FileInputStream(configFile), StandardCharsets.UTF_8)) {
|
||||
return JSONUtils.parseMap(getGson(), reader);
|
||||
} catch (IOException e) {
|
||||
} catch (Exception e) {
|
||||
throw new DBException("Error parsing server configuration", e);
|
||||
}
|
||||
}
|
||||
@@ -784,7 +827,7 @@ public abstract class CBApplication extends BaseWebApplication implements WebAut
|
||||
@NotNull String newServerURL,
|
||||
@NotNull String adminName,
|
||||
@Nullable String adminPassword,
|
||||
@NotNull List<WebAuthInfo> authInfoList,
|
||||
@NotNull List<AuthInfo> authInfoList,
|
||||
long sessionExpireTime,
|
||||
@NotNull CBAppConfig appConfig,
|
||||
@Nullable SMCredentialsProvider credentialsProvider
|
||||
@@ -820,7 +863,8 @@ public abstract class CBApplication extends BaseWebApplication implements WebAut
|
||||
configurationMode = CommonUtils.isEmpty(serverName);
|
||||
|
||||
// Reloading configuration by services
|
||||
for (DBWServiceServerConfigurator wsc : WebServiceRegistry.getInstance().getWebServices(DBWServiceServerConfigurator.class)) {
|
||||
for (DBWServiceServerConfigurator wsc : WebServiceRegistry.getInstance()
|
||||
.getWebServices(DBWServiceServerConfigurator.class)) {
|
||||
try {
|
||||
wsc.reloadConfiguration(appConfig);
|
||||
} catch (Exception e) {
|
||||
@@ -844,7 +888,7 @@ public abstract class CBApplication extends BaseWebApplication implements WebAut
|
||||
protected abstract void finishSecurityServiceConfiguration(
|
||||
@NotNull String adminName,
|
||||
@Nullable String adminPassword,
|
||||
@NotNull List<WebAuthInfo> authInfoList
|
||||
@NotNull List<AuthInfo> authInfoList
|
||||
) throws DBException;
|
||||
|
||||
public synchronized void flushConfiguration(SMCredentialsProvider credentialsProvider) throws DBException {
|
||||
@@ -857,11 +901,13 @@ public abstract class CBApplication extends BaseWebApplication implements WebAut
|
||||
String anonymousTeamId = appConfig.getAnonymousUserTeam();
|
||||
var securityController = getSecurityController();
|
||||
for (DBPDataSourceContainer ds : WebServiceUtils.getGlobalDataSourceRegistry().getDataSources()) {
|
||||
var datasourcePermissions = securityController.getObjectPermissions(anonymousTeamId, ds.getId(), SMObjects.DATASOURCE);
|
||||
var datasourcePermissions = securityController.getObjectPermissions(anonymousTeamId,
|
||||
ds.getId(),
|
||||
SMObjectType.datasource);
|
||||
if (ArrayUtils.isEmpty(datasourcePermissions.getPermissions())) {
|
||||
securityController.setObjectPermissions(
|
||||
Set.of(ds.getId()),
|
||||
SMObjects.DATASOURCE,
|
||||
SMObjectType.datasource,
|
||||
Set.of(anonymousTeamId),
|
||||
Set.of(SMConstants.DATA_SOURCE_ACCESS_PERMISSION),
|
||||
adminName
|
||||
@@ -873,6 +919,37 @@ public abstract class CBApplication extends BaseWebApplication implements WebAut
|
||||
}
|
||||
}
|
||||
|
||||
private void grantPermissionsToConnections() {
|
||||
try {
|
||||
var globalRegistry = WebDataSourceUtils.getGlobalDataSourceRegistry();
|
||||
var permissionsConfiguration = readConnectionsPermissionsConfiguration(globalRegistry.getProject()
|
||||
.getMetadataFolder(false));
|
||||
if (permissionsConfiguration == null) {
|
||||
return;
|
||||
}
|
||||
for (var entry : permissionsConfiguration.entrySet()) {
|
||||
var dataSourceId = entry.getKey();
|
||||
var ds = globalRegistry.getDataSource(dataSourceId);
|
||||
if (ds == null) {
|
||||
log.error("Connection " + dataSourceId + " is not found in project " + globalRegistry.getProject()
|
||||
.getName());
|
||||
}
|
||||
List<String> permissions = JSONUtils.getStringList(permissionsConfiguration, dataSourceId);
|
||||
var securityController = getSecurityController();
|
||||
securityController.deleteAllObjectPermissions(dataSourceId, SMObjectType.datasource);
|
||||
securityController.setObjectPermissions(
|
||||
Set.of(dataSourceId),
|
||||
SMObjectType.datasource,
|
||||
new HashSet<>(permissions),
|
||||
Set.of(SMConstants.DATA_SOURCE_ACCESS_PERMISSION),
|
||||
CBConstants.ADMIN_AUTO_GRANT
|
||||
);
|
||||
}
|
||||
} catch (DBException e) {
|
||||
log.error("Error granting permissions to connections", e);
|
||||
}
|
||||
}
|
||||
|
||||
protected void saveRuntimeConfig(
|
||||
String newServerName,
|
||||
String newServerURL,
|
||||
@@ -921,7 +998,10 @@ public abstract class CBApplication extends BaseWebApplication implements WebAut
|
||||
var originServerConfig = getServerConfigProps(this.originalConfigurationProperties); // get server properties from original configuration file
|
||||
rootConfig.put("server", serverConfigProperties);
|
||||
if (!CommonUtils.isEmpty(newServerName)) {
|
||||
copyConfigValue(originServerConfig, serverConfigProperties, CBConstants.PARAM_SERVER_NAME, newServerName);
|
||||
copyConfigValue(originServerConfig,
|
||||
serverConfigProperties,
|
||||
CBConstants.PARAM_SERVER_NAME,
|
||||
newServerName);
|
||||
}
|
||||
if (!CommonUtils.isEmpty(newServerURL)) {
|
||||
copyConfigValue(
|
||||
@@ -929,10 +1009,14 @@ public abstract class CBApplication extends BaseWebApplication implements WebAut
|
||||
}
|
||||
if (sessionExpireTime > 0) {
|
||||
copyConfigValue(
|
||||
originServerConfig, serverConfigProperties, CBConstants.PARAM_SESSION_EXPIRE_PERIOD, sessionExpireTime);
|
||||
originServerConfig,
|
||||
serverConfigProperties,
|
||||
CBConstants.PARAM_SESSION_EXPIRE_PERIOD,
|
||||
sessionExpireTime);
|
||||
}
|
||||
var databaseConfigProperties = new LinkedHashMap<String, Object>();
|
||||
Map<String, Object> oldRuntimeDBConfig = JSONUtils.getObject(originServerConfig, CBConstants.PARAM_DB_CONFIGURATION);
|
||||
Map<String, Object> oldRuntimeDBConfig = JSONUtils.getObject(originServerConfig,
|
||||
CBConstants.PARAM_DB_CONFIGURATION);
|
||||
if (!CommonUtils.isEmpty(databaseConfiguration) && !isDistributed()) {
|
||||
for (Map.Entry<String, Object> mp : databaseConfiguration.entrySet()) {
|
||||
copyConfigValue(oldRuntimeDBConfig, databaseConfigProperties, mp.getKey(), mp.getValue());
|
||||
@@ -948,23 +1032,41 @@ public abstract class CBApplication extends BaseWebApplication implements WebAut
|
||||
copyConfigValue(
|
||||
oldAppConfig, appConfigProperties, "anonymousAccessEnabled", appConfig.isAnonymousAccessEnabled());
|
||||
copyConfigValue(
|
||||
oldAppConfig, appConfigProperties, "supportsCustomConnections", appConfig.isSupportsCustomConnections());
|
||||
oldAppConfig,
|
||||
appConfigProperties,
|
||||
"supportsCustomConnections",
|
||||
appConfig.isSupportsCustomConnections());
|
||||
copyConfigValue(
|
||||
oldAppConfig, appConfigProperties, "publicCredentialsSaveEnabled", appConfig.isPublicCredentialsSaveEnabled());
|
||||
oldAppConfig,
|
||||
appConfigProperties,
|
||||
"publicCredentialsSaveEnabled",
|
||||
appConfig.isPublicCredentialsSaveEnabled());
|
||||
copyConfigValue(
|
||||
oldAppConfig, appConfigProperties, "adminCredentialsSaveEnabled", appConfig.isAdminCredentialsSaveEnabled());
|
||||
oldAppConfig,
|
||||
appConfigProperties,
|
||||
"adminCredentialsSaveEnabled",
|
||||
appConfig.isAdminCredentialsSaveEnabled());
|
||||
copyConfigValue(
|
||||
oldAppConfig, appConfigProperties, "enableReverseProxyAuth", appConfig.isEnabledReverseProxyAuth());
|
||||
copyConfigValue(
|
||||
oldAppConfig, appConfigProperties, "forwardProxy", appConfig.isEnabledForwardProxy());
|
||||
copyConfigValue(
|
||||
oldAppConfig, appConfigProperties, "linkExternalCredentialsWithUser", appConfig.isLinkExternalCredentialsWithUser());
|
||||
oldAppConfig,
|
||||
appConfigProperties,
|
||||
"linkExternalCredentialsWithUser",
|
||||
appConfig.isLinkExternalCredentialsWithUser());
|
||||
copyConfigValue(
|
||||
oldAppConfig, appConfigProperties, "redirectOnFederatedAuth", appConfig.isRedirectOnFederatedAuth());
|
||||
copyConfigValue(
|
||||
oldAppConfig, appConfigProperties, CBConstants.PARAM_RESOURCE_MANAGER_ENABLED, appConfig.isResourceManagerEnabled());
|
||||
oldAppConfig,
|
||||
appConfigProperties,
|
||||
CBConstants.PARAM_RESOURCE_MANAGER_ENABLED,
|
||||
appConfig.isResourceManagerEnabled());
|
||||
copyConfigValue(
|
||||
oldAppConfig, appConfigProperties, CBConstants.PARAM_SHOW_READ_ONLY_CONN_INFO, appConfig.isShowReadOnlyConnectionInfo());
|
||||
oldAppConfig,
|
||||
appConfigProperties,
|
||||
CBConstants.PARAM_SHOW_READ_ONLY_CONN_INFO,
|
||||
appConfig.isShowReadOnlyConnectionInfo());
|
||||
copyConfigValue(
|
||||
oldAppConfig,
|
||||
appConfigProperties,
|
||||
@@ -972,7 +1074,8 @@ public abstract class CBApplication extends BaseWebApplication implements WebAut
|
||||
appConfig.isGrantConnectionsAccessToAnonymousTeam());
|
||||
|
||||
Map<String, Object> resourceQuotas = new LinkedHashMap<>();
|
||||
Map<String, Object> originResourceQuotas = JSONUtils.getObject(oldAppConfig, CBConstants.PARAM_RESOURCE_QUOTAS);
|
||||
Map<String, Object> originResourceQuotas = JSONUtils.getObject(oldAppConfig,
|
||||
CBConstants.PARAM_RESOURCE_QUOTAS);
|
||||
for (Map.Entry<String, Object> mp : appConfig.getResourceQuotas().entrySet()) {
|
||||
copyConfigValue(originResourceQuotas, resourceQuotas, mp.getKey(), mp.getValue());
|
||||
}
|
||||
@@ -1086,7 +1189,12 @@ public abstract class CBApplication extends BaseWebApplication implements WebAut
|
||||
}
|
||||
|
||||
// gets info about patterns from original configuration file and saves it to runtime config
|
||||
private void copyConfigValue(Map<String, Object> oldConfig, Map<String, Object> newConfig, String key, Object defaultValue) {
|
||||
private void copyConfigValue(
|
||||
Map<String, Object> oldConfig,
|
||||
Map<String, Object> newConfig,
|
||||
String key,
|
||||
Object defaultValue
|
||||
) {
|
||||
Object value = oldConfig.get(key);
|
||||
if (value instanceof Map && defaultValue instanceof Map) {
|
||||
Map<String, Object> subValue = new LinkedHashMap<>();
|
||||
@@ -1117,4 +1225,13 @@ public abstract class CBApplication extends BaseWebApplication implements WebAut
|
||||
}
|
||||
return containerId;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public Path getSslConfigurationPath() {
|
||||
if (sslConfigurationPath == null) {
|
||||
return null;
|
||||
}
|
||||
var sslConfiguration = Path.of(sslConfigurationPath);
|
||||
return sslConfiguration.isAbsolute() ? sslConfiguration : getHomeDirectory().resolve(sslConfiguration);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,9 +16,9 @@
|
||||
*/
|
||||
package io.cloudbeaver.server;
|
||||
|
||||
import org.jkiss.dbeaver.model.auth.AuthInfo;
|
||||
import io.cloudbeaver.auth.NoAuthCredentialsProvider;
|
||||
import io.cloudbeaver.model.rm.local.LocalResourceController;
|
||||
import io.cloudbeaver.model.session.WebAuthInfo;
|
||||
import io.cloudbeaver.service.security.CBEmbeddedSecurityController;
|
||||
import io.cloudbeaver.service.security.EmbeddedSecurityControllerFactory;
|
||||
import org.jkiss.code.NotNull;
|
||||
@@ -31,7 +31,6 @@ import org.jkiss.dbeaver.model.auth.SMCredentialsProvider;
|
||||
import org.jkiss.dbeaver.model.rm.RMController;
|
||||
import org.jkiss.dbeaver.model.security.SMAdminController;
|
||||
import org.jkiss.dbeaver.model.security.SMController;
|
||||
import org.jkiss.dbeaver.registry.BasePlatformImpl;
|
||||
import org.jkiss.dbeaver.registry.LocalFileController;
|
||||
import org.jkiss.dbeaver.runtime.DBWorkbench;
|
||||
|
||||
@@ -94,7 +93,7 @@ public class CBApplicationCE extends CBApplication {
|
||||
protected void finishSecurityServiceConfiguration(
|
||||
@NotNull String adminName,
|
||||
@Nullable String adminPassword,
|
||||
@NotNull List<WebAuthInfo> authInfoList
|
||||
@NotNull List<AuthInfo> authInfoList
|
||||
) throws DBException {
|
||||
if (securityController instanceof CBEmbeddedSecurityController) {
|
||||
((CBEmbeddedSecurityController) securityController).finishConfiguration(adminName, adminPassword, authInfoList);
|
||||
|
||||
@@ -28,6 +28,7 @@ public class CBConstants {
|
||||
public static final String PARAM_SERVER_PORT = "serverPort";
|
||||
public static final String PARAM_SERVER_HOST = "serverHost";
|
||||
public static final String PARAM_SERVER_NAME = "serverName";
|
||||
public static final String PARAM_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";
|
||||
@@ -60,6 +61,7 @@ public class CBConstants {
|
||||
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";
|
||||
public static final String DEFAULT_ADMIN_TEAM = "admin";
|
||||
|
||||
@@ -79,4 +81,5 @@ public class CBConstants {
|
||||
public static final String APPLICATION_JSON = "application/json";
|
||||
|
||||
public static final String QUOTA_PROP_FILE_LIMIT = "dataExportFileSizeLimit";
|
||||
public static final String ADMIN_AUTO_GRANT = "auto-grant";
|
||||
}
|
||||
|
||||
@@ -22,8 +22,10 @@ import io.cloudbeaver.server.jobs.SessionStateJob;
|
||||
import io.cloudbeaver.server.jobs.WebSessionMonitorJob;
|
||||
import io.cloudbeaver.service.session.WebSessionManager;
|
||||
import org.eclipse.core.resources.ResourcesPlugin;
|
||||
import org.eclipse.core.runtime.IStatus;
|
||||
import org.eclipse.core.runtime.Platform;
|
||||
import org.eclipse.core.runtime.Plugin;
|
||||
import org.eclipse.core.runtime.Status;
|
||||
import org.jkiss.code.NotNull;
|
||||
import org.jkiss.code.Nullable;
|
||||
import org.jkiss.dbeaver.DBException;
|
||||
@@ -39,6 +41,7 @@ import org.jkiss.dbeaver.model.impl.app.DefaultCertificateStorage;
|
||||
import org.jkiss.dbeaver.model.preferences.DBPPreferenceStore;
|
||||
import org.jkiss.dbeaver.model.qm.QMRegistry;
|
||||
import org.jkiss.dbeaver.model.qm.QMUtils;
|
||||
import org.jkiss.dbeaver.model.runtime.AbstractJob;
|
||||
import org.jkiss.dbeaver.model.runtime.DBRProgressMonitor;
|
||||
import org.jkiss.dbeaver.model.runtime.VoidProgressMonitor;
|
||||
import org.jkiss.dbeaver.registry.BasePlatformImpl;
|
||||
@@ -49,6 +52,7 @@ import org.jkiss.dbeaver.runtime.qm.QMLogFileWriter;
|
||||
import org.jkiss.dbeaver.runtime.qm.QMRegistryImpl;
|
||||
import org.jkiss.dbeaver.utils.ContentUtils;
|
||||
import org.jkiss.dbeaver.utils.GeneralUtils;
|
||||
import org.jkiss.utils.IOUtils;
|
||||
import org.osgi.framework.Bundle;
|
||||
|
||||
import java.io.IOException;
|
||||
@@ -67,6 +71,7 @@ public class CBPlatform extends BasePlatformImpl {
|
||||
public static final String PLUGIN_ID = "io.cloudbeaver.server"; //$NON-NLS-1$
|
||||
|
||||
private static final Log log = Log.getLog(CBPlatform.class);
|
||||
private static final String TEMP_FILE_FOLDER = "temp-sql-upload-files";
|
||||
|
||||
public static final String WORK_DATA_FOLDER_NAME = ".work-data";
|
||||
|
||||
@@ -159,6 +164,17 @@ public class CBPlatform extends BasePlatformImpl {
|
||||
new SessionStateJob(this)
|
||||
.scheduleMonitor();
|
||||
|
||||
new AbstractJob("Delete temp folder") {
|
||||
@Override
|
||||
protected IStatus run(DBRProgressMonitor monitor) {
|
||||
try {
|
||||
IOUtils.deleteDirectory(getTempFolder(monitor, TEMP_FILE_FOLDER));
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
return Status.OK_STATUS;
|
||||
}
|
||||
}.schedule();
|
||||
log.info("Web platform initialized (" + (System.currentTimeMillis() - startTime) + "ms)");
|
||||
}
|
||||
|
||||
@@ -177,7 +193,7 @@ public class CBPlatform extends BasePlatformImpl {
|
||||
this.queryManager.dispose();
|
||||
//queryManager = null;
|
||||
}
|
||||
DataSourceProviderRegistry.getInstance().dispose();
|
||||
DataSourceProviderRegistry.dispose();
|
||||
|
||||
if (workspace != null) {
|
||||
try {
|
||||
|
||||
+53
@@ -0,0 +1,53 @@
|
||||
/*
|
||||
* DBeaver - Universal Database Manager
|
||||
* Copyright (C) 2010-2023 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.events;
|
||||
|
||||
import io.cloudbeaver.server.CBPlatform;
|
||||
import org.jkiss.code.NotNull;
|
||||
import org.jkiss.dbeaver.Log;
|
||||
import org.jkiss.dbeaver.model.runtime.VoidProgressMonitor;
|
||||
import org.jkiss.dbeaver.model.websocket.WSEventHandler;
|
||||
import org.jkiss.dbeaver.model.websocket.event.WSEventDeleteTempFile;
|
||||
import org.jkiss.utils.IOUtils;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
|
||||
public class WSDeleteTempFileHandler implements WSEventHandler<WSEventDeleteTempFile> {
|
||||
|
||||
private static final Log log = Log.getLog(WSDeleteTempFileHandler.class);
|
||||
private static final String TEMP_FILE_FOLDER = "temp-sql-upload-files";
|
||||
|
||||
public void resetTempFolder(String sessionId) {
|
||||
Path path = CBPlatform.getInstance()
|
||||
.getTempFolder(new VoidProgressMonitor(), TEMP_FILE_FOLDER)
|
||||
.resolve(sessionId);
|
||||
if (Files.exists(path)) {
|
||||
try {
|
||||
IOUtils.deleteDirectory(path);
|
||||
} catch (IOException e) {
|
||||
log.error("Error deleting temp path", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleEvent(@NotNull WSEventDeleteTempFile event) {
|
||||
resetTempFolder(event.getSessionId());
|
||||
}
|
||||
}
|
||||
+35
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
* DBeaver - Universal Database Manager
|
||||
* Copyright (C) 2010-2023 DBeaver Corp
|
||||
*
|
||||
* All Rights Reserved.
|
||||
*
|
||||
* NOTICE: All information contained herein is, and remains
|
||||
* the property of DBeaver Corp and its suppliers, if any.
|
||||
* The intellectual and technical concepts contained
|
||||
* herein are proprietary to DBeaver Corp and its suppliers
|
||||
* and may be covered by U.S. and Foreign Patents,
|
||||
* patents in process, and are protected by trade secret or copyright law.
|
||||
* Dissemination of this information or reproduction of this material
|
||||
* is strictly forbidden unless prior written permission is obtained
|
||||
* from DBeaver Corp.
|
||||
*/
|
||||
package io.cloudbeaver.server.events;
|
||||
|
||||
import org.jkiss.code.NotNull;
|
||||
import org.jkiss.dbeaver.Log;
|
||||
import org.jkiss.dbeaver.model.WorkspaceConfigEventManager;
|
||||
import org.jkiss.dbeaver.model.websocket.event.WSEventType;
|
||||
import org.jkiss.dbeaver.model.websocket.event.WSWorkspaceConfigurationChangedEvent;
|
||||
|
||||
public class WSEventHandlerWorkspaceConfigUpdate extends WSDefaultEventHandler<WSWorkspaceConfigurationChangedEvent> {
|
||||
private static final Log log = Log.getLog(WSEventHandlerWorkspaceConfigUpdate.class);
|
||||
|
||||
@Override
|
||||
public void handleEvent(@NotNull WSWorkspaceConfigurationChangedEvent event) {
|
||||
String configFileName = event.getConfigFilePath();
|
||||
WorkspaceConfigEventManager.fireConfigChangedEvent(configFileName);
|
||||
super.handleEvent(event);
|
||||
}
|
||||
|
||||
}
|
||||
+33
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
* DBeaver - Universal Database Manager
|
||||
* Copyright (C) 2010-2023 DBeaver Corp
|
||||
*
|
||||
* All Rights Reserved.
|
||||
*
|
||||
* NOTICE: All information contained herein is, and remains
|
||||
* the property of DBeaver Corp and its suppliers, if any.
|
||||
* The intellectual and technical concepts contained
|
||||
* herein are proprietary to DBeaver Corp and its suppliers
|
||||
* and may be covered by U.S. and Foreign Patents,
|
||||
* patents in process, and are protected by trade secret or copyright law.
|
||||
* Dissemination of this information or reproduction of this material
|
||||
* is strictly forbidden unless prior written permission is obtained
|
||||
* from DBeaver Corp.
|
||||
*/
|
||||
package io.cloudbeaver.server.events;
|
||||
|
||||
import io.cloudbeaver.model.WebServerMessage;
|
||||
import io.cloudbeaver.model.session.BaseWebSession;
|
||||
import org.jkiss.code.NotNull;
|
||||
import org.jkiss.dbeaver.model.websocket.event.WSSessionLogUpdatedEvent;
|
||||
|
||||
public class WSLogEventHandler extends WSDefaultEventHandler<WSSessionLogUpdatedEvent> {
|
||||
|
||||
protected void updateSessionData(@NotNull BaseWebSession activeUserSession, @NotNull WSSessionLogUpdatedEvent event) {
|
||||
activeUserSession.addSessionMessage(new WebServerMessage(event.getMessageType(), event.getMessage()));
|
||||
}
|
||||
|
||||
protected boolean isAcceptableInSession(@NotNull BaseWebSession activeUserSession, @NotNull WSSessionLogUpdatedEvent event) {
|
||||
return WSWebUtils.isSessionIdEquals(activeUserSession, event.getSessionId());
|
||||
}
|
||||
}
|
||||
+121
@@ -0,0 +1,121 @@
|
||||
/*
|
||||
* DBeaver - Universal Database Manager
|
||||
* Copyright (C) 2010-2023 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.events;
|
||||
|
||||
import io.cloudbeaver.model.session.BaseWebSession;
|
||||
import io.cloudbeaver.model.session.WebSession;
|
||||
import io.cloudbeaver.utils.WebAppUtils;
|
||||
import org.jkiss.code.NotNull;
|
||||
import org.jkiss.dbeaver.DBException;
|
||||
import org.jkiss.dbeaver.Log;
|
||||
import org.jkiss.dbeaver.model.security.SMObjectType;
|
||||
import org.jkiss.dbeaver.model.websocket.event.WSEventType;
|
||||
import org.jkiss.dbeaver.model.websocket.event.WSProjectUpdateEvent;
|
||||
import org.jkiss.dbeaver.model.websocket.event.datasource.WSDataSourceEvent;
|
||||
import org.jkiss.dbeaver.model.websocket.event.datasource.WSDataSourceProperty;
|
||||
import org.jkiss.dbeaver.model.websocket.event.permissions.WSObjectPermissionEvent;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class WSObjectPermissionUpdatedEventHandler extends WSDefaultEventHandler<WSObjectPermissionEvent> {
|
||||
private static final Log log = Log.getLog(WSObjectPermissionUpdatedEventHandler.class);
|
||||
|
||||
@Override
|
||||
protected void updateSessionData(@NotNull BaseWebSession activeUserSession, @NotNull WSObjectPermissionEvent event) {
|
||||
try {
|
||||
// we have accessible data sources only in web session
|
||||
if (event.getSmObjectType() == SMObjectType.datasource && !(activeUserSession instanceof WebSession)) {
|
||||
return;
|
||||
}
|
||||
var objectId = event.getObjectId();
|
||||
|
||||
boolean isAccessibleNow;
|
||||
switch (event.getSmObjectType()) {
|
||||
case project:
|
||||
if (WSEventType.OBJECT_PERMISSIONS_UPDATED.getEventId().equals(event.getId())) {
|
||||
var accessibleProjectIds = activeUserSession.getUserContext().getAccessibleProjectIds();
|
||||
if (accessibleProjectIds.contains(event.getObjectId())) {
|
||||
return;
|
||||
}
|
||||
activeUserSession.addSessionProject(objectId);
|
||||
activeUserSession.addSessionEvent(
|
||||
WSProjectUpdateEvent.create(
|
||||
event.getSessionId(),
|
||||
event.getUserId(),
|
||||
objectId
|
||||
)
|
||||
);
|
||||
} else if (WSEventType.OBJECT_PERMISSIONS_DELETED.getEventId().equals(event.getId())) {
|
||||
activeUserSession.removeSessionProject(objectId);
|
||||
activeUserSession.addSessionEvent(
|
||||
WSProjectUpdateEvent.delete(
|
||||
event.getSessionId(),
|
||||
event.getUserId(),
|
||||
objectId
|
||||
)
|
||||
);
|
||||
}
|
||||
break;
|
||||
case datasource:
|
||||
var webSession = (WebSession) activeUserSession;
|
||||
var dataSources = List.of(objectId);
|
||||
|
||||
var project = webSession.getProjectById(WebAppUtils.getGlobalProjectId());
|
||||
if (project == null) {
|
||||
log.error("Project " + WebAppUtils.getGlobalProjectId() +
|
||||
" is not found in session " + activeUserSession.getSessionId());
|
||||
return;
|
||||
}
|
||||
if (WSEventType.OBJECT_PERMISSIONS_UPDATED.getEventId().equals(event.getId())) {
|
||||
isAccessibleNow = webSession.findWebConnectionInfo(objectId) != null;
|
||||
if (isAccessibleNow) {
|
||||
return;
|
||||
}
|
||||
webSession.addAccessibleConnectionToCache(objectId);
|
||||
webSession.addSessionEvent(
|
||||
WSDataSourceEvent.create(
|
||||
event.getSessionId(),
|
||||
event.getUserId(),
|
||||
WebAppUtils.getGlobalProjectId(),
|
||||
dataSources,
|
||||
WSDataSourceProperty.CONFIGURATION
|
||||
)
|
||||
);
|
||||
} else if (WSEventType.OBJECT_PERMISSIONS_DELETED.getEventId().equals(event.getId())) {
|
||||
webSession.removeAccessibleConnectionFromCache(objectId);
|
||||
webSession.addSessionEvent(
|
||||
WSDataSourceEvent.delete(
|
||||
event.getSessionId(),
|
||||
event.getUserId(),
|
||||
WebAppUtils.getGlobalProjectId(),
|
||||
dataSources,
|
||||
WSDataSourceProperty.CONFIGURATION
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
} catch (DBException e) {
|
||||
log.error("Error on changing permissions for project " +
|
||||
event.getObjectId() + " in session " + activeUserSession.getSessionId(), e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean isAcceptableInSession(@NotNull BaseWebSession activeUserSession, @NotNull WSObjectPermissionEvent event) {
|
||||
return activeUserSession.getUserContext().getUser() != null && super.isAcceptableInSession(activeUserSession, event);
|
||||
}
|
||||
}
|
||||
-100
@@ -1,100 +0,0 @@
|
||||
/*
|
||||
* DBeaver - Universal Database Manager
|
||||
* Copyright (C) 2010-2023 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.events;
|
||||
|
||||
import io.cloudbeaver.model.session.BaseWebSession;
|
||||
import io.cloudbeaver.server.CBPlatform;
|
||||
import org.jkiss.code.NotNull;
|
||||
import org.jkiss.dbeaver.DBException;
|
||||
import org.jkiss.dbeaver.Log;
|
||||
import org.jkiss.dbeaver.model.security.SMObjectPermissionsGrant;
|
||||
import org.jkiss.dbeaver.model.security.SMObjects;
|
||||
import org.jkiss.dbeaver.model.websocket.event.WSProjectUpdateEvent;
|
||||
import org.jkiss.dbeaver.model.websocket.event.permissions.WSObjectPermissionEvent;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class WSPermissionUpdatedEventHandler extends WSDefaultEventHandler<WSObjectPermissionEvent> {
|
||||
private static final Log log = Log.getLog(WSPermissionUpdatedEventHandler.class);
|
||||
private volatile Set<String> subjectsWithProjectAccess;
|
||||
|
||||
@Override
|
||||
public void handleEvent(@NotNull WSObjectPermissionEvent event) {
|
||||
if (!SMObjects.PROJECT_OBJECT_TYPE_ID.equals(event.getSmObjectType().getObjectType())) {
|
||||
return;
|
||||
}
|
||||
var smController = CBPlatform.getInstance().getApplication().getSecurityController();
|
||||
try {
|
||||
var permissionGrants = smController.getObjectPermissionGrants(event.getObjectId(), event.getSmObjectType());
|
||||
subjectsWithProjectAccess = permissionGrants.stream()
|
||||
.map(SMObjectPermissionsGrant::getSubjectId)
|
||||
.collect(Collectors.toSet());
|
||||
super.handleEvent(event);
|
||||
subjectsWithProjectAccess = Set.of();
|
||||
} catch (DBException e) {
|
||||
log.error("Cannot get permission grants", e);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void updateSessionData(@NotNull BaseWebSession activeUserSession, @NotNull WSObjectPermissionEvent event) {
|
||||
try {
|
||||
var user = activeUserSession.getUserContext().getUser();
|
||||
var projectId = event.getObjectId();
|
||||
|
||||
var userSubjects = new HashSet<>(Set.of(user.getTeams()));
|
||||
userSubjects.add(user.getUserId());
|
||||
|
||||
var accessibleProjectIds = activeUserSession.getUserContext().getAccessibleProjectIds();
|
||||
var isAccessibleNow = accessibleProjectIds.contains(projectId);
|
||||
|
||||
var shouldBeAccessible = subjectsWithProjectAccess.stream().anyMatch(userSubjects::contains);
|
||||
|
||||
if (shouldBeAccessible && !isAccessibleNow) {
|
||||
// adding project to session cache
|
||||
activeUserSession.addSessionProject(projectId);
|
||||
activeUserSession.addSessionEvent(
|
||||
WSProjectUpdateEvent.create(
|
||||
event.getSessionId(),
|
||||
event.getUserId(),
|
||||
projectId
|
||||
)
|
||||
);
|
||||
} else if (!shouldBeAccessible && isAccessibleNow) {
|
||||
// removing project from session cache
|
||||
activeUserSession.removeSessionProject(projectId);
|
||||
activeUserSession.addSessionEvent(
|
||||
WSProjectUpdateEvent.delete(
|
||||
event.getSessionId(),
|
||||
event.getUserId(),
|
||||
projectId
|
||||
)
|
||||
);
|
||||
};
|
||||
} catch (DBException e) {
|
||||
log.error("Error on changing permissions for project " + event.getObjectId() + " in session " + event.getSessionId(), e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean isAcceptableInSession(@NotNull BaseWebSession activeUserSession, @NotNull WSObjectPermissionEvent event) {
|
||||
return activeUserSession.getUserContext().getUser() != null && super.isAcceptableInSession(activeUserSession, event);
|
||||
}
|
||||
}
|
||||
+4
-11
@@ -43,35 +43,28 @@ public class WSRmResourceUpdatedEventHandlerImpl extends WSAbstractProjectEventH
|
||||
@Override
|
||||
protected void updateSessionData(@NotNull BaseWebSession activeUserSession, @NotNull WSResourceUpdatedEvent event) {
|
||||
if (activeUserSession instanceof WebSession) {
|
||||
var parsedResourcePath = event.getResourceParsedPath();
|
||||
var resourceParsedPath = parsedResourcePath instanceof RMResource[]
|
||||
? (RMResource[]) parsedResourcePath
|
||||
: gson.fromJson(gson.toJson(parsedResourcePath), RMResource[].class);
|
||||
var webSession = (WebSession) activeUserSession;
|
||||
acceptChangesInNavigatorTree(
|
||||
WSEventType.valueById(event.getId()),
|
||||
resourceParsedPath,
|
||||
event.getResourcePath(),
|
||||
webSession.getProjectById(event.getProjectId())
|
||||
);
|
||||
}
|
||||
activeUserSession.addSessionEvent(event);
|
||||
}
|
||||
|
||||
private void acceptChangesInNavigatorTree(WSEventType eventType,
|
||||
RMResource[] resourceParsedPath,
|
||||
WebProjectImpl project) {
|
||||
List<RMResource> rmResourcePath = Arrays.asList(resourceParsedPath);
|
||||
private void acceptChangesInNavigatorTree(WSEventType eventType, String resourcePath, WebProjectImpl project) {
|
||||
if (eventType == WSEventType.RM_RESOURCE_CREATED) {
|
||||
RMEventManager.fireEvent(
|
||||
new RMEvent(RMEvent.Action.RESOURCE_ADD,
|
||||
project.getRmProject(),
|
||||
rmResourcePath)
|
||||
resourcePath)
|
||||
);
|
||||
} else if (eventType == WSEventType.RM_RESOURCE_DELETED) {
|
||||
RMEventManager.fireEvent(
|
||||
new RMEvent(RMEvent.Action.RESOURCE_DELETE,
|
||||
project.getRmProject(),
|
||||
rmResourcePath)
|
||||
resourcePath)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
+69
@@ -0,0 +1,69 @@
|
||||
/*
|
||||
* DBeaver - Universal Database Manager
|
||||
* Copyright (C) 2010-2023 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.events;
|
||||
|
||||
import io.cloudbeaver.model.session.BaseWebSession;
|
||||
import io.cloudbeaver.service.security.SMUtils;
|
||||
import org.jkiss.code.NotNull;
|
||||
import org.jkiss.dbeaver.DBException;
|
||||
import org.jkiss.dbeaver.Log;
|
||||
import org.jkiss.dbeaver.model.websocket.event.permissions.WSSubjectPermissionEvent;
|
||||
import org.jkiss.utils.ArrayUtils;
|
||||
import org.jkiss.utils.CommonUtils;
|
||||
|
||||
import java.util.HashSet;
|
||||
|
||||
public class WSSubjectPermissionUpdatedEventHandler extends WSDefaultEventHandler<WSSubjectPermissionEvent> {
|
||||
private static final Log log = Log.getLog(WSSubjectPermissionUpdatedEventHandler.class);
|
||||
|
||||
@Override
|
||||
protected void updateSessionData(@NotNull BaseWebSession activeUserSession, @NotNull WSSubjectPermissionEvent event) {
|
||||
var oldUserPermissions = new HashSet<>(activeUserSession.getUserContext().getUserPermissions());
|
||||
try {
|
||||
activeUserSession.getUserContext().refreshSMSession();
|
||||
} catch (DBException e) {
|
||||
activeUserSession.addSessionError(e);
|
||||
log.error("Error refreshing session", e);
|
||||
}
|
||||
activeUserSession.refreshUserData();
|
||||
var newUserPermissions = activeUserSession.getUserContext().getUserPermissions();
|
||||
boolean shouldUpdateData = !(SMUtils.isRMAdmin(oldUserPermissions) && SMUtils.isRMAdmin(newUserPermissions));
|
||||
if (shouldUpdateData) {
|
||||
super.updateSessionData(activeUserSession, event);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean isAcceptableInSession(@NotNull BaseWebSession activeUserSession, @NotNull WSSubjectPermissionEvent event) {
|
||||
if (!super.isAcceptableInSession(activeUserSession, event)) {
|
||||
return false;
|
||||
}
|
||||
var user = activeUserSession.getUserContext().getUser();
|
||||
if (user == null) {
|
||||
return false;
|
||||
}
|
||||
var subjectId = event.getSubjectId();
|
||||
switch (event.getSubjectType()) {
|
||||
case user:
|
||||
return CommonUtils.equalObjects(user.getUserId(), subjectId);
|
||||
case team:
|
||||
return ArrayUtils.containsIgnoreCase(user.getTeams(), subjectId);
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
+24
-7
@@ -33,7 +33,9 @@ import org.eclipse.jetty.servlet.ErrorPageErrorHandler;
|
||||
import org.eclipse.jetty.servlet.ServletContextHandler;
|
||||
import org.eclipse.jetty.servlet.ServletHolder;
|
||||
import org.eclipse.jetty.servlet.ServletMapping;
|
||||
import org.eclipse.jetty.util.resource.PathResource;
|
||||
import org.eclipse.jetty.websocket.server.config.JettyWebSocketServletContainerInitializer;
|
||||
import org.eclipse.jetty.xml.XmlConfiguration;
|
||||
import org.jkiss.code.NotNull;
|
||||
import org.jkiss.dbeaver.DBException;
|
||||
import org.jkiss.dbeaver.Log;
|
||||
@@ -71,11 +73,25 @@ public class CBJettyServer {
|
||||
JettyServer server;
|
||||
int serverPort = application.getServerPort();
|
||||
String serverHost = application.getServerHost();
|
||||
if (CommonUtils.isEmpty(serverHost)) {
|
||||
server = new JettyServer(serverPort);
|
||||
Path sslPath = application.getSslConfigurationPath();
|
||||
|
||||
boolean sslConfigurationExists = sslPath != null && Files.exists(sslPath);
|
||||
if (sslConfigurationExists) {
|
||||
server = new JettyServer();
|
||||
XmlConfiguration sslConfiguration = new XmlConfiguration(new PathResource(sslPath));
|
||||
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
|
||||
// method sslConfiguration.configure() does not see the context class of the Loader,
|
||||
// so we have to configure it manually, then return the old classLoader.
|
||||
Thread.currentThread().setContextClassLoader(this.getClass().getClassLoader());
|
||||
sslConfiguration.configure(server);
|
||||
Thread.currentThread().setContextClassLoader(classLoader);
|
||||
} else {
|
||||
server = new JettyServer(
|
||||
InetSocketAddress.createUnresolved(serverHost, serverPort));
|
||||
if (CommonUtils.isEmpty(serverHost)) {
|
||||
server = new JettyServer(serverPort);
|
||||
} else {
|
||||
server = new JettyServer(
|
||||
InetSocketAddress.createUnresolved(serverHost, serverPort));
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
@@ -112,8 +128,6 @@ public class CBJettyServer {
|
||||
|
||||
server.setHandler(servletContextHandler);
|
||||
|
||||
var serverConnector = new ServerConnector(server);
|
||||
server.addConnector(serverConnector);
|
||||
JettyWebSocketServletContainerInitializer.configure(servletContextHandler,
|
||||
(context, wsContainer) -> {
|
||||
wsContainer.setIdleTimeout(Duration.ofMinutes(5));
|
||||
@@ -205,11 +219,14 @@ public class CBJettyServer {
|
||||
servletContextHandler.setSessionHandler(sessionHandler);
|
||||
}
|
||||
|
||||
private static class JettyServer extends Server {
|
||||
public static class JettyServer extends Server {
|
||||
public JettyServer(int serverPort) {
|
||||
super(serverPort);
|
||||
}
|
||||
|
||||
public JettyServer() {
|
||||
super();
|
||||
}
|
||||
public JettyServer(InetSocketAddress addr) {
|
||||
super(addr);
|
||||
}
|
||||
|
||||
+1
-1
@@ -23,7 +23,7 @@ import org.jkiss.dbeaver.model.runtime.DBRProgressMonitor;
|
||||
|
||||
public class SessionStateJob extends PeriodicSystemJob {
|
||||
private static final Log log = Log.getLog(SessionStateJob.class);
|
||||
private static final int PERIOD_MS = 60_000; // once per 60 seconds
|
||||
private static final int PERIOD_MS = 30_000; // once per 30 seconds
|
||||
|
||||
public SessionStateJob(@NotNull CBPlatform platform) {
|
||||
super("Session state sender", platform, PERIOD_MS);
|
||||
|
||||
+114
@@ -0,0 +1,114 @@
|
||||
/*
|
||||
* DBeaver - Universal Database Manager
|
||||
* Copyright (C) 2010-2023 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.jobs;
|
||||
|
||||
import io.cloudbeaver.model.session.WebSession;
|
||||
import org.eclipse.core.runtime.IStatus;
|
||||
import org.eclipse.core.runtime.Status;
|
||||
import org.jkiss.code.NotNull;
|
||||
import org.jkiss.code.Nullable;
|
||||
import org.jkiss.dbeaver.Log;
|
||||
import org.jkiss.dbeaver.model.exec.DBCException;
|
||||
import org.jkiss.dbeaver.model.exec.DBCExecutionContext;
|
||||
import org.jkiss.dbeaver.model.exec.DBCStatement;
|
||||
import org.jkiss.dbeaver.model.exec.output.DBCOutputSeverity;
|
||||
import org.jkiss.dbeaver.model.exec.output.DBCOutputWriter;
|
||||
import org.jkiss.dbeaver.model.exec.output.DBCServerOutputReader;
|
||||
import org.jkiss.dbeaver.model.runtime.AbstractJob;
|
||||
import org.jkiss.dbeaver.model.runtime.DBRProgressMonitor;
|
||||
import org.jkiss.dbeaver.model.websocket.event.WSOutputLogInfo;
|
||||
import org.jkiss.dbeaver.model.websocket.event.session.WSOutputDBLogEvent;
|
||||
import org.jkiss.dbeaver.runtime.DBWorkbench;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
|
||||
public class SqlOutputLogReaderJob extends AbstractJob {
|
||||
|
||||
private static final Log log = Log.getLog(SqlOutputLogReaderJob.class);
|
||||
|
||||
@NotNull
|
||||
private final WebSession webSession;
|
||||
@NotNull
|
||||
private final DBCExecutionContext dbcExecutionContext;
|
||||
@NotNull
|
||||
private final DBCStatement dbcStatement;
|
||||
@NotNull
|
||||
private final DBCServerOutputReader dbcServerOutputReader;
|
||||
@NotNull
|
||||
private final String contextInfoId;
|
||||
|
||||
public SqlOutputLogReaderJob(@NotNull WebSession webSession,
|
||||
@NotNull DBCExecutionContext dbcExecutionContext,
|
||||
@NotNull DBCStatement dbcStatement,
|
||||
@NotNull DBCServerOutputReader dbcServerOutputReader,
|
||||
@NotNull String contextInfoId) {
|
||||
super("Sql log state job");
|
||||
this.webSession = webSession;
|
||||
this.dbcExecutionContext = dbcExecutionContext;
|
||||
this.dbcStatement = dbcStatement;
|
||||
this.dbcServerOutputReader = dbcServerOutputReader;
|
||||
this.contextInfoId = contextInfoId;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected IStatus run(DBRProgressMonitor monitor) {
|
||||
if (!DBWorkbench.getPlatform().isShuttingDown()) {
|
||||
try {
|
||||
if (!dbcStatement.isStatementClosed()) {
|
||||
dumpOutput(monitor);
|
||||
schedule(100);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.debug("Failed to execute job " + e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
return Status.OK_STATUS;
|
||||
}
|
||||
|
||||
private void dumpOutput(DBRProgressMonitor monitor) {
|
||||
if (!monitor.isCanceled()) {
|
||||
if (dbcServerOutputReader.isAsyncOutputReadSupported()) {
|
||||
try {
|
||||
if (!dbcStatement.isStatementClosed()) {
|
||||
List<WSOutputLogInfo> messages = new ArrayList<>();
|
||||
dbcServerOutputReader.readServerOutput(monitor, dbcExecutionContext, null, dbcStatement, new DBCOutputWriter() {
|
||||
@Override
|
||||
public void println(@Nullable DBCOutputSeverity severity, @Nullable String message) {
|
||||
if (message != null && severity != null) {
|
||||
messages.add(new WSOutputLogInfo(severity.getName(), message));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void flush() {
|
||||
messages.clear();
|
||||
}
|
||||
});
|
||||
webSession.addSessionEvent(new WSOutputDBLogEvent(
|
||||
contextInfoId,
|
||||
messages,
|
||||
System.currentTimeMillis()));
|
||||
}
|
||||
} catch (DBCException e) {
|
||||
log.error(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user