mirror of
https://github.com/dbeaver/cloudbeaver.git
synced 2026-10-16 14:50:15 +08:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2f120d6a4f | ||
|
|
d3be30f55b | ||
|
|
2c7df05c45 | ||
|
|
11167d5802 | ||
|
|
4cfb27b33b | ||
|
|
0126b204c0 | ||
|
|
6dbcfe408e | ||
|
|
de2d9f029f | ||
|
|
2f895f6dab | ||
|
|
c7af629d8a | ||
|
|
1782ce45a7 | ||
|
|
51e0218682 | ||
|
|
e08353f13f | ||
|
|
383952c60e | ||
|
|
5282175b7c | ||
|
|
b4db34ea48 | ||
|
|
9672e5b3b4 | ||
|
|
48a7a4f806 | ||
|
|
cbceee6ec3 | ||
|
|
ffe0a18828 | ||
|
|
115cfb3bde | ||
|
|
e62538b583 | ||
|
|
5255e9949d | ||
|
|
8e4faa5935 | ||
|
|
39ad2994b2 | ||
|
|
00788615e1 | ||
|
|
ac937698bd | ||
|
|
be88091950 | ||
|
|
9dcaec0c09 | ||
|
|
f677f48890 | ||
|
|
a44cadbb8a | ||
|
|
5c992dc8ee | ||
|
|
05f85db7e6 | ||
|
|
94ea41c2c9 | ||
|
|
d49342ce48 | ||
|
|
21fe547dcd | ||
|
|
67c54d29eb | ||
|
|
7427543b7f | ||
|
|
439c806020 | ||
|
|
a4bb23b21e | ||
|
|
fd711b055c | ||
|
|
c6626f4364 | ||
|
|
d44c9f3863 | ||
|
|
ea055fde49 | ||
|
|
3d8dc7d3d2 | ||
|
|
eb41e6156a | ||
|
|
c7fa466509 | ||
|
|
f475c71311 | ||
|
|
6220df4845 | ||
|
|
2e5b71e9dd | ||
|
|
1a5192815c | ||
|
|
3a85f1a4be | ||
|
|
2e5b092ce7 | ||
|
|
5c92d43c96 | ||
|
|
8b54e3177e | ||
|
|
a4c36247fa | ||
|
|
2698e713a7 | ||
|
|
a48097ec25 | ||
|
|
8c30761caa | ||
|
|
9103b28e72 | ||
|
|
e45f077bfa | ||
|
|
fb1b2b21ed | ||
|
|
2a1adc1374 | ||
|
|
c46e9343ac | ||
|
|
16cd103585 | ||
|
|
0850b2d94f | ||
|
|
2c2a1dfed6 | ||
|
|
37abea3f44 | ||
|
|
0e32052b24 | ||
|
|
e1887714ab | ||
|
|
f5ac3cfe2a | ||
|
|
434a988919 | ||
|
|
3fbc227734 | ||
|
|
bb9a62da63 | ||
|
|
34c6e6c9be | ||
|
|
b35e3f83ed | ||
|
|
a42c7dc8de | ||
|
|
eb33e0e895 | ||
|
|
c3c15bf7cc | ||
|
|
6604b0887c | ||
|
|
735024b57b | ||
|
|
3118eac266 | ||
|
|
38445f3843 | ||
|
|
01d08fc3ce | ||
|
|
9bc6bd022e | ||
|
|
cf764f5807 | ||
|
|
0bf6783d00 | ||
|
|
0e3da49b23 | ||
|
|
f84bd6040f | ||
|
|
6339bee0dc | ||
|
|
8f54e759dd | ||
|
|
5f53e0ffa2 | ||
|
|
fe020f8f7d | ||
|
|
4b9e1c8b94 | ||
|
|
dc5592d052 | ||
|
|
1746d91072 | ||
|
|
d230275768 | ||
|
|
3b3bb147b7 | ||
|
|
6ad148850d | ||
|
|
a4130804bf | ||
|
|
37d06ecebd | ||
|
|
b869bddded | ||
|
|
efb42d33a7 | ||
|
|
90acaaf00e | ||
|
|
46309e9505 | ||
|
|
aebb2d47ef | ||
|
|
12e0107617 | ||
|
|
3c5aa6b7e8 | ||
|
|
4df7e01d19 | ||
|
|
801a55cd38 | ||
|
|
252ff71f03 | ||
|
|
2d1bde22a2 | ||
|
|
4f0db5b854 | ||
|
|
5b527872bd | ||
|
|
30e74ce09f | ||
|
|
3ca9904631 | ||
|
|
ea2246af31 | ||
|
|
12e1fb57d7 | ||
|
|
03d0f26530 | ||
|
|
9e657d2280 | ||
|
|
461e251753 | ||
|
|
e54bfb3154 | ||
|
|
6e575d3e7e | ||
|
|
24ff77de47 | ||
|
|
5ce49faea0 | ||
|
|
d2b6734dd3 | ||
|
|
e62943c548 | ||
|
|
ceefea48c5 | ||
|
|
638e5bc97b | ||
|
|
31cbac1c9c | ||
|
|
bb7a5c384c | ||
|
|
611d06b2f8 | ||
|
|
e0d2d367dc | ||
|
|
b2967257e2 | ||
|
|
6af5a32a9d | ||
|
|
8467a592e3 | ||
|
|
0374ddb929 | ||
|
|
7b5c10b9e2 | ||
|
|
af31c5cf60 | ||
|
|
805ae26c3c | ||
|
|
f565864124 | ||
|
|
a69e760c06 | ||
|
|
cb972cb299 | ||
|
|
74aa8c7980 | ||
|
|
443cbfcde5 | ||
|
|
678ec32c44 | ||
|
|
54544b6334 | ||
|
|
ab077070c0 | ||
|
|
dfd5838f38 | ||
|
|
00c1cc81ac | ||
|
|
1ef728ab85 | ||
|
|
fe56f61db3 | ||
|
|
d186859cb5 | ||
|
|
a51ab7d145 | ||
|
|
bdaf5dc3b4 | ||
|
|
4a4f959da8 | ||
|
|
f82660657d | ||
|
|
d650ec104b | ||
|
|
7ca8cfe998 | ||
|
|
fd4a503f8b | ||
|
|
5287ec9859 | ||
|
|
542171ad1e | ||
|
|
10b3a53c4a | ||
|
|
16de895ed4 | ||
|
|
cab257d141 | ||
|
|
b48e173515 | ||
|
|
5617d49597 | ||
|
|
7fcfe9b5c8 | ||
|
|
351a323d9c | ||
|
|
1de2502835 | ||
|
|
b07510afc3 | ||
|
|
dd5a39e78a | ||
|
|
f15c6b5ec9 | ||
|
|
378f60aa94 | ||
|
|
f982b4b552 | ||
|
|
95046f076f | ||
|
|
92546849f8 | ||
|
|
2d45b29a07 | ||
|
|
de00a60aad | ||
|
|
d118287b0e | ||
|
|
8d14e42797 | ||
|
|
36c089ed06 | ||
|
|
62393937fc | ||
|
|
8e0e0c9193 | ||
|
|
de85721c8f | ||
|
|
f059daf693 | ||
|
|
5d071e5dfd | ||
|
|
afb4ea205c | ||
|
|
74486b35b4 | ||
|
|
babb29f05a | ||
|
|
12b5b2b941 | ||
|
|
6ca587a5b0 | ||
|
|
23b70441b8 | ||
|
|
ffee7a1f2f | ||
|
|
62d1f05188 | ||
|
|
ea00ce653e | ||
|
|
b7da47a816 | ||
|
|
b2dfa0a1bb | ||
|
|
6486612c0e | ||
|
|
c349697fa1 | ||
|
|
ab7e17900c | ||
|
|
04e8610b12 | ||
|
|
c4f9764e02 | ||
|
|
45683f1a48 | ||
|
|
00aa2eb3e4 | ||
|
|
701e681fbd | ||
|
|
5be8582bfd | ||
|
|
081b0eaead | ||
|
|
fa0cd1ea78 | ||
|
|
949722cd5a | ||
|
|
0171240ac8 | ||
|
|
e4345ffc63 | ||
|
|
7959357668 | ||
|
|
6b91a0e9e6 | ||
|
|
f834169414 | ||
|
|
3a10080956 | ||
|
|
2c541b2c98 | ||
|
|
b967cde145 | ||
|
|
e49086203c | ||
|
|
cf276f6a13 | ||
|
|
93ddddc913 | ||
|
|
1895f3ab2e | ||
|
|
5b436463b9 | ||
|
|
75fdc2bcec | ||
|
|
b48684b65d | ||
|
|
f5149c6324 | ||
|
|
87cd84881d | ||
|
|
16e2920937 | ||
|
|
d0f82d95a6 | ||
|
|
fb9cd47bed | ||
|
|
364cb6e516 | ||
|
|
9e74f18be3 | ||
|
|
d94a6f9588 | ||
|
|
6afd8698fe | ||
|
|
c8436f93e1 | ||
|
|
8cd8f83e5f | ||
|
|
190a9db695 | ||
|
|
aa52372422 | ||
|
|
f88eb8610c | ||
|
|
021516ae7f | ||
|
|
a40993f77d | ||
|
|
1298f7e294 | ||
|
|
3ea4443f48 | ||
|
|
1be6ddaf1c | ||
|
|
0d53211b88 | ||
|
|
115efeb613 | ||
|
|
220c72487f | ||
|
|
17dcd9464c | ||
|
|
e259606422 | ||
|
|
042da7cc7c | ||
|
|
1e2eee87d3 | ||
|
|
faafcee1a4 | ||
|
|
6efbd6904a | ||
|
|
8f740b732b | ||
|
|
7e4411c0a9 | ||
|
|
b6ac0b93e7 | ||
|
|
b2c6306d21 | ||
|
|
49875e4dc1 | ||
|
|
4433018ed2 | ||
|
|
0755561250 | ||
|
|
ea87a48619 | ||
|
|
c2330e9c6a | ||
|
|
09de0c328a | ||
|
|
dab4a0d033 | ||
|
|
05507db706 | ||
|
|
6e4f16e824 | ||
|
|
56a2e8c13d | ||
|
|
d8b6655da5 | ||
|
|
4842cba903 | ||
|
|
4078b6aa16 | ||
|
|
241f2393be | ||
|
|
5851c8f2c4 | ||
|
|
c70c932e9c | ||
|
|
b155c98c02 | ||
|
|
1d0fac3eb0 | ||
|
|
124fa7f691 | ||
|
|
af5cbedf22 | ||
|
|
b46dd9b8a8 | ||
|
|
cdd3a356ac | ||
|
|
9a471bed70 | ||
|
|
e779a23f0e | ||
|
|
240dab7c0f | ||
|
|
422e6283df | ||
|
|
0b98569c9e | ||
|
|
340c6e850c | ||
|
|
bdfb637633 | ||
|
|
df4b88c7e4 | ||
|
|
e7f6cbd2fc | ||
|
|
a84f822124 | ||
|
|
c5399de7af | ||
|
|
a65dff32d6 | ||
|
|
1c9b4f1ece | ||
|
|
b13b58b589 | ||
|
|
3d56701dc6 | ||
|
|
078c11c657 | ||
|
|
baffbfd838 | ||
|
|
0134b79ee0 | ||
|
|
791da679fe | ||
|
|
dc0e3075fa | ||
|
|
f04c2b1452 | ||
|
|
ce6cfd3e3c | ||
|
|
776b6d5c6d | ||
|
|
9a729cb47f | ||
|
|
c3ae4acdfb | ||
|
|
4cb171ed19 | ||
|
|
b3952e7247 | ||
|
|
d83822d4ad | ||
|
|
5579970447 | ||
|
|
c216a77f23 | ||
|
|
e7ff007151 | ||
|
|
b62b144ced | ||
|
|
8eaabce90b | ||
|
|
6077a52181 | ||
|
|
7b728311c0 | ||
|
|
baad9d6f98 | ||
|
|
d7658d4f93 | ||
|
|
4e1a593321 | ||
|
|
57a517111b | ||
|
|
c893e476e8 | ||
|
|
0151edf002 | ||
|
|
f9962b90f9 | ||
|
|
b1e62d93e4 | ||
|
|
ea1f3460eb | ||
|
|
c6127b0ac5 | ||
|
|
1133112a66 | ||
|
|
965d96c386 | ||
|
|
407259b8b2 | ||
|
|
c60d513e66 | ||
|
|
674627ebea | ||
|
|
22634c7c19 | ||
|
|
15e5574ed6 | ||
|
|
95f1a4e6ad | ||
|
|
2abe057299 | ||
|
|
b9b02828dd | ||
|
|
691cceb784 | ||
|
|
0957684bce | ||
|
|
f9b8e85239 | ||
|
|
1e599fdd88 | ||
|
|
e05dafd710 | ||
|
|
fac08c1b93 | ||
|
|
77ae1a59d2 | ||
|
|
e3a859fee7 | ||
|
|
72ce16b2e8 | ||
|
|
7cd22a84c2 | ||
|
|
eaf19cf1d2 | ||
|
|
0dddeb70b2 | ||
|
|
d701cb4fbe | ||
|
|
53efa4ef40 | ||
|
|
554c983283 | ||
|
|
02d70e1029 | ||
|
|
176864c5a5 | ||
|
|
14858f13f3 | ||
|
|
0b27db0b38 | ||
|
|
10582d1333 | ||
|
|
d606ca0c77 | ||
|
|
494101d659 | ||
|
|
6416486513 | ||
|
|
6061ec340c | ||
|
|
b40646e69b | ||
|
|
003a89be57 | ||
|
|
bbd296f197 | ||
|
|
39bfc54ca5 | ||
|
|
bac47d20ce | ||
|
|
5fce0a544b | ||
|
|
833dae58ce | ||
|
|
12bc21257d | ||
|
|
d280171b97 | ||
|
|
c8ec5716b8 | ||
|
|
7045575d95 | ||
|
|
bdea460218 | ||
|
|
674c522d55 | ||
|
|
d0933b2b17 | ||
|
|
56143132d6 | ||
|
|
639f42fa1c | ||
|
|
c0bbe7fdc5 | ||
|
|
64103bc369 | ||
|
|
87e7f774e9 | ||
|
|
00cb84c71d | ||
|
|
c8457c2004 | ||
|
|
1f3ae6a508 | ||
|
|
a95f8d021f | ||
|
|
6fd824e6a8 | ||
|
|
836acf879f | ||
|
|
b44840730a | ||
|
|
1bffe8a28a | ||
|
|
885e423048 | ||
|
|
8f87ec6618 | ||
|
|
01ea4794ea | ||
|
|
7aaee5047e | ||
|
|
b701d21d89 | ||
|
|
426650076a | ||
|
|
035d2fe156 | ||
|
|
d70470b853 | ||
|
|
368c9e2179 | ||
|
|
6ad136f2fa | ||
|
|
00f56e8c58 | ||
|
|
ccefc5cb9b | ||
|
|
2a612aea63 | ||
|
|
b1349c7a07 | ||
|
|
99b9f6f193 | ||
|
|
51ef899922 | ||
|
|
acde2c5b8b | ||
|
|
49247ade5d | ||
|
|
e355cdc953 | ||
|
|
2198d5f69f | ||
|
|
1e373d9ff4 | ||
|
|
8aeacfeb4e | ||
|
|
3a304b1902 | ||
|
|
916055eb6d | ||
|
|
91d83fdac8 | ||
|
|
3e71a7a6ce | ||
|
|
9051abe90b | ||
|
|
9a46bb2a88 | ||
|
|
c7ed9f3b5b | ||
|
|
e45267c17c | ||
|
|
8f29b67ba6 | ||
|
|
0550cb9982 | ||
|
|
530af6dfed | ||
|
|
2a610c3f50 | ||
|
|
09dfae412c | ||
|
|
d605e09ef4 | ||
|
|
2dc97fb706 | ||
|
|
9e3e4322a3 | ||
|
|
433eaacd24 | ||
|
|
fec9e856f6 | ||
|
|
28714a628f | ||
|
|
fd82b24e5f | ||
|
|
d34d59ee6c | ||
|
|
b09a85d324 | ||
|
|
5ac7ece108 |
@@ -1,35 +0,0 @@
|
||||
// For format details, see https://aka.ms/devcontainer.json. For config options, see the
|
||||
// README at: https://github.com/devcontainers/templates/tree/main/src/java
|
||||
{
|
||||
"name": "Java",
|
||||
// Or use a Dockerfile or Docker Compose file. More info: https://containers.dev/guide/dockerfile
|
||||
"image": "mcr.microsoft.com/devcontainers/java:1-17-bookworm",
|
||||
|
||||
"features": {
|
||||
"ghcr.io/devcontainers/features/java:1": {
|
||||
"version": "none",
|
||||
"installMaven": "true",
|
||||
"installGradle": "false"
|
||||
},
|
||||
"ghcr.io/devcontainers/features/github-cli:1": {},
|
||||
"ghcr.io/devcontainers/features/node:1": {}
|
||||
},
|
||||
|
||||
"hostRequirements": {
|
||||
"cpus": 8,
|
||||
"memory": "16gb",
|
||||
"storage": "64gb"
|
||||
}
|
||||
|
||||
// Use 'forwardPorts' to make a list of ports inside the container available locally.
|
||||
// "forwardPorts": [],
|
||||
|
||||
// Use 'postCreateCommand' to run commands after the container is created.
|
||||
// "postCreateCommand": "java -version",
|
||||
|
||||
// Configure tool-specific properties.
|
||||
// "customizations": {},
|
||||
|
||||
// Uncomment to connect as root instead. More info: https://aka.ms/dev-containers-non-root.
|
||||
// "remoteUser": "root"
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
name: Backend Build
|
||||
|
||||
on:
|
||||
# Allows you to reuse workflows by referencing their YAML files
|
||||
workflow_call:
|
||||
|
||||
jobs:
|
||||
build-backend:
|
||||
name: Build
|
||||
timeout-minutes: 10
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
path: cloudbeaver
|
||||
|
||||
- name: Clone Deps Repositories
|
||||
uses: dbeaver/github-actions/clone-repositories@devel
|
||||
with:
|
||||
project_deps_path: "./cloudbeaver/project.deps"
|
||||
|
||||
- name: Set up JDK 17
|
||||
uses: actions/setup-java@v4
|
||||
with:
|
||||
distribution: "temurin"
|
||||
java-version: "17"
|
||||
cache: maven
|
||||
|
||||
- uses: dbeaver/github-actions/install-maven@devel
|
||||
|
||||
- name: Run build script
|
||||
run: ./build-backend.sh
|
||||
shell: bash
|
||||
working-directory: ./cloudbeaver/deploy
|
||||
|
||||
# - name: Archive build artifacts
|
||||
# uses: actions/upload-artifact@v4
|
||||
# with:
|
||||
# name: backend-build-artifacts
|
||||
# path: cloudbeaver/deploy/cloudbeaver
|
||||
# if-no-files-found: error
|
||||
@@ -0,0 +1,14 @@
|
||||
name: Cleanup
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
types: [closed]
|
||||
push:
|
||||
branches:
|
||||
- devel
|
||||
|
||||
jobs:
|
||||
delete-caches:
|
||||
name: Cleanup
|
||||
uses: dbeaver/dbeaver-common/.github/workflows/cleanup-caches.yml@devel
|
||||
secrets: inherit
|
||||
@@ -0,0 +1,93 @@
|
||||
name: Build and Push Docker Image
|
||||
|
||||
on:
|
||||
# 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-push-docker:
|
||||
name: Build and Push Docker Image
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Download backend artifacts
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: backend-build-artifacts
|
||||
path: deploy/cloudbeaver/
|
||||
|
||||
- name: Download frontend artifacts
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: frontend-build-artifacts
|
||||
path: deploy/cloudbeaver/web
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
|
||||
- name: Run custom Docker build script
|
||||
run: ./make-docker-container.sh
|
||||
shell: bash
|
||||
working-directory: ./deploy/docker
|
||||
|
||||
- name: Tag Docker Image
|
||||
run: |
|
||||
REPO_NAME=$(basename ${{ github.repository }})
|
||||
IMAGE_NAME=ghcr.io/${{ github.repository_owner }}/$REPO_NAME
|
||||
BRANCH_NAME=${{ github.head_ref || github.ref_name }}
|
||||
TAG_NAME=$(echo $BRANCH_NAME | sed 's/[^a-zA-Z0-9._-]/-/g')
|
||||
docker tag dbeaver/cloudbeaver:dev $IMAGE_NAME:$TAG_NAME
|
||||
echo "image=$IMAGE_NAME:$TAG_NAME" >> $GITHUB_ENV
|
||||
|
||||
# - name: Install Docker Credential Helper
|
||||
# run: |
|
||||
# sudo apt-get update
|
||||
# sudo apt-get install -y gnupg2 pass
|
||||
# curl -fsSL https://github.com/docker/docker-credential-helpers/releases/download/v0.6.4/docker-credential-pass-v0.6.4-amd64.tar.gz -o docker-credential-pass.tar.gz
|
||||
# tar xzvf docker-credential-pass.tar.gz
|
||||
# sudo mv docker-credential-pass /usr/local/bin/docker-credential-pass
|
||||
# sudo chmod +x /usr/local/bin/docker-credential-pass
|
||||
|
||||
# - name: Configure Docker to use Credential Helper
|
||||
# run: |
|
||||
# mkdir -p ~/.docker
|
||||
# echo '{"credsStore":"pass"}' > ~/.docker/config.json
|
||||
|
||||
# - name: Initialize Password Store
|
||||
# run: |
|
||||
# gpg --batch --gen-key <<EOF
|
||||
# %no-protection
|
||||
# Key-Type: default
|
||||
# Subkey-Type: default
|
||||
# Name-Real: Docker Credential Helper
|
||||
# Expire-Date: 0
|
||||
# %commit
|
||||
# EOF
|
||||
# pass init "$(gpg --list-keys --with-colons | grep '^pub' | cut -d: -f5)"
|
||||
|
||||
- name: Log in to GitHub Container Registry
|
||||
run: echo "${{ secrets.GITHUB_TOKEN }}" | docker login ghcr.io -u ${{ github.repository_owner }} --password-stdin
|
||||
|
||||
- name: Push Docker Image
|
||||
run: docker push ${{ env.image }}
|
||||
|
||||
# - name: Comment on Pull Request
|
||||
# if: github.event_name == 'pull_request'
|
||||
# uses: actions/github-script@v6
|
||||
# with:
|
||||
# github-token: ${{ secrets.PAT }}
|
||||
# script: |
|
||||
# const tag = process.env.TAG_NAME;
|
||||
# const commentBody = `The Docker image has been built and pushed to Docker Hub.\n\nYou can pull the image using the following command:\n\`\`\`\ndocker pull dbeaver/cloudbeaver:${tag}\n\`\`\``;
|
||||
# github.rest.issues.createComment({
|
||||
# owner: context.repo.owner,
|
||||
# repo: context.repo.repo,
|
||||
# issue_number: context.issue.number,
|
||||
# body: commentBody
|
||||
# });
|
||||
@@ -0,0 +1,76 @@
|
||||
name: Frontend Build
|
||||
|
||||
on:
|
||||
# Allows you to reuse workflows by referencing their YAML files
|
||||
workflow_call:
|
||||
outputs:
|
||||
build-status:
|
||||
description: "Build status"
|
||||
value: ${{ jobs.frontend-build.outputs.build-status }}
|
||||
test-status:
|
||||
description: "Test status"
|
||||
value: ${{ jobs.frontend-build.outputs.test-status }}
|
||||
inputs:
|
||||
skip_cache:
|
||||
required: false
|
||||
type: string
|
||||
|
||||
jobs:
|
||||
frontend-build:
|
||||
name: Build
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 10
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
outputs:
|
||||
build-status: ${{ steps.build.outcome }}
|
||||
test-status: ${{ steps.test.outcome }}
|
||||
|
||||
defaults:
|
||||
run:
|
||||
working-directory: ./webapp
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- run: corepack enable
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: "20"
|
||||
cache: yarn
|
||||
cache-dependency-path: webapp/yarn.lock
|
||||
|
||||
- name: restore node_modules
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: "**/node_modules"
|
||||
key: ${{ runner.os }}-node_modules-${{ hashFiles('webapp/yarn.lock') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-node_modules-
|
||||
|
||||
- name: restore typescript cache
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: "**/packages/*/dist"
|
||||
key: ${{ runner.os }}-dist-${{ hashFiles('webapp/yarn.lock') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-dist-
|
||||
|
||||
- name: yarn clean
|
||||
if: env.skip_cache == 'true'
|
||||
run: yarn clear
|
||||
|
||||
- run: yarn install --immutable
|
||||
|
||||
- run: yarn bundle
|
||||
working-directory: ./webapp/packages/product-default
|
||||
|
||||
- run: yarn test
|
||||
|
||||
# - name: Archive build artifacts
|
||||
# uses: actions/upload-artifact@v4
|
||||
# with:
|
||||
# name: frontend-build-artifacts
|
||||
# path: webapp/packages/product-default/lib
|
||||
# if-no-files-found: error
|
||||
@@ -1,76 +0,0 @@
|
||||
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
|
||||
@@ -1,29 +0,0 @@
|
||||
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,45 @@
|
||||
name: Frontend Lint
|
||||
|
||||
on:
|
||||
# Allows you to reuse workflows by referencing their YAML files
|
||||
workflow_call:
|
||||
|
||||
jobs:
|
||||
lint:
|
||||
name: Lint
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 5
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
defaults:
|
||||
run:
|
||||
working-directory: ./webapp
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- run: corepack enable
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: "20"
|
||||
cache: yarn
|
||||
cache-dependency-path: webapp/yarn.lock
|
||||
|
||||
- name: restore node_modules
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: "**/node_modules"
|
||||
key: ${{ runner.os }}-node_modules-${{ hashFiles('webapp/yarn.lock') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-node_modules-
|
||||
|
||||
- run: |
|
||||
yarn install --immutable
|
||||
git fetch origin "${{ github.base_ref }}" --depth=1
|
||||
FILES=$(git diff --name-only 'origin/${{ github.base_ref }}' ${{ github.sha }} -- . | sed 's|^webapp/||')
|
||||
if [ -n "$FILES" ]; then
|
||||
yarn lint --pass-on-no-patterns --no-error-on-unmatched-pattern $FILES
|
||||
else
|
||||
echo "No files to lint"
|
||||
fi
|
||||
@@ -1,80 +0,0 @@
|
||||
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: "20"
|
||||
|
||||
- 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
|
||||
@@ -0,0 +1,54 @@
|
||||
name: CI
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
types:
|
||||
- opened
|
||||
- synchronize
|
||||
- reopened
|
||||
push:
|
||||
branches: [devel]
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.event_name == 'pull_request' && format('pr-{0}', github.event.pull_request.number) || 'push-pr-devel' }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
build-server:
|
||||
name: Server
|
||||
uses: ./.github/workflows/backend-build.yml
|
||||
secrets: inherit
|
||||
|
||||
build-frontend:
|
||||
name: Frontend
|
||||
uses: ./.github/workflows/frontend-build.yml
|
||||
secrets: inherit
|
||||
|
||||
lint-server:
|
||||
name: Server
|
||||
uses: dbeaver/dbeaver-common/.github/workflows/java-checkstyle.yml@devel
|
||||
secrets: inherit
|
||||
|
||||
lint-frontend:
|
||||
name: Frontend
|
||||
uses: ./.github/workflows/frontend-lint.yml
|
||||
secrets: inherit
|
||||
|
||||
# call-frontend-tests:
|
||||
# name: Frontend Unit Tests
|
||||
# needs: call-frontend-build
|
||||
# runs-on: ubuntu-latest
|
||||
# steps:
|
||||
# - name: Check if tests passed
|
||||
# if: ${{ needs.call-frontend-build.outputs.test-status != 'success' }}
|
||||
# run: |
|
||||
# echo "Tests failed"
|
||||
# exit 1
|
||||
# - name: Continue if tests passed
|
||||
# if: ${{ needs.call-frontend-build.outputs.test-status == 'success' }}
|
||||
# run: echo "Tests passed"
|
||||
|
||||
# call-docker-build-and-push:
|
||||
# name: Run
|
||||
# needs: [call-backend-build, call-frontend-build]
|
||||
# uses: ./.github/workflows/docker-build-and-push.yml
|
||||
@@ -1,17 +0,0 @@
|
||||
name: validation
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
branches:
|
||||
- devel
|
||||
types:
|
||||
- opened
|
||||
- synchronize
|
||||
- reopened
|
||||
- edited
|
||||
- ready_for_review
|
||||
- labeled
|
||||
|
||||
jobs:
|
||||
commit-message:
|
||||
uses: dbeaver/dbeaver/.github/workflows/reused-commit-msgs-validator.yml@devel
|
||||
+4
-1
@@ -26,7 +26,7 @@ dist/
|
||||
|
||||
*.iml
|
||||
/idea/
|
||||
.idea/
|
||||
|
||||
|
||||
## Mac OS
|
||||
.DS_Store
|
||||
@@ -40,6 +40,9 @@ server/test/io.cloudbeaver.test.platform/workspace/.data/
|
||||
.classpath
|
||||
.settings/
|
||||
|
||||
## Eclipse PDE
|
||||
*.product.launch
|
||||
|
||||
workspace-dev-ce/
|
||||
deploy/cloudbeaver
|
||||
server/**/target
|
||||
|
||||
Vendored
-1
@@ -15,7 +15,6 @@
|
||||
"streetsidesoftware.code-spell-checker",
|
||||
"streetsidesoftware.code-spell-checker-russian",
|
||||
"syler.sass-indented",
|
||||
"VisualStudioExptTeam.intellicode-api-usage-examples",
|
||||
"VisualStudioExptTeam.vscodeintellicode",
|
||||
"yzhang.markdown-all-in-one",
|
||||
"GraphQL.vscode-graphql-syntax",
|
||||
|
||||
Vendored
+53
-69
@@ -4,7 +4,7 @@
|
||||
{
|
||||
"type": "chrome",
|
||||
"request": "launch",
|
||||
"name": "Chrome",
|
||||
"name": "CloudBeaver CE",
|
||||
"url": "http://localhost:8080",
|
||||
"webRoot": "${workspaceFolder}/..",
|
||||
"outFiles": [
|
||||
@@ -17,76 +17,60 @@
|
||||
},
|
||||
{
|
||||
"type": "java",
|
||||
"name": "CloudBeaver CE",
|
||||
"cwd": "${workspaceFolder}/workspace-dev-ce",
|
||||
"preLaunchTask": "Generate dev proprties for CBCE",
|
||||
"name": "CloudBeaver CE Server",
|
||||
"cwd": "${workspaceFolder}/../opt/cbce",
|
||||
"request": "launch",
|
||||
"mainClass": "org.jkiss.dbeaver.launcher.DBeaverLauncher",
|
||||
"windows": {
|
||||
"type": "java",
|
||||
"name": "CloudBeaver CE",
|
||||
"request": "launch",
|
||||
"mainClass": "org.jkiss.dbeaver.launcher.DBeaverLauncher",
|
||||
"args": [
|
||||
"-product",
|
||||
"io.cloudbeaver.product.ce.product",
|
||||
"-configuration",
|
||||
"file:${workspaceFolder}/../eclipse/workspace/.metadata/.plugins/org.eclipse.pde.core/CloudbeaverServer.product/",
|
||||
"-dev",
|
||||
"file:${workspaceFolder}/../eclipse/workspace/.metadata/.plugins/org.eclipse.pde.core/CloudbeaverServer.product/dev.properties",
|
||||
"-os",
|
||||
"win32",
|
||||
"-ws",
|
||||
"win32",
|
||||
"-arch",
|
||||
"x86_64",
|
||||
"-nl",
|
||||
"en",
|
||||
"-showsplash",
|
||||
"-web-config",
|
||||
"conf/cloudbeaver.conf"
|
||||
],
|
||||
"vmArgs": [
|
||||
"-XX:+IgnoreUnrecognizedVMOptions",
|
||||
"--add-modules=ALL-SYSTEM",
|
||||
"-Xms64m",
|
||||
"-Xmx1024m",
|
||||
"-Declipse.pde.launch=true"
|
||||
]
|
||||
},
|
||||
"osx": {
|
||||
"type": "java",
|
||||
"name": "CloudBeaver CE",
|
||||
"request": "launch",
|
||||
"mainClass": "org.jkiss.dbeaver.launcher.DBeaverLauncher",
|
||||
"args": [
|
||||
"-product",
|
||||
"io.cloudbeaver.product.ce.product",
|
||||
"-configuration",
|
||||
"file:${workspaceFolder}/../eclipse/workspace/.metadata/.plugins/org.eclipse.pde.core/CloudbeaverServer.product/",
|
||||
"-dev",
|
||||
"file:${workspaceFolder}/../eclipse/workspace/.metadata/.plugins/org.eclipse.pde.core/CloudbeaverServer.product/dev.properties",
|
||||
"-os",
|
||||
"macosx",
|
||||
"-ws",
|
||||
"cocoa",
|
||||
"-arch",
|
||||
"aarch64",
|
||||
"-nl",
|
||||
"en",
|
||||
"-showsplash",
|
||||
"-web-config",
|
||||
"conf/cloudbeaver.conf"
|
||||
],
|
||||
"vmArgs": [
|
||||
"-XX:+IgnoreUnrecognizedVMOptions",
|
||||
"--add-modules=ALL-SYSTEM",
|
||||
"-Xms64m",
|
||||
"-Xmx1024m",
|
||||
"-Declipse.pde.launch=true",
|
||||
"-XstartOnFirstThread"
|
||||
]
|
||||
}
|
||||
"args": [
|
||||
"-product",
|
||||
"io.cloudbeaver.product.ce.product",
|
||||
"-configuration",
|
||||
"file:${workspaceFolder}/../dbeaver-workspace/products/CloudbeaverServer.product/",
|
||||
"-dev",
|
||||
"file:${workspaceFolder}/../dbeaver-workspace/products/CloudbeaverServer.product/dev.properties",
|
||||
"-nl",
|
||||
"en",
|
||||
"-web-config",
|
||||
"conf/cloudbeaver.conf",
|
||||
"-registryMultiLanguage"
|
||||
],
|
||||
// "windows": {
|
||||
// "args": ["-os", "win32", "-ws", "win32", "-arch", "x86_64"]
|
||||
// },
|
||||
// "osx": {
|
||||
// "args": ["-os", "macosx", "-ws", "cocoa", "-arch", "aarch64"]
|
||||
// },
|
||||
|
||||
"vmArgs": [
|
||||
"-XX:+IgnoreUnrecognizedVMOptions",
|
||||
"-Xms64m",
|
||||
"-Xmx1024m",
|
||||
"-Declipse.pde.launch=true",
|
||||
"-XstartOnFirstThread",
|
||||
"-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"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
Vendored
+1
-3
@@ -9,9 +9,7 @@
|
||||
" *",
|
||||
" * Licensed under the Apache License, Version 2.0.",
|
||||
" * you may not use this file except in compliance with the License.",
|
||||
" */",
|
||||
"",
|
||||
""
|
||||
" */"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
Vendored
+1
-1
@@ -46,6 +46,6 @@
|
||||
"*.css": "postcss"
|
||||
},
|
||||
|
||||
"java.checkstyle.configuration": "${workspaceFolder}/../dbeaver/dbeaver-checkstyle-config.xml",
|
||||
"java.checkstyle.configuration": "${workspaceFolder}/../dbeaver-common/.github/dbeaver-checkstyle-config.xml",
|
||||
"java.checkstyle.version": "10.12.0"
|
||||
}
|
||||
|
||||
Vendored
+17
-20
@@ -17,7 +17,7 @@
|
||||
"type": "shell",
|
||||
"command": "yarn",
|
||||
"group": "test",
|
||||
"args": ["lerna", "run", "test", "--no-bail", "--stream", "--"]
|
||||
"args": ["test"]
|
||||
},
|
||||
{
|
||||
"label": "Run DevServer CE",
|
||||
@@ -34,27 +34,16 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"label": "Generate dev proprties for CBCE",
|
||||
"label": "Generate Eclipse PDE CloudBeaver",
|
||||
"type": "shell",
|
||||
"command": "mvn",
|
||||
"osx": {
|
||||
"args": [
|
||||
"package",
|
||||
"-q",
|
||||
"exec:java",
|
||||
"-Dexec.args=-config ${workspaceFolder}/../idea-workspace-dbeaver/rcp-gen.properties -productFile ${workspaceFolder}/server/product/web-server/CloudbeaverServer.product -projectsFolder ${workspaceFolder}/../ -eclipse ${workspaceFolder}/../eclipse/Eclipse.app/Contents/Eclipse -output ${workspaceFolder}/../eclipse/workspace/.metadata/.plugins/org.eclipse.pde.core/CloudbeaverServer.product"
|
||||
]
|
||||
"command": "./generate_workspace.sh"
|
||||
},
|
||||
"windows": {
|
||||
"args": [
|
||||
"package",
|
||||
"-q",
|
||||
"exec:java",
|
||||
"-Dexec.args=-config ${workspaceFolder}/../idea-workspace-dbeaver/rcp-gen.properties -productFile ${workspaceFolder}/server/product/web-server/CloudbeaverServer.product -projectsFolder ${workspaceFolder}/../ -eclipse ${workspaceFolder}/../eclipse/eclipse -output ${workspaceFolder}/../eclipse/workspace/.metadata/.plugins/org.eclipse.pde.core/CloudbeaverServer.product"
|
||||
]
|
||||
"command": "./generate_workspace.cmd"
|
||||
},
|
||||
"options": {
|
||||
"cwd": "${workspaceFolder}/../idea-rcp-launch-config-generator"
|
||||
"cwd": "${workspaceFolder}/../cloudbeaver"
|
||||
},
|
||||
"presentation": {
|
||||
"reveal": "silent",
|
||||
@@ -66,10 +55,10 @@
|
||||
"label": "Build CE",
|
||||
"type": "shell",
|
||||
"windows": {
|
||||
"command": "./build-sqlite.bat"
|
||||
"command": "./build.bat"
|
||||
},
|
||||
"osx": {
|
||||
"command": "./build-sqlite.sh"
|
||||
"command": "./build.sh"
|
||||
},
|
||||
"options": {
|
||||
"cwd": "${workspaceFolder}/deploy"
|
||||
@@ -95,7 +84,7 @@
|
||||
{
|
||||
"label": "Yarn Install",
|
||||
"type": "shell",
|
||||
"command": "yarn install; yarn lerna bootstrap",
|
||||
"command": "yarn install",
|
||||
"args": [],
|
||||
"presentation": {
|
||||
"reveal": "silent",
|
||||
@@ -106,7 +95,15 @@
|
||||
{
|
||||
"label": "Update TS references CE",
|
||||
"type": "shell",
|
||||
"command": "yarn lerna run update-ts-references --stream",
|
||||
"command": "yarn run update-ts-references",
|
||||
"options": {
|
||||
"cwd": "${workspaceFolder}/webapp"
|
||||
}
|
||||
},
|
||||
{
|
||||
"label": "Clean CE",
|
||||
"type": "shell",
|
||||
"command": "yarn clean",
|
||||
"options": {
|
||||
"cwd": "${workspaceFolder}/webapp"
|
||||
}
|
||||
|
||||
@@ -1,13 +1,16 @@
|
||||
<img src="https://github.com/dbeaver/cloudbeaver/wiki/images/cloudbeaver-logo.png" align="right" width="250"/>
|
||||
|
||||
# CloudBeaver Community
|
||||
|
||||
<img src="https://github.com/dbeaver/cloudbeaver/wiki/images/cloudbeaver-logo.png" width="250"/>
|
||||
|
||||
Cloud Database Manager - Community Edition.
|
||||
CloudBeaver is a web server which provides rich web interface. Server itself is a Java application, web part is written on TypeScript and React.
|
||||
CloudBeaver is a web server that provides a rich web interface. The server itself is a Java application, and the web part is written in TypeScript and React.
|
||||
It is free to use and open-source (licensed under [Apache 2](https://github.com/dbeaver/cloudbeaver/blob/devel/LICENSE) license).
|
||||
See out [WIKI](https://github.com/dbeaver/cloudbeaver/wiki) for more details.
|
||||
See our [WIKI](https://github.com/dbeaver/cloudbeaver/wiki) for more details.
|
||||
|
||||

|
||||
<img src="https://github.com/dbeaver/cloudbeaver/wiki/images/connection-creation-demo.png" width="400"/>
|
||||
<img src="https://github.com/dbeaver/cloudbeaver/wiki/images/gis-demo.png" width="400"/>
|
||||
<img src="https://github.com/dbeaver/cloudbeaver/wiki/images/data-transfer-demo.png" width="400"/>
|
||||
<img src="https://github.com/dbeaver/cloudbeaver/wiki/images/sql-editor-demo.png" width="400"/>
|
||||
|
||||
## Run in Docker
|
||||
|
||||
@@ -16,47 +19,65 @@ See out [WIKI](https://github.com/dbeaver/cloudbeaver/wiki) for more details.
|
||||
|
||||
## Demo server
|
||||
|
||||
You can see live demo of CloudBeaver here: https://demo.cloudbeaver.io
|
||||
You can see a live demo of CloudBeaver here: https://demo.cloudbeaver.io
|
||||
|
||||
[Database access instructions](https://github.com/dbeaver/cloudbeaver/wiki/Demo-Server)
|
||||
|
||||
## Changelog
|
||||
|
||||
### 24.0.1. 2024-03-25
|
||||
- Added support for two modes for committing changes to the database:
|
||||
- Auto-commit transfers all changes that you make immediately to the database;
|
||||
- Manual commit requires your confirmation before committing a change to the database or rolling it back.
|
||||
- Different bug fixes and enhancements have been made.
|
||||
|
||||
### 24.0.0. 2024-03-04
|
||||
- Changes since 23.3.0:
|
||||
### 24.2.3. 2024-10-21
|
||||
- Important update:
|
||||
- Connections Templates feature is declared as obsolete and will be removed in future releases.
|
||||
- General:
|
||||
- Data editor enhancements: Rows with focused cells are specially marked to make it easier to locate a position in large tables;
|
||||
- DB2i driver has been updated to version 20.0.7;
|
||||
- The URL mode for PostgreSQL now supports connecting to multiple databases;
|
||||
- The issue with displaying BLOB data types in DuckDB has been resolved.
|
||||
|
||||
- Main updates:
|
||||
- Have enhanced security for unauthorised access;
|
||||
- Have added a password policy for the local authorization. Password parameters can be set in the configuration file;
|
||||
- Have added validation for mandatory fields in all forms to create and edit entities;
|
||||
- Have added an ability to edit the default preferences of the following parts: interface, tools and data viewer in the settings panel in the administrative part;
|
||||
- Performance:
|
||||
- Have improved the performance of the server.
|
||||
### 24.2.2. 2024-10-07
|
||||
- Schemas were added to the SQL autocompletion for PostgreSQL, H2, and SQL Server;
|
||||
- CloudBeaver can now correctly display negative dates for MySQL database;
|
||||
- A search option was added for preferences in the Administration part;
|
||||
- Keyboard navigation has been enhanced. You can now use the arrow keys to move through navigator tree elements and the tab key to switch between editors tabs;
|
||||
- Sample SQLite database was removed.
|
||||
|
||||
### 24.2.1. 2024-09-23
|
||||
- Chinese localization has been improved (thanks to [cashlifei](https://github.com/cashlifei));
|
||||
- Environment variables configuration has been improved - now you can configure more variables on the initial stage of the Docker setup;
|
||||
- SQL Server driver has been updated to version 12.8.0.
|
||||
|
||||
### 24.2.0. 2024-09-02
|
||||
### Changes since 24.1.0:
|
||||
- General:
|
||||
- French language support was added (thanks to [matthieukhl](https://github.com/matthieukhl))
|
||||
- Added the ability to close editor tabs with the middle mouse button
|
||||
- Added right-click support to open the context menu in the Metadata Editor
|
||||
- The list of forbidden characters for naming and renaming resource manager files has been updated, and now it includes the following characters: / : " \ ' <> | ? *
|
||||
- Application cookies security was improved
|
||||
- Authentication:
|
||||
- Improved LDAP authentication: added the ability to filter users via service account parameters and ability to specify custom unique user identifiers
|
||||
- Data Editor:
|
||||
- Added additional notifications about the restricted operations
|
||||
- Enhanced IPv6 and DateTime32 data representation for Clickhouse
|
||||
- Data editing was fixed for DuckDB
|
||||
- SQL Editor:
|
||||
- SQL Editor auto-completion was enhanced to get column, table, and function names faster
|
||||
- Fixed the dollar-quoted string parsing in the SQL Editor for PostgreSQL
|
||||
- Improved display of the long error messages in the SQL Editor and Log viewer
|
||||
- Changed the save script icon to a floppy disk for better recognition
|
||||
- Improved application behavior when closing a connection - open editors won't be closed on disconnect
|
||||
- Administration:
|
||||
- Have added the ability to configure reverse proxy header name and redirect URL at logout. Now the administrator can configure all settings. The delimiter symbol for the reverse proxy team headers is configurable;
|
||||
- Preferences:
|
||||
- Have added the ability in the admin panel to customise copying in the Tables and Values panel for all users except the administrator;
|
||||
- Have added the ability to disable alias autocomplete for all users.
|
||||
- Access Management:
|
||||
- A default user group was added to the product. This group includes all users which could not be deleted.
|
||||
- Connections:
|
||||
- The 'Keep alive' option has been added to the connection settings to keep the connection active even in case of inactivity.
|
||||
- Data Editor:
|
||||
- Have added the ability to view decoded binary-type data in the Value panel;
|
||||
- Text wrap is activated by default for texts and BLOBs in the Values panel for better visibility. The user can switch to the one-line mode using a button on the toolbar;
|
||||
- Have added the ability to display full text for a string data type in the Value panel;
|
||||
- Have added the ability to display the OpenStreetMap layer with the coordinate system ESPG 4326.
|
||||
- Resource management:
|
||||
- Read-only scripts now have a padlock icon.
|
||||
- Redesigned administration navigation panel - now it is more compact and clear
|
||||
- Added the ability to change the default commit mode for each connection separately
|
||||
- Added the ability to configure the server property rootURI parameter (thanks to [arioko](https://github.com/arioko))
|
||||
- Databases:
|
||||
- The DuckDB driver has been added;
|
||||
- Updated PostgreSQL driver to 42.5.2;
|
||||
- ClickHouse Legacy driver has been removed to prevent potential vulnerability issues;
|
||||
- Updated MariaDB driver to version 3.3.2.
|
||||
- Different bug fixes and enhancements have been made.
|
||||
- Added the "Keep alive" setting for Db2 LUW and IMB i, Apache Kyuubi, Clickhouse, Firebird, and Trino
|
||||
- Updated Firebird driver to version 5.0.4
|
||||
- DDL generation for Oracle Tablespaces was added (thanks to [pandya09](https://github.com/pandya09))
|
||||
|
||||
## Contribution
|
||||
As a community-driven open-source project, we warmly welcome contributions through GitHub pull requests.
|
||||
|
||||
[We are happy to reward](https://dbeaver.com/help-dbeaver/) our most active contributors every major sprint.
|
||||
The most significant contribution to our code for the major release 24.2.0 was made by:
|
||||
1. [matthieukhl](https://github.com/matthieukhl)
|
||||
|
||||
+4
-3
@@ -9,9 +9,10 @@ currently being supported with security updates.
|
||||
| ------- | --------- |
|
||||
| 22.x | yes |
|
||||
| 23.x | yes |
|
||||
| 24.x | yes |
|
||||
|
||||
## Reporting a Vulnerability
|
||||
|
||||
Please report (suspected) security vulnerabilities to devops@dbeaver.com.
|
||||
You will receive a response from us within 48 hours.
|
||||
If the issue is confirmed, we will release a patch as soon as possible depending on complexity but historically within a few days.
|
||||
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>2.2.220</version>
|
||||
<version>2.1.214</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
<build>
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"folders": {},
|
||||
"connections": {}
|
||||
}
|
||||
@@ -0,0 +1,104 @@
|
||||
{
|
||||
server: {
|
||||
serverPort: "${CLOUDBEAVER_SERVICE_PORT:8978}",
|
||||
|
||||
workspaceLocation: "${CLOUDBEAVER_WORKSPACE_LOCATION:workspace}",
|
||||
contentRoot: "web",
|
||||
driversLocation: "drivers",
|
||||
|
||||
sslConfigurationPath:"${CLOUDBEAVER_SSL_CONF_PATH:workspace/.data/ssl-config.xml}",
|
||||
|
||||
rootURI: "${CLOUDBEAVER_ROOT_URI:/}",
|
||||
serviceURI: "/api/",
|
||||
|
||||
productSettings: {
|
||||
# Global properties
|
||||
core.theming.theme: 'light',
|
||||
core.localization.localization: 'en',
|
||||
plugin.sql-editor.autoSave: true,
|
||||
plugin.sql-editor.disabled: false,
|
||||
# max size of the file that can be uploaded to the editor (in kilobytes)
|
||||
plugin.sql-editor.maxFileSize: 10240,
|
||||
plugin.log-viewer.disabled: false,
|
||||
plugin.log-viewer.logBatchSize: 1000,
|
||||
plugin.log-viewer.maxLogRecords: 2000,
|
||||
sql.proposals.insert.table.alias: PLAIN
|
||||
},
|
||||
|
||||
expireSessionAfterPeriod: "${CLOUDBEAVER_EXPIRE_SESSION_AFTER_PERIOD:1800000}",
|
||||
|
||||
develMode: "${CLOUDBEAVER_DEVEL_MODE:false}",
|
||||
|
||||
enableSecurityManager: false,
|
||||
|
||||
sm: {
|
||||
enableBruteForceProtection: "${CLOUDBEAVER_BRUTE_FORCE_PROTECTION_ENABLED:true}",
|
||||
maxFailedLogin: "${CLOUDBEAVER_MAX_FAILED_LOGINS:10}",
|
||||
minimumLoginTimeout: "${CLOUDBEAVER_MINIMUM_LOGIN_TIMEOUT:1}",
|
||||
blockLoginPeriod: "${CLOUDBEAVER_BLOCK_PERIOD:300}",
|
||||
passwordPolicy: {
|
||||
minLength: "${CLOUDBEAVER_POLICY_MIN_LENGTH:8}",
|
||||
requireMixedCase: "${CLOUDBEAVER_POLICY_REQUIRE_MIXED_CASE:true}",
|
||||
minNumberCount: "${CLOUDBEAVER_POLICY_MIN_NUMBER_COUNT:1}",
|
||||
minSymbolCount: "${CLOUDBEAVER_POLICY_MIN_SYMBOL_COUNT:0}"
|
||||
}
|
||||
},
|
||||
|
||||
database: {
|
||||
driver: "${CLOUDBEAVER_DB_DRIVER:h2_embedded_v2}",
|
||||
url: "${CLOUDBEAVER_DB_URL:jdbc:h2:${workspace}/.data/cb.h2v2.dat}",
|
||||
schema: "${CLOUDBEAVER_DB_SCHEMA:''}",
|
||||
user: "${CLOUDBEAVER_DB_USER:''}",
|
||||
password: "${CLOUDBEAVER_DB_PASSWORD:''}",
|
||||
initialDataConfiguration: "${CLOUDBEAVER_DB_INITIAL_DATA:conf/initial-data.conf}",
|
||||
pool: {
|
||||
minIdleConnections: "${CLOUDBEAVER_DB_MIN_IDLE_CONNECTIONS:4}",
|
||||
maxIdleConnections: "${CLOUDBEAVER_DB_MAX_IDLE_CONNECTIONS:10}",
|
||||
maxConnections: "${CLOUDBEAVER_DB_MAX_CONNECTIONS:100}",
|
||||
validationQuery: "${CLOUDBEAVER_DB_VALIDATION_QUERY:SELECT 1}"
|
||||
},
|
||||
backupEnabled: "${CLOUDBEAVER_DB_BACKUP_ENABLED:true}"
|
||||
}
|
||||
|
||||
},
|
||||
app: {
|
||||
anonymousAccessEnabled: "${CLOUDBEAVER_APP_ANONYMOUS_ACCESS_ENABLED:true}",
|
||||
anonymousUserRole: user,
|
||||
defaultUserTeam: "${CLOUDBEAVER_APP_DEFAULT_USER_TEAM:user}",
|
||||
grantConnectionsAccessToAnonymousTeam: "${CLOUDBEAVER_APP_GRANT_CONNECTIONS_ACCESS_TO_ANONYMOUS_TEAM:false}",
|
||||
supportsCustomConnections: "${CLOUDBEAVER_APP_SUPPORTS_CUSTOM_CONNECTIONS:false}",
|
||||
showReadOnlyConnectionInfo: "${CLOUDBEAVER_APP_READ_ONLY_CONNECTION_INFO:false}",
|
||||
systemVariablesResolvingEnabled: "${CLOUDBEAVER_SYSTEM_VARIABLES_RESOLVING_ENABLED:false}",
|
||||
|
||||
forwardProxy: "${CLOUDBEAVER_APP_FORWARD_PROXY:false}",
|
||||
|
||||
publicCredentialsSaveEnabled: "${CLOUDBEAVER_APP_PUBLIC_CREDENTIALS_SAVE_ENABLED:true}",
|
||||
adminCredentialsSaveEnabled: "${CLOUDBEAVER_APP_ADMIN_CREDENTIALS_SAVE_ENABLED:true}",
|
||||
|
||||
resourceManagerEnabled: "${CLOUDBEAVER_APP_RESOURCE_MANAGER_ENABLED:true}",
|
||||
|
||||
resourceQuotas: {
|
||||
dataExportFileSizeLimit: "${CLOUDBEAVER_RESOURCE_QUOTA_DATA_EXPORT_FILE_SIZE_LIMIT:10000000}",
|
||||
resourceManagerFileSizeLimit: "${CLOUDBEAVER_RESOURCE_QUOTA_RESOURCE_MANAGER_FILE_SIZE_LIMIT:500000}",
|
||||
sqlMaxRunningQueries: "${CLOUDBEAVER_RESOURCE_QUOTA_SQL_MAX_RUNNING_QUERIES:100}",
|
||||
sqlResultSetRowsLimit: "${CLOUDBEAVER_RESOURCE_QUOTA_SQL_RESULT_SET_ROWS_LIMIT:100000}",
|
||||
sqlTextPreviewMaxLength: "${CLOUDBEAVER_RESOURCE_QUOTA_SQL_TEXT_PREVIEW_MAX_LENGTH:4096}",
|
||||
sqlBinaryPreviewMaxLength: "${CLOUDBEAVER_RESOURCE_QUOTA_SQL_BINARY_PREVIEW_MAX_LENGTH:261120}"
|
||||
},
|
||||
enabledAuthProviders: [
|
||||
"local"
|
||||
],
|
||||
|
||||
disabledDrivers: [
|
||||
"sqlite:sqlite_jdbc",
|
||||
"h2:h2_embedded",
|
||||
"h2:h2_embedded_v2",
|
||||
"clickhouse:yandex_clickhouse"
|
||||
],
|
||||
disabledBetaFeatures: [
|
||||
|
||||
]
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
-24
@@ -1,24 +0,0 @@
|
||||
{
|
||||
"folders": {},
|
||||
"connections": {
|
||||
"postgresql-template-1": {
|
||||
"provider": "postgresql",
|
||||
"driver": "postgres-jdbc",
|
||||
"name": "PostgreSQL (Template)",
|
||||
"save-password": false,
|
||||
"show-system-objects": false,
|
||||
"read-only": true,
|
||||
"template": true,
|
||||
"configuration": {
|
||||
"host": "localhost",
|
||||
"port": "5432",
|
||||
"database": "postgres",
|
||||
"url": "jdbc:postgresql://localhost:5432/postgres",
|
||||
"type": "dev",
|
||||
"provider-properties": {
|
||||
"@dbeaver-show-non-default-db@": "false"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,100 +0,0 @@
|
||||
{
|
||||
server: {
|
||||
serverPort: 8978,
|
||||
|
||||
workspaceLocation: "workspace",
|
||||
contentRoot: "web",
|
||||
driversLocation: "drivers",
|
||||
|
||||
sslConfigurationPath:"${CLOUDBEAVER_SSL_CONF_PATH:workspace/.data/ssl-config.xml}",
|
||||
|
||||
rootURI: "/",
|
||||
serviceURI: "/api/",
|
||||
|
||||
productSettings: {
|
||||
# Global properties
|
||||
core.theming.theme: 'light',
|
||||
core.localization.localization: 'en',
|
||||
plugin.sql-editor.autoSave: true,
|
||||
plugin.sql-editor.disabled: false,
|
||||
# max size of the file that can be uploaded to the editor (in kilobytes)
|
||||
plugin.sql-editor.maxFileSize: 10240,
|
||||
plugin.log-viewer.disabled: false,
|
||||
plugin.log-viewer.logBatchSize: 1000,
|
||||
plugin.log-viewer.maxFailedRequests: 3,
|
||||
plugin.log-viewer.maxLogRecords: 2000,
|
||||
plugin.log-viewer.refreshTimeout: 3000,
|
||||
sql.proposals.insert.table.alias: PLAIN
|
||||
},
|
||||
|
||||
expireSessionAfterPeriod: 1800000,
|
||||
|
||||
develMode: false,
|
||||
|
||||
enableSecurityManager: false,
|
||||
|
||||
sm: {
|
||||
enableBruteForceProtection: "${CLOUDBEAVER_BRUTE_FORCE_PROTECTION_ENABLED:true}",
|
||||
maxFailedLogin: "${CLOUDBEAVER_MAX_FAILED_LOGINS:10}",
|
||||
minimumLoginTimeout: "${CLOUDBEAVER_MINIMUM_LOGIN_TIMEOUT:1}",
|
||||
blockLoginPeriod: "${CLOUDBEAVER_BLOCK_PERIOD:300}",
|
||||
passwordPolicy: {
|
||||
minLength: "${CLOUDBEAVER_POLICY_MIN_LENGTH:8}",
|
||||
requireMixedCase: "${CLOUDBEAVER_POLICY_REQUIRE_MIXED_CASE:true}",
|
||||
minNumberCount: "${CLOUDBEAVER_POLICY_MIN_NUMBER_COUNT:1}",
|
||||
minSymbolCount: "${CLOUDBEAVER_POLICY_MIN_SYMBOL_COUNT:0}"
|
||||
}
|
||||
},
|
||||
|
||||
database: {
|
||||
driver: "h2_embedded_v2",
|
||||
url: "jdbc:h2:${workspace}/.data/cb.h2v2.dat",
|
||||
initialDataConfiguration: "conf/initial-data.conf",
|
||||
pool: {
|
||||
minIdleConnections: 4,
|
||||
maxIdleConnections: 10,
|
||||
maxConnections: 100,
|
||||
validationQuery: "SELECT 1"
|
||||
}
|
||||
}
|
||||
|
||||
},
|
||||
app: {
|
||||
anonymousAccessEnabled: true,
|
||||
anonymousUserRole: "user",
|
||||
defaultUserTeam: "user",
|
||||
grantConnectionsAccessToAnonymousTeam: false,
|
||||
supportsCustomConnections: false,
|
||||
showReadOnlyConnectionInfo: false,
|
||||
systemVariablesResolvingEnabled: "${CLOUDBEAVER_SYSTEM_VARIABLES_RESOLVING_ENABLED:false}",
|
||||
|
||||
forwardProxy: false,
|
||||
|
||||
publicCredentialsSaveEnabled: true,
|
||||
adminCredentialsSaveEnabled: true,
|
||||
|
||||
resourceManagerEnabled: true,
|
||||
|
||||
resourceQuotas: {
|
||||
dataExportFileSizeLimit: 10000000,
|
||||
resourceManagerFileSizeLimit: 500000,
|
||||
sqlMaxRunningQueries: 100,
|
||||
sqlResultSetRowsLimit: 100000,
|
||||
sqlResultSetMemoryLimit: 2000000,
|
||||
sqlTextPreviewMaxLength: 4096,
|
||||
sqlBinaryPreviewMaxLength: 261120
|
||||
},
|
||||
enabledAuthProviders: [
|
||||
"local"
|
||||
],
|
||||
|
||||
disabledDrivers: [
|
||||
"sqlite:sqlite_jdbc",
|
||||
"h2:h2_embedded",
|
||||
"h2:h2_embedded_v2",
|
||||
"clickhouse:yandex_clickhouse"
|
||||
]
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,3 +0,0 @@
|
||||
### Sample databases
|
||||
|
||||
Provides access to locally deployed SQLite sample database
|
||||
-39
@@ -1,39 +0,0 @@
|
||||
{
|
||||
"folders": {},
|
||||
"connections": {
|
||||
"sqlite_xerial-sample-database": {
|
||||
"provider": "generic",
|
||||
"driver": "sqlite_jdbc",
|
||||
"name": "SQLite - Chinook (Sample)",
|
||||
"save-password": true,
|
||||
"navigator-show-only-entities": false,
|
||||
"navigator-hide-folders": false,
|
||||
"read-only": false,
|
||||
"template": false,
|
||||
"configuration": {
|
||||
"database": "${application.path}/../samples/db/Chinook.sqlitedb",
|
||||
"type": "dev",
|
||||
"auth-model": "native"
|
||||
}
|
||||
},
|
||||
"postgresql-template-1": {
|
||||
"provider": "postgresql",
|
||||
"driver": "postgres-jdbc",
|
||||
"name": "PostgreSQL (Template)",
|
||||
"save-password": false,
|
||||
"show-system-objects": false,
|
||||
"read-only": true,
|
||||
"template": true,
|
||||
"configuration": {
|
||||
"host": "localhost",
|
||||
"port": "5432",
|
||||
"database": "postgres",
|
||||
"url": "jdbc:postgresql://localhost:5432/postgres",
|
||||
"type": "dev",
|
||||
"provider-properties": {
|
||||
"@dbeaver-show-non-default-db@": "false"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
-4
@@ -1,4 +0,0 @@
|
||||
{
|
||||
"folders": {},
|
||||
"connections": {}
|
||||
}
|
||||
@@ -1,95 +0,0 @@
|
||||
{
|
||||
server: {
|
||||
serverPort: 8978,
|
||||
|
||||
workspaceLocation: "workspace",
|
||||
contentRoot: "web",
|
||||
driversLocation: "drivers",
|
||||
|
||||
rootURI: "/",
|
||||
serviceURI: "/api/",
|
||||
|
||||
productSettings: {
|
||||
# Global properties
|
||||
core.theming.theme: 'light',
|
||||
core.localization.localization: 'en',
|
||||
plugin.sql-editor.autoSave: true,
|
||||
plugin.sql-editor.disabled: false,
|
||||
# max size of the file that can be uploaded to the editor (in kilobytes)
|
||||
plugin.sql-editor.maxFileSize: 10240,
|
||||
plugin.log-viewer.disabled: false,
|
||||
plugin.log-viewer.logBatchSize: 1000,
|
||||
plugin.log-viewer.maxFailedRequests: 3,
|
||||
plugin.log-viewer.maxLogRecords: 2000,
|
||||
plugin.log-viewer.refreshTimeout: 3000,
|
||||
sql.proposals.insert.table.alias: PLAIN
|
||||
},
|
||||
|
||||
expireSessionAfterPeriod: 1800000,
|
||||
|
||||
develMode: false,
|
||||
|
||||
enableSecurityManager: false,
|
||||
|
||||
database: {
|
||||
driver:"h2_embedded_v2",
|
||||
url: "jdbc:h2:${workspace}/.data/cb.h2v2.dat",
|
||||
initialDataConfiguration: "conf/initial-data.conf",
|
||||
pool: {
|
||||
minIdleConnections: 4,
|
||||
maxIdleConnections: 10,
|
||||
maxConnections: 100,
|
||||
validationQuery: "SELECT 1"
|
||||
}
|
||||
},
|
||||
sm: {
|
||||
enableBruteForceProtection: "${CLOUDBEAVER_BRUTE_FORCE_PROTECTION_ENABLED:true}",
|
||||
maxFailedLogin: "${CLOUDBEAVER_MAX_FAILED_LOGINS:10}",
|
||||
minimumLoginTimeout: "${CLOUDBEAVER_MINIMUM_LOGIN_TIMEOUT:1}",
|
||||
blockLoginPeriod: "${CLOUDBEAVER_BLOCK_PERIOD:300}",
|
||||
passwordPolicy: {
|
||||
minLength: "${CLOUDBEAVER_POLICY_MIN_LENGTH:8}",
|
||||
requireMixedCase: "${CLOUDBEAVER_POLICY_REQUIRE_MIXED_CASE:true}",
|
||||
minNumberCount: "${CLOUDBEAVER_POLICY_MIN_NUMBER_COUNT:1}",
|
||||
minSymbolCount: "${CLOUDBEAVER_POLICY_MIN_SYMBOL_COUNT:0}"
|
||||
}
|
||||
}
|
||||
|
||||
},
|
||||
app: {
|
||||
anonymousAccessEnabled: true,
|
||||
anonymousUserRole: "user",
|
||||
grantConnectionsAccessToAnonymousTeam: false,
|
||||
supportsCustomConnections: false,
|
||||
showReadOnlyConnectionInfo: false,
|
||||
systemVariablesResolvingEnabled: "${CLOUDBEAVER_SYSTEM_VARIABLES_RESOLVING_ENABLED:false}",
|
||||
|
||||
forwardProxy: false,
|
||||
|
||||
publicCredentialsSaveEnabled: true,
|
||||
adminCredentialsSaveEnabled: true,
|
||||
|
||||
resourceManagerEnabled: true,
|
||||
|
||||
resourceQuotas: {
|
||||
dataExportFileSizeLimit: 10000000,
|
||||
resourceManagerFileSizeLimit: 500000,
|
||||
sqlMaxRunningQueries: 100,
|
||||
sqlResultSetRowsLimit: 100000,
|
||||
sqlResultSetMemoryLimit: 2000000,
|
||||
sqlTextPreviewMaxLength: 4096,
|
||||
sqlBinaryPreviewMaxLength: 261120
|
||||
},
|
||||
enabledAuthProviders: [
|
||||
"local"
|
||||
],
|
||||
|
||||
disabledDrivers: [
|
||||
"h2:h2_embedded",
|
||||
"h2:h2_embedded_v2",
|
||||
"clickhouse:yandex_clickhouse"
|
||||
]
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
Binary file not shown.
@@ -1,6 +0,0 @@
|
||||
Chinook Database - Version 1.3
|
||||
Script: Chinook_Sqlite.sql
|
||||
Description: Creates and populates the Chinook database.
|
||||
DB Server: Sqlite
|
||||
Author: Luis Rocha
|
||||
License: http://www.codeplex.com/ChinookDatabase/license
|
||||
+1
-20
@@ -2,12 +2,6 @@
|
||||
set -Eeo pipefail
|
||||
set +u
|
||||
|
||||
# #command line arguments
|
||||
# CONFIGURATION_PATH=${1-"../config/sample-databases/DefaultConfiguration"}
|
||||
# SAMPLE_DATABASE_PATH=${2-""}
|
||||
|
||||
# echo $CONFIGURATION_PATH
|
||||
# echo $SAMPLE_DATABASE_PATH
|
||||
echo "Clone and build Cloudbeaver"
|
||||
|
||||
rm -rf ./drivers
|
||||
@@ -16,7 +10,6 @@ mkdir ./cloudbeaver
|
||||
mkdir ./cloudbeaver/server
|
||||
mkdir ./cloudbeaver/conf
|
||||
mkdir ./cloudbeaver/workspace
|
||||
mkdir ./cloudbeaver/web
|
||||
|
||||
echo "Pull cloudbeaver platform"
|
||||
|
||||
@@ -44,20 +37,8 @@ cp -rp ../server/product/web-server/target/products/io.cloudbeaver.product/all/a
|
||||
cp -p ./scripts/* ./cloudbeaver
|
||||
mkdir cloudbeaver/samples
|
||||
|
||||
if [[ -z $SAMPLE_DATABASE_PATH ]]; then
|
||||
SAMPLE_DATABASE_PATH=""
|
||||
else
|
||||
mkdir cloudbeaver/samples/db
|
||||
cp -rp "${SAMPLE_DATABASE_PATH}" cloudbeaver/samples/
|
||||
fi
|
||||
|
||||
if [[ -z "$CONFIGURATION_PATH" ]]; then
|
||||
CONFIGURATION_PATH="../config/sample-databases/DefaultConfiguration"
|
||||
fi
|
||||
|
||||
cp -rp ../config/core/* cloudbeaver/conf
|
||||
cp -rp "${CONFIGURATION_PATH}"/GlobalConfiguration/.dbeaver/data-sources.json cloudbeaver/conf/initial-data-sources.conf
|
||||
cp -p "${CONFIGURATION_PATH}"/*.conf cloudbeaver/conf/
|
||||
cp -rp ../config/GlobalConfiguration/.dbeaver/data-sources.json cloudbeaver/conf/initial-data-sources.conf
|
||||
mv drivers cloudbeaver
|
||||
|
||||
echo "End of backend build"
|
||||
@@ -3,15 +3,25 @@ set -Eeuo pipefail
|
||||
|
||||
echo "Build static content"
|
||||
|
||||
mkdir ./cloudbeaver/web
|
||||
|
||||
cd ../../cloudbeaver/webapp
|
||||
|
||||
yarn
|
||||
yarn lerna run bootstrap
|
||||
yarn lerna run bundle --no-bail --stream --scope=@cloudbeaver/product-default #-- -- --env source-map
|
||||
cd ./packages/product-default
|
||||
yarn run bundle
|
||||
|
||||
if [[ "$?" -ne 0 ]] ; then
|
||||
echo 'Application build failed'; exit $rc
|
||||
fi
|
||||
|
||||
cd ../../
|
||||
yarn test
|
||||
|
||||
if [[ "$?" -ne 0 ]] ; then
|
||||
echo 'Frontend tests failed'; exit $rc
|
||||
fi
|
||||
|
||||
cd ../deploy
|
||||
|
||||
echo "Copy static content"
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
@call build.bat ..\config\sample-databases\SQLiteConfiguration ..\config\sample-databases\db
|
||||
@@ -1,9 +0,0 @@
|
||||
#!/bin/bash
|
||||
set -Eeuo pipefail
|
||||
|
||||
#command line arguments
|
||||
CONFIGURATION_PATH='../config/sample-databases/SQLiteConfiguration'
|
||||
SAMPLE_DATABASE_PATH='../config/sample-databases/db'
|
||||
|
||||
source build-backend.sh
|
||||
source build-frontend.sh
|
||||
+22
-18
@@ -1,11 +1,6 @@
|
||||
@echo off
|
||||
|
||||
rem command line arguments
|
||||
SET CONFIGURATION_PATH=%1
|
||||
SET SAMPLE_DATABASE_PATH=%2
|
||||
|
||||
IF "%CONFIGURATION_PATH%"=="" SET CONFIGURATION_PATH="..\config\sample-databases\DefaultConfiguration"
|
||||
echo "Configuration path=%CONFIGURATION_PATH%"
|
||||
|
||||
echo Clone and build Cloudbeaver
|
||||
|
||||
@@ -40,32 +35,41 @@ xcopy /E /Q ..\server\product\web-server\target\products\io.cloudbeaver.product\
|
||||
copy scripts\* cloudbeaver >NUL
|
||||
mkdir cloudbeaver\samples
|
||||
|
||||
IF NOT "%SAMPLE_DATABASE_PATH%"=="" (
|
||||
mkdir cloudbeaver\samples\db
|
||||
xcopy /E /Q %SAMPLE_DATABASE_PATH% cloudbeaver\samples\db >NUL
|
||||
)
|
||||
|
||||
copy ..\config\core\* cloudbeaver\conf >NUL
|
||||
copy %CONFIGURATION_PATH%\GlobalConfiguration\.dbeaver\data-sources.json cloudbeaver\conf\initial-data-sources.conf >NUL
|
||||
copy %CONFIGURATION_PATH%\*.conf cloudbeaver\conf >NUL
|
||||
copy ..\config\DefaultConfiguration\GlobalConfiguration\.dbeaver\data-sources.json cloudbeaver\conf\initial-data-sources.conf >NUL
|
||||
|
||||
move drivers cloudbeaver >NUL
|
||||
|
||||
echo Build static content
|
||||
echo "Build static content"
|
||||
|
||||
cd ..\
|
||||
mkdir .\cloudbeaver\web
|
||||
|
||||
cd ..\cloudbeaver\webapp
|
||||
cd ..\webapp
|
||||
|
||||
call yarn
|
||||
call yarn lerna bootstrap
|
||||
call yarn lerna run bundle --no-bail --stream --scope=@cloudbeaver/product-default &::-- -- --env source-map
|
||||
cd .\packages\product-default
|
||||
call yarn run bundle
|
||||
|
||||
if %ERRORLEVEL% neq 0 (
|
||||
echo 'Application build failed'
|
||||
exit /b %ERRORLEVEL%
|
||||
)
|
||||
|
||||
cd ..\..\
|
||||
call yarn test
|
||||
|
||||
if %ERRORLEVEL% neq 0 (
|
||||
echo 'Frontend tests failed'
|
||||
exit /b %ERRORLEVEL%
|
||||
)
|
||||
|
||||
cd ..\deploy
|
||||
|
||||
echo Copy static content
|
||||
echo "Copy static content"
|
||||
|
||||
xcopy /E /Q ..\webapp\packages\product-default\lib cloudbeaver\web >NUL
|
||||
|
||||
echo Cloudbeaver is ready. Run run-server.bat in cloudbeaver folder to start the server.
|
||||
echo "Cloudbeaver is ready. Run run-server.bat in cloudbeaver folder to start the server."
|
||||
|
||||
pause
|
||||
|
||||
@@ -1,9 +1,5 @@
|
||||
#!/bin/bash
|
||||
set -Eeuo pipefail
|
||||
|
||||
#command line arguments
|
||||
CONFIGURATION_PATH="../config/sample-databases/DefaultConfiguration"
|
||||
SAMPLE_DATABASE_PATH=""
|
||||
|
||||
source build-backend.sh
|
||||
source build-frontend.sh
|
||||
@@ -1,10 +1,12 @@
|
||||
FROM ubuntu:23.10
|
||||
FROM ubuntu:24.04
|
||||
|
||||
MAINTAINER DBeaver Corp, devops@dbeaver.com
|
||||
|
||||
ENV DEBIAN_FRONTEND=noninteractive
|
||||
|
||||
RUN set -eux; \
|
||||
apt-get update; \
|
||||
DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \
|
||||
apt-get install -y --no-install-recommends \
|
||||
# curl required for historical reasons, see https://github.com/adoptium/containers/issues/255
|
||||
curl \
|
||||
wget \
|
||||
@@ -43,10 +45,3 @@ RUN set -eux; \
|
||||
|
||||
### Patch java security
|
||||
COPY java.security* ${JAVA_HOME}/conf/security/java.security
|
||||
|
||||
COPY cloudbeaver /opt/cloudbeaver
|
||||
|
||||
EXPOSE 8978
|
||||
RUN find /opt/cloudbeaver -type d -exec chmod 775 {} \;
|
||||
WORKDIR /opt/cloudbeaver/
|
||||
ENTRYPOINT ["./run-server.sh"]
|
||||
@@ -0,0 +1,13 @@
|
||||
FROM dbeaver/base-java
|
||||
|
||||
MAINTAINER DBeaver Corp, devops@dbeaver.com
|
||||
|
||||
RUN apt-get update; \
|
||||
apt-get upgrade -y;
|
||||
|
||||
COPY cloudbeaver /opt/cloudbeaver
|
||||
|
||||
EXPOSE 8978
|
||||
RUN find /opt/cloudbeaver -type d -exec chmod 775 {} \;
|
||||
WORKDIR /opt/cloudbeaver/
|
||||
ENTRYPOINT ["./run-server.sh"]
|
||||
@@ -1,3 +1,3 @@
|
||||
cd ..
|
||||
docker build -t dbeaver/cloudbeaver:dev . --file ./docker/Dockerfile
|
||||
docker build -t dbeaver/cloudbeaver:dev . --file ./docker/cloudbeaver-ce/Dockerfile
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@ echo "Starting Cloudbeaver Server"
|
||||
&& [ ! -f "workspace/GlobalConfiguration/.dbeaver/data-sources.json" ] \
|
||||
&& cp conf/initial-data-sources.conf workspace/GlobalConfiguration/.dbeaver/data-sources.json
|
||||
|
||||
java ${JAVA_OPTS} \
|
||||
exec java ${JAVA_OPTS} \
|
||||
-Dfile.encoding=UTF-8 \
|
||||
--add-modules=ALL-SYSTEM \
|
||||
--add-opens=java.base/java.io=ALL-UNNAMED \
|
||||
|
||||
@@ -1,10 +0,0 @@
|
||||
[program:cloudbeaver]
|
||||
directory=/opt/cloudbeaver/
|
||||
command=/opt/cloudbeaver/run-server.sh
|
||||
stopasgroup=true
|
||||
killasgroup=true
|
||||
stopsignal=INT
|
||||
autostart=true
|
||||
autorestart=true
|
||||
redirect_stderr=true
|
||||
stdout_logfile=/var/log/cloudbeaver-server.log
|
||||
@@ -0,0 +1,3 @@
|
||||
SET mypath=%~dp0
|
||||
|
||||
.\..\idea-rcp-launch-config-generator\runGenerator.cmd -f %mypath%
|
||||
@@ -0,0 +1 @@
|
||||
./../idea-rcp-launch-config-generator/runGenerator.sh -f "$PWD"
|
||||
@@ -1,4 +1,3 @@
|
||||
{
|
||||
"projects": ["../dbeaver-common", "../dbeaver", "../cloudbeaver/server"],
|
||||
"targetPlatform": "./org.jkiss.cloudbeaver.tp.target"
|
||||
}
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<?pde version="3.8"?>
|
||||
<target name="org.jkiss.tp.cloudbeaver">
|
||||
<target name="org.jkiss.cloudbeaver.tp">
|
||||
<locations>
|
||||
<location path="../eclipse/Eclipse.app/Contents/Eclipse/plugins" type="Directory"/>
|
||||
<location path="../eclipse/eclipse/plugins" type="Directory"/>
|
||||
<location path="../dbeaver-workspace/dependencies/plugins" type="Directory"/>
|
||||
</locations>
|
||||
</target>
|
||||
@@ -0,0 +1,33 @@
|
||||
workspaceName=cloudbeaver-ce
|
||||
featuresPaths=\
|
||||
dbeaver/features;\
|
||||
cloudbeaver/server/features;
|
||||
bundlesPaths=\
|
||||
dbeaver-common/modules;\
|
||||
dbeaver/plugins;\
|
||||
cloudbeaver/server/bundles;
|
||||
testLibraries=\
|
||||
org.junit;\
|
||||
org.mockito.mockito-core;\
|
||||
junit-jupiter-api;\
|
||||
org.opentest4j
|
||||
repositories=\
|
||||
https://p2.dev.dbeaver.com/eclipse-repo/;\
|
||||
https://download.eclipse.org/releases/${eclipse-version}/;
|
||||
testBundles=\
|
||||
org.junit;\
|
||||
org.mockito.mockito-core;\
|
||||
junit-jupiter-api;\
|
||||
org.opentest4j
|
||||
productsPaths=\
|
||||
dbeaver/product/community/DBeaver.product;\
|
||||
cloudbeaver/server/product/web-server/CloudbeaverServer.product:../../opt/cloudbeaver;
|
||||
ideaConfigurationFilesPaths=\
|
||||
dbeaver/.ide/.idea;
|
||||
testBundlePaths=\
|
||||
dbeaver/test;\
|
||||
cloudbeaver/server/test;
|
||||
additionalModuleRoots=\
|
||||
opt;
|
||||
optionalFeatureRepositories=\
|
||||
dbeaver/product/repositories
|
||||
@@ -3,8 +3,8 @@ Bundle-ManifestVersion: 2
|
||||
Bundle-Vendor: DBeaver Corp
|
||||
Bundle-Name: Cloudbeaver Web Model
|
||||
Bundle-SymbolicName: io.cloudbeaver.model;singleton:=true
|
||||
Bundle-Version: 1.0.50.qualifier
|
||||
Bundle-Release-Date: 20240408
|
||||
Bundle-Version: 1.0.64.qualifier
|
||||
Bundle-Release-Date: 20241104
|
||||
Bundle-RequiredExecutionEnvironment: JavaSE-17
|
||||
Bundle-ActivationPolicy: lazy
|
||||
Bundle-ClassPath: .
|
||||
@@ -18,16 +18,19 @@ Require-Bundle: org.jkiss.dbeaver.data.gis;visibility:=reexport,
|
||||
org.jkiss.bundle.graphql.java;visibility:=reexport,
|
||||
org.jkiss.bundle.apache.dbcp,
|
||||
com.google.gson;visibility:=reexport,
|
||||
jakarta.servlet;visibility:=reexport
|
||||
jakarta.servlet-api;bundle-version:="6.0.0";visibility:=reexport
|
||||
Export-Package: io.cloudbeaver,
|
||||
io.cloudbeaver.auth,
|
||||
io.cloudbeaver.auth.provider,
|
||||
io.cloudbeaver.auth.provider.fa,
|
||||
io.cloudbeaver.auth.provider.local,
|
||||
io.cloudbeaver.auth.provisioning,
|
||||
io.cloudbeaver.websocket,
|
||||
io.cloudbeaver.model,
|
||||
io.cloudbeaver.model.app,
|
||||
io.cloudbeaver.model.config,
|
||||
io.cloudbeaver.model.fs,
|
||||
io.cloudbeaver.model.log,
|
||||
io.cloudbeaver.model.rm,
|
||||
io.cloudbeaver.model.rm.local,
|
||||
io.cloudbeaver.model.rm.lock,
|
||||
@@ -40,6 +43,7 @@ Export-Package: io.cloudbeaver,
|
||||
io.cloudbeaver.service.admin,
|
||||
io.cloudbeaver.service.security,
|
||||
io.cloudbeaver.service.sql,
|
||||
io.cloudbeaver.test,
|
||||
io.cloudbeaver.utils,
|
||||
io.cloudbeaver.utils.file
|
||||
Automatic-Module-Name: io.cloudbeaver.model
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
<relativePath>../</relativePath>
|
||||
</parent>
|
||||
<artifactId>io.cloudbeaver.model</artifactId>
|
||||
<version>1.0.50-SNAPSHOT</version>
|
||||
<version>1.0.64-SNAPSHOT</version>
|
||||
<packaging>eclipse-plugin</packaging>
|
||||
|
||||
</project>
|
||||
|
||||
@@ -16,23 +16,20 @@
|
||||
*/
|
||||
package io.cloudbeaver;
|
||||
|
||||
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.impl.app.BaseProjectImpl;
|
||||
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;
|
||||
|
||||
import java.nio.file.Path;
|
||||
import java.util.Collection;
|
||||
|
||||
public class BaseWebProjectImpl extends BaseProjectImpl implements RMControllerProvider {
|
||||
public abstract class BaseWebProjectImpl extends BaseProjectImpl implements RMControllerProvider {
|
||||
|
||||
@NotNull
|
||||
private final RMProject project;
|
||||
@@ -40,7 +37,6 @@ public class BaseWebProjectImpl extends BaseProjectImpl implements RMControllerP
|
||||
@NotNull
|
||||
private final Path path;
|
||||
@NotNull
|
||||
protected final DataSourceFilter dataSourceFilter;
|
||||
private final RMController resourceController;
|
||||
|
||||
public BaseWebProjectImpl(
|
||||
@@ -48,13 +44,12 @@ public class BaseWebProjectImpl extends BaseProjectImpl implements RMControllerP
|
||||
@NotNull RMController resourceController,
|
||||
@NotNull SMSessionContext sessionContext,
|
||||
@NotNull RMProject project,
|
||||
@NotNull DataSourceFilter dataSourceFilter
|
||||
@NotNull Path path
|
||||
) {
|
||||
super(workspace, sessionContext);
|
||||
this.resourceController = resourceController;
|
||||
this.path = RMUtils.getProjectPath(project);
|
||||
this.path = path;
|
||||
this.project = project;
|
||||
this.dataSourceFilter = dataSourceFilter;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@@ -91,12 +86,6 @@ public class BaseWebProjectImpl extends BaseProjectImpl implements RMControllerP
|
||||
return path;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public IProject getEclipseProject() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isOpen() {
|
||||
return true;
|
||||
@@ -112,11 +101,6 @@ public class BaseWebProjectImpl extends BaseProjectImpl implements RMControllerP
|
||||
return false;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public RMProject getRmProject() {
|
||||
return this.project;
|
||||
}
|
||||
|
||||
/**
|
||||
* Method for Bulk Update of resources properties paths
|
||||
*
|
||||
|
||||
@@ -35,6 +35,7 @@ public interface DBWConstants {
|
||||
String PERMISSION_EDIT_DATA = "edit-data";
|
||||
|
||||
String STATE_ATTR_SIGN_IN_STATE = "state.signin";
|
||||
String WORK_DATA_FOLDER_NAME = ".work-data";
|
||||
|
||||
enum SignInState {
|
||||
GLOBAL,
|
||||
|
||||
@@ -21,7 +21,7 @@ import graphql.ErrorType;
|
||||
import graphql.GraphQLError;
|
||||
import graphql.language.SourceLocation;
|
||||
import org.jkiss.dbeaver.DBException;
|
||||
import org.jkiss.dbeaver.model.DBPDataSource;
|
||||
import org.jkiss.dbeaver.model.sql.SQLState;
|
||||
import org.jkiss.dbeaver.utils.GeneralUtils;
|
||||
import org.jkiss.utils.CommonUtils;
|
||||
|
||||
@@ -38,6 +38,7 @@ public class DBWebException extends DBException implements GraphQLError {
|
||||
|
||||
public static final String ERROR_CODE_SESSION_EXPIRED = "sessionExpired";
|
||||
public static final String ERROR_CODE_ACCESS_DENIED = "accessDenied";
|
||||
public static final String ERROR_CODE_SERVER_NOT_INITIALIZED = "serverNotInitialized";
|
||||
public static final String ERROR_CODE_LICENSE_DENIED = "licenseRequired";
|
||||
public static final String ERROR_CODE_IDENT_REQUIRED = "identRequired";
|
||||
public static final String ERROR_CODE_AUTH_REQUIRED = "authRequired";
|
||||
@@ -65,12 +66,8 @@ public class DBWebException extends DBException implements GraphQLError {
|
||||
this.webErrorCode = errorCode;
|
||||
}
|
||||
|
||||
public DBWebException(Throwable cause, DBPDataSource dataSource) {
|
||||
super(cause, dataSource);
|
||||
}
|
||||
|
||||
public DBWebException(String message, Throwable cause, DBPDataSource dataSource) {
|
||||
super(makeMessage(message, cause), cause, dataSource);
|
||||
public DBWebException(Throwable cause) {
|
||||
super(null, cause);
|
||||
}
|
||||
|
||||
public String getWebErrorCode() {
|
||||
@@ -128,14 +125,14 @@ public class DBWebException extends DBException implements GraphQLError {
|
||||
//stString = stString.substring(divPos + 1).trim();
|
||||
}
|
||||
extensions.put("stackTrace", stString.trim());
|
||||
int errorCode = getErrorCode();
|
||||
int errorCode = SQLState.getCodeFromException(this);
|
||||
if (errorCode != ERROR_CODE_NONE) {
|
||||
extensions.put("errorCode", errorCode);
|
||||
}
|
||||
if (!CommonUtils.isEmpty(webErrorCode)) {
|
||||
extensions.put("webErrorCode", webErrorCode);
|
||||
}
|
||||
String databaseState = getDatabaseState();
|
||||
String databaseState = SQLState.getStateFromException(this);
|
||||
if (databaseState != null) {
|
||||
extensions.put("databaseState", databaseState);
|
||||
}
|
||||
|
||||
+8
-1
@@ -29,6 +29,7 @@ import org.jkiss.dbeaver.model.app.DBPProject;
|
||||
import org.jkiss.dbeaver.model.connection.DBPConnectionConfiguration;
|
||||
import org.jkiss.dbeaver.model.connection.DBPDriver;
|
||||
import org.jkiss.dbeaver.model.net.DBWNetworkProfile;
|
||||
import org.jkiss.dbeaver.model.preferences.DBPPreferenceStore;
|
||||
import org.jkiss.dbeaver.model.runtime.DBRProgressMonitor;
|
||||
import org.jkiss.dbeaver.model.secret.DBSSecretController;
|
||||
import org.jkiss.dbeaver.model.struct.DBSObjectFilter;
|
||||
@@ -260,7 +261,7 @@ public class WebDataSourceRegistryProxy implements DBPDataSourceRegistry, DataSo
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeAuthProfile(DBAAuthProfile profile) {
|
||||
public void removeAuthProfile(@NotNull DBAAuthProfile profile) {
|
||||
dataSourceRegistry.removeAuthProfile(profile);
|
||||
}
|
||||
|
||||
@@ -322,6 +323,12 @@ public class WebDataSourceRegistryProxy implements DBPDataSourceRegistry, DataSo
|
||||
return dataSourceRegistry.getTemporaryFolders();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public DBPPreferenceStore getPreferenceStore() {
|
||||
return dataSourceRegistry.getPreferenceStore();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean loadDataSources(
|
||||
@NotNull List<DBPDataSourceConfigurationStorage> storages,
|
||||
|
||||
+38
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* DBeaver - Universal Database Manager
|
||||
* Copyright (C) 2010-2024 DBeaver Corp and others
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package io.cloudbeaver;
|
||||
|
||||
import io.cloudbeaver.model.session.WebHeadlessSession;
|
||||
import org.jkiss.code.NotNull;
|
||||
import org.jkiss.dbeaver.model.rm.RMProject;
|
||||
import org.jkiss.dbeaver.model.rm.RMUtils;
|
||||
|
||||
public class WebHeadlessSessionProjectImpl extends WebProjectImpl {
|
||||
public WebHeadlessSessionProjectImpl(
|
||||
@NotNull WebHeadlessSession session,
|
||||
@NotNull RMProject project
|
||||
) {
|
||||
super(
|
||||
session.getWorkspace(),
|
||||
session.getUserContext().getRmController(),
|
||||
session.getSessionContext(),
|
||||
project,
|
||||
session.getUserContext().getPreferenceStore(),
|
||||
RMUtils.getProjectPath(project)
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -23,22 +23,30 @@ import org.jkiss.dbeaver.Log;
|
||||
import org.jkiss.dbeaver.model.app.DBPDataSourceRegistry;
|
||||
import org.jkiss.dbeaver.model.app.DBPWorkspace;
|
||||
import org.jkiss.dbeaver.model.auth.SMSessionContext;
|
||||
import org.jkiss.dbeaver.model.preferences.DBPPreferenceStore;
|
||||
import org.jkiss.dbeaver.model.rm.RMController;
|
||||
import org.jkiss.dbeaver.model.rm.RMProject;
|
||||
import org.jkiss.dbeaver.model.task.DBTTaskManager;
|
||||
import org.jkiss.dbeaver.registry.rm.DataSourceRegistryRM;
|
||||
import org.jkiss.dbeaver.runtime.DBWorkbench;
|
||||
|
||||
import java.nio.file.Path;
|
||||
|
||||
public abstract class WebProjectImpl extends BaseWebProjectImpl {
|
||||
private static final Log log = Log.getLog(WebProjectImpl.class);
|
||||
@NotNull
|
||||
protected final DBPPreferenceStore preferenceStore;
|
||||
|
||||
public WebProjectImpl(
|
||||
@NotNull DBPWorkspace workspace,
|
||||
@NotNull RMController resourceController,
|
||||
@NotNull SMSessionContext sessionContext,
|
||||
@NotNull RMProject project,
|
||||
@NotNull DataSourceFilter dataSourceFilter
|
||||
@NotNull DBPPreferenceStore preferenceStore,
|
||||
@NotNull Path path
|
||||
) {
|
||||
super(workspace, resourceController, sessionContext, project, dataSourceFilter);
|
||||
super(workspace, resourceController, sessionContext, project, path);
|
||||
this.preferenceStore = preferenceStore;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@@ -66,13 +74,24 @@ public abstract class WebProjectImpl extends BaseWebProjectImpl {
|
||||
return DBWorkbench.isDistributed();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public DBTTaskManager getTaskManager() {
|
||||
throw new IllegalStateException("Task manager not supported");
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
protected DBPDataSourceRegistry createDataSourceRegistry() {
|
||||
return new WebDataSourceRegistryProxy(
|
||||
new DataSourceRegistryRM(this, getResourceController()),
|
||||
dataSourceFilter
|
||||
new DataSourceRegistryRM(this, getResourceController(), preferenceStore),
|
||||
getDataSourceFilter()
|
||||
);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public DataSourceFilter getDataSourceFilter() {
|
||||
return (ds) -> true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+118
@@ -0,0 +1,118 @@
|
||||
/*
|
||||
* DBeaver - Universal Database Manager
|
||||
* Copyright (C) 2010-2024 DBeaver Corp and others
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package io.cloudbeaver;
|
||||
|
||||
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.DBPDataSourceContainer;
|
||||
import org.jkiss.dbeaver.model.DBPEvent;
|
||||
import org.jkiss.dbeaver.model.rm.RMProject;
|
||||
import org.jkiss.dbeaver.model.security.SMObjectType;
|
||||
import org.jkiss.dbeaver.model.security.user.SMObjectPermissions;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* Global project.
|
||||
* Connections there can be not accessible.
|
||||
*/
|
||||
public class WebSessionGlobalProjectImpl extends WebSessionProjectImpl {
|
||||
private static final Log log = Log.getLog(WebSessionGlobalProjectImpl.class);
|
||||
private Set<String> accessibleConnectionIds = Collections.emptySet();
|
||||
|
||||
public WebSessionGlobalProjectImpl(@NotNull WebSession webSession, @NotNull RMProject project) {
|
||||
super(webSession, project);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update info about accessible connections from a database.
|
||||
*/
|
||||
public synchronized void refreshAccessibleConnectionIds() {
|
||||
this.accessibleConnectionIds = readAccessibleConnectionIds();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private Set<String> readAccessibleConnectionIds() {
|
||||
try {
|
||||
return webSession.getSecurityController()
|
||||
.getAllAvailableObjectsPermissions(SMObjectType.datasource)
|
||||
.stream()
|
||||
.map(SMObjectPermissions::getObjectId)
|
||||
.collect(Collectors.toSet());
|
||||
} catch (DBException e) {
|
||||
webSession.addSessionError(e);
|
||||
log.error("Error reading connection grants", e);
|
||||
return Collections.emptySet();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if connection is accessible for current user.
|
||||
*/
|
||||
public boolean isDataSourceAccessible(@NotNull DBPDataSourceContainer dataSource) {
|
||||
return dataSource.isExternallyProvided() ||
|
||||
dataSource.isTemporary() ||
|
||||
webSession.hasPermission(DBWConstants.PERMISSION_ADMIN) ||
|
||||
accessibleConnectionIds.contains(dataSource.getId());
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a connection if it became accessible.
|
||||
* The method is processed when connection permissions were updated.
|
||||
*/
|
||||
public synchronized void addAccessibleConnectionToCache(@NotNull String dsId) {
|
||||
if (!getRMProject().isGlobal()) {
|
||||
return;
|
||||
}
|
||||
this.accessibleConnectionIds.add(dsId);
|
||||
var registry = getDataSourceRegistry();
|
||||
var dataSource = registry.getDataSource(dsId);
|
||||
if (dataSource != null) {
|
||||
addConnection(dataSource);
|
||||
// reflect changes is navigator model
|
||||
registry.notifyDataSourceListeners(new DBPEvent(DBPEvent.Action.OBJECT_ADD, dataSource, true));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes a connection if it became not accessible.
|
||||
* The method is processed when connection permissions were updated.
|
||||
*/
|
||||
public synchronized void removeAccessibleConnectionFromCache(@NotNull String dsId) {
|
||||
if (!getRMProject().isGlobal()) {
|
||||
return;
|
||||
}
|
||||
var registry = getDataSourceRegistry();
|
||||
var dataSource = registry.getDataSource(dsId);
|
||||
if (dataSource != null) {
|
||||
this.accessibleConnectionIds.remove(dsId);
|
||||
removeConnection(dataSource);
|
||||
// reflect changes is navigator model
|
||||
registry.notifyDataSourceListeners(new DBPEvent(DBPEvent.Action.OBJECT_REMOVE, dataSource));
|
||||
dataSource.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public DataSourceFilter getDataSourceFilter() {
|
||||
return this::isDataSourceAccessible;
|
||||
}
|
||||
}
|
||||
@@ -16,18 +16,60 @@
|
||||
*/
|
||||
package io.cloudbeaver;
|
||||
|
||||
import io.cloudbeaver.model.WebConnectionInfo;
|
||||
import io.cloudbeaver.model.session.WebSession;
|
||||
import io.cloudbeaver.utils.WebDataSourceUtils;
|
||||
import org.jkiss.code.NotNull;
|
||||
import org.jkiss.code.Nullable;
|
||||
import org.jkiss.dbeaver.Log;
|
||||
import org.jkiss.dbeaver.model.DBPDataSourceContainer;
|
||||
import org.jkiss.dbeaver.model.app.DBPDataSourceRegistry;
|
||||
import org.jkiss.dbeaver.model.app.DBPDataSourceRegistryCache;
|
||||
import org.jkiss.dbeaver.model.navigator.DBNModel;
|
||||
import org.jkiss.dbeaver.model.rm.RMProject;
|
||||
import org.jkiss.dbeaver.model.rm.RMUtils;
|
||||
import org.jkiss.dbeaver.model.websocket.event.WSEventType;
|
||||
import org.jkiss.dbeaver.registry.DataSourceDescriptor;
|
||||
import org.jkiss.dbeaver.runtime.jobs.DisconnectJob;
|
||||
|
||||
import java.nio.file.Path;
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class WebSessionProjectImpl extends WebProjectImpl {
|
||||
private static final Log log = Log.getLog(WebSessionProjectImpl.class);
|
||||
protected final WebSession webSession;
|
||||
private final Map<String, WebConnectionInfo> connections = new HashMap<>();
|
||||
private boolean registryIsLoaded = false;
|
||||
|
||||
private final WebSession webSession;
|
||||
public WebSessionProjectImpl(
|
||||
@NotNull WebSession webSession,
|
||||
@NotNull RMProject project
|
||||
) {
|
||||
super(
|
||||
webSession.getWorkspace(),
|
||||
webSession.getRmController(),
|
||||
webSession.getSessionContext(),
|
||||
project,
|
||||
webSession.getUserPreferenceStore(),
|
||||
RMUtils.getProjectPath(project)
|
||||
);
|
||||
this.webSession = webSession;
|
||||
}
|
||||
|
||||
public WebSessionProjectImpl(@NotNull WebSession webSession, @NotNull RMProject project, @NotNull DataSourceFilter dataSourceFilter) {
|
||||
super(webSession.getWorkspace(), webSession.getRmController(), webSession.getSessionContext(), project, dataSourceFilter);
|
||||
public WebSessionProjectImpl(
|
||||
@NotNull WebSession webSession,
|
||||
@NotNull RMProject project,
|
||||
@NotNull Path path
|
||||
) {
|
||||
super(
|
||||
webSession.getWorkspace(),
|
||||
webSession.getRmController(),
|
||||
webSession.getSessionContext(),
|
||||
project,
|
||||
webSession.getUserPreferenceStore(),
|
||||
path
|
||||
);
|
||||
this.webSession = webSession;
|
||||
}
|
||||
|
||||
@@ -36,4 +78,156 @@ public class WebSessionProjectImpl extends WebProjectImpl {
|
||||
public DBNModel getNavigatorModel() {
|
||||
return webSession.getNavigatorModel();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
protected DBPDataSourceRegistry createDataSourceRegistry() {
|
||||
DBPDataSourceRegistry dataSourceRegistry = super.createDataSourceRegistry();
|
||||
dataSourceRegistry.setAuthCredentialsProvider(webSession);
|
||||
return dataSourceRegistry;
|
||||
}
|
||||
|
||||
private synchronized void addDataSourcesToCache() {
|
||||
if (registryIsLoaded) {
|
||||
return;
|
||||
}
|
||||
getDataSourceRegistry().getDataSources().forEach(this::addConnection);
|
||||
Throwable lastError = getDataSourceRegistry().getLastError();
|
||||
if (lastError != null) {
|
||||
webSession.addSessionError(lastError);
|
||||
log.error("Error refreshing connections from project '" + getId() + "'", lastError);
|
||||
}
|
||||
registryIsLoaded = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispose() {
|
||||
super.dispose();
|
||||
Map<String, WebConnectionInfo> conCopy;
|
||||
synchronized (this.connections) {
|
||||
conCopy = new HashMap<>(this.connections);
|
||||
this.connections.clear();
|
||||
}
|
||||
|
||||
for (WebConnectionInfo connectionInfo : conCopy.values()) {
|
||||
if (connectionInfo.isConnected()) {
|
||||
new DisconnectJob(connectionInfo.getDataSourceContainer()).schedule();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns web connection info from cache (if exists).
|
||||
*/
|
||||
@Nullable
|
||||
public WebConnectionInfo findWebConnectionInfo(@NotNull String connectionId) {
|
||||
synchronized (connections) {
|
||||
return connections.get(connectionId);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns web connection info from cache, adds it to cache if not present.
|
||||
* Throws exception if connection is not found.
|
||||
*/
|
||||
@NotNull
|
||||
public WebConnectionInfo getWebConnectionInfo(@NotNull String connectionId) throws DBWebException {
|
||||
WebConnectionInfo connectionInfo = findWebConnectionInfo(connectionId);
|
||||
if (connectionInfo != null) {
|
||||
return connectionInfo;
|
||||
}
|
||||
DBPDataSourceContainer dataSource = getDataSourceRegistry().getDataSource(connectionId);
|
||||
if (dataSource != null) {
|
||||
return addConnection(dataSource);
|
||||
}
|
||||
throw new DBWebException("Connection '%s' not found".formatted(connectionId));
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds connection to project cache.
|
||||
*/
|
||||
@NotNull
|
||||
public synchronized WebConnectionInfo addConnection(@NotNull DBPDataSourceContainer dataSourceContainer) {
|
||||
WebConnectionInfo connection = new WebConnectionInfo(webSession, dataSourceContainer);
|
||||
synchronized (connections) {
|
||||
connections.put(dataSourceContainer.getId(), connection);
|
||||
}
|
||||
return connection;
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes connection from project cache.
|
||||
*/
|
||||
public void removeConnection(@NotNull DBPDataSourceContainer dataSourceContainer) {
|
||||
WebConnectionInfo webConnectionInfo = connections.get(dataSourceContainer.getId());
|
||||
if (webConnectionInfo != null) {
|
||||
webConnectionInfo.clearCache();
|
||||
synchronized (connections) {
|
||||
connections.remove(dataSourceContainer.getId());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads connection from registry if they are not loaded.
|
||||
*
|
||||
* @return connections from cache.
|
||||
*/
|
||||
public List<WebConnectionInfo> getConnections() {
|
||||
if (!registryIsLoaded) {
|
||||
addDataSourcesToCache();
|
||||
registryIsLoaded = true;
|
||||
}
|
||||
synchronized (connections) {
|
||||
return new ArrayList<>(connections.values());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* updates data sources based on event in web session
|
||||
*
|
||||
* @param dataSourceIds list of updated connections
|
||||
* @param type type of event
|
||||
*/
|
||||
public synchronized boolean updateProjectDataSources(@NotNull List<String> dataSourceIds, @NotNull WSEventType type) {
|
||||
var sendDataSourceUpdatedEvent = false;
|
||||
DBPDataSourceRegistry registry = getDataSourceRegistry();
|
||||
// save old connections
|
||||
var oldDataSources = dataSourceIds.stream()
|
||||
.map(registry::getDataSource)
|
||||
.filter(Objects::nonNull)
|
||||
.collect(Collectors.toMap(
|
||||
DBPDataSourceContainer::getId,
|
||||
ds -> new DataSourceDescriptor((DataSourceDescriptor) ds, ds.getRegistry())
|
||||
));
|
||||
if (type == WSEventType.DATASOURCE_CREATED || type == WSEventType.DATASOURCE_UPDATED) {
|
||||
registry.refreshConfig(dataSourceIds);
|
||||
}
|
||||
for (String dsId : dataSourceIds) {
|
||||
DataSourceDescriptor ds = (DataSourceDescriptor) registry.getDataSource(dsId);
|
||||
if (ds == null) {
|
||||
continue;
|
||||
}
|
||||
switch (type) {
|
||||
case DATASOURCE_CREATED -> {
|
||||
addConnection(ds);
|
||||
sendDataSourceUpdatedEvent = true;
|
||||
}
|
||||
case DATASOURCE_UPDATED -> // if settings were changed we need to send event
|
||||
sendDataSourceUpdatedEvent |= !ds.equalSettings(oldDataSources.get(dsId));
|
||||
case DATASOURCE_DELETED -> {
|
||||
WebDataSourceUtils.disconnectDataSource(webSession, ds);
|
||||
if (registry instanceof DBPDataSourceRegistryCache dsrc) {
|
||||
dsrc.removeDataSourceFromList(ds);
|
||||
}
|
||||
removeConnection(ds);
|
||||
sendDataSourceUpdatedEvent = true;
|
||||
}
|
||||
default -> {
|
||||
}
|
||||
}
|
||||
}
|
||||
return sendDataSourceUpdatedEvent;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,6 +25,7 @@ public interface CBAuthConstants {
|
||||
String CB_AUTH_ID_REQUEST_PARAM = "authId";
|
||||
String CB_AUTO_LOGIN_REQUEST_PARAM = "autoLogin";
|
||||
String CB_REDIRECT_URL_REQUEST_PARAM = "redirectUrl";
|
||||
String CB_FORCE_SESSIONS_LOGOUT = "forceSessionsLogout";
|
||||
|
||||
// Default max idle time (10 minutes)
|
||||
long MAX_SESSION_IDLE_TIME = 10 * 60 * 1000;
|
||||
|
||||
+3
@@ -46,6 +46,9 @@ public interface SMAuthProviderFederated extends SMSignOutLinkProvider {
|
||||
@Nullable
|
||||
String getAcsLink(String id, @NotNull Map<String, Object> providerConfig) throws DBException;
|
||||
|
||||
@Nullable
|
||||
String getEntityIdLink(String id, @NotNull Map<String, Object> providerConfig) throws DBException;
|
||||
|
||||
@Nullable
|
||||
default String getRedirectLink(String id, @NotNull Map<String, Object> providerConfig) throws DBException {
|
||||
return null;
|
||||
|
||||
+79
@@ -0,0 +1,79 @@
|
||||
/*
|
||||
* DBeaver - Universal Database Manager
|
||||
* Copyright (C) 2010-2024 DBeaver Corp and others
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package io.cloudbeaver.auth.provider.fa;
|
||||
|
||||
import org.jkiss.code.NotNull;
|
||||
import org.jkiss.code.Nullable;
|
||||
import org.jkiss.dbeaver.model.auth.*;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.Map;
|
||||
|
||||
public abstract class AbstractSessionExternal implements SMSessionExternal {
|
||||
|
||||
@NotNull
|
||||
protected final Map<String, Object> authParameters;
|
||||
@NotNull
|
||||
protected final SMSession parentSession;
|
||||
@NotNull
|
||||
protected final SMAuthSpace space;
|
||||
|
||||
protected AbstractSessionExternal(
|
||||
@NotNull SMSession parentSession,
|
||||
@NotNull SMAuthSpace space,
|
||||
@NotNull Map<String, Object> authParameters
|
||||
) {
|
||||
this.parentSession = parentSession;
|
||||
this.space = space;
|
||||
this.authParameters = authParameters;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public SMAuthSpace getSessionSpace() {
|
||||
return space;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public SMSessionContext getSessionContext() {
|
||||
return this.parentSession.getSessionContext();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public SMSessionPrincipal getSessionPrincipal() {
|
||||
return parentSession.getSessionPrincipal();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public LocalDateTime getSessionStart() {
|
||||
return parentSession.getSessionStart();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
// do nothing
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Object> getAuthParameters() {
|
||||
return authParameters;
|
||||
}
|
||||
}
|
||||
+59
-12
@@ -24,12 +24,11 @@ import io.cloudbeaver.service.sql.WebDataFormat;
|
||||
import io.cloudbeaver.utils.CBModelConstants;
|
||||
import io.cloudbeaver.utils.WebAppUtils;
|
||||
import io.cloudbeaver.utils.WebCommonUtils;
|
||||
import org.jkiss.code.NotNull;
|
||||
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;
|
||||
import org.jkiss.dbeaver.model.DBPDataSourceFolder;
|
||||
import org.jkiss.dbeaver.model.*;
|
||||
import org.jkiss.dbeaver.model.admin.sessions.DBAServerSessionManager;
|
||||
import org.jkiss.dbeaver.model.app.DBPProject;
|
||||
import org.jkiss.dbeaver.model.connection.DBPAuthModelDescriptor;
|
||||
import org.jkiss.dbeaver.model.connection.DBPConnectionConfiguration;
|
||||
@@ -40,6 +39,7 @@ import org.jkiss.dbeaver.model.navigator.DBNBrowseSettings;
|
||||
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.RMConstants;
|
||||
import org.jkiss.dbeaver.model.rm.RMProjectPermission;
|
||||
import org.jkiss.dbeaver.model.runtime.DBRRunnableParametrized;
|
||||
import org.jkiss.dbeaver.runtime.DBWorkbench;
|
||||
@@ -56,6 +56,17 @@ public class WebConnectionInfo {
|
||||
|
||||
private static final Log log = Log.getLog(WebConnectionInfo.class);
|
||||
public static final String SECURED_VALUE = "********";
|
||||
|
||||
private static final String FEATURE_HAS_TOOLS = "hasTools";
|
||||
private static final String FEATURE_CONNECTED = "connected";
|
||||
private static final String FEATURE_VIRTUAL = "virtual";
|
||||
private static final String FEATURE_TEMPORARY = "temporary";
|
||||
private static final String FEATURE_READ_ONLY = "readOnly";
|
||||
private static final String FEATURE_PROVIDED = "provided";
|
||||
private static final String FEATURE_MANAGEABLE = "manageable";
|
||||
|
||||
private static final String TOOL_SESSION_MANAGER = "sessionManager";
|
||||
|
||||
private final WebSession session;
|
||||
private final DBPDataSourceContainer dataSourceContainer;
|
||||
private WebServerError connectError;
|
||||
@@ -243,22 +254,25 @@ public class WebConnectionInfo {
|
||||
List<String> features = new ArrayList<>();
|
||||
|
||||
if (dataSourceContainer.isConnected()) {
|
||||
features.add("connected");
|
||||
features.add(FEATURE_CONNECTED);
|
||||
if (!getTools().isEmpty()) {
|
||||
features.add(FEATURE_HAS_TOOLS);
|
||||
}
|
||||
}
|
||||
if (dataSourceContainer.isHidden()) {
|
||||
features.add("virtual");
|
||||
features.add(FEATURE_VIRTUAL);
|
||||
}
|
||||
if (dataSourceContainer.isTemporary()) {
|
||||
features.add("temporary");
|
||||
features.add(FEATURE_TEMPORARY);
|
||||
}
|
||||
if (dataSourceContainer.isConnectionReadOnly()) {
|
||||
features.add("readOnly");
|
||||
features.add(FEATURE_READ_ONLY);
|
||||
}
|
||||
if (dataSourceContainer.isProvided()) {
|
||||
features.add("provided");
|
||||
features.add(FEATURE_PROVIDED);
|
||||
}
|
||||
if (dataSourceContainer.isManageable()) {
|
||||
features.add("manageable");
|
||||
features.add(FEATURE_MANAGEABLE);
|
||||
}
|
||||
|
||||
return features.toArray(new String[0]);
|
||||
@@ -384,6 +398,16 @@ public class WebConnectionInfo {
|
||||
}
|
||||
}
|
||||
|
||||
@Property
|
||||
public Map<String, String> getMainPropertyValues() {
|
||||
Map<String, String> mainProperties = new LinkedHashMap<>();
|
||||
mainProperties.put(DBConstants.PROP_HOST, getHost());
|
||||
mainProperties.put(DBConstants.PROP_PORT, getPort());
|
||||
mainProperties.put(DBConstants.PROP_DATABASE, getDatabaseName());
|
||||
mainProperties.put(DBConstants.PROP_SERVER, getServerName());
|
||||
return mainProperties;
|
||||
}
|
||||
|
||||
@Property
|
||||
public Map<String, String> getProviderProperties() {
|
||||
return dataSourceContainer.getConnectionConfiguration().getProviderProperties();
|
||||
@@ -421,10 +445,10 @@ public class WebConnectionInfo {
|
||||
|
||||
private boolean hasProjectPermission(RMProjectPermission projectPermission) {
|
||||
DBPProject project = dataSourceContainer.getProject();
|
||||
if (!(project instanceof WebProjectImpl)) {
|
||||
if (!(project instanceof WebProjectImpl webProject)) {
|
||||
return false;
|
||||
}
|
||||
return SMUtils.hasProjectPermission(session, ((WebProjectImpl) project).getRmProject(), projectPermission);
|
||||
return SMUtils.hasProjectPermission(session, webProject.getRMProject(), projectPermission);
|
||||
}
|
||||
|
||||
private boolean canViewReadOnlyConnections() {
|
||||
@@ -448,6 +472,15 @@ public class WebConnectionInfo {
|
||||
return dataSourceContainer.getConnectionConfiguration().getKeepAliveInterval();
|
||||
}
|
||||
|
||||
@Property
|
||||
public boolean isAutocommit() {
|
||||
Boolean isAutoCommit = dataSourceContainer.getConnectionConfiguration().getBootstrap().getDefaultAutoCommit();
|
||||
if (isAutoCommit == null) {
|
||||
return true;
|
||||
}
|
||||
return isAutoCommit;
|
||||
}
|
||||
|
||||
@Property
|
||||
public List<WebSecretInfo> getSharedSecrets() throws DBException {
|
||||
return dataSourceContainer.listSharedCredentials()
|
||||
@@ -456,4 +489,18 @@ public class WebConnectionInfo {
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Property
|
||||
public List<String> getTools() {
|
||||
if (!session.hasPermission(RMConstants.PERMISSION_DATABASE_DEVELOPER)) {
|
||||
return List.of();
|
||||
}
|
||||
List<String> tools = new ArrayList<>();
|
||||
// checks inside that datasource is not null in container, and it is adaptable to session manager class
|
||||
if (DBUtils.getAdapter(DBAServerSessionManager.class, dataSourceContainer) != null) {
|
||||
tools.add(TOOL_SESSION_MANAGER);
|
||||
}
|
||||
return tools;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -53,11 +53,13 @@ public class WebProjectInfo {
|
||||
}
|
||||
|
||||
@Property
|
||||
public boolean isGlobal() { return project.getRmProject().isGlobal(); }
|
||||
public boolean isGlobal() {
|
||||
return project.getRMProject().isGlobal();
|
||||
}
|
||||
|
||||
@Property
|
||||
public boolean isShared() {
|
||||
return project.getRmProject().isShared();
|
||||
return project.getRMProject().isShared();
|
||||
}
|
||||
|
||||
@Property
|
||||
@@ -72,7 +74,7 @@ public class WebProjectInfo {
|
||||
|
||||
@Property
|
||||
public boolean isCanEditDataSources() {
|
||||
if (project.getRmProject().getType() == RMProjectType.USER && !customPrivateConnectionsEnabled) {
|
||||
if (project.getRMProject().getType() == RMProjectType.USER && !customPrivateConnectionsEnabled) {
|
||||
return false;
|
||||
}
|
||||
return hasDataSourcePermission(RMProjectPermission.DATA_SOURCES_EDIT);
|
||||
@@ -94,12 +96,12 @@ public class WebProjectInfo {
|
||||
}
|
||||
|
||||
private boolean hasDataSourcePermission(RMProjectPermission permission) {
|
||||
return SMUtils.hasProjectPermission(session, project.getRmProject(), permission);
|
||||
return SMUtils.hasProjectPermission(session, project.getRMProject(), permission);
|
||||
}
|
||||
|
||||
@Property
|
||||
public RMResourceType[] getResourceTypes() {
|
||||
RMResourceType[] resourceTypes = project.getRmProject().getResourceTypes();
|
||||
RMResourceType[] resourceTypes = project.getRMProject().getResourceTypes();
|
||||
|
||||
if(resourceTypes == null) {
|
||||
return ArrayUtils.toArray(RMResourceType.class, new ArrayList<>());
|
||||
|
||||
@@ -17,8 +17,8 @@
|
||||
package io.cloudbeaver.model;
|
||||
|
||||
import io.cloudbeaver.DBWebException;
|
||||
import org.jkiss.dbeaver.DBException;
|
||||
import org.jkiss.dbeaver.model.sql.DBQuotaException;
|
||||
import org.jkiss.dbeaver.model.sql.SQLState;
|
||||
|
||||
import java.io.PrintWriter;
|
||||
import java.io.StringWriter;
|
||||
@@ -28,22 +28,18 @@ import java.io.StringWriter;
|
||||
*/
|
||||
public class WebServerError {
|
||||
|
||||
private String message;
|
||||
private String stackTrace;
|
||||
private String errorType;
|
||||
private String errorCode;
|
||||
private final String message;
|
||||
private final String stackTrace;
|
||||
private final String errorType;
|
||||
private final String errorCode;
|
||||
|
||||
public WebServerError(Throwable ex) {
|
||||
this.message = ex.getMessage();
|
||||
StringWriter buf = new StringWriter();
|
||||
ex.printStackTrace(new PrintWriter(buf, true));
|
||||
this.stackTrace = buf.toString();
|
||||
if (ex instanceof DBException) {
|
||||
errorCode = String.valueOf(((DBException) ex).getErrorCode());
|
||||
}
|
||||
if (ex instanceof DBQuotaException) {
|
||||
errorType = DBWebException.ERROR_CODE_QUOTA_EXCEEDED;
|
||||
}
|
||||
errorCode = String.valueOf(SQLState.getCodeFromException(ex));
|
||||
errorType = ex instanceof DBQuotaException ? DBWebException.ERROR_CODE_QUOTA_EXCEEDED : null;
|
||||
}
|
||||
|
||||
public String getMessage() {
|
||||
|
||||
-144
@@ -1,144 +0,0 @@
|
||||
/*
|
||||
* DBeaver - Universal Database Manager
|
||||
* Copyright (C) 2010-2024 DBeaver Corp and others
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package io.cloudbeaver.model.app;
|
||||
|
||||
import com.google.gson.annotations.Expose;
|
||||
import io.cloudbeaver.auth.provider.local.LocalAuthProviderConstants;
|
||||
import io.cloudbeaver.registry.WebAuthProviderDescriptor;
|
||||
import io.cloudbeaver.registry.WebAuthProviderRegistry;
|
||||
import org.jkiss.code.NotNull;
|
||||
import org.jkiss.code.Nullable;
|
||||
import org.jkiss.dbeaver.model.security.SMAuthProviderCustomConfiguration;
|
||||
import org.jkiss.utils.ArrayUtils;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
public abstract class BaseAuthWebAppConfiguration extends BaseWebAppConfiguration implements WebAuthConfiguration {
|
||||
private String defaultAuthProvider;
|
||||
private String[] enabledAuthProviders;
|
||||
private final Set<SMAuthProviderCustomConfiguration> authConfigurations;
|
||||
// Legacy auth configs, left for backward compatibility
|
||||
@Expose(serialize = false)
|
||||
private final Map<String, SMAuthProviderCustomConfiguration> authConfiguration;
|
||||
|
||||
public BaseAuthWebAppConfiguration() {
|
||||
super();
|
||||
this.defaultAuthProvider = LocalAuthProviderConstants.PROVIDER_ID;
|
||||
this.enabledAuthProviders = null;
|
||||
this.authConfigurations = new LinkedHashSet<>();
|
||||
this.authConfiguration = new LinkedHashMap<>();
|
||||
}
|
||||
|
||||
public BaseAuthWebAppConfiguration(BaseAuthWebAppConfiguration src) {
|
||||
super(src);
|
||||
this.defaultAuthProvider = src.defaultAuthProvider;
|
||||
this.enabledAuthProviders = src.enabledAuthProviders;
|
||||
this.authConfigurations = new LinkedHashSet<>(src.authConfigurations);
|
||||
this.authConfiguration = new LinkedHashMap<>(src.authConfiguration);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDefaultAuthProvider() {
|
||||
return defaultAuthProvider;
|
||||
}
|
||||
|
||||
public void setDefaultAuthProvider(String defaultAuthProvider) {
|
||||
this.defaultAuthProvider = defaultAuthProvider;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] getEnabledAuthProviders() {
|
||||
if (enabledAuthProviders == null) {
|
||||
// No config - enable all providers (+backward compatibility)
|
||||
return WebAuthProviderRegistry.getInstance().getAuthProviders()
|
||||
.stream().map(WebAuthProviderDescriptor::getId).toArray(String[]::new);
|
||||
}
|
||||
return enabledAuthProviders;
|
||||
}
|
||||
|
||||
public void setEnabledAuthProviders(String[] enabledAuthProviders) {
|
||||
this.enabledAuthProviders = enabledAuthProviders;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean isAuthProviderEnabled(String id) {
|
||||
var authProviderDescriptor = WebAuthProviderRegistry.getInstance().getAuthProvider(id);
|
||||
if (authProviderDescriptor == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!ArrayUtils.contains(getEnabledAuthProviders(), id)) {
|
||||
return false;
|
||||
}
|
||||
if (!ArrayUtils.isEmpty(authProviderDescriptor.getRequiredFeatures())) {
|
||||
for (String rf : authProviderDescriptor.getRequiredFeatures()) {
|
||||
if (!isFeatureEnabled(rf)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////
|
||||
// Auth provider configs
|
||||
@Override
|
||||
public Set<SMAuthProviderCustomConfiguration> getAuthCustomConfigurations() {
|
||||
return authConfigurations;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public SMAuthProviderCustomConfiguration getAuthProviderConfiguration(@NotNull String id) {
|
||||
synchronized (authConfigurations) {
|
||||
return authConfigurations.stream().filter(c -> c.getId().equals(id)).findAny().orElse(null);
|
||||
}
|
||||
}
|
||||
|
||||
public void addAuthProviderConfiguration(@NotNull SMAuthProviderCustomConfiguration config) {
|
||||
synchronized (authConfigurations) {
|
||||
authConfigurations.removeIf(c -> c.getId().equals(config.getId()));
|
||||
authConfigurations.add(config);
|
||||
}
|
||||
}
|
||||
|
||||
public void setAuthProvidersConfigurations(Collection<SMAuthProviderCustomConfiguration> authProviders) {
|
||||
synchronized (authConfigurations) {
|
||||
authConfigurations.clear();
|
||||
authConfigurations.addAll(authProviders);
|
||||
}
|
||||
}
|
||||
|
||||
public boolean deleteAuthProviderConfiguration(@NotNull String id) {
|
||||
synchronized (authConfigurations) {
|
||||
return authConfigurations.removeIf(c -> c.getId().equals(id));
|
||||
}
|
||||
}
|
||||
|
||||
public void loadLegacyCustomConfigs() {
|
||||
// Convert legacy map of configs into list
|
||||
if (!authConfiguration.isEmpty()) {
|
||||
for (Map.Entry<String, SMAuthProviderCustomConfiguration> entry : authConfiguration.entrySet()) {
|
||||
entry.getValue().setId(entry.getKey());
|
||||
authConfigurations.add(entry.getValue());
|
||||
}
|
||||
authConfiguration.clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
+72
-2
@@ -18,17 +18,87 @@ package io.cloudbeaver.model.app;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.GsonBuilder;
|
||||
import org.jkiss.code.NotNull;
|
||||
import org.jkiss.dbeaver.DBException;
|
||||
import org.jkiss.dbeaver.Log;
|
||||
import org.jkiss.dbeaver.registry.fs.FileSystemProviderRegistry;
|
||||
import org.jkiss.utils.IOUtils;
|
||||
|
||||
import java.net.URI;
|
||||
import java.nio.file.FileSystem;
|
||||
import java.nio.file.FileSystems;
|
||||
import java.nio.file.Path;
|
||||
|
||||
/**
|
||||
* Abstract class that contains methods for loading configuration with gson.
|
||||
*/
|
||||
public abstract class BaseServerConfigurationController<T extends WebServerConfiguration> implements WebServerConfigurationController<T> {
|
||||
public abstract class BaseServerConfigurationController<T extends WebServerConfiguration>
|
||||
implements WebServerConfigurationController<T> {
|
||||
private static final Log log = Log.getLog(BaseServerConfigurationController.class);
|
||||
@NotNull
|
||||
private final Path homeDirectory;
|
||||
|
||||
protected Gson getGson() {
|
||||
protected Path workspacePath;
|
||||
|
||||
protected BaseServerConfigurationController(@NotNull Path homeDirectory) {
|
||||
this.homeDirectory = homeDirectory;
|
||||
//default workspaceLocation
|
||||
this.workspacePath = homeDirectory.resolve("workspace");
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public Gson getGson() {
|
||||
return getGsonBuilder().create();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
protected abstract GsonBuilder getGsonBuilder();
|
||||
|
||||
public abstract T getServerConfiguration();
|
||||
|
||||
|
||||
@NotNull
|
||||
protected synchronized void initWorkspacePath() throws DBException {
|
||||
if (workspacePath != null && !IOUtils.isFileFromDefaultFS(workspacePath)) {
|
||||
log.warn("Workspace directory already initialized: " + workspacePath);
|
||||
}
|
||||
String workspaceLocation = getWorkspaceLocation();
|
||||
URI workspaceUri = URI.create(workspaceLocation);
|
||||
if (workspaceUri.getScheme() == null) {
|
||||
// default filesystem
|
||||
this.workspacePath = getHomeDirectory().resolve(workspaceLocation);
|
||||
} else {
|
||||
var externalFsProvider =
|
||||
FileSystemProviderRegistry.getInstance().getFileSystemProviderBySchema(workspaceUri.getScheme());
|
||||
if (externalFsProvider == null) {
|
||||
throw new DBException("File system not found for scheme: " + workspaceUri.getScheme());
|
||||
}
|
||||
ClassLoader fsClassloader = externalFsProvider.getInstance().getClass().getClassLoader();
|
||||
try (FileSystem externalFileSystem = FileSystems.newFileSystem(workspaceUri,
|
||||
System.getenv(),
|
||||
fsClassloader);) {
|
||||
this.workspacePath = externalFileSystem.provider().getPath(workspaceUri);
|
||||
} catch (Exception e) {
|
||||
throw new DBException("Failed to initialize workspace path: " + workspaceUri, e);
|
||||
}
|
||||
}
|
||||
log.info("Workspace path initialized: " + workspacePath);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
protected abstract String getWorkspaceLocation();
|
||||
|
||||
@NotNull
|
||||
protected Path getHomeDirectory() {
|
||||
return homeDirectory;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public Path getWorkspacePath() {
|
||||
if (workspacePath == null) {
|
||||
throw new RuntimeException("Workspace path not initialized");
|
||||
}
|
||||
return workspacePath;
|
||||
}
|
||||
}
|
||||
|
||||
+13
-1
@@ -29,15 +29,18 @@ public abstract class BaseWebAppConfiguration implements WebAppConfiguration {
|
||||
public static final String DEFAULT_APP_ANONYMOUS_TEAM_NAME = "user";
|
||||
|
||||
protected final Map<String, Object> plugins;
|
||||
protected String defaultUserTeam;
|
||||
protected String defaultUserTeam = DEFAULT_APP_ANONYMOUS_TEAM_NAME;
|
||||
protected boolean resourceManagerEnabled;
|
||||
protected boolean showReadOnlyConnectionInfo;
|
||||
protected String[] enabledFeatures;
|
||||
protected String[] disabledBetaFeatures;
|
||||
|
||||
|
||||
public BaseWebAppConfiguration() {
|
||||
this.plugins = new LinkedHashMap<>();
|
||||
this.resourceManagerEnabled = true;
|
||||
this.enabledFeatures = null;
|
||||
this.disabledBetaFeatures = new String[0];
|
||||
this.showReadOnlyConnectionInfo = false;
|
||||
}
|
||||
|
||||
@@ -46,6 +49,7 @@ public abstract class BaseWebAppConfiguration implements WebAppConfiguration {
|
||||
this.defaultUserTeam = src.defaultUserTeam;
|
||||
this.resourceManagerEnabled = src.resourceManagerEnabled;
|
||||
this.enabledFeatures = src.enabledFeatures;
|
||||
this.disabledBetaFeatures = src.disabledBetaFeatures;
|
||||
this.showReadOnlyConnectionInfo = src.showReadOnlyConnectionInfo;
|
||||
}
|
||||
|
||||
@@ -80,14 +84,18 @@ public abstract class BaseWebAppConfiguration implements WebAppConfiguration {
|
||||
return resourceManagerEnabled;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isFeatureEnabled(String id) {
|
||||
return ArrayUtils.contains(getEnabledFeatures(), id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isFeaturesEnabled(String[] features) {
|
||||
return ArrayUtils.containsAll(getEnabledFeatures(), features);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public String[] getEnabledFeatures() {
|
||||
if (enabledFeatures == null) {
|
||||
// No config - enable all features (+backward compatibility)
|
||||
@@ -104,4 +112,8 @@ public abstract class BaseWebAppConfiguration implements WebAppConfiguration {
|
||||
public boolean isShowReadOnlyConnectionInfo() {
|
||||
return showReadOnlyConnectionInfo;
|
||||
}
|
||||
|
||||
public String[] getDisabledBetaFeatures() {
|
||||
return disabledBetaFeatures;
|
||||
}
|
||||
}
|
||||
|
||||
+40
-45
@@ -16,13 +16,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;
|
||||
import org.eclipse.core.resources.IWorkspace;
|
||||
import org.eclipse.core.runtime.Platform;
|
||||
import org.eclipse.equinox.app.IApplicationContext;
|
||||
import org.jkiss.code.NotNull;
|
||||
@@ -30,17 +24,16 @@ import org.jkiss.code.Nullable;
|
||||
import org.jkiss.dbeaver.DBException;
|
||||
import org.jkiss.dbeaver.Log;
|
||||
import org.jkiss.dbeaver.model.DBFileController;
|
||||
import org.jkiss.dbeaver.model.app.DBPPlatform;
|
||||
import org.jkiss.dbeaver.model.app.DBPWorkspace;
|
||||
import org.jkiss.dbeaver.model.auth.SMCredentialsProvider;
|
||||
import org.jkiss.dbeaver.model.auth.SMSessionContext;
|
||||
import org.jkiss.dbeaver.model.data.json.JSONUtils;
|
||||
import org.jkiss.dbeaver.model.impl.app.ApplicationRegistry;
|
||||
import org.jkiss.dbeaver.model.impl.app.BaseApplicationImpl;
|
||||
import org.jkiss.dbeaver.model.impl.app.BaseWorkspaceImpl;
|
||||
import org.jkiss.dbeaver.model.rm.RMController;
|
||||
import org.jkiss.dbeaver.model.rm.RMProject;
|
||||
import org.jkiss.dbeaver.model.secret.DBSSecretController;
|
||||
import org.jkiss.dbeaver.model.websocket.event.WSEventController;
|
||||
import org.jkiss.dbeaver.registry.BaseApplicationImpl;
|
||||
import org.jkiss.dbeaver.runtime.IVariableResolver;
|
||||
import org.jkiss.dbeaver.utils.GeneralUtils;
|
||||
import org.jkiss.dbeaver.utils.RuntimeUtils;
|
||||
@@ -61,14 +54,9 @@ public abstract class BaseWebApplication extends BaseApplicationImpl implements
|
||||
public static final String CLI_PARAM_WEB_CONFIG = "-web-config";
|
||||
public static final String LOGBACK_FILE_NAME = "logback.xml";
|
||||
|
||||
|
||||
private static final Log log = Log.getLog(BaseWebApplication.class);
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public DBPWorkspace createWorkspace(@NotNull DBPPlatform platform, @NotNull IWorkspace eclipseWorkspace) {
|
||||
return new WebGlobalWorkspace(platform, eclipseWorkspace);
|
||||
}
|
||||
private String instanceId;
|
||||
|
||||
@Override
|
||||
public RMController createResourceController(
|
||||
@@ -102,16 +90,7 @@ public abstract class BaseWebApplication extends BaseApplicationImpl implements
|
||||
|
||||
protected boolean loadServerConfiguration() throws DBException {
|
||||
Path configFilePath = getMainConfigurationFilePath().toAbsolutePath();
|
||||
Path configFolder = configFilePath.getParent();
|
||||
|
||||
// Configure logging
|
||||
Path logbackConfigPath = getLogbackConfigPath(configFolder);
|
||||
|
||||
if (logbackConfigPath == null) {
|
||||
System.err.println("Can't find slf4j configuration file in " + configFilePath.getParent());
|
||||
} else {
|
||||
System.setProperty("logback.configurationFile", logbackConfigPath.toString());
|
||||
}
|
||||
Log.setLogHandler(new SLF4JLogHandler());
|
||||
|
||||
// Load config file
|
||||
@@ -176,19 +155,6 @@ public abstract class BaseWebApplication extends BaseApplicationImpl implements
|
||||
return Files.exists(customConfigPath) ? customConfigPath : configPath.resolve(fileName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public WebProjectImpl createProjectImpl(
|
||||
@NotNull WebSession webSession,
|
||||
@NotNull RMProject project,
|
||||
@NotNull DataSourceFilter dataSourceFilter
|
||||
) {
|
||||
return new WebSessionProjectImpl(
|
||||
webSession,
|
||||
project,
|
||||
dataSourceFilter
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* There is no secret controller in base web app.
|
||||
* Method returns VoidSecretController instance.
|
||||
@@ -207,7 +173,9 @@ public abstract class BaseWebApplication extends BaseApplicationImpl implements
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public static void patchConfigurationWithProperties(Map<String, Object> configProps, IVariableResolver varResolver) {
|
||||
public static void patchConfigurationWithProperties(
|
||||
Map<String, Object> configProps, IVariableResolver varResolver
|
||||
) {
|
||||
for (Map.Entry<String, Object> entry : configProps.entrySet()) {
|
||||
Object propValue = entry.getValue();
|
||||
if (propValue instanceof String) {
|
||||
@@ -242,16 +210,35 @@ public abstract class BaseWebApplication extends BaseApplicationImpl implements
|
||||
protected abstract void startServer() throws DBException;
|
||||
|
||||
@Override
|
||||
public String getApplicationInstanceId() throws DBException {
|
||||
try {
|
||||
byte[] macAddress = RuntimeUtils.getLocalMacAddress();
|
||||
String appId = ApplicationRegistry.getInstance().getApplication().getId();
|
||||
return appId + "_" + CommonUtils.toHexString(macAddress) + getServerPort();
|
||||
} catch (Exception e) {
|
||||
throw new DBException("Error during generation instance id generation", e);
|
||||
public synchronized String getApplicationInstanceId() throws DBException {
|
||||
if (instanceId == null) {
|
||||
try {
|
||||
byte[] macAddress = RuntimeUtils.getLocalMacAddress();
|
||||
instanceId = String.join(
|
||||
"_",
|
||||
ApplicationRegistry.getInstance().getApplication().getId(),
|
||||
getWorkspaceIdProperty(), // workspace id is read from property file
|
||||
CommonUtils.toHexString(macAddress),
|
||||
CommonUtils.toString(getServerPort())
|
||||
);
|
||||
} catch (Exception e) {
|
||||
throw new DBException("Error during generation instance id generation", e);
|
||||
}
|
||||
}
|
||||
return instanceId;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public String getWorkspaceIdProperty() throws DBException {
|
||||
return BaseWorkspaceImpl.readWorkspaceIdProperty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Path getWorkspaceDirectory() {
|
||||
return getServerConfigurationController().getWorkspacePath();
|
||||
}
|
||||
|
||||
|
||||
public String getApplicationId() {
|
||||
try {
|
||||
return getApplicationInstanceId();
|
||||
@@ -271,4 +258,12 @@ public abstract class BaseWebApplication extends BaseApplicationImpl implements
|
||||
public boolean isEnvironmentVariablesAccessible() {
|
||||
return false;
|
||||
}
|
||||
|
||||
protected void closeResource(String name, Runnable closeFunction) {
|
||||
try {
|
||||
closeFunction.run();
|
||||
} catch (Exception e) {
|
||||
log.error("Failed close " + name, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+7
@@ -17,6 +17,7 @@
|
||||
package io.cloudbeaver.model.app;
|
||||
|
||||
import org.jkiss.code.NotNull;
|
||||
import org.jkiss.code.Nullable;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
@@ -28,6 +29,7 @@ public interface WebAppConfiguration {
|
||||
|
||||
boolean isAnonymousAccessEnabled();
|
||||
|
||||
@Nullable
|
||||
<T> T getResourceQuota(String quotaId);
|
||||
|
||||
String getDefaultUserTeam();
|
||||
@@ -42,6 +44,11 @@ public interface WebAppConfiguration {
|
||||
|
||||
boolean isFeatureEnabled(String id);
|
||||
|
||||
@NotNull
|
||||
default String[] getEnabledFeatures() {
|
||||
return new String[0];
|
||||
}
|
||||
|
||||
default boolean isSupportsCustomConnections() {
|
||||
return true;
|
||||
}
|
||||
|
||||
+9
-10
@@ -16,9 +16,6 @@
|
||||
*/
|
||||
package io.cloudbeaver.model.app;
|
||||
|
||||
import io.cloudbeaver.DataSourceFilter;
|
||||
import io.cloudbeaver.WebProjectImpl;
|
||||
import io.cloudbeaver.model.session.WebSession;
|
||||
import org.jkiss.code.NotNull;
|
||||
import org.jkiss.dbeaver.DBException;
|
||||
import org.jkiss.dbeaver.model.DBFileController;
|
||||
@@ -27,13 +24,13 @@ import org.jkiss.dbeaver.model.app.DBPWorkspace;
|
||||
import org.jkiss.dbeaver.model.auth.SMCredentialsProvider;
|
||||
import org.jkiss.dbeaver.model.auth.SMSessionContext;
|
||||
import org.jkiss.dbeaver.model.rm.RMController;
|
||||
import org.jkiss.dbeaver.model.rm.RMProject;
|
||||
import org.jkiss.dbeaver.model.secret.DBSSecretController;
|
||||
import org.jkiss.dbeaver.model.security.SMAdminController;
|
||||
import org.jkiss.dbeaver.model.security.SMController;
|
||||
import org.jkiss.dbeaver.model.websocket.event.WSEventController;
|
||||
|
||||
import java.nio.file.Path;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Base interface for web application
|
||||
@@ -41,6 +38,10 @@ import java.nio.file.Path;
|
||||
public interface WebApplication extends DBPApplication {
|
||||
boolean isConfigurationMode();
|
||||
|
||||
default boolean isInitializationMode() {
|
||||
return false;
|
||||
}
|
||||
|
||||
WebAppConfiguration getAppConfiguration();
|
||||
|
||||
WebServerConfiguration getServerConfiguration();
|
||||
@@ -53,12 +54,6 @@ public interface WebApplication extends DBPApplication {
|
||||
|
||||
boolean isMultiNode();
|
||||
|
||||
WebProjectImpl createProjectImpl(
|
||||
@NotNull WebSession webSession,
|
||||
@NotNull RMProject project,
|
||||
@NotNull DataSourceFilter dataSourceFilter
|
||||
);
|
||||
|
||||
SMController createSecurityController(@NotNull SMCredentialsProvider credentialsProvider) throws DBException;
|
||||
|
||||
SMAdminController getAdminSecurityController(@NotNull SMCredentialsProvider credentialsProvider) throws DBException;
|
||||
@@ -95,4 +90,8 @@ public interface WebApplication extends DBPApplication {
|
||||
int getServerPort();
|
||||
|
||||
boolean isLicenseRequired();
|
||||
|
||||
default void getStatusInfo(Map<String, Object> infoMap) {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
+13
@@ -16,6 +16,11 @@
|
||||
*/
|
||||
package io.cloudbeaver.model.app;
|
||||
|
||||
import io.cloudbeaver.server.WebServerPreferenceStore;
|
||||
import org.jkiss.code.NotNull;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Web server configuration.
|
||||
* Contains only server configuration properties.
|
||||
@@ -27,4 +32,12 @@ public interface WebServerConfiguration {
|
||||
return "";
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the setting values that will be used in {@link WebServerPreferenceStore}
|
||||
*/
|
||||
@NotNull
|
||||
default Map<String, Object> getProductSettings() {
|
||||
return Map.of();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+15
@@ -16,9 +16,12 @@
|
||||
*/
|
||||
package io.cloudbeaver.model.app;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import org.jkiss.code.NotNull;
|
||||
import org.jkiss.dbeaver.DBException;
|
||||
|
||||
import java.nio.file.Path;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Server configuration controller.
|
||||
@@ -31,4 +34,16 @@ public interface WebServerConfigurationController<T extends WebServerConfigurati
|
||||
*/
|
||||
void loadServerConfiguration(Path configPath) throws DBException;
|
||||
|
||||
@NotNull
|
||||
default Map<String, Object> getOriginalConfigurationProperties() {
|
||||
return Map.of();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
Path getWorkspacePath();
|
||||
|
||||
@NotNull
|
||||
Gson getGson();
|
||||
|
||||
void validateFinalServerConfiguration() throws DBException;
|
||||
}
|
||||
|
||||
+114
-55
@@ -14,21 +14,19 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package io.cloudbeaver.server;
|
||||
package io.cloudbeaver.model.config;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.GsonBuilder;
|
||||
import io.cloudbeaver.DBWebException;
|
||||
import io.cloudbeaver.WebServiceUtils;
|
||||
import io.cloudbeaver.model.app.BaseAuthWebAppConfiguration;
|
||||
import com.google.gson.annotations.Expose;
|
||||
import io.cloudbeaver.auth.provider.local.LocalAuthProviderConstants;
|
||||
import io.cloudbeaver.model.app.BaseWebAppConfiguration;
|
||||
import io.cloudbeaver.model.app.WebAuthConfiguration;
|
||||
import io.cloudbeaver.registry.WebAuthProviderDescriptor;
|
||||
import io.cloudbeaver.registry.WebAuthProviderRegistry;
|
||||
import org.jkiss.code.NotNull;
|
||||
import org.jkiss.dbeaver.DBException;
|
||||
import org.jkiss.code.Nullable;
|
||||
import org.jkiss.dbeaver.Log;
|
||||
import org.jkiss.dbeaver.model.connection.DBPDriver;
|
||||
import org.jkiss.dbeaver.model.navigator.DBNBrowseSettings;
|
||||
import org.jkiss.dbeaver.model.security.SMAuthProviderCustomConfiguration;
|
||||
import org.jkiss.dbeaver.registry.DataSourceNavigatorSettings;
|
||||
import org.jkiss.utils.ArrayUtils;
|
||||
import org.jkiss.utils.CommonUtils;
|
||||
@@ -38,11 +36,15 @@ import java.util.*;
|
||||
/**
|
||||
* Application configuration
|
||||
*/
|
||||
public class CBAppConfig extends BaseAuthWebAppConfiguration implements WebAuthConfiguration {
|
||||
public class CBAppConfig extends BaseWebAppConfiguration implements WebAuthConfiguration {
|
||||
private static final Log log = Log.getLog(CBAppConfig.class);
|
||||
public static final DataSourceNavigatorSettings.Preset PRESET_WEB = new DataSourceNavigatorSettings.Preset("web", "Web", "Default view");
|
||||
|
||||
public static final DataSourceNavigatorSettings DEFAULT_VIEW_SETTINGS = PRESET_WEB.getSettings();
|
||||
private final Set<SMAuthProviderCustomConfiguration> authConfigurations;
|
||||
// Legacy auth configs, left for backward compatibility
|
||||
@Expose(serialize = false)
|
||||
private final Map<String, SMAuthProviderCustomConfiguration> authConfiguration;
|
||||
|
||||
private boolean supportsCustomConnections;
|
||||
private boolean supportsConnectionBrowser;
|
||||
@@ -66,9 +68,14 @@ public class CBAppConfig extends BaseAuthWebAppConfiguration implements WebAuthC
|
||||
private DataSourceNavigatorSettings defaultNavigatorSettings;
|
||||
|
||||
private final Map<String, Object> resourceQuotas;
|
||||
private String defaultAuthProvider;
|
||||
private String[] enabledAuthProviders;
|
||||
|
||||
public CBAppConfig() {
|
||||
super();
|
||||
this.defaultAuthProvider = LocalAuthProviderConstants.PROVIDER_ID;
|
||||
this.enabledAuthProviders = null;
|
||||
this.authConfigurations = new LinkedHashSet<>();
|
||||
this.authConfiguration = new LinkedHashMap<>();
|
||||
this.anonymousAccessEnabled = false;
|
||||
this.anonymousUserRole = DEFAULT_APP_ANONYMOUS_TEAM_NAME;
|
||||
this.anonymousUserTeam = DEFAULT_APP_ANONYMOUS_TEAM_NAME;
|
||||
@@ -91,6 +98,10 @@ public class CBAppConfig extends BaseAuthWebAppConfiguration implements WebAuthC
|
||||
|
||||
public CBAppConfig(CBAppConfig src) {
|
||||
super(src);
|
||||
this.defaultAuthProvider = src.defaultAuthProvider;
|
||||
this.enabledAuthProviders = src.enabledAuthProviders;
|
||||
this.authConfigurations = new LinkedHashSet<>(src.authConfigurations);
|
||||
this.authConfiguration = new LinkedHashMap<>(src.authConfiguration);
|
||||
this.anonymousAccessEnabled = src.anonymousAccessEnabled;
|
||||
this.anonymousUserRole = src.anonymousUserRole;
|
||||
this.anonymousUserTeam = src.anonymousUserTeam;
|
||||
@@ -169,6 +180,10 @@ public class CBAppConfig extends BaseAuthWebAppConfiguration implements WebAuthC
|
||||
return enabledDrivers;
|
||||
}
|
||||
|
||||
public void setEnabledDrivers(String[] enabledDrivers) {
|
||||
this.enabledDrivers = enabledDrivers;
|
||||
}
|
||||
|
||||
public String[] getDisabledDrivers() {
|
||||
return disabledDrivers;
|
||||
}
|
||||
@@ -204,19 +219,6 @@ public class CBAppConfig extends BaseAuthWebAppConfiguration implements WebAuthC
|
||||
return getPluginConfig(pluginId, false);
|
||||
}
|
||||
|
||||
public <T> T getPluginOptions(@NotNull String pluginId, @NotNull String option, Class<T> theClass) throws DBException {
|
||||
Map<String, Object> iamSettingsMap = CBPlatform.getInstance().getApplication().getAppConfiguration().getPluginOption(
|
||||
pluginId, option);
|
||||
if (CommonUtils.isEmpty(iamSettingsMap)) {
|
||||
throw new DBException("Settings '" + option + "' not specified in plugin '" + pluginId + "' configuration");
|
||||
}
|
||||
|
||||
Gson gson = new GsonBuilder().create();
|
||||
return gson.fromJson(
|
||||
gson.toJsonTree(iamSettingsMap),
|
||||
theClass);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////
|
||||
// Quotas
|
||||
|
||||
@@ -264,38 +266,6 @@ public class CBAppConfig extends BaseAuthWebAppConfiguration implements WebAuthC
|
||||
return grantConnectionsAccessToAnonymousTeam;
|
||||
}
|
||||
|
||||
|
||||
// we disable embedded drivers by default and enable it in enabled drivers list
|
||||
// that's why we need so complicated logic for disabling drivers
|
||||
|
||||
public void updateDisabledDriversConfig(String[] disabledDriversConfig) {
|
||||
Set<String> disabledIds = new LinkedHashSet<>(Arrays.asList(disabledDriversConfig));
|
||||
Set<String> enabledIds = new LinkedHashSet<>(Arrays.asList(this.enabledDrivers));
|
||||
|
||||
// remove all disabled embedded drivers from enabled drivers list
|
||||
enabledIds.removeAll(disabledIds);
|
||||
|
||||
// enable embedded driver if it is not in disabled drivers list
|
||||
for (String driverId : this.disabledDrivers) {
|
||||
if (disabledIds.contains(driverId)) {
|
||||
// driver is also disabled
|
||||
continue;
|
||||
}
|
||||
// driver is removed from disabled list
|
||||
// we need to enable if it is embedded
|
||||
try {
|
||||
DBPDriver driver = WebServiceUtils.getDriverById(driverId);
|
||||
if (driver.isEmbedded()) {
|
||||
enabledIds.add(driverId);
|
||||
}
|
||||
} catch (DBWebException e) {
|
||||
log.error("Failed to find driver by id", e);
|
||||
}
|
||||
}
|
||||
this.disabledDrivers = disabledDriversConfig;
|
||||
this.enabledDrivers = enabledIds.toArray(String[]::new);
|
||||
}
|
||||
|
||||
public boolean isDriverForceEnabled(@NotNull String driverId) {
|
||||
return ArrayUtils.containsIgnoreCase(getEnabledDrivers(), driverId);
|
||||
}
|
||||
@@ -303,4 +273,93 @@ public class CBAppConfig extends BaseAuthWebAppConfiguration implements WebAuthC
|
||||
public boolean isSystemVariablesResolvingEnabled() {
|
||||
return systemVariablesResolvingEnabled;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDefaultAuthProvider() {
|
||||
return defaultAuthProvider;
|
||||
}
|
||||
|
||||
public void setDefaultAuthProvider(String defaultAuthProvider) {
|
||||
this.defaultAuthProvider = defaultAuthProvider;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] getEnabledAuthProviders() {
|
||||
if (enabledAuthProviders == null) {
|
||||
// No config - enable all providers (+backward compatibility)
|
||||
return WebAuthProviderRegistry.getInstance().getAuthProviders()
|
||||
.stream().map(WebAuthProviderDescriptor::getId).toArray(String[]::new);
|
||||
}
|
||||
return enabledAuthProviders;
|
||||
}
|
||||
|
||||
public void setEnabledAuthProviders(String[] enabledAuthProviders) {
|
||||
this.enabledAuthProviders = enabledAuthProviders;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAuthProviderEnabled(String id) {
|
||||
var authProviderDescriptor = WebAuthProviderRegistry.getInstance().getAuthProvider(id);
|
||||
if (authProviderDescriptor == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!ArrayUtils.contains(getEnabledAuthProviders(), id)) {
|
||||
return false;
|
||||
}
|
||||
if (!ArrayUtils.isEmpty(authProviderDescriptor.getRequiredFeatures())) {
|
||||
for (String rf : authProviderDescriptor.getRequiredFeatures()) {
|
||||
if (!isFeatureEnabled(rf)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////
|
||||
// Auth provider configs
|
||||
@Override
|
||||
public Set<SMAuthProviderCustomConfiguration> getAuthCustomConfigurations() {
|
||||
return authConfigurations;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public SMAuthProviderCustomConfiguration getAuthProviderConfiguration(@NotNull String id) {
|
||||
synchronized (authConfigurations) {
|
||||
return authConfigurations.stream().filter(c -> c.getId().equals(id)).findAny().orElse(null);
|
||||
}
|
||||
}
|
||||
|
||||
public void addAuthProviderConfiguration(@NotNull SMAuthProviderCustomConfiguration config) {
|
||||
synchronized (authConfigurations) {
|
||||
authConfigurations.removeIf(c -> c.getId().equals(config.getId()));
|
||||
authConfigurations.add(config);
|
||||
}
|
||||
}
|
||||
|
||||
public void setAuthProvidersConfigurations(Collection<SMAuthProviderCustomConfiguration> authProviders) {
|
||||
synchronized (authConfigurations) {
|
||||
authConfigurations.clear();
|
||||
authConfigurations.addAll(authProviders);
|
||||
}
|
||||
}
|
||||
|
||||
public boolean deleteAuthProviderConfiguration(@NotNull String id) {
|
||||
synchronized (authConfigurations) {
|
||||
return authConfigurations.removeIf(c -> c.getId().equals(id));
|
||||
}
|
||||
}
|
||||
|
||||
public void loadLegacyCustomConfigs() {
|
||||
// Convert legacy map of configs into list
|
||||
if (!authConfiguration.isEmpty()) {
|
||||
for (Map.Entry<String, SMAuthProviderCustomConfiguration> entry : authConfiguration.entrySet()) {
|
||||
entry.getValue().setId(entry.getKey());
|
||||
authConfigurations.add(entry.getValue());
|
||||
}
|
||||
authConfiguration.clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
+8
-17
@@ -14,18 +14,15 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package io.cloudbeaver.server;
|
||||
package io.cloudbeaver.model.config;
|
||||
|
||||
import com.google.gson.annotations.SerializedName;
|
||||
import io.cloudbeaver.auth.CBAuthConstants;
|
||||
import io.cloudbeaver.model.app.WebServerConfiguration;
|
||||
import io.cloudbeaver.service.security.db.WebDatabaseConfig;
|
||||
import io.cloudbeaver.server.CBConstants;
|
||||
import org.jkiss.code.NotNull;
|
||||
import org.jkiss.dbeaver.Log;
|
||||
import org.jkiss.utils.CommonUtils;
|
||||
|
||||
import java.net.InetAddress;
|
||||
import java.net.UnknownHostException;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
@@ -50,23 +47,13 @@ public class CBServerConfig implements WebServerConfiguration {
|
||||
private boolean enableSecurityManager = false;
|
||||
private final Map<String, Object> productSettings = new HashMap<>();
|
||||
|
||||
@SerializedName("sm")
|
||||
protected final SMControllerConfiguration securityManagerConfiguration = new SMControllerConfiguration();
|
||||
@SerializedName("database")
|
||||
private WebDatabaseConfig databaseConfiguration = new WebDatabaseConfig();
|
||||
private String staticContent = "";
|
||||
|
||||
public String getServerURL() {
|
||||
if (serverURL == null) {
|
||||
String hostName = serverHost;
|
||||
if (CommonUtils.isEmpty(hostName)) {
|
||||
try {
|
||||
hostName = InetAddress.getLocalHost().getHostName();
|
||||
} catch (UnknownHostException e) {
|
||||
log.debug("Error resolving localhost address: " + e.getMessage());
|
||||
hostName = CBApplication.HOST_LOCALHOST;
|
||||
}
|
||||
}
|
||||
serverURL = "http://" + hostName + ":" + serverPort;
|
||||
}
|
||||
return serverURL;
|
||||
}
|
||||
|
||||
@@ -191,4 +178,8 @@ public class CBServerConfig implements WebServerConfiguration {
|
||||
public Map<String, Object> getProductSettings() {
|
||||
return productSettings;
|
||||
}
|
||||
|
||||
public SMControllerConfiguration getSecurityManagerConfiguration() {
|
||||
return securityManagerConfiguration;
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -14,7 +14,7 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package io.cloudbeaver.service.security;
|
||||
package io.cloudbeaver.model.config;
|
||||
|
||||
import org.jkiss.dbeaver.model.meta.Property;
|
||||
|
||||
+1
-1
@@ -15,7 +15,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package io.cloudbeaver.service.security;
|
||||
package io.cloudbeaver.model.config;
|
||||
|
||||
public class SMControllerConfiguration {
|
||||
//in minutes
|
||||
+26
-2
@@ -14,10 +14,10 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package io.cloudbeaver.service.security.db;
|
||||
package io.cloudbeaver.model.config;
|
||||
|
||||
import org.jkiss.code.NotNull;
|
||||
import org.jkiss.dbeaver.registry.storage.InternalDatabaseConfig;
|
||||
import org.jkiss.dbeaver.model.connection.InternalDatabaseConfig;
|
||||
|
||||
/**
|
||||
* Database configuration
|
||||
@@ -31,6 +31,8 @@ public class WebDatabaseConfig implements InternalDatabaseConfig {
|
||||
|
||||
private String initialDataConfiguration;
|
||||
|
||||
private boolean backupEnabled;
|
||||
|
||||
private final Pool pool = new Pool();
|
||||
|
||||
@Override
|
||||
@@ -52,11 +54,16 @@ public class WebDatabaseConfig implements InternalDatabaseConfig {
|
||||
this.url = url;
|
||||
}
|
||||
|
||||
public void setBackupEnabled(boolean backupEnabled) {
|
||||
this.backupEnabled = backupEnabled;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getUser() {
|
||||
return user;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public String getPassword() {
|
||||
return password;
|
||||
@@ -70,7 +77,24 @@ public class WebDatabaseConfig implements InternalDatabaseConfig {
|
||||
return pool;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isBackupEnabled() {
|
||||
return backupEnabled;
|
||||
}
|
||||
|
||||
public String getSchema() {
|
||||
return schema;
|
||||
}
|
||||
|
||||
public void setPassword(String password) {
|
||||
this.password = password;
|
||||
}
|
||||
|
||||
public void setSchema(String schema) {
|
||||
this.schema = schema;
|
||||
}
|
||||
|
||||
public void setUser(String user) {
|
||||
this.user = user;
|
||||
}
|
||||
}
|
||||
@@ -48,7 +48,7 @@ public class FSUtils {
|
||||
public static DBNPathBase getNodeByPath(@NotNull WebSession webSession, @NotNull String nodePath) throws DBException {
|
||||
DBRProgressMonitor monitor = webSession.getProgressMonitor();
|
||||
|
||||
DBNModel navigatorModel = webSession.getNavigatorModel();
|
||||
DBNModel navigatorModel = webSession.getNavigatorModelOrThrow();
|
||||
DBNNode node = navigatorModel.getNodeByPath(monitor, nodePath);
|
||||
if (!(node instanceof DBNPathBase dbnPath)) {
|
||||
throw new DBWebException("Node '" + nodePath + "' is not found in File Systems");
|
||||
|
||||
+26
-28
@@ -1,18 +1,18 @@
|
||||
/*
|
||||
* DBeaver - Universal Database Manager
|
||||
* Copyright (C) 2010-2024 DBeaver Corp
|
||||
* Copyright (C) 2010-2024 DBeaver Corp and others
|
||||
*
|
||||
* All Rights Reserved.
|
||||
* 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
|
||||
*
|
||||
* 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.
|
||||
* 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;
|
||||
@@ -20,7 +20,6 @@ package io.cloudbeaver.model.rm;
|
||||
import org.jkiss.code.NotNull;
|
||||
import org.jkiss.code.Nullable;
|
||||
import org.jkiss.dbeaver.DBException;
|
||||
import org.jkiss.dbeaver.Log;
|
||||
import org.jkiss.dbeaver.model.DBIcon;
|
||||
import org.jkiss.dbeaver.model.DBPImage;
|
||||
import org.jkiss.dbeaver.model.DBPObject;
|
||||
@@ -37,7 +36,6 @@ import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class DBNResourceManagerProject extends DBNAbstractResourceManagerNode {
|
||||
private static final Log log = Log.getLog(DBNResourceManagerProject.class);
|
||||
|
||||
private final RMProject project;
|
||||
|
||||
@@ -93,8 +91,8 @@ public class DBNResourceManagerProject extends DBNAbstractResourceManagerNode {
|
||||
}
|
||||
|
||||
@Override
|
||||
public DBNResourceManagerResource[] getChildren(DBRProgressMonitor monitor) throws DBException {
|
||||
if (children == null) {
|
||||
public DBNResourceManagerResource[] getChildren(@NotNull DBRProgressMonitor monitor) throws DBException {
|
||||
if (children == null && !monitor.isForceCacheUsage()) {
|
||||
List<DBNResourceManagerResource> rfList = new ArrayList<>();
|
||||
for (RMResource resource : getResourceController().listResources(
|
||||
project.getId(), null, null, true, false, false)) {
|
||||
@@ -123,21 +121,15 @@ public class DBNResourceManagerProject extends DBNAbstractResourceManagerNode {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public String toString() {
|
||||
return getNodeDisplayName();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public DBPProject getOwnerProject() {
|
||||
public DBPProject getOwnerProjectOrNull() {
|
||||
List<? extends DBPProject> globalProjects = getModel().getModelProjects();
|
||||
if (globalProjects == null) {
|
||||
return null;
|
||||
}
|
||||
for (DBPProject modelProject : globalProjects) {
|
||||
if (CommonUtils.equalObjects(modelProject.getId(), project.getId())) {
|
||||
return modelProject;
|
||||
if (globalProjects != null) {
|
||||
for (DBPProject modelProject : globalProjects) {
|
||||
if (CommonUtils.equalObjects(modelProject.getId(), project.getId())) {
|
||||
return modelProject;
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
@@ -148,4 +140,10 @@ public class DBNResourceManagerProject extends DBNAbstractResourceManagerNode {
|
||||
public DBPObject getObjectDetails(@NotNull DBRProgressMonitor monitor, @NotNull SMSessionContext sessionContext, @NotNull Object dataSource) throws DBException {
|
||||
return project;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return getNodeDisplayName();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+16
-15
@@ -1,18 +1,18 @@
|
||||
/*
|
||||
* DBeaver - Universal Database Manager
|
||||
* Copyright (C) 2010-2024 DBeaver Corp
|
||||
* Copyright (C) 2010-2024 DBeaver Corp and others
|
||||
*
|
||||
* All Rights Reserved.
|
||||
* 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
|
||||
*
|
||||
* 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.
|
||||
* 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;
|
||||
@@ -110,8 +110,8 @@ public class DBNResourceManagerResource extends DBNAbstractResourceManagerNode {
|
||||
}
|
||||
|
||||
@Override
|
||||
public DBNNode[] getChildren(DBRProgressMonitor monitor) throws DBException {
|
||||
if (children == null) {
|
||||
public DBNNode[] getChildren(@NotNull DBRProgressMonitor monitor) throws DBException {
|
||||
if (children == null && !monitor.isForceCacheUsage()) {
|
||||
List<DBNResourceManagerResource> rfList = new ArrayList<>();
|
||||
for (RMResource resource : getResourceController().listResources(
|
||||
getResourceProject().getId(), getResourceFolder(), null, true, false, false)) {
|
||||
@@ -203,9 +203,10 @@ public class DBNResourceManagerResource extends DBNAbstractResourceManagerNode {
|
||||
return resource;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public DBPProject getOwnerProject() {
|
||||
return getParentNode().getOwnerProject();
|
||||
public DBPProject getOwnerProjectOrNull() {
|
||||
return getParentNode().getOwnerProjectOrNull();
|
||||
}
|
||||
|
||||
public RMResource getResource() {
|
||||
|
||||
+21
-15
@@ -1,24 +1,25 @@
|
||||
/*
|
||||
* DBeaver - Universal Database Manager
|
||||
* Copyright (C) 2010-2024 DBeaver Corp
|
||||
* Copyright (C) 2010-2024 DBeaver Corp and others
|
||||
*
|
||||
* All Rights Reserved.
|
||||
* 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
|
||||
*
|
||||
* 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.
|
||||
* 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.model.session.WebSession;
|
||||
import org.jkiss.code.NotNull;
|
||||
import org.jkiss.code.Nullable;
|
||||
import org.jkiss.dbeaver.DBException;
|
||||
import org.jkiss.dbeaver.Log;
|
||||
import org.jkiss.dbeaver.model.DBPHiddenObject;
|
||||
@@ -81,8 +82,8 @@ public class DBNResourceManagerRoot extends DBNNode implements DBPHiddenObject,
|
||||
}
|
||||
|
||||
@Override
|
||||
public DBNResourceManagerProject[] getChildren(DBRProgressMonitor monitor) throws DBException {
|
||||
if (projects == null) {
|
||||
public DBNResourceManagerProject[] getChildren(@NotNull DBRProgressMonitor monitor) throws DBException {
|
||||
if (projects == null && !monitor.isForceCacheUsage()) {
|
||||
List<? extends DBPProject> projectList = getParentNode().getModel().getModelProjects();
|
||||
if (CommonUtils.isEmpty(projectList)) {
|
||||
return new DBNResourceManagerProject[0];
|
||||
@@ -154,8 +155,13 @@ public class DBNResourceManagerRoot extends DBNNode implements DBPHiddenObject,
|
||||
return;
|
||||
}
|
||||
var projectNode = getProjectNode(project);
|
||||
var rmResourcePath = Arrays.asList(resourcePath.split("/"));
|
||||
projectNode.ifPresent(dbnResourceManagerProject -> dbnResourceManagerProject.removeChildResourceNode(new ArrayDeque<>(rmResourcePath)));
|
||||
var rmResourcePath = Arrays.stream(resourcePath.split("/"))
|
||||
.filter(CommonUtils::isNotEmpty)
|
||||
.toList();
|
||||
projectNode.ifPresent(
|
||||
dbnResourceManagerProject -> dbnResourceManagerProject.removeChildResourceNode(new ArrayDeque<>(
|
||||
rmResourcePath))
|
||||
);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
|
||||
+352
@@ -0,0 +1,352 @@
|
||||
/*
|
||||
* DBeaver - Universal Database Manager
|
||||
* Copyright (C) 2010-2024 DBeaver Corp and others
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package io.cloudbeaver.model.rm.local;
|
||||
|
||||
import io.cloudbeaver.BaseWebProjectImpl;
|
||||
import io.cloudbeaver.model.rm.lock.RMFileLockController;
|
||||
import org.jkiss.code.NotNull;
|
||||
import org.jkiss.code.Nullable;
|
||||
import org.jkiss.dbeaver.DBException;
|
||||
import org.jkiss.dbeaver.Log;
|
||||
import org.jkiss.dbeaver.model.DBPDataSourceConfigurationStorage;
|
||||
import org.jkiss.dbeaver.model.DBPDataSourceContainer;
|
||||
import org.jkiss.dbeaver.model.DBPDataSourceFolder;
|
||||
import org.jkiss.dbeaver.model.app.DBPDataSourceRegistry;
|
||||
import org.jkiss.dbeaver.model.app.DBPProject;
|
||||
import org.jkiss.dbeaver.model.app.DBPWorkspace;
|
||||
import org.jkiss.dbeaver.model.impl.auth.SessionContextImpl;
|
||||
import org.jkiss.dbeaver.model.rm.RMController;
|
||||
import org.jkiss.dbeaver.model.rm.RMEvent;
|
||||
import org.jkiss.dbeaver.model.rm.RMEventManager;
|
||||
import org.jkiss.dbeaver.model.rm.RMProject;
|
||||
import org.jkiss.dbeaver.model.runtime.VoidProgressMonitor;
|
||||
import org.jkiss.dbeaver.registry.*;
|
||||
import org.jkiss.dbeaver.utils.GeneralUtils;
|
||||
import org.jkiss.utils.ArrayUtils;
|
||||
import org.jkiss.utils.IOUtils;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.List;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
public abstract class BaseLocalResourceController implements RMController {
|
||||
private static final Log log = Log.getLog(BaseLocalResourceController.class);
|
||||
|
||||
public static final String DEFAULT_CHANGE_ID = "0";
|
||||
private static final String FILE_REGEX = "(?U)[\\w.$()@/\\\\ -]+";
|
||||
private static final String PROJECT_REGEX = "(?U)[\\w.$()@ -]+"; // slash not allowed in project name
|
||||
|
||||
@NotNull
|
||||
protected final DBPWorkspace workspace;
|
||||
@NotNull
|
||||
protected final RMFileLockController lockController;
|
||||
|
||||
protected BaseLocalResourceController(
|
||||
@NotNull DBPWorkspace workspace,
|
||||
@NotNull RMFileLockController lockController
|
||||
) {
|
||||
this.workspace = workspace;
|
||||
this.lockController = lockController;
|
||||
}
|
||||
|
||||
@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(
|
||||
listResources(projectId, null, null, readProperties, false, true)
|
||||
);
|
||||
return null;
|
||||
});
|
||||
}
|
||||
return project;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getProjectProperty(@NotNull String projectId, @NotNull String propName) throws DBException {
|
||||
var project = getWebProject(projectId, false);
|
||||
return doFileReadOperation(projectId,
|
||||
project.getMetadataFilePath(),
|
||||
() -> project.getProjectProperty(propName));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setProjectProperty(
|
||||
@NotNull String projectId,
|
||||
@NotNull String propName,
|
||||
@NotNull Object propValue
|
||||
) throws DBException {
|
||||
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;
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getProjectsDataSources(@NotNull String projectId, @Nullable String[] dataSourceIds)
|
||||
throws DBException {
|
||||
DBPProject projectMetadata = getWebProject(projectId, false);
|
||||
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)) {
|
||||
filter = ds -> ArrayUtils.contains(dataSourceIds, ds.getId());
|
||||
}
|
||||
((DataSourcePersistentRegistry) registry).saveConfigurationToManager(new VoidProgressMonitor(),
|
||||
buffer,
|
||||
filter);
|
||||
registry.checkForErrors();
|
||||
|
||||
return new String(buffer.getData(), StandardCharsets.UTF_8);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void createProjectDataSources(
|
||||
@NotNull String projectId,
|
||||
@NotNull String configuration,
|
||||
@Nullable List<String> dataSourceIds
|
||||
) throws DBException {
|
||||
updateProjectDataSources(projectId, configuration, dataSourceIds);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean updateProjectDataSources(
|
||||
@NotNull String projectId,
|
||||
@NotNull String configuration,
|
||||
@Nullable List<String> dataSourceIds
|
||||
) throws DBException {
|
||||
try (var lock = lockController.lockProject(projectId, "updateProjectDataSources")) {
|
||||
DBPProject project = getWebProject(projectId, false);
|
||||
return doFileWriteOperation(projectId, project.getMetadataFolder(false),
|
||||
() -> {
|
||||
DBPDataSourceRegistry registry = project.getDataSourceRegistry();
|
||||
DBPDataSourceConfigurationStorage storage = new DataSourceMemoryStorage(configuration.getBytes(
|
||||
StandardCharsets.UTF_8));
|
||||
DataSourceConfigurationManager manager = new DataSourceConfigurationManagerBuffer();
|
||||
var configChanged = ((DataSourcePersistentRegistry) registry).loadDataSources(
|
||||
List.of(storage),
|
||||
manager,
|
||||
dataSourceIds,
|
||||
true,
|
||||
false
|
||||
);
|
||||
registry.checkForErrors();
|
||||
log.debug("Save data sources configuration in project '" + projectId + "'");
|
||||
((DataSourcePersistentRegistry) registry).saveDataSources();
|
||||
registry.checkForErrors();
|
||||
return configChanged;
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deleteProjectDataSources(
|
||||
@NotNull String projectId,
|
||||
@NotNull String[] dataSourceIds
|
||||
) throws DBException {
|
||||
try (var projectLock = lockController.lockProject(projectId, "deleteDatasources")) {
|
||||
DBPProject project = getWebProject(projectId, false);
|
||||
doFileWriteOperation(projectId, project.getMetadataFolder(false), () -> {
|
||||
DBPDataSourceRegistry registry = project.getDataSourceRegistry();
|
||||
for (String dataSourceId : dataSourceIds) {
|
||||
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");
|
||||
}
|
||||
}
|
||||
registry.checkForErrors();
|
||||
return null;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void createProjectDataSourceFolder(
|
||||
@NotNull String projectId,
|
||||
@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();
|
||||
var result = Path.of(folderPath);
|
||||
var newName = result.getFileName().toString();
|
||||
GeneralUtils.validateResourceName(newName);
|
||||
var parent = result.getParent();
|
||||
var parentFolder = parent == null ? null : registry.getFolder(parent.toString().replace("\\", "/"));
|
||||
DBPDataSourceFolder newFolder = registry.addFolder(parentFolder, newName);
|
||||
registry.checkForErrors();
|
||||
return null;
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deleteProjectDataSourceFolders(
|
||||
@NotNull String projectId,
|
||||
@NotNull String[] folderPaths,
|
||||
boolean dropContents
|
||||
) throws DBException {
|
||||
try (var projectLock = lockController.lockProject(projectId, "createDatasourceFolder")) {
|
||||
DBPProject project = getWebProject(projectId, false);
|
||||
doFileWriteOperation(projectId, project.getMetadataFolder(false),
|
||||
() -> {
|
||||
DBPDataSourceRegistry registry = project.getDataSourceRegistry();
|
||||
for (String folderPath : folderPaths) {
|
||||
DBPDataSourceFolder folder = registry.getFolder(folderPath);
|
||||
if (folder != null) {
|
||||
log.debug("Deleting data source folder '" + folderPath + "' in project '" + projectId + "'");
|
||||
registry.removeFolder(folder, dropContents);
|
||||
} else {
|
||||
log.warn("Can not find folder by path [" + folderPath + "] for deletion");
|
||||
}
|
||||
}
|
||||
registry.checkForErrors();
|
||||
return null;
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void moveProjectDataSourceFolder(
|
||||
@NotNull String projectId,
|
||||
@NotNull String oldPath,
|
||||
@NotNull String newPath
|
||||
) 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();
|
||||
registry.moveFolder(oldPath, newPath);
|
||||
registry.checkForErrors();
|
||||
return null;
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
protected abstract BaseWebProjectImpl getWebProject(String projectId, boolean refresh) throws DBException;
|
||||
|
||||
protected abstract <T> T doFileWriteOperation(String projectId, Path file, RMFileOperation<T> operation)
|
||||
throws DBException;
|
||||
|
||||
protected abstract <T> T doFileReadOperation(String projectId, Path file, RMFileOperation<T> operation)
|
||||
throws DBException;
|
||||
|
||||
protected abstract <T> T doProjectOperation(String projectId, RMFileOperation<T> operation) throws DBException;
|
||||
|
||||
protected abstract RMProject makeProjectFromId(String projectId, boolean loadPermissions) throws DBException;
|
||||
|
||||
protected void validateResourcePath(String resourcePath) throws DBException {
|
||||
var fullPath = Paths.get(resourcePath);
|
||||
for (Path path : fullPath) {
|
||||
String fileName = IOUtils.getFileNameWithoutExtension(path);
|
||||
GeneralUtils.validateResourceName(fileName);
|
||||
}
|
||||
}
|
||||
|
||||
protected void createFolder(Path targetPath) throws DBException {
|
||||
if (!Files.exists(targetPath)) {
|
||||
try {
|
||||
Files.createDirectories(targetPath);
|
||||
} catch (IOException e) {
|
||||
throw new DBException("Error creating folder '" + targetPath + "'");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected class InternalWebProjectImpl extends BaseWebProjectImpl {
|
||||
public InternalWebProjectImpl(
|
||||
@NotNull SessionContextImpl sessionContext,
|
||||
@NotNull RMProject rmProject,
|
||||
@NotNull Path projectPath
|
||||
) {
|
||||
super(
|
||||
BaseLocalResourceController.this.workspace,
|
||||
BaseLocalResourceController.this,
|
||||
sessionContext,
|
||||
rmProject,
|
||||
projectPath
|
||||
);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
protected DBPDataSourceRegistry createDataSourceRegistry() {
|
||||
return new DataSourceRegistry(this);
|
||||
}
|
||||
}
|
||||
|
||||
protected void fireRmResourceAddEvent(@NotNull String projectId, @NotNull String resourcePath) throws DBException {
|
||||
RMEventManager.fireEvent(
|
||||
new RMEvent(RMEvent.Action.RESOURCE_ADD,
|
||||
getProject(projectId, false, false),
|
||||
resourcePath)
|
||||
);
|
||||
}
|
||||
|
||||
protected void fireRmResourceDeleteEvent(@NotNull String projectId, @NotNull String resourcePath)
|
||||
throws DBException {
|
||||
RMEventManager.fireEvent(
|
||||
new RMEvent(RMEvent.Action.RESOURCE_DELETE,
|
||||
makeProjectFromId(projectId, false),
|
||||
resourcePath
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
protected void fireRmProjectAddEvent(@NotNull RMProject project) {
|
||||
RMEventManager.fireEvent(
|
||||
new RMEvent(
|
||||
RMEvent.Action.RESOURCE_ADD,
|
||||
project
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
+49
-264
@@ -29,39 +29,31 @@ import org.jkiss.code.NotNull;
|
||||
import org.jkiss.code.Nullable;
|
||||
import org.jkiss.dbeaver.DBException;
|
||||
import org.jkiss.dbeaver.Log;
|
||||
import org.jkiss.dbeaver.model.DBPDataSourceConfigurationStorage;
|
||||
import org.jkiss.dbeaver.model.DBPDataSourceContainer;
|
||||
import org.jkiss.dbeaver.model.DBPDataSourceFolder;
|
||||
import org.jkiss.dbeaver.model.app.DBPDataSourceRegistry;
|
||||
import org.jkiss.dbeaver.model.app.DBPProject;
|
||||
import org.jkiss.dbeaver.model.app.DBPWorkspace;
|
||||
import org.jkiss.dbeaver.model.auth.SMCredentials;
|
||||
import org.jkiss.dbeaver.model.auth.SMCredentialsProvider;
|
||||
import org.jkiss.dbeaver.model.impl.app.BaseProjectImpl;
|
||||
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.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.registry.ResourceTypeDescriptor;
|
||||
import org.jkiss.dbeaver.registry.ResourceTypeRegistry;
|
||||
import org.jkiss.dbeaver.runtime.DBWorkbench;
|
||||
import org.jkiss.dbeaver.utils.GeneralUtils;
|
||||
import org.jkiss.utils.ArrayUtils;
|
||||
import org.jkiss.utils.CommonUtils;
|
||||
import org.jkiss.utils.IOUtils;
|
||||
import org.jkiss.utils.Pair;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.*;
|
||||
import java.text.MessageFormat;
|
||||
import java.time.OffsetDateTime;
|
||||
import java.time.ZoneId;
|
||||
import java.util.*;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.function.Supplier;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
@@ -69,15 +61,10 @@ import java.util.stream.Stream;
|
||||
/**
|
||||
* Resource manager API
|
||||
*/
|
||||
public class LocalResourceController implements RMController {
|
||||
public class LocalResourceController extends BaseLocalResourceController {
|
||||
|
||||
private static final Log log = Log.getLog(LocalResourceController.class);
|
||||
|
||||
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;
|
||||
protected final SMCredentialsProvider credentialsProvider;
|
||||
|
||||
private final Path rootPath;
|
||||
@@ -85,7 +72,6 @@ public class LocalResourceController implements RMController {
|
||||
private final Path sharedProjectsPath;
|
||||
private final String globalProjectName;
|
||||
private Supplier<SMController> smControllerSupplier;
|
||||
protected final RMFileLockController lockController;
|
||||
protected final List<RMFileOperationHandler> fileHandlers;
|
||||
|
||||
private final Map<String, BaseWebProjectImpl> projectRegistries = new LinkedHashMap<>();
|
||||
@@ -98,13 +84,12 @@ public class LocalResourceController implements RMController {
|
||||
Path sharedProjectsPath,
|
||||
Supplier<SMController> smControllerSupplier
|
||||
) throws DBException {
|
||||
this.workspace = workspace;
|
||||
super(workspace, new RMFileLockController(WebAppUtils.getWebApplication()));
|
||||
this.credentialsProvider = credentialsProvider;
|
||||
this.rootPath = rootPath;
|
||||
this.userProjectsPath = userProjectsPath;
|
||||
this.sharedProjectsPath = sharedProjectsPath;
|
||||
this.smControllerSupplier = smControllerSupplier;
|
||||
this.lockController = new RMFileLockController(WebAppUtils.getWebApplication());
|
||||
|
||||
this.globalProjectName = DBWorkbench.getPlatform().getApplication().getDefaultProjectName();
|
||||
this.fileHandlers = RMFileOperationHandlersRegistry.getInstance().getFileHandlers();
|
||||
@@ -130,12 +115,7 @@ public class LocalResourceController implements RMController {
|
||||
if (project == null || refresh) {
|
||||
SessionContextImpl sessionContext = new SessionContextImpl(null);
|
||||
RMProject rmProject = makeProjectFromId(projectId, false);
|
||||
project = new BaseWebProjectImpl(
|
||||
workspace,
|
||||
this,
|
||||
sessionContext,
|
||||
rmProject,
|
||||
(container) -> true);
|
||||
project = new InternalWebProjectImpl(sessionContext, rmProject, getProjectPath(projectId));
|
||||
projectRegistries.put(projectId, project);
|
||||
}
|
||||
return project;
|
||||
@@ -256,18 +236,20 @@ public class LocalResourceController implements RMController {
|
||||
return new RMProject[0];
|
||||
}
|
||||
var projects = new ArrayList<RMProject>();
|
||||
var allPaths = Files.list(sharedProjectsPath).collect(Collectors.toList());
|
||||
for (Path path : allPaths) {
|
||||
var projectPerms = getProjectPermissions(
|
||||
makeProjectIdFromPath(path, RMProjectType.SHARED),
|
||||
RMProjectType.SHARED
|
||||
);
|
||||
var rmProject = makeProjectFromPath(path, projectPerms, RMProjectType.SHARED, false);
|
||||
projects.add(rmProject);
|
||||
try (Stream<Path> list = Files.list(sharedProjectsPath)) {
|
||||
var allPaths = list.toList();
|
||||
for (Path path : allPaths) {
|
||||
var projectPerms = getProjectPermissions(
|
||||
makeProjectIdFromPath(path, RMProjectType.SHARED),
|
||||
RMProjectType.SHARED
|
||||
);
|
||||
var rmProject = makeProjectFromPath(path, projectPerms, RMProjectType.SHARED, false);
|
||||
projects.add(rmProject);
|
||||
}
|
||||
return projects.stream()
|
||||
.filter(Objects::nonNull)
|
||||
.toArray(RMProject[]::new);
|
||||
}
|
||||
return projects.stream()
|
||||
.filter(Objects::nonNull)
|
||||
.toArray(RMProject[]::new);
|
||||
} catch (IOException e) {
|
||||
throw new DBException("Error reading shared projects", e);
|
||||
}
|
||||
@@ -344,182 +326,6 @@ public class LocalResourceController implements RMController {
|
||||
return project;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getProjectProperty(@NotNull String projectId, @NotNull String propName) throws DBException {
|
||||
var project = getWebProject(projectId, false);
|
||||
return doFileReadOperation(projectId, project.getMetadataFilePath(), () -> project.getProjectProperty(propName));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setProjectProperty(
|
||||
@NotNull String projectId,
|
||||
@NotNull String propName,
|
||||
@NotNull Object propValue
|
||||
) throws DBException {
|
||||
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;
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getProjectsDataSources(@NotNull String projectId, @Nullable String[] dataSourceIds) throws DBException {
|
||||
DBPProject projectMetadata = getWebProject(projectId, false);
|
||||
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)) {
|
||||
filter = ds -> ArrayUtils.contains(dataSourceIds, ds.getId());
|
||||
}
|
||||
((DataSourcePersistentRegistry) registry).saveConfigurationToManager(new VoidProgressMonitor(), buffer, filter);
|
||||
registry.checkForErrors();
|
||||
|
||||
return new String(buffer.getData(), StandardCharsets.UTF_8);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void createProjectDataSources(
|
||||
@NotNull String projectId,
|
||||
@NotNull String configuration,
|
||||
@Nullable List<String> dataSourceIds
|
||||
) throws DBException {
|
||||
updateProjectDataSources(projectId, configuration, dataSourceIds);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean updateProjectDataSources(
|
||||
@NotNull String projectId,
|
||||
@NotNull String configuration,
|
||||
@Nullable List<String> dataSourceIds
|
||||
) throws DBException {
|
||||
try (var lock = lockController.lockProject(projectId, "updateProjectDataSources")) {
|
||||
DBPProject project = getWebProject(projectId, false);
|
||||
return doFileWriteOperation(projectId, project.getMetadataFolder(false),
|
||||
() -> {
|
||||
DBPDataSourceRegistry registry = project.getDataSourceRegistry();
|
||||
DBPDataSourceConfigurationStorage storage = new DataSourceMemoryStorage(configuration.getBytes(StandardCharsets.UTF_8));
|
||||
DataSourceConfigurationManager manager = new DataSourceConfigurationManagerBuffer();
|
||||
var configChanged = ((DataSourcePersistentRegistry) registry).loadDataSources(
|
||||
List.of(storage),
|
||||
manager,
|
||||
dataSourceIds,
|
||||
true,
|
||||
false
|
||||
);
|
||||
registry.checkForErrors();
|
||||
log.debug("Save data sources configuration in project '" + projectId + "'");
|
||||
((DataSourcePersistentRegistry) registry).saveDataSources();
|
||||
registry.checkForErrors();
|
||||
return configChanged;
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deleteProjectDataSources(@NotNull String projectId,
|
||||
@NotNull String[] dataSourceIds) throws DBException {
|
||||
try (var projectLock = lockController.lockProject(projectId, "deleteDatasources")) {
|
||||
DBPProject project = getWebProject(projectId, false);
|
||||
doFileWriteOperation(projectId, project.getMetadataFolder(false), () -> {
|
||||
DBPDataSourceRegistry registry = project.getDataSourceRegistry();
|
||||
for (String dataSourceId : dataSourceIds) {
|
||||
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");
|
||||
}
|
||||
}
|
||||
registry.checkForErrors();
|
||||
return null;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void createProjectDataSourceFolder(@NotNull String projectId,
|
||||
@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();
|
||||
var result = Path.of(folderPath);
|
||||
var newName = result.getFileName().toString();
|
||||
GeneralUtils.validateResourceName(newName);
|
||||
var parent = result.getParent();
|
||||
var parentFolder = parent == null ? null : registry.getFolder(parent.toString().replace("\\", "/"));
|
||||
DBPDataSourceFolder newFolder = registry.addFolder(parentFolder, newName);
|
||||
registry.checkForErrors();
|
||||
return null;
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deleteProjectDataSourceFolders(
|
||||
@NotNull String projectId,
|
||||
@NotNull String[] folderPaths,
|
||||
boolean dropContents
|
||||
) throws DBException {
|
||||
try (var projectLock = lockController.lockProject(projectId, "createDatasourceFolder")) {
|
||||
DBPProject project = getWebProject(projectId, false);
|
||||
doFileWriteOperation(projectId, project.getMetadataFolder(false),
|
||||
() -> {
|
||||
DBPDataSourceRegistry registry = project.getDataSourceRegistry();
|
||||
for (String folderPath : folderPaths) {
|
||||
DBPDataSourceFolder folder = registry.getFolder(folderPath);
|
||||
if (folder != null) {
|
||||
log.debug("Deleting data source folder '" + folderPath + "' in project '" + projectId + "'");
|
||||
registry.removeFolder(folder, dropContents);
|
||||
} else {
|
||||
log.warn("Can not find folder by path [" + folderPath + "] for deletion");
|
||||
}
|
||||
}
|
||||
registry.checkForErrors();
|
||||
return null;
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void moveProjectDataSourceFolder(
|
||||
@NotNull String projectId,
|
||||
@NotNull String oldPath,
|
||||
@NotNull String newPath
|
||||
) 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();
|
||||
registry.moveFolder(oldPath, newPath);
|
||||
registry.checkForErrors();
|
||||
return null;
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public RMResource[] listResources(
|
||||
@@ -581,7 +387,7 @@ public class LocalResourceController implements RMController {
|
||||
@NotNull String resourcePath,
|
||||
boolean isFolder
|
||||
) throws DBException {
|
||||
try (var projectLock = lockController.lockProject(projectId, "createResource")) {
|
||||
try (var ignoredLock = lockController.lockProject(projectId, "createResource")) {
|
||||
validateResourcePath(resourcePath);
|
||||
Path targetPath = getTargetPath(projectId, resourcePath);
|
||||
if (Files.exists(targetPath)) {
|
||||
@@ -613,7 +419,7 @@ public class LocalResourceController implements RMController {
|
||||
@NotNull String oldResourcePath,
|
||||
@NotNull String newResourcePath
|
||||
) throws DBException {
|
||||
try (var projectLock = lockController.lockProject(projectId, "moveResource")) {
|
||||
try (var ignoredLock = lockController.lockProject(projectId, "moveResource")) {
|
||||
var normalizedOldResourcePath = CommonUtils.normalizeResourcePath(oldResourcePath);
|
||||
var normalizedNewResourcePath = CommonUtils.normalizeResourcePath(newResourcePath);
|
||||
if (log.isDebugEnabled()) {
|
||||
@@ -627,7 +433,10 @@ public class LocalResourceController implements RMController {
|
||||
throw new DBException("Resource '" + oldTargetPath + "' doesn't exists");
|
||||
}
|
||||
Path newTargetPath = getTargetPath(projectId, normalizedNewResourcePath);
|
||||
validateResourcePath(newTargetPath.toString());
|
||||
validateResourcePath(rootPath.relativize(newTargetPath).toString());
|
||||
if (Files.exists(newTargetPath)) {
|
||||
throw new DBException("Resource with name %s already exists".formatted(newTargetPath.getFileName()));
|
||||
}
|
||||
try {
|
||||
Files.move(oldTargetPath, newTargetPath);
|
||||
} catch (IOException e) {
|
||||
@@ -679,11 +488,10 @@ public class LocalResourceController implements RMController {
|
||||
|
||||
@Override
|
||||
public void deleteResource(@NotNull String projectId, @NotNull String resourcePath, boolean recursive) throws DBException {
|
||||
try (var projectLock = lockController.lockProject(projectId, "deleteResource")) {
|
||||
try (var ignoredLock = lockController.lockProject(projectId, "deleteResource")) {
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug("Removing resource from '" + resourcePath + "' in project '" + projectId + "'" + (recursive ? " recursive" : ""));
|
||||
}
|
||||
validateResourcePath(resourcePath);
|
||||
Path targetPath = getTargetPath(projectId, resourcePath);
|
||||
doFileWriteOperation(projectId, targetPath, () -> {
|
||||
if (!Files.exists(targetPath)) {
|
||||
@@ -768,7 +576,7 @@ public class LocalResourceController implements RMController {
|
||||
@NotNull byte[] data,
|
||||
boolean forceOverwrite
|
||||
) throws DBException {
|
||||
try (var lock = lockController.lockProject(projectId, "setResourceContents")) {
|
||||
try (var ignoredLock = lockController.lockProject(projectId, "setResourceContents")) {
|
||||
validateResourcePath(resourcePath);
|
||||
Number fileSizeLimit = WebAppUtils.getWebApplication()
|
||||
.getAppConfiguration()
|
||||
@@ -804,15 +612,6 @@ public class LocalResourceController implements RMController {
|
||||
return DEFAULT_CHANGE_ID;
|
||||
}
|
||||
|
||||
protected void createFolder(Path targetPath) throws DBException {
|
||||
if (!Files.exists(targetPath)) {
|
||||
try {
|
||||
Files.createDirectories(targetPath);
|
||||
} catch (IOException e) {
|
||||
throw new DBException("Error creating folder '" + targetPath + "'");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
@@ -822,7 +621,7 @@ public class LocalResourceController implements RMController {
|
||||
@NotNull String propertyName,
|
||||
@Nullable Object propertyValue
|
||||
) throws DBException {
|
||||
try (var projectLock = lockController.lockProject(projectId, "resourcePropertyUpdate")) {
|
||||
try (var ignoredLock = lockController.lockProject(projectId, "resourcePropertyUpdate")) {
|
||||
validateResourcePath(resourcePath);
|
||||
BaseWebProjectImpl webProject = getWebProject(projectId, false);
|
||||
doFileWriteOperation(projectId, webProject.getMetadataFilePath(),
|
||||
@@ -836,11 +635,24 @@ public class LocalResourceController implements RMController {
|
||||
}
|
||||
}
|
||||
|
||||
private void validateResourcePath(String resourcePath) throws DBException {
|
||||
var fullPath = Paths.get(resourcePath);
|
||||
for (Path path : fullPath) {
|
||||
String fileName = IOUtils.getFileNameWithoutExtension(path);
|
||||
GeneralUtils.validateResourceName(fileName);
|
||||
@NotNull
|
||||
@Override
|
||||
public String setResourceProperties(
|
||||
@NotNull String projectId,
|
||||
@NotNull String resourcePath,
|
||||
@NotNull Map<String, Object> properties
|
||||
) throws DBException {
|
||||
try (var ignoredLock = lockController.lockProject(projectId, "resourcePropertyUpdate")) {
|
||||
validateResourcePath(resourcePath);
|
||||
BaseWebProjectImpl webProject = getWebProject(projectId, false);
|
||||
doFileWriteOperation(projectId, webProject.getMetadataFilePath(),
|
||||
() -> {
|
||||
log.debug("Updating resource '" + resourcePath + "' properties in project '" + projectId + "'");
|
||||
webProject.setResourceProperties(resourcePath, properties);
|
||||
return null;
|
||||
}
|
||||
);
|
||||
return DEFAULT_CHANGE_ID;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -860,7 +672,7 @@ public class LocalResourceController implements RMController {
|
||||
if (!targetPath.startsWith(projectPath)) {
|
||||
throw new DBException("Invalid resource path");
|
||||
}
|
||||
return WebAppUtils.getWebApplication().getHomeDirectory().relativize(targetPath);
|
||||
return targetPath;
|
||||
} catch (InvalidPathException e) {
|
||||
throw new DBException("Resource path contains invalid characters");
|
||||
}
|
||||
@@ -873,7 +685,7 @@ public class LocalResourceController implements RMController {
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private RMProject makeProjectFromId(String projectId, boolean loadPermissions) throws DBException {
|
||||
protected RMProject makeProjectFromId(String projectId, boolean loadPermissions) throws DBException {
|
||||
var projectName = parseProjectName(projectId);
|
||||
var projectPath = getProjectPath(projectId);
|
||||
if (!Files.exists(projectPath)) {
|
||||
@@ -1089,7 +901,7 @@ public class LocalResourceController implements RMController {
|
||||
);
|
||||
}
|
||||
if (readProperties) {
|
||||
final BaseProjectImpl project = (BaseProjectImpl) getWebProject(projectId, true);
|
||||
final BaseProjectImpl project = getWebProject(projectId, true);
|
||||
final String resourcePath = getProjectRelativePath(projectId, path);
|
||||
final Map<String, Object> properties = project.getResourceProperties(resourcePath);
|
||||
|
||||
@@ -1117,32 +929,6 @@ public class LocalResourceController implements RMController {
|
||||
return getProjectPath(projectId).toAbsolutePath().relativize(path).toString().replace('\\', IPath.SEPARATOR);
|
||||
}
|
||||
|
||||
private void fireRmResourceAddEvent(@NotNull String projectId, @NotNull String resourcePath) throws DBException {
|
||||
RMEventManager.fireEvent(
|
||||
new RMEvent(RMEvent.Action.RESOURCE_ADD,
|
||||
getProject(projectId, false, false),
|
||||
resourcePath)
|
||||
);
|
||||
}
|
||||
|
||||
private void fireRmResourceDeleteEvent(@NotNull String projectId, @NotNull String resourcePath) throws DBException {
|
||||
RMEventManager.fireEvent(
|
||||
new RMEvent(RMEvent.Action.RESOURCE_DELETE,
|
||||
makeProjectFromId(projectId, false),
|
||||
resourcePath
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
private void fireRmProjectAddEvent(@NotNull RMProject project) throws DBException {
|
||||
RMEventManager.fireEvent(
|
||||
new RMEvent(
|
||||
RMEvent.Action.RESOURCE_ADD,
|
||||
project
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
protected void handleProjectOpened(String projectId) throws DBException {
|
||||
createResourceTypeFolders(getProjectPath(projectId));
|
||||
}
|
||||
@@ -1263,5 +1049,4 @@ public class LocalResourceController implements RMController {
|
||||
rmProjectName.name.equals(userId);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
+11
-5
@@ -23,6 +23,7 @@ import org.jkiss.code.Nullable;
|
||||
import org.jkiss.dbeaver.DBException;
|
||||
import org.jkiss.dbeaver.Log;
|
||||
import org.jkiss.dbeaver.model.app.DBPWorkspace;
|
||||
import org.jkiss.utils.IOUtils;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.Reader;
|
||||
@@ -73,18 +74,23 @@ public class RMFileLockController {
|
||||
* @return - lock
|
||||
*/
|
||||
@NotNull
|
||||
public RMLock lockProject(@NotNull String projectId,@NotNull String operationName) throws DBException {
|
||||
public RMLock lockProject(@NotNull String projectId, @NotNull String operationName) throws DBException {
|
||||
synchronized (RMFileLockController.class) {
|
||||
try {
|
||||
createLockFolderIfNeeded();
|
||||
createProjectFolder(projectId);
|
||||
Path projectLockFile = getProjectLockFilePath(projectId);
|
||||
|
||||
RMLockInfo lockInfo = new RMLockInfo.Builder(projectId, UUID.randomUUID().toString())
|
||||
.setApplicationId(applicationId)
|
||||
.setOperationName(operationName)
|
||||
.setOperationStartTime(System.currentTimeMillis())
|
||||
.build();
|
||||
Path projectLockFile = getProjectLockFilePath(projectId);
|
||||
|
||||
if (!IOUtils.isFileFromDefaultFS(lockFolderPath)) {
|
||||
// fake lock for external file system?
|
||||
return new RMLock(projectLockFile);
|
||||
}
|
||||
createLockFolderIfNeeded();
|
||||
createProjectFolder(projectId);
|
||||
|
||||
createLockFile(projectLockFile, lockInfo);
|
||||
return new RMLock(projectLockFile);
|
||||
} catch (Exception e) {
|
||||
|
||||
+28
-9
@@ -51,27 +51,33 @@ public abstract class BaseWebSession extends AbstractSessionPersistent {
|
||||
@NotNull
|
||||
protected final WebUserContext userContext;
|
||||
@NotNull
|
||||
protected final WebAuthApplication application;
|
||||
protected final WebApplication application;
|
||||
protected volatile long lastAccessTime;
|
||||
|
||||
private final List<CBWebSessionEventHandler> sessionEventHandlers = new CopyOnWriteArrayList<>();
|
||||
private WebSessionEventsFilter eventsFilter = new WebSessionEventsFilter();
|
||||
private final WebSessionWorkspace workspace;
|
||||
|
||||
public BaseWebSession(@NotNull String id, @NotNull WebAuthApplication application) throws DBException {
|
||||
public BaseWebSession(@NotNull String id, @NotNull WebApplication application) throws DBException {
|
||||
this.id = id;
|
||||
this.application = application;
|
||||
this.createTime = System.currentTimeMillis();
|
||||
this.lastAccessTime = this.createTime;
|
||||
this.workspace = new WebSessionWorkspace(this);
|
||||
this.workspace = createWebWorkspace();
|
||||
this.workspace.getAuthContext().addSession(this);
|
||||
this.userContext = createUserContext();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
protected WebSessionWorkspace createWebWorkspace() {
|
||||
return new WebSessionWorkspace(this);
|
||||
}
|
||||
|
||||
protected WebUserContext createUserContext() throws DBException {
|
||||
return new WebUserContext(this.application, this.workspace);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public WebSessionWorkspace getWorkspace() {
|
||||
return workspace;
|
||||
}
|
||||
@@ -173,14 +179,24 @@ public abstract class BaseWebSession extends AbstractSessionPersistent {
|
||||
@Override
|
||||
public void close() {
|
||||
super.close();
|
||||
var sessionExpiredEvent = new WSSessionExpiredEvent();
|
||||
cleanUpSession(true);
|
||||
}
|
||||
|
||||
public void close(boolean clearTokens, boolean sendSessionExpiredEvent) {
|
||||
cleanUpSession(sendSessionExpiredEvent);
|
||||
}
|
||||
|
||||
private void cleanUpSession(boolean sendSessionExpiredEvent) {
|
||||
application.getEventController().addEvent(new WSEventDeleteTempFile(getSessionId()));
|
||||
synchronized (sessionEventHandlers) {
|
||||
var sessionExpiredEvent = new WSSessionExpiredEvent();
|
||||
for (CBWebSessionEventHandler sessionEventHandler : sessionEventHandlers) {
|
||||
try {
|
||||
sessionEventHandler.handleWebSessionEvent(sessionExpiredEvent);
|
||||
} catch (DBException e) {
|
||||
log.warn("Failed to send session expiration event", e);
|
||||
if (sendSessionExpiredEvent) {
|
||||
try {
|
||||
sessionEventHandler.handleWebSessionEvent(sessionExpiredEvent);
|
||||
} catch (DBException e) {
|
||||
log.warn("Failed to send session expiration event", e);
|
||||
}
|
||||
}
|
||||
sessionEventHandler.close();
|
||||
}
|
||||
@@ -222,6 +238,9 @@ public abstract class BaseWebSession extends AbstractSessionPersistent {
|
||||
|
||||
@Property
|
||||
public long getRemainingTime() {
|
||||
return application.getMaxSessionIdleTime() + lastAccessTime - System.currentTimeMillis();
|
||||
if (application instanceof WebAuthApplication authApplication) {
|
||||
return authApplication.getMaxSessionIdleTime() + lastAccessTime - System.currentTimeMillis();
|
||||
}
|
||||
return Integer.MAX_VALUE;
|
||||
}
|
||||
}
|
||||
|
||||
+11
-11
@@ -1,18 +1,18 @@
|
||||
/*
|
||||
* DBeaver - Universal Database Manager
|
||||
* Copyright (C) 2010-2024 DBeaver Corp
|
||||
* Copyright (C) 2010-2024 DBeaver Corp and others
|
||||
*
|
||||
* All Rights Reserved.
|
||||
* 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
|
||||
*
|
||||
* 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.
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package io.cloudbeaver.model.session;
|
||||
|
||||
|
||||
+57
@@ -0,0 +1,57 @@
|
||||
/*
|
||||
* DBeaver - Universal Database Manager
|
||||
* Copyright (C) 2010-2024 DBeaver Corp and others
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package io.cloudbeaver.model.session;
|
||||
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
|
||||
public class WebHttpRequestInfo {
|
||||
|
||||
private final String id;
|
||||
private final Object locale;
|
||||
private final String lastRemoteAddress;
|
||||
private final String lastRemoteUserAgent;
|
||||
|
||||
public WebHttpRequestInfo(HttpServletRequest request) {
|
||||
this.id = request.getSession().getId();
|
||||
this.locale = request.getAttribute("locale");
|
||||
this.lastRemoteAddress = request.getRemoteAddr();
|
||||
this.lastRemoteUserAgent = request.getHeader("User-Agent");
|
||||
}
|
||||
|
||||
public WebHttpRequestInfo(String id, Object locale, String lastRemoteAddress, String lastRemoteUserAgent) {
|
||||
this.id = id;
|
||||
this.locale = locale;
|
||||
this.lastRemoteAddress = lastRemoteAddress;
|
||||
this.lastRemoteUserAgent = lastRemoteUserAgent;
|
||||
}
|
||||
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public Object getLocale() {
|
||||
return locale;
|
||||
}
|
||||
|
||||
public String getLastRemoteAddress() {
|
||||
return lastRemoteAddress;
|
||||
}
|
||||
|
||||
public String getLastRemoteUserAgent() {
|
||||
return lastRemoteUserAgent;
|
||||
}
|
||||
}
|
||||
+114
-258
@@ -19,21 +19,18 @@ package io.cloudbeaver.model.session;
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.GsonBuilder;
|
||||
import com.google.gson.InstanceCreator;
|
||||
import io.cloudbeaver.DBWConstants;
|
||||
import io.cloudbeaver.DBWebException;
|
||||
import io.cloudbeaver.DataSourceFilter;
|
||||
import io.cloudbeaver.WebProjectImpl;
|
||||
import com.google.gson.Strictness;
|
||||
import io.cloudbeaver.*;
|
||||
import io.cloudbeaver.model.WebAsyncTaskInfo;
|
||||
import io.cloudbeaver.model.WebConnectionInfo;
|
||||
import io.cloudbeaver.model.WebServerMessage;
|
||||
import io.cloudbeaver.model.app.WebApplication;
|
||||
import io.cloudbeaver.model.app.WebAuthApplication;
|
||||
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 jakarta.servlet.http.HttpServletRequest;
|
||||
import org.eclipse.core.runtime.IAdaptable;
|
||||
import org.eclipse.core.runtime.IStatus;
|
||||
import org.eclipse.core.runtime.Status;
|
||||
@@ -43,12 +40,8 @@ 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;
|
||||
import org.jkiss.dbeaver.model.app.DBPDataSourceRegistryCache;
|
||||
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;
|
||||
@@ -56,6 +49,7 @@ 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.preferences.DBPPreferenceStore;
|
||||
import org.jkiss.dbeaver.model.rm.RMController;
|
||||
import org.jkiss.dbeaver.model.rm.RMProject;
|
||||
import org.jkiss.dbeaver.model.rm.RMProjectType;
|
||||
@@ -67,18 +61,15 @@ 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.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;
|
||||
import org.jkiss.dbeaver.runtime.DBWorkbench;
|
||||
import org.jkiss.dbeaver.runtime.jobs.DisconnectJob;
|
||||
import org.jkiss.utils.CommonUtils;
|
||||
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.nio.file.Path;
|
||||
import java.time.Instant;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
@@ -88,6 +79,7 @@ import java.util.stream.Collectors;
|
||||
* Web session.
|
||||
* Is the main source of data in web application
|
||||
*/
|
||||
//TODO: split to authenticated and non authenticated context
|
||||
public class WebSession extends BaseWebSession
|
||||
implements SMSessionWithAuth, SMCredentialsProvider, DBACredentialsProvider, IAdaptable {
|
||||
|
||||
@@ -104,12 +96,10 @@ public class WebSession extends BaseWebSession
|
||||
private String lastRemoteAddr;
|
||||
private String lastRemoteUserAgent;
|
||||
|
||||
private Set<String> accessibleConnectionIds = Collections.emptySet();
|
||||
|
||||
private String locale;
|
||||
private boolean cacheExpired;
|
||||
|
||||
private final Map<String, WebConnectionInfo> connections = new HashMap<>();
|
||||
protected WebSessionGlobalProjectImpl globalProject;
|
||||
private final List<WebServerMessage> sessionMessages = new ArrayList<>();
|
||||
|
||||
private final Map<String, WebAsyncTaskInfo> asyncTasks = new HashMap<>();
|
||||
@@ -123,21 +113,34 @@ public class WebSession extends BaseWebSession
|
||||
private final Map<String, DBWSessionHandler> sessionHandlers;
|
||||
|
||||
public WebSession(
|
||||
@NotNull HttpServletRequest request,
|
||||
@NotNull WebHttpRequestInfo requestInfo,
|
||||
@NotNull WebAuthApplication application,
|
||||
@NotNull Map<String, DBWSessionHandler> sessionHandlers
|
||||
) throws DBException {
|
||||
super(request.getSession().getId(), application);
|
||||
this(requestInfo.getId(),
|
||||
CommonUtils.toString(requestInfo.getLocale()),
|
||||
application,
|
||||
sessionHandlers
|
||||
);
|
||||
updateSessionParameters(requestInfo);
|
||||
}
|
||||
|
||||
protected WebSession(
|
||||
@NotNull String id,
|
||||
@Nullable String locale,
|
||||
@NotNull WebApplication application,
|
||||
@NotNull Map<String, DBWSessionHandler> sessionHandlers
|
||||
) throws DBException {
|
||||
super(id, application);
|
||||
this.lastAccessTime = this.createTime;
|
||||
setLocale(CommonUtils.toString(request.getSession().getAttribute(ATTR_LOCALE), this.locale));
|
||||
this.sessionHandlers = sessionHandlers;
|
||||
setLocale(CommonUtils.toString(locale, this.locale));
|
||||
//force authorization of anonymous session to avoid access error,
|
||||
//because before authorization could be called by any request,
|
||||
//but now 'updateInfo' is called only in special requests,
|
||||
//and the order of requests is not guaranteed.
|
||||
//look at CB-4747
|
||||
refreshSessionAuth();
|
||||
updateSessionParameters(request);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@@ -151,8 +154,8 @@ public class WebSession extends BaseWebSession
|
||||
}
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public DBPProject getSingletonProject() {
|
||||
@Nullable
|
||||
public WebSessionProjectImpl getSingletonProject() {
|
||||
return getWorkspace().getActiveProject();
|
||||
}
|
||||
|
||||
@@ -249,69 +252,6 @@ public class WebSession extends BaseWebSession
|
||||
initNavigatorModel();
|
||||
}
|
||||
|
||||
/**
|
||||
* updates data sources based on event in web session
|
||||
*
|
||||
* @param project project of connection
|
||||
* @param dataSourceIds list of updated connections
|
||||
* @param type type of event
|
||||
*/
|
||||
public synchronized boolean updateProjectDataSources(
|
||||
DBPProject project,
|
||||
List<String> dataSourceIds,
|
||||
WSEventType type
|
||||
) {
|
||||
var sendDataSourceUpdatedEvent = false;
|
||||
DBPDataSourceRegistry registry = project.getDataSourceRegistry();
|
||||
// save old connections
|
||||
var oldDataSources = dataSourceIds.stream()
|
||||
.map(registry::getDataSource)
|
||||
.filter(Objects::nonNull)
|
||||
.collect(Collectors.toMap(
|
||||
DBPDataSourceContainer::getId,
|
||||
ds -> new DataSourceDescriptor((DataSourceDescriptor) ds, ds.getRegistry())
|
||||
));
|
||||
if (type == WSEventType.DATASOURCE_CREATED || type == WSEventType.DATASOURCE_UPDATED) {
|
||||
registry.refreshConfig(dataSourceIds);
|
||||
}
|
||||
for (String dsId : dataSourceIds) {
|
||||
DataSourceDescriptor ds = (DataSourceDescriptor) registry.getDataSource(dsId);
|
||||
if (ds == null) {
|
||||
continue;
|
||||
}
|
||||
switch (type) {
|
||||
case DATASOURCE_CREATED -> {
|
||||
WebConnectionInfo connectionInfo = new WebConnectionInfo(this, ds);
|
||||
this.connections.put(getConnectionId(ds), connectionInfo);
|
||||
sendDataSourceUpdatedEvent = true;
|
||||
}
|
||||
case DATASOURCE_UPDATED -> // if settings were changed we need to send event
|
||||
sendDataSourceUpdatedEvent |= !ds.equalSettings(oldDataSources.get(dsId));
|
||||
case DATASOURCE_DELETED -> {
|
||||
WebDataSourceUtils.disconnectDataSource(this, ds);
|
||||
if (registry instanceof DBPDataSourceRegistryCache dsrc) {
|
||||
dsrc.removeDataSourceFromList(ds);
|
||||
}
|
||||
this.connections.remove(getConnectionId(ds));
|
||||
sendDataSourceUpdatedEvent = true;
|
||||
}
|
||||
default -> {
|
||||
}
|
||||
}
|
||||
}
|
||||
return sendDataSourceUpdatedEvent;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private String getConnectionId(@NotNull DBPDataSourceContainer container) {
|
||||
return getConnectionId(container.getProject().getId(), container.getId());
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private String getConnectionId(@NotNull String projectId, @NotNull String dsId) {
|
||||
return projectId + ":" + dsId;
|
||||
}
|
||||
|
||||
// Note: for admin use only
|
||||
public synchronized void resetUserState() throws DBException {
|
||||
clearAuthTokens();
|
||||
@@ -332,7 +272,7 @@ public class WebSession extends BaseWebSession
|
||||
this.navigatorModel.dispose();
|
||||
this.navigatorModel = null;
|
||||
}
|
||||
this.connections.clear();
|
||||
this.globalProject = null;
|
||||
|
||||
loadProjects();
|
||||
|
||||
@@ -352,14 +292,13 @@ public class WebSession extends BaseWebSession
|
||||
// No anonymous mode in distributed apps
|
||||
return;
|
||||
}
|
||||
refreshAccessibleConnectionIds();
|
||||
try {
|
||||
RMController controller = getRmController();
|
||||
RMProject[] rmProjects = controller.listAccessibleProjects();
|
||||
for (RMProject project : rmProjects) {
|
||||
createWebProject(project);
|
||||
}
|
||||
if (user == null) {
|
||||
if (user == null && application.getAppConfiguration().isAnonymousAccessEnabled()) {
|
||||
WebProjectImpl anonymousProject = createWebProject(RMUtils.createAnonymousProject());
|
||||
anonymousProject.setInMemory(true);
|
||||
}
|
||||
@@ -372,57 +311,33 @@ public class WebSession extends BaseWebSession
|
||||
}
|
||||
}
|
||||
|
||||
public WebProjectImpl createWebProject(RMProject project) {
|
||||
// Do not filter data sources from user project
|
||||
DataSourceFilter filter = project.getType() == RMProjectType.GLOBAL
|
||||
? this::isDataSourceAccessible
|
||||
: x -> true;
|
||||
WebProjectImpl sessionProject = application.createProjectImpl(this, project, filter);
|
||||
private WebSessionProjectImpl createWebProject(RMProject project) throws DBException {
|
||||
WebSessionProjectImpl sessionProject;
|
||||
if (project.isGlobal()) {
|
||||
sessionProject = createGlobalProject(project);
|
||||
} else {
|
||||
sessionProject = new WebSessionProjectImpl(this, project, getProjectPath(project));
|
||||
}
|
||||
// do not load data sources for anonymous project
|
||||
if (project.getType() == RMProjectType.USER && userContext.getUser() == null) {
|
||||
sessionProject.setInMemory(true);
|
||||
}
|
||||
DBPDataSourceRegistry dataSourceRegistry = sessionProject.getDataSourceRegistry();
|
||||
dataSourceRegistry.setAuthCredentialsProvider(this);
|
||||
addSessionProject(sessionProject);
|
||||
if (!project.isShared() || application.isConfigurationMode()) {
|
||||
getWorkspace().setActiveProject(sessionProject);
|
||||
}
|
||||
for (DBPDataSourceContainer ds : dataSourceRegistry.getDataSources()) {
|
||||
addConnection(new WebConnectionInfo(this, ds));
|
||||
}
|
||||
Throwable lastError = dataSourceRegistry.getLastError();
|
||||
if (lastError != null) {
|
||||
addSessionError(lastError);
|
||||
log.error("Error refreshing connections from project '" + project.getId() + "'", lastError);
|
||||
}
|
||||
return sessionProject;
|
||||
}
|
||||
|
||||
public void filterAccessibleConnections(List<WebConnectionInfo> connections) {
|
||||
connections.removeIf(c -> !isDataSourceAccessible(c.getDataSourceContainer()));
|
||||
}
|
||||
|
||||
private boolean isDataSourceAccessible(DBPDataSourceContainer dataSource) {
|
||||
return dataSource.isExternallyProvided() ||
|
||||
dataSource.isTemporary() ||
|
||||
this.hasPermission(DBWConstants.PERMISSION_ADMIN) ||
|
||||
accessibleConnectionIds.contains(dataSource.getId());
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private Set<String> readAccessibleConnectionIds() {
|
||||
try {
|
||||
return getSecurityController()
|
||||
.getAllAvailableObjectsPermissions(SMObjectType.datasource)
|
||||
.stream()
|
||||
.map(SMObjectPermissions::getObjectId)
|
||||
.collect(Collectors.toSet());
|
||||
} catch (DBException e) {
|
||||
addSessionError(e);
|
||||
log.error("Error reading connection grants", e);
|
||||
return Collections.emptySet();
|
||||
}
|
||||
protected Path getProjectPath(@NotNull RMProject project) throws DBException {
|
||||
return RMUtils.getProjectPath(project);
|
||||
}
|
||||
|
||||
protected WebSessionProjectImpl createGlobalProject(RMProject project) {
|
||||
globalProject = new WebSessionGlobalProjectImpl(this, project);
|
||||
globalProject.refreshAccessibleConnectionIds();
|
||||
return globalProject;
|
||||
}
|
||||
|
||||
private void resetSessionCache() throws DBCException {
|
||||
@@ -440,17 +355,7 @@ public class WebSession extends BaseWebSession
|
||||
}
|
||||
|
||||
private void resetNavigationModel() {
|
||||
Map<String, WebConnectionInfo> conCopy;
|
||||
synchronized (this.connections) {
|
||||
conCopy = new HashMap<>(this.connections);
|
||||
this.connections.clear();
|
||||
}
|
||||
|
||||
for (WebConnectionInfo connectionInfo : conCopy.values()) {
|
||||
if (connectionInfo.isConnected()) {
|
||||
new DisconnectJob(connectionInfo.getDataSourceContainer()).schedule();
|
||||
}
|
||||
}
|
||||
getWorkspace().getProjects().forEach(WebSessionProjectImpl::dispose);
|
||||
|
||||
if (this.navigatorModel != null) {
|
||||
this.navigatorModel.dispose();
|
||||
@@ -464,7 +369,9 @@ public class WebSession extends BaseWebSession
|
||||
authAsAnonymousUser();
|
||||
} else if (getUserId() != null) {
|
||||
userContext.refreshPermissions();
|
||||
refreshAccessibleConnectionIds();
|
||||
if (globalProject != null) {
|
||||
globalProject.refreshAccessibleConnectionIds();
|
||||
}
|
||||
}
|
||||
|
||||
} catch (Exception e) {
|
||||
@@ -473,32 +380,6 @@ public class WebSession extends BaseWebSession
|
||||
}
|
||||
}
|
||||
|
||||
private synchronized void refreshAccessibleConnectionIds() {
|
||||
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(getConnectionId(dataSource), 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(getConnectionId(dataSource));
|
||||
// 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()) {
|
||||
@@ -518,10 +399,18 @@ public class WebSession extends BaseWebSession
|
||||
this.locale = locale != null ? locale : Locale.getDefault().getLanguage();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public DBNModel getNavigatorModel() {
|
||||
return navigatorModel;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public DBNModel getNavigatorModelOrThrow() throws DBWebException {
|
||||
if (navigatorModel != null) {
|
||||
return navigatorModel;
|
||||
}
|
||||
throw new DBWebException("Navigator model is not found in session");
|
||||
}
|
||||
/**
|
||||
* Returns and clears progress messages
|
||||
*/
|
||||
@@ -557,75 +446,12 @@ public class WebSession extends BaseWebSession
|
||||
}
|
||||
}
|
||||
|
||||
public synchronized void updateSessionParameters(HttpServletRequest request) {
|
||||
this.lastRemoteAddr = request.getRemoteAddr();
|
||||
this.lastRemoteUserAgent = request.getHeader("User-Agent");
|
||||
public synchronized void updateSessionParameters(WebHttpRequestInfo requestInfo) {
|
||||
this.lastRemoteAddr = requestInfo.getLastRemoteAddress();
|
||||
this.lastRemoteUserAgent = requestInfo.getLastRemoteUserAgent();
|
||||
this.cacheExpired = false;
|
||||
}
|
||||
|
||||
@Association
|
||||
public List<WebConnectionInfo> getConnections() {
|
||||
synchronized (connections) {
|
||||
return new ArrayList<>(connections.values());
|
||||
}
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public WebConnectionInfo getWebConnectionInfo(@Nullable String projectId, String connectionID) throws DBWebException {
|
||||
WebConnectionInfo connectionInfo = null;
|
||||
synchronized (connections) {
|
||||
if (projectId != null) {
|
||||
connectionInfo = connections.get(getConnectionId(projectId, connectionID));
|
||||
} else {
|
||||
addWarningMessage("Project id is not defined in request. Try to find it from connection cache");
|
||||
for (Map.Entry<String, WebConnectionInfo> entry : connections.entrySet()) {
|
||||
String k = entry.getKey();
|
||||
WebConnectionInfo v = entry.getValue();
|
||||
if (k.contains(connectionID)) {
|
||||
connectionInfo = v;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (connectionInfo == null) {
|
||||
WebProjectImpl project = getProjectById(projectId);
|
||||
if (project == null) {
|
||||
throw new DBWebException("Project '" + projectId + "' not found in web workspace");
|
||||
}
|
||||
DBPDataSourceContainer dataSource = project.getDataSourceRegistry().getDataSource(connectionID);
|
||||
if (dataSource != null) {
|
||||
connectionInfo = new WebConnectionInfo(this, dataSource);
|
||||
synchronized (connections) {
|
||||
connections.put(getConnectionId(dataSource), connectionInfo);
|
||||
}
|
||||
} else {
|
||||
throw new DBWebException("Connection '" + connectionID + "' not found");
|
||||
}
|
||||
}
|
||||
return connectionInfo;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public WebConnectionInfo findWebConnectionInfo(String projectId, String connectionId) {
|
||||
synchronized (connections) {
|
||||
return connections.get(getConnectionId(projectId, connectionId));
|
||||
}
|
||||
}
|
||||
|
||||
public void addConnection(WebConnectionInfo connectionInfo) {
|
||||
synchronized (connections) {
|
||||
connections.put(getConnectionId(connectionInfo.getDataSourceContainer()), connectionInfo);
|
||||
}
|
||||
}
|
||||
|
||||
public void removeConnection(WebConnectionInfo connectionInfo) {
|
||||
connectionInfo.clearCache();
|
||||
synchronized (connections) {
|
||||
connections.remove(getConnectionId(connectionInfo.getDataSourceContainer()));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
try {
|
||||
@@ -643,6 +469,25 @@ public class WebSession extends BaseWebSession
|
||||
super.close();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close(boolean clearTokens, boolean sendSessionExpiredEvent) {
|
||||
try {
|
||||
resetNavigationModel();
|
||||
resetSessionCache();
|
||||
} catch (Throwable e) {
|
||||
log.error(e);
|
||||
}
|
||||
if (clearTokens) {
|
||||
try {
|
||||
clearAuthTokens();
|
||||
} catch (Exception e) {
|
||||
log.error("Error closing web session tokens");
|
||||
}
|
||||
}
|
||||
this.userContext.setUser(null);
|
||||
super.close(clearTokens, sendSessionExpiredEvent);
|
||||
}
|
||||
|
||||
private List<WebAuthInfo> clearAuthTokens() throws DBException {
|
||||
ArrayList<WebAuthInfo> tokensCopy;
|
||||
synchronized (authTokens) {
|
||||
@@ -790,7 +635,7 @@ public class WebSession extends BaseWebSession
|
||||
synchronized (attributes) {
|
||||
Object value = attributes.get(name);
|
||||
if (value instanceof PersistentAttribute persistentAttribute) {
|
||||
value = persistentAttribute.getValue();
|
||||
value = persistentAttribute.value();
|
||||
}
|
||||
return (T) value;
|
||||
}
|
||||
@@ -806,7 +651,7 @@ public class WebSession extends BaseWebSession
|
||||
synchronized (attributes) {
|
||||
Object value = attributes.get(name);
|
||||
if (value instanceof PersistentAttribute persistentAttribute) {
|
||||
value = persistentAttribute.getValue();
|
||||
value = persistentAttribute.value();
|
||||
}
|
||||
if (value == null) {
|
||||
value = creator.apply(null);
|
||||
@@ -949,9 +794,12 @@ public class WebSession extends BaseWebSession
|
||||
}
|
||||
configuration.setRuntimeAttribute(RUNTIME_PARAM_AUTH_INFOS, getAllAuthInfo());
|
||||
|
||||
WebConnectionInfo webConnectionInfo = findWebConnectionInfo(dataSourceContainer.getProject().getId(), dataSourceContainer.getId());
|
||||
if (webConnectionInfo != null) {
|
||||
WebDataSourceUtils.saveCredentialsInDataSource(webConnectionInfo, dataSourceContainer, configuration);
|
||||
WebSessionProjectImpl project = getProjectById(dataSourceContainer.getProject().getId());
|
||||
if (project != null) {
|
||||
WebConnectionInfo webConnectionInfo = project.findWebConnectionInfo(dataSourceContainer.getId());
|
||||
if (webConnectionInfo != null) {
|
||||
WebDataSourceUtils.saveCredentialsInDataSource(webConnectionInfo, dataSourceContainer, configuration);
|
||||
}
|
||||
}
|
||||
|
||||
// uncommented because we had the problem with non-native auth models
|
||||
@@ -960,7 +808,7 @@ public class WebSession extends BaseWebSession
|
||||
|
||||
InstanceCreator<DBAAuthCredentials> credTypeAdapter = type -> credentials;
|
||||
Gson credGson = new GsonBuilder()
|
||||
.setLenient()
|
||||
.setStrictness(Strictness.LENIENT)
|
||||
.registerTypeAdapter(credentials.getClass(), credTypeAdapter)
|
||||
.create();
|
||||
|
||||
@@ -1042,12 +890,17 @@ public class WebSession extends BaseWebSession
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public WebProjectImpl getProjectById(@Nullable String projectId) {
|
||||
public WebSessionProjectImpl getProjectById(@Nullable String projectId) {
|
||||
return getWorkspace().getProjectById(projectId);
|
||||
}
|
||||
|
||||
public WebProjectImpl getAccessibleProjectById(@Nullable String projectId) throws DBWebException {
|
||||
WebProjectImpl project = null;
|
||||
/**
|
||||
* Returns project info from session cache.
|
||||
*
|
||||
* @throws DBWebException if project with provided id is not found.
|
||||
*/
|
||||
public WebSessionProjectImpl getAccessibleProjectById(@Nullable String projectId) throws DBWebException {
|
||||
WebSessionProjectImpl project = null;
|
||||
if (projectId != null) {
|
||||
project = getWorkspace().getProjectById(projectId);
|
||||
}
|
||||
@@ -1057,18 +910,24 @@ public class WebSession extends BaseWebSession
|
||||
return project;
|
||||
}
|
||||
|
||||
public List<WebProjectImpl> getAccessibleProjects() {
|
||||
public List<WebSessionProjectImpl> getAccessibleProjects() {
|
||||
return getWorkspace().getProjects();
|
||||
}
|
||||
|
||||
public void addSessionProject(@NotNull WebProjectImpl project) {
|
||||
/**
|
||||
* Adds project to session cache and navigator tree.
|
||||
*/
|
||||
public void addSessionProject(@NotNull WebSessionProjectImpl project) {
|
||||
getWorkspace().addProject(project);
|
||||
if (navigatorModel != null) {
|
||||
navigatorModel.getRoot().addProject(project, false);
|
||||
}
|
||||
}
|
||||
|
||||
public void deleteSessionProject(@Nullable WebProjectImpl project) {
|
||||
/**
|
||||
* Removes project from session cache and navigator tree.
|
||||
*/
|
||||
public void deleteSessionProject(@Nullable WebSessionProjectImpl project) {
|
||||
if (project != null) {
|
||||
project.dispose();
|
||||
}
|
||||
@@ -1093,10 +952,6 @@ public class WebSession extends BaseWebSession
|
||||
return;
|
||||
}
|
||||
deleteSessionProject(project);
|
||||
var projectConnections = project.getDataSourceRegistry().getDataSources();
|
||||
for (DBPDataSourceContainer c : projectConnections) {
|
||||
removeConnection(new WebConnectionInfo(this, c));
|
||||
}
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@@ -1108,6 +963,16 @@ public class WebSession extends BaseWebSession
|
||||
return project.getFileSystemManager();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public DBPPreferenceStore getUserPreferenceStore() {
|
||||
return getUserContext().getPreferenceStore();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public WebSessionGlobalProjectImpl getGlobalProject() {
|
||||
return globalProject;
|
||||
}
|
||||
|
||||
private class SessionProgressMonitor extends BaseProgressMonitor {
|
||||
@Override
|
||||
public void beginTask(String name, int totalWork) {
|
||||
@@ -1142,15 +1007,6 @@ public class WebSession extends BaseWebSession
|
||||
}
|
||||
}
|
||||
|
||||
private static class PersistentAttribute {
|
||||
private final Object value;
|
||||
|
||||
public PersistentAttribute(Object value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public Object getValue() {
|
||||
return value;
|
||||
}
|
||||
private record PersistentAttribute(Object value) {
|
||||
}
|
||||
}
|
||||
|
||||
+3
@@ -50,6 +50,9 @@ public class WebSessionEventsFilter {
|
||||
}
|
||||
|
||||
public boolean isEventAllowed(WSEvent event) {
|
||||
if (event.isForceProcessed()) {
|
||||
return true;
|
||||
}
|
||||
if (!subscribedEventTopics.isEmpty() && !subscribedEventTopics.contains(event.getTopicId())
|
||||
) {
|
||||
return false;
|
||||
|
||||
+72
@@ -0,0 +1,72 @@
|
||||
/*
|
||||
* DBeaver - Universal Database Manager
|
||||
* Copyright (C) 2010-2024 DBeaver Corp and others
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package io.cloudbeaver.model.session;
|
||||
|
||||
import org.jkiss.code.NotNull;
|
||||
import org.jkiss.dbeaver.model.impl.preferences.AbstractUserPreferenceStore;
|
||||
import org.jkiss.dbeaver.model.preferences.DBPPreferenceStore;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Map;
|
||||
|
||||
public class WebSessionPreferenceStore extends AbstractUserPreferenceStore {
|
||||
public WebSessionPreferenceStore(
|
||||
@NotNull DBPPreferenceStore parentStore
|
||||
) {
|
||||
super(parentStore);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public Map<String, Object> getCustomUserParameters() {
|
||||
return userPreferences;
|
||||
}
|
||||
|
||||
// to avoid redundant sm api call
|
||||
public void updatePreferenceValues(@NotNull Map<String, Object> newValues) {
|
||||
userPreferences.putAll(newValues);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void setUserPreference(String name, Object value) {
|
||||
throw new RuntimeException("Not implemented");
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDefaultString(String name) {
|
||||
return parentStore.getDefaultString(name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isDefault(String name) {
|
||||
return !userPreferences.containsKey(name) && parentStore.isDefault(name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean needsSaving() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setToDefault(String name) {
|
||||
throw new RuntimeException("Not implemented");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void save() throws IOException {
|
||||
throw new RuntimeException("Not implemented");
|
||||
}
|
||||
}
|
||||
+26
-15
@@ -16,11 +16,12 @@
|
||||
*/
|
||||
package io.cloudbeaver.model.session;
|
||||
|
||||
import io.cloudbeaver.WebProjectImpl;
|
||||
import io.cloudbeaver.WebSessionProjectImpl;
|
||||
import org.jkiss.code.NotNull;
|
||||
import org.jkiss.code.Nullable;
|
||||
import org.jkiss.dbeaver.model.DBPAdaptable;
|
||||
import org.jkiss.dbeaver.model.DBPImage;
|
||||
import org.jkiss.dbeaver.model.app.DBPPlatform;
|
||||
import org.jkiss.dbeaver.model.app.DBPProject;
|
||||
import org.jkiss.dbeaver.model.app.DBPWorkspace;
|
||||
import org.jkiss.dbeaver.model.impl.auth.SessionContextImpl;
|
||||
import org.jkiss.dbeaver.model.rm.RMUtils;
|
||||
@@ -38,8 +39,8 @@ public class WebSessionWorkspace implements DBPWorkspace {
|
||||
|
||||
private final BaseWebSession session;
|
||||
private final SessionContextImpl workspaceAuthContext;
|
||||
private final List<WebProjectImpl> accessibleProjects = new ArrayList<>();
|
||||
private WebProjectImpl activeProject;
|
||||
private final List<WebSessionProjectImpl> accessibleProjects = new ArrayList<>();
|
||||
private WebSessionProjectImpl activeProject;
|
||||
|
||||
public WebSessionWorkspace(BaseWebSession session) {
|
||||
this.session = session;
|
||||
@@ -81,20 +82,20 @@ public class WebSessionWorkspace implements DBPWorkspace {
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public List<WebProjectImpl> getProjects() {
|
||||
public List<WebSessionProjectImpl> getProjects() {
|
||||
return accessibleProjects;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public DBPProject getActiveProject() {
|
||||
public WebSessionProjectImpl getActiveProject() {
|
||||
return activeProject;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public WebProjectImpl getProject(@NotNull String projectName) {
|
||||
for (WebProjectImpl project : accessibleProjects) {
|
||||
public WebSessionProjectImpl getProject(@NotNull String projectName) {
|
||||
for (WebSessionProjectImpl project : accessibleProjects) {
|
||||
if (project.getName().equals(projectName)) {
|
||||
return project;
|
||||
}
|
||||
@@ -104,11 +105,11 @@ public class WebSessionWorkspace implements DBPWorkspace {
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public WebProjectImpl getProjectById(@NotNull String projectId) {
|
||||
public WebSessionProjectImpl getProjectById(@NotNull String projectId) {
|
||||
if (projectId == null) {
|
||||
return activeProject;
|
||||
}
|
||||
for (WebProjectImpl project : accessibleProjects) {
|
||||
for (WebSessionProjectImpl project : accessibleProjects) {
|
||||
if (project.getId().equals(projectId)) {
|
||||
return project;
|
||||
}
|
||||
@@ -122,26 +123,36 @@ public class WebSessionWorkspace implements DBPWorkspace {
|
||||
return workspaceAuthContext;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initializeProjects() {
|
||||
// noop
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispose() {
|
||||
clearProjects();
|
||||
}
|
||||
|
||||
public void setActiveProject(DBPProject activeProject) {
|
||||
this.activeProject = (WebProjectImpl) activeProject;
|
||||
@Override
|
||||
public DBPImage getResourceIcon(DBPAdaptable resourceAdapter) {
|
||||
return null;
|
||||
}
|
||||
|
||||
void addProject(WebProjectImpl project) {
|
||||
public void setActiveProject(WebSessionProjectImpl activeProject) {
|
||||
this.activeProject = activeProject;
|
||||
}
|
||||
|
||||
void addProject(WebSessionProjectImpl project) {
|
||||
accessibleProjects.add(project);
|
||||
}
|
||||
|
||||
void removeProject(WebProjectImpl project) {
|
||||
void removeProject(WebSessionProjectImpl project) {
|
||||
accessibleProjects.remove(project);
|
||||
}
|
||||
|
||||
void clearProjects() {
|
||||
if (!this.accessibleProjects.isEmpty()) {
|
||||
for (WebProjectImpl project : accessibleProjects) {
|
||||
for (WebSessionProjectImpl project : accessibleProjects) {
|
||||
project.dispose();
|
||||
}
|
||||
this.activeProject = null;
|
||||
|
||||
+15
-4
@@ -36,18 +36,17 @@ import org.jkiss.dbeaver.model.secret.DBSSecretController;
|
||||
import org.jkiss.dbeaver.model.security.SMAdminController;
|
||||
import org.jkiss.dbeaver.model.security.SMController;
|
||||
import org.jkiss.dbeaver.model.security.user.SMAuthPermissions;
|
||||
import org.jkiss.dbeaver.runtime.DBWorkbench;
|
||||
import org.jkiss.utils.CommonUtils;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* Web user context.
|
||||
* Contains user state and services based on available permissions
|
||||
*/
|
||||
//TODO: split to authenticated and non authenticated context
|
||||
public class WebUserContext implements SMCredentialsProvider {
|
||||
private static final Log log = Log.getLog(WebUserContext.class);
|
||||
|
||||
@@ -66,6 +65,7 @@ public class WebUserContext implements SMCredentialsProvider {
|
||||
private RMController rmController;
|
||||
private DBFileController fileController;
|
||||
private Set<String> accessibleProjectIds = new HashSet<>();
|
||||
private final WebSessionPreferenceStore preferenceStore;
|
||||
|
||||
public WebUserContext(WebApplication application, DBPWorkspace workspace) throws DBException {
|
||||
this.application = application;
|
||||
@@ -73,6 +73,7 @@ public class WebUserContext implements SMCredentialsProvider {
|
||||
this.securityController = application.createSecurityController(this);
|
||||
this.rmController = application.createResourceController(this, workspace);
|
||||
this.fileController = application.createFileController(this);
|
||||
this.preferenceStore = new WebSessionPreferenceStore(DBWorkbench.getPlatform().getPreferenceStore());
|
||||
setUserPermissions(getDefaultPermissions());
|
||||
}
|
||||
|
||||
@@ -116,7 +117,13 @@ public class WebUserContext implements SMCredentialsProvider {
|
||||
setRefreshToken(smRefreshToken);
|
||||
setUserPermissions(smAuthPermissions.getPermissions());
|
||||
this.adminSecurityController = application.getAdminSecurityController(this);
|
||||
this.rmController = application.createResourceController(this, workspace);
|
||||
if (isSessionChanged) {
|
||||
if (smAuthPermissions.getUserId() != null) {
|
||||
this.preferenceStore.updateAllUserPreferences(securityController.getCurrentUserParameters());
|
||||
} else {
|
||||
this.preferenceStore.updateAllUserPreferences(Map.of());
|
||||
}
|
||||
this.smSessionId = smAuthPermissions.getSessionId();
|
||||
setUser(smAuthPermissions.getUserId() == null ? null : new WebUser(securityController.getCurrentUser()));
|
||||
refreshAccessibleProjects();
|
||||
@@ -251,4 +258,8 @@ public class WebUserContext implements SMCredentialsProvider {
|
||||
private void setRefreshToken(@Nullable String refreshToken) {
|
||||
this.refreshToken = refreshToken;
|
||||
}
|
||||
|
||||
public WebSessionPreferenceStore getPreferenceStore() {
|
||||
return preferenceStore;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -70,6 +70,7 @@ public class WebUser {
|
||||
return Collections.emptyMap();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public String[] getTeams() {
|
||||
return user.getUserTeams();
|
||||
}
|
||||
|
||||
+9
-1
@@ -80,7 +80,7 @@ public class WebAuthProviderConfiguration {
|
||||
}
|
||||
|
||||
private String buildRedirectUrl(String baseUrl) {
|
||||
return baseUrl + "?" + CBAuthConstants.CB_REDIRECT_URL_REQUEST_PARAM + "=" + WebAppUtils.getWebApplication().getServerURL();
|
||||
return baseUrl + "?" + CBAuthConstants.CB_REDIRECT_URL_REQUEST_PARAM + "=" + WebAppUtils.getFullServerUrl();
|
||||
}
|
||||
|
||||
@Property
|
||||
@@ -109,6 +109,14 @@ public class WebAuthProviderConfiguration {
|
||||
return instance instanceof SMAuthProviderFederated ? ((SMAuthProviderFederated) instance).getAcsLink(getId(), config.getParameters()) : null;
|
||||
}
|
||||
|
||||
@Property
|
||||
public String getEntityIdLink() throws DBException {
|
||||
SMAuthProvider<?> instance = providerDescriptor.getInstance();
|
||||
return instance instanceof SMAuthProviderFederated
|
||||
? ((SMAuthProviderFederated) instance).getEntityIdLink(getId(), config.getParameters())
|
||||
: null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return getDisplayName();
|
||||
|
||||
@@ -0,0 +1,161 @@
|
||||
/*
|
||||
* DBeaver - Universal Database Manager
|
||||
* Copyright (C) 2010-2024 DBeaver Corp and others
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package io.cloudbeaver.server;
|
||||
|
||||
import io.cloudbeaver.DBWConstants;
|
||||
import io.cloudbeaver.model.app.WebApplication;
|
||||
import org.eclipse.core.runtime.Plugin;
|
||||
import org.jkiss.code.NotNull;
|
||||
import org.jkiss.dbeaver.Log;
|
||||
import org.jkiss.dbeaver.model.DBConstants;
|
||||
import org.jkiss.dbeaver.model.app.DBACertificateStorage;
|
||||
import org.jkiss.dbeaver.model.app.DBPWorkspace;
|
||||
import org.jkiss.dbeaver.model.impl.app.DefaultCertificateStorage;
|
||||
import org.jkiss.dbeaver.model.qm.QMRegistry;
|
||||
import org.jkiss.dbeaver.model.qm.QMUtils;
|
||||
import org.jkiss.dbeaver.model.runtime.DBRProgressMonitor;
|
||||
import org.jkiss.dbeaver.registry.BasePlatformImpl;
|
||||
import org.jkiss.dbeaver.registry.DataSourceProviderRegistry;
|
||||
import org.jkiss.dbeaver.runtime.SecurityProviderUtils;
|
||||
import org.jkiss.dbeaver.runtime.qm.QMLogFileWriter;
|
||||
import org.jkiss.dbeaver.runtime.qm.QMRegistryImpl;
|
||||
import org.jkiss.dbeaver.utils.ContentUtils;
|
||||
import org.jkiss.utils.CommonUtils;
|
||||
import org.jkiss.utils.StandardConstants;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
|
||||
public abstract class BaseGQLPlatform extends BasePlatformImpl {
|
||||
private static final Log log = Log.getLog(BaseGQLPlatform.class);
|
||||
public static final String BASE_TEMP_DIR = "dbeaver";
|
||||
|
||||
private Path tempFolder;
|
||||
|
||||
private QMRegistryImpl queryManager;
|
||||
private QMLogFileWriter qmLogWriter;
|
||||
private DBACertificateStorage certificateStorage;
|
||||
private WebGlobalWorkspace workspace;
|
||||
|
||||
@Override
|
||||
protected synchronized void initialize() {
|
||||
// Register BC security provider
|
||||
SecurityProviderUtils.registerSecurityProvider();
|
||||
|
||||
// Register properties adapter
|
||||
this.workspace = new WebGlobalWorkspace(this, (WebApplication) getApplication());
|
||||
this.workspace.initializeProjects();
|
||||
QMUtils.initApplication(this);
|
||||
|
||||
this.queryManager = new QMRegistryImpl();
|
||||
|
||||
this.qmLogWriter = new QMLogFileWriter();
|
||||
this.queryManager.registerMetaListener(qmLogWriter);
|
||||
|
||||
this.certificateStorage = new DefaultCertificateStorage(
|
||||
WebPlatformActivator.getInstance()
|
||||
.getStateLocation()
|
||||
.toFile()
|
||||
.toPath()
|
||||
.resolve(DBConstants.CERTIFICATE_STORAGE_FOLDER));
|
||||
super.initialize();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Plugin getProductPlugin() {
|
||||
return WebPlatformActivator.getInstance();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public DBACertificateStorage getCertificateStorage() {
|
||||
return certificateStorage;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public DBPWorkspace getWorkspace() {
|
||||
return workspace;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public Path getTempFolder(@NotNull DBRProgressMonitor monitor, @NotNull String name) {
|
||||
|
||||
if (tempFolder == null) {
|
||||
synchronized (this) {
|
||||
if (tempFolder == null) {
|
||||
initTempFolder(monitor);
|
||||
}
|
||||
}
|
||||
}
|
||||
Path folder = tempFolder.resolve(name);
|
||||
if (!Files.exists(folder)) {
|
||||
try {
|
||||
Files.createDirectories(folder);
|
||||
} catch (IOException e) {
|
||||
log.error("Error creating temp folder '" + folder.toAbsolutePath() + "'", e);
|
||||
}
|
||||
}
|
||||
return folder;
|
||||
}
|
||||
|
||||
private void initTempFolder(@NotNull DBRProgressMonitor monitor) {
|
||||
// Make temp folder
|
||||
monitor.subTask("Create temp folder");
|
||||
String sysTempFolder = System.getProperty(StandardConstants.ENV_TMP_DIR);
|
||||
if (CommonUtils.isNotEmpty(sysTempFolder)) {
|
||||
tempFolder = Path.of(sysTempFolder).resolve(BASE_TEMP_DIR).resolve(DBWConstants.WORK_DATA_FOLDER_NAME);
|
||||
} else {
|
||||
//we do not use workspace because it can be in external file system
|
||||
tempFolder = getApplication().getHomeDirectory().resolve(DBWConstants.WORK_DATA_FOLDER_NAME);
|
||||
}
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public abstract WebApplication getApplication();
|
||||
|
||||
@Override
|
||||
public synchronized void dispose() {
|
||||
super.dispose();
|
||||
if (this.qmLogWriter != null) {
|
||||
this.queryManager.unregisterMetaListener(qmLogWriter);
|
||||
this.qmLogWriter.dispose();
|
||||
this.qmLogWriter = null;
|
||||
}
|
||||
if (this.queryManager != null) {
|
||||
this.queryManager.dispose();
|
||||
//queryManager = null;
|
||||
}
|
||||
DataSourceProviderRegistry.dispose();
|
||||
|
||||
// Remove temp folder
|
||||
if (tempFolder != null) {
|
||||
|
||||
if (!ContentUtils.deleteFileRecursive(tempFolder.toFile())) {
|
||||
log.warn("Can't delete temp folder '" + tempFolder.toAbsolutePath() + "'");
|
||||
}
|
||||
tempFolder = null;
|
||||
}
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public QMRegistry getQueryManager() {
|
||||
return queryManager;
|
||||
}
|
||||
|
||||
}
|
||||
+2
@@ -87,4 +87,6 @@ public class CBConstants {
|
||||
|
||||
public static final String QUOTA_PROP_FILE_LIMIT = "dataExportFileSizeLimit";
|
||||
public static final String ADMIN_AUTO_GRANT = "auto-grant";
|
||||
public static final String HOST_LOCALHOST = "localhost";
|
||||
public static final String HOST_127_0_0_1 = "127.0.0.1";
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
/*
|
||||
* DBeaver - Universal Database Manager
|
||||
* Copyright (C) 2010-2024 DBeaver Corp and others
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package io.cloudbeaver.server;
|
||||
|
||||
import org.jkiss.code.NotNull;
|
||||
import org.jkiss.code.Nullable;
|
||||
import org.jkiss.dbeaver.Log;
|
||||
import org.jkiss.dbeaver.model.app.DBPDataSourceRegistry;
|
||||
import org.jkiss.dbeaver.model.app.DBPWorkspace;
|
||||
import org.jkiss.dbeaver.model.auth.SMSessionContext;
|
||||
import org.jkiss.dbeaver.model.impl.app.BaseProjectImpl;
|
||||
import org.jkiss.dbeaver.registry.DataSourceRegistry;
|
||||
|
||||
import java.nio.file.Path;
|
||||
|
||||
/**
|
||||
* Web global project.
|
||||
*/
|
||||
public class WebGlobalProject extends BaseProjectImpl {
|
||||
|
||||
private static final Log log = Log.getLog(WebGlobalProject.class);
|
||||
|
||||
private final String projectName;
|
||||
|
||||
public WebGlobalProject(
|
||||
@NotNull DBPWorkspace workspace,
|
||||
@Nullable SMSessionContext sessionContext,
|
||||
@NotNull String projectName
|
||||
) {
|
||||
super(workspace, sessionContext);
|
||||
this.projectName = projectName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isVirtual() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public String getName() {
|
||||
return projectName;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public Path getAbsolutePath() {
|
||||
return getWorkspace().getAbsolutePath().resolve(projectName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isOpen() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void ensureOpen() {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isUseSecretStorage() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
protected DBPDataSourceRegistry createDataSourceRegistry() {
|
||||
return new DataSourceRegistry(this);
|
||||
}
|
||||
}
|
||||
+80
-7
@@ -16,19 +16,92 @@
|
||||
*/
|
||||
package io.cloudbeaver.server;
|
||||
|
||||
import org.eclipse.core.resources.IWorkspace;
|
||||
import io.cloudbeaver.WebProjectImpl;
|
||||
import io.cloudbeaver.model.app.WebApplication;
|
||||
import io.cloudbeaver.utils.WebAppUtils;
|
||||
import org.jkiss.code.NotNull;
|
||||
import org.jkiss.code.Nullable;
|
||||
import org.jkiss.dbeaver.Log;
|
||||
import org.jkiss.dbeaver.model.app.DBPPlatform;
|
||||
import org.jkiss.dbeaver.registry.EclipseWorkspaceImpl;
|
||||
import org.jkiss.dbeaver.model.app.DBPProject;
|
||||
import org.jkiss.dbeaver.model.impl.app.BaseProjectImpl;
|
||||
import org.jkiss.dbeaver.model.impl.app.BaseWorkspaceImpl;
|
||||
import org.jkiss.utils.CommonUtils;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Web global workspace.
|
||||
* <p>
|
||||
* Basically just a wrapper around Eclipse workspace.
|
||||
*/
|
||||
public class WebGlobalWorkspace extends EclipseWorkspaceImpl {
|
||||
public class WebGlobalWorkspace extends BaseWorkspaceImpl {
|
||||
|
||||
public WebGlobalWorkspace(DBPPlatform platform, IWorkspace eclipseWorkspace) {
|
||||
super(platform, eclipseWorkspace);
|
||||
private static final Log log = Log.getLog(WebGlobalWorkspace.class);
|
||||
|
||||
protected final Map<String, WebProjectImpl> projects = new LinkedHashMap<>();
|
||||
private WebGlobalProject globalProject;
|
||||
|
||||
private final WebApplication application;
|
||||
|
||||
public WebGlobalWorkspace(
|
||||
@NotNull DBPPlatform platform,
|
||||
@NotNull WebApplication application
|
||||
) {
|
||||
super(platform, application.getWorkspaceDirectory());
|
||||
this.application = application;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initializeProjects() {
|
||||
initializeWorkspaceSession();
|
||||
|
||||
// Load global project
|
||||
Path globalProjectPath = getAbsolutePath().resolve(WebAppUtils.getGlobalProjectId());
|
||||
if (!Files.exists(globalProjectPath)) {
|
||||
try {
|
||||
Files.createDirectories(globalProjectPath);
|
||||
} catch (IOException e) {
|
||||
log.error("Error creating global project path: " + globalProject, e);
|
||||
}
|
||||
}
|
||||
|
||||
globalProject = new WebGlobalProject(
|
||||
this,
|
||||
getAuthContext(),
|
||||
CommonUtils.notEmpty(WebAppUtils.getWebApplication().getDefaultProjectName())
|
||||
);
|
||||
activeProject = globalProject;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public String getWorkspaceId() {
|
||||
return readWorkspaceIdProperty();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public DBPProject getActiveProject() {
|
||||
return super.getActiveProject();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public List<BaseProjectImpl> getProjects() {
|
||||
return Collections.singletonList(globalProject);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public BaseProjectImpl getProject(@NotNull String projectName) {
|
||||
if (globalProject != null && globalProject.getId().equals(projectName)) {
|
||||
return globalProject;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
-11
@@ -16,8 +16,6 @@
|
||||
*/
|
||||
package io.cloudbeaver.server;
|
||||
|
||||
import org.eclipse.core.resources.IWorkspace;
|
||||
import org.eclipse.core.resources.ResourcesPlugin;
|
||||
import org.eclipse.core.runtime.Plugin;
|
||||
import org.jkiss.dbeaver.ModelPreferences;
|
||||
import org.jkiss.dbeaver.model.impl.preferences.BundlePreferenceStore;
|
||||
@@ -25,7 +23,6 @@ import org.jkiss.dbeaver.model.preferences.DBPPreferenceStore;
|
||||
import org.osgi.framework.Bundle;
|
||||
import org.osgi.framework.BundleContext;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.PrintStream;
|
||||
|
||||
/**
|
||||
@@ -35,7 +32,6 @@ public class WebPlatformActivator extends Plugin {
|
||||
|
||||
// The shared instance
|
||||
private static WebPlatformActivator instance;
|
||||
private static File configDir;
|
||||
private PrintStream debugWriter;
|
||||
private DBPPreferenceStore preferences;
|
||||
|
||||
@@ -76,13 +72,6 @@ public class WebPlatformActivator extends Plugin {
|
||||
return preferences;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the workspace instance.
|
||||
*/
|
||||
public static IWorkspace getWorkspace() {
|
||||
return ResourcesPlugin.getWorkspace();
|
||||
}
|
||||
|
||||
protected void shutdownPlatform() {
|
||||
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user