mirror of
https://github.com/dbeaver/cloudbeaver.git
synced 2026-10-16 14:50:15 +08:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
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 |
@@ -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,56 @@
|
||||
name: Backend Build
|
||||
|
||||
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-backend:
|
||||
name: Backend
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Set up JDK 17
|
||||
uses: actions/setup-java@v4
|
||||
with:
|
||||
distribution: "temurin"
|
||||
java-version: "17"
|
||||
cache: maven
|
||||
|
||||
- uses: stCarolas/setup-maven@v5
|
||||
with:
|
||||
maven-version: 3.9.0
|
||||
|
||||
- name: Give permissions
|
||||
run: |
|
||||
sudo chmod 777 ../
|
||||
shell: bash
|
||||
|
||||
- name: Determine branches
|
||||
id: determine-branch
|
||||
run: |
|
||||
echo "pr_branch=${{ github.head_ref }}" >> $GITHUB_ENV
|
||||
echo "base_branch=${{ github.event.pull_request.base.ref }}" >> $GITHUB_ENV
|
||||
|
||||
- name: Clone dbeaver/dbeaver
|
||||
id: clone-repo
|
||||
run: |
|
||||
git clone -b ${{ env.pr_branch }} https://github.com/dbeaver/dbeaver.git ../dbeaver || git clone -b ${{ env.base_branch }} https://github.com/dbeaver/dbeaver.git ../dbeaver
|
||||
|
||||
- name: Run build script
|
||||
run: ./build-backend.sh
|
||||
shell: bash
|
||||
working-directory: ./deploy
|
||||
|
||||
- name: Archive build artifacts
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: backend-build-artifacts
|
||||
path: deploy/cloudbeaver
|
||||
if-no-files-found: error
|
||||
@@ -0,0 +1,40 @@
|
||||
name: Backend Lint
|
||||
|
||||
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:
|
||||
inputs:
|
||||
skip_cache:
|
||||
required: false
|
||||
type: string
|
||||
|
||||
jobs:
|
||||
lint:
|
||||
name: Backend
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Checkout checkstyle config repository
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
repository: dbeaver/dbeaver
|
||||
path: dbeaver-config
|
||||
|
||||
- name: Copy checkstyle config
|
||||
run: cp dbeaver-config/dbeaver-checkstyle-config.xml ./dbeaver-checkstyle-config.xml
|
||||
|
||||
- name: Remove checkstyle config directory
|
||||
run: rm -rf dbeaver-config
|
||||
|
||||
- uses: dbelyaev/action-checkstyle@master
|
||||
with:
|
||||
github_token: ${{ secrets.GITHUB_TOKEN }}
|
||||
reporter: github-pr-review
|
||||
filter_mode: diff_context
|
||||
checkstyle_config: ./dbeaver-checkstyle-config.xml
|
||||
fail_on_error: true
|
||||
@@ -0,0 +1,57 @@
|
||||
name: Cleanup checks
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
types: [closed]
|
||||
|
||||
jobs:
|
||||
delete-caches:
|
||||
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
|
||||
|
||||
# delete-docker-image:
|
||||
# name: Delete Docker Image
|
||||
# if: github.event.pull_request.merged == true
|
||||
# runs-on: ubuntu-latest
|
||||
|
||||
# steps:
|
||||
# - name: Check out the repository
|
||||
# uses: actions/checkout@v4
|
||||
|
||||
# - name: Set up Docker Buildx
|
||||
# uses: docker/setup-buildx-action@v3
|
||||
|
||||
# - name: Determine Docker Image Tag
|
||||
# run: |
|
||||
# REPO_NAME=$(basename ${{ github.repository }})
|
||||
# IMAGE_NAME=ghcr.io/${{ github.repository_owner }}/$REPO_NAME
|
||||
# BRANCH_NAME=${{ github.event.pull_request.head.ref }}
|
||||
# TAG_NAME=$(echo $BRANCH_NAME | sed 's/[^a-zA-Z0-9._-]/-/g')
|
||||
# echo "image=$IMAGE_NAME:$TAG_NAME" >> $GITHUB_ENV
|
||||
|
||||
# - name: Log in to GitHub Container Registry
|
||||
# run: echo "${{ secrets.GITHUB_TOKEN }}" | docker login ghcr.io -u ${{ github.repository_owner }} --password-stdin
|
||||
|
||||
# - name: Delete Docker Image
|
||||
# run: |
|
||||
# docker rmi ${{ env.image }}
|
||||
# echo "Deleted image: ${{ env.image }}"
|
||||
@@ -0,0 +1,56 @@
|
||||
name: Check
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- devel
|
||||
pull_request:
|
||||
branches:
|
||||
- devel
|
||||
|
||||
# Allows you to run this workflow manually from the Actions tab
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
skip_cache:
|
||||
description: "Skip cache restoration"
|
||||
required: false
|
||||
default: "false"
|
||||
|
||||
jobs:
|
||||
call-backend-build:
|
||||
name: Build
|
||||
uses: ./.github/workflows/backend-build.yml
|
||||
|
||||
call-frontend-build:
|
||||
name: Build
|
||||
uses: ./.github/workflows/frontend-build.yml
|
||||
with:
|
||||
skip_cache: ${{ github.event.inputs.skip_cache }}
|
||||
|
||||
call-frontend-lint:
|
||||
name: Lint
|
||||
needs: call-frontend-build
|
||||
uses: ./.github/workflows/frontend-lint.yml
|
||||
|
||||
call-backend-lint:
|
||||
name: Lint
|
||||
uses: ./.github/workflows/backend-lint.yml
|
||||
|
||||
# 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
|
||||
@@ -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,91 @@
|
||||
name: Frontend Build
|
||||
|
||||
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:
|
||||
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: Frontend
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
outputs:
|
||||
build-status: ${{ steps.build.outcome }}
|
||||
test-status: ${{ steps.test.outcome }}
|
||||
|
||||
defaults:
|
||||
run:
|
||||
working-directory: ./webapp
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- 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'
|
||||
uses: borales/actions-yarn@v5
|
||||
with:
|
||||
dir: webapp
|
||||
cmd: clean
|
||||
|
||||
- name: yarn install --frozen-lockfile
|
||||
uses: borales/actions-yarn@v5
|
||||
with:
|
||||
dir: webapp
|
||||
cmd: install
|
||||
|
||||
- name: build
|
||||
id: build
|
||||
uses: borales/actions-yarn@v5
|
||||
with:
|
||||
dir: webapp/packages/product-default
|
||||
cmd: bundle
|
||||
|
||||
- name: test
|
||||
id: test
|
||||
uses: borales/actions-yarn@v5
|
||||
with:
|
||||
dir: webapp
|
||||
cmd: 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 run this workflow manually from the Actions tab
|
||||
workflow_dispatch:
|
||||
|
||||
# Allows you to reuse workflows by referencing their YAML files
|
||||
workflow_call:
|
||||
|
||||
jobs:
|
||||
lint:
|
||||
name: Frontend
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- 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: yarn install --frozen-lockfile
|
||||
uses: borales/actions-yarn@v5
|
||||
with:
|
||||
dir: webapp
|
||||
cmd: install
|
||||
|
||||
- name: Lint
|
||||
uses: reviewdog/action-eslint@v1
|
||||
with:
|
||||
reporter: github-pr-review
|
||||
filter_mode: file
|
||||
workdir: webapp
|
||||
fail_on_error: true
|
||||
eslint_flags: "--ext .ts,.tsx"
|
||||
@@ -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
|
||||
@@ -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
|
||||
+1
-1
@@ -26,7 +26,7 @@ dist/
|
||||
|
||||
*.iml
|
||||
/idea/
|
||||
.idea/
|
||||
|
||||
|
||||
## Mac OS
|
||||
.DS_Store
|
||||
|
||||
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
-68
@@ -4,7 +4,7 @@
|
||||
{
|
||||
"type": "chrome",
|
||||
"request": "launch",
|
||||
"name": "Chrome",
|
||||
"name": "CloudBeaver CE",
|
||||
"url": "http://localhost:8080",
|
||||
"webRoot": "${workspaceFolder}/..",
|
||||
"outFiles": [
|
||||
@@ -17,75 +17,60 @@
|
||||
},
|
||||
{
|
||||
"type": "java",
|
||||
"name": "CloudBeaver CE",
|
||||
"cwd": "${workspaceFolder}/workspace-dev-ce",
|
||||
"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}/../dbeaver-workspace/products/CloudbeaverServer.product/",
|
||||
"-dev",
|
||||
"file:${workspaceFolder}/../dbeaver-workspace/products/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}/../dbeaver-workspace/products/CloudbeaverServer.product/",
|
||||
"-dev",
|
||||
"file:${workspaceFolder}/../dbeaver-workspace/products/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
+15
-18
@@ -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= -eclipse.version ${eclipse-version} -config ${workspaceFolder}/../idea-workspace-dbeaver/rcp-gen.properties -productFile ${workspaceFolder}/server/product/web-server/CloudbeaverServer.product -projectsFolder ${workspaceFolder}/../ -eclipse ${workspaceFolder}/../dbeaver-workspace/dependencies -output ${workspaceFolder}/../dbeaver-workspace/products/CloudbeaverServer.product"
|
||||
]
|
||||
"command": "./generate_workspace.sh"
|
||||
},
|
||||
"windows": {
|
||||
"args": [
|
||||
"package",
|
||||
"-q",
|
||||
"exec:java",
|
||||
"-Dexec.args= -eclipse.version ${eclipse-version} -config ${workspaceFolder}/../idea-workspace-dbeaver/rcp-gen.properties -productFile ${workspaceFolder}/server/product/web-server/CloudbeaverServer.product -projectsFolder ${workspaceFolder}/../ -eclipse ${workspaceFolder}/../dbeaver-workspace/dependencies -output ${workspaceFolder}/../dbeaver-workspace/products/CloudbeaverServer.product"
|
||||
]
|
||||
"command": "./generate_workspace.cmd"
|
||||
},
|
||||
"options": {
|
||||
"cwd": "${workspaceFolder}/../idea-rcp-launch-config-generator"
|
||||
"cwd": "${workspaceFolder}/../cloudbeaver"
|
||||
},
|
||||
"presentation": {
|
||||
"reveal": "silent",
|
||||
@@ -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,48 +19,49 @@ 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.5. 2024-05-20
|
||||
- The process of application update was improved - you can track the application update process now;
|
||||
- All popup dialogs became available for screen readers, including JAWS, to improve the experience for users with disabilities;
|
||||
### 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:
|
||||
- Large text values (more than 100 Kb) are now automatically opened in the Value panel;
|
||||
- DuckDB:
|
||||
- Spatial data visualization support was added;
|
||||
- The driver has been updated to version 0.10.2;
|
||||
- Different bug fixes and enhancements have been made.
|
||||
- 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:
|
||||
- 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:
|
||||
- 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))
|
||||
|
||||
### 24.0.4. 2024-05-06
|
||||
- Added the ability to stop the process of file upload in the table;
|
||||
- Row count calculation in the grid can be cancelled for Data Editor and SQL Editor;
|
||||
- Added the ability to set null values for BLOB and GIS data via the cell's context menu in the table;
|
||||
- Oracle driver has been updated to version 23.2.0.0.0;
|
||||
- SQLite driver has been updated to version 3.44.1.0;
|
||||
- Different bug fixes and enhancements have been made.
|
||||
## Contribution
|
||||
As a community-driven open-source project, we warmly welcome contributions through GitHub pull requests.
|
||||
|
||||
### 24.0.3. 2024-04-22
|
||||
- Unauthorized access vulnerability was fixed;
|
||||
- Added the ability for users to configure personal settings for the interface, SQL editor, and data viewer through the settings panel;
|
||||
- Added the ability to backup of the internal database before schema migration (for H2);
|
||||
- Different bug fixes and enhancements have been made.
|
||||
|
||||
### 24.0.2. 2024-04-08
|
||||
- Added the ability to import data to the database from CSV file;
|
||||
- Implemented support for utilizing environment variables within connection configurations;
|
||||
- The ability to select a case for column names has been added to CSV export;
|
||||
- A new Apache Kyuubi driver has been added;
|
||||
- Trino driver has been updated to version 438;
|
||||
- Clickhouse driver has been updated to version 0.6.0-patch2;
|
||||
- Have enhanced security of H2 connection;
|
||||
- Different bug fixes and enhancements have been made.
|
||||
|
||||
### 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.
|
||||
[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)
|
||||
|
||||
@@ -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,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: [
|
||||
|
||||
]
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,104 +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"
|
||||
},
|
||||
backupEnabled: "${CLOUDBEAVER_DB_BACKUP_ENABLED:true}"
|
||||
}
|
||||
|
||||
},
|
||||
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"
|
||||
],
|
||||
disabledBetaFeatures: [
|
||||
|
||||
]
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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,99 +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"
|
||||
},
|
||||
backupEnabled: "${CLOUDBEAVER_DB_BACKUP_ENABLED:true}"
|
||||
},
|
||||
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"
|
||||
],
|
||||
disabledBetaFeatures: [
|
||||
|
||||
]
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
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
|
||||
+2
-11
@@ -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,13 +35,9 @@ 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
|
||||
|
||||
|
||||
@@ -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
|
||||
@@ -2,6 +2,9 @@ FROM dbeaver/base-java
|
||||
|
||||
MAINTAINER DBeaver Corp, devops@dbeaver.com
|
||||
|
||||
RUN apt-get update; \
|
||||
apt-get upgrade -y;
|
||||
|
||||
COPY cloudbeaver /opt/cloudbeaver
|
||||
|
||||
EXPOSE 8978
|
||||
|
||||
@@ -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,6 +1,6 @@
|
||||
<?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="../dbeaver-workspace/dependencies/plugins" type="Directory"/>
|
||||
</locations>
|
||||
|
||||
@@ -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.54.qualifier
|
||||
Bundle-Release-Date: 20240603
|
||||
Bundle-Version: 1.0.62.qualifier
|
||||
Bundle-Release-Date: 20241007
|
||||
Bundle-RequiredExecutionEnvironment: JavaSE-17
|
||||
Bundle-ActivationPolicy: lazy
|
||||
Bundle-ClassPath: .
|
||||
@@ -18,7 +18,7 @@ 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,
|
||||
@@ -28,7 +28,9 @@ Export-Package: io.cloudbeaver,
|
||||
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,
|
||||
@@ -41,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.54-SNAPSHOT</version>
|
||||
<version>1.0.62-SNAPSHOT</version>
|
||||
<packaging>eclipse-plugin</packaging>
|
||||
|
||||
</project>
|
||||
|
||||
@@ -16,23 +16,21 @@
|
||||
*/
|
||||
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,21 +38,18 @@ public class BaseWebProjectImpl extends BaseProjectImpl implements RMControllerP
|
||||
@NotNull
|
||||
private final Path path;
|
||||
@NotNull
|
||||
protected final DataSourceFilter dataSourceFilter;
|
||||
private final RMController resourceController;
|
||||
|
||||
public BaseWebProjectImpl(
|
||||
@NotNull DBPWorkspace workspace,
|
||||
@NotNull RMController resourceController,
|
||||
@NotNull SMSessionContext sessionContext,
|
||||
@NotNull RMProject project,
|
||||
@NotNull DataSourceFilter dataSourceFilter
|
||||
@NotNull RMProject project
|
||||
) {
|
||||
super(workspace, sessionContext);
|
||||
this.resourceController = resourceController;
|
||||
this.path = RMUtils.getProjectPath(project);
|
||||
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);
|
||||
}
|
||||
|
||||
+1
-1
@@ -261,7 +261,7 @@ public class WebDataSourceRegistryProxy implements DBPDataSourceRegistry, DataSo
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeAuthProfile(DBAAuthProfile profile) {
|
||||
public void removeAuthProfile(@NotNull DBAAuthProfile profile) {
|
||||
dataSourceRegistry.removeAuthProfile(profile);
|
||||
}
|
||||
|
||||
|
||||
+36
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
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()
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -26,22 +26,22 @@ 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;
|
||||
|
||||
public abstract class WebProjectImpl extends BaseWebProjectImpl {
|
||||
private static final Log log = Log.getLog(WebProjectImpl.class);
|
||||
@NotNull
|
||||
private final DBPPreferenceStore preferenceStore;
|
||||
protected final DBPPreferenceStore preferenceStore;
|
||||
public WebProjectImpl(
|
||||
@NotNull DBPWorkspace workspace,
|
||||
@NotNull RMController resourceController,
|
||||
@NotNull SMSessionContext sessionContext,
|
||||
@NotNull RMProject project,
|
||||
@NotNull DataSourceFilter dataSourceFilter,
|
||||
@NotNull DBPPreferenceStore preferenceStore
|
||||
) {
|
||||
super(workspace, resourceController, sessionContext, project, dataSourceFilter);
|
||||
super(workspace, resourceController, sessionContext, project);
|
||||
this.preferenceStore = preferenceStore;
|
||||
}
|
||||
|
||||
@@ -70,13 +70,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(), preferenceStore),
|
||||
dataSourceFilter
|
||||
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,27 +16,39 @@
|
||||
*/
|
||||
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.websocket.event.WSEventType;
|
||||
import org.jkiss.dbeaver.registry.DataSourceDescriptor;
|
||||
import org.jkiss.dbeaver.runtime.jobs.DisconnectJob;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class WebSessionProjectImpl extends WebProjectImpl {
|
||||
|
||||
private final WebSession webSession;
|
||||
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;
|
||||
|
||||
public WebSessionProjectImpl(
|
||||
@NotNull WebSession webSession,
|
||||
@NotNull RMProject project,
|
||||
@NotNull DataSourceFilter dataSourceFilter
|
||||
@NotNull RMProject project
|
||||
) {
|
||||
super(
|
||||
webSession.getWorkspace(),
|
||||
webSession.getRmController(),
|
||||
webSession.getSessionContext(),
|
||||
project,
|
||||
dataSourceFilter,
|
||||
webSession.getUserPreferenceStore()
|
||||
);
|
||||
this.webSession = webSession;
|
||||
@@ -47,4 +59,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;
|
||||
}
|
||||
}
|
||||
|
||||
+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;
|
||||
|
||||
+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();
|
||||
}
|
||||
}
|
||||
}
|
||||
+5
-2
@@ -18,13 +18,16 @@ package io.cloudbeaver.model.app;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.GsonBuilder;
|
||||
import org.jkiss.code.NotNull;
|
||||
|
||||
/**
|
||||
* 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> {
|
||||
|
||||
protected Gson getGson() {
|
||||
@NotNull
|
||||
public Gson getGson() {
|
||||
return getGsonBuilder().create();
|
||||
}
|
||||
|
||||
|
||||
+2
-30
@@ -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,18 +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.registry.BaseWorkspaceImpl;
|
||||
import org.jkiss.dbeaver.runtime.IVariableResolver;
|
||||
import org.jkiss.dbeaver.utils.GeneralUtils;
|
||||
import org.jkiss.dbeaver.utils.RuntimeUtils;
|
||||
@@ -62,17 +54,10 @@ 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);
|
||||
|
||||
private String instanceId;
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public DBPWorkspace createWorkspace(@NotNull DBPPlatform platform, @NotNull IWorkspace eclipseWorkspace) {
|
||||
return new WebGlobalWorkspace(platform, eclipseWorkspace);
|
||||
}
|
||||
|
||||
@Override
|
||||
public RMController createResourceController(
|
||||
@NotNull SMCredentialsProvider credentialsProvider,
|
||||
@@ -170,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.
|
||||
|
||||
+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) {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
+10
@@ -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,11 @@ public interface WebServerConfigurationController<T extends WebServerConfigurati
|
||||
*/
|
||||
void loadServerConfiguration(Path configPath) throws DBException;
|
||||
|
||||
@NotNull
|
||||
default Map<String, Object> getOriginalConfigurationProperties() {
|
||||
return Map.of();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
Gson getGson();
|
||||
}
|
||||
|
||||
+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();
|
||||
}
|
||||
}
|
||||
}
|
||||
+2
-18
@@ -14,19 +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.SMControllerConfiguration;
|
||||
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;
|
||||
|
||||
@@ -58,18 +54,6 @@ public class CBServerConfig implements WebServerConfiguration {
|
||||
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;
|
||||
}
|
||||
|
||||
+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
|
||||
+14
-1
@@ -14,7 +14,7 @@
|
||||
* 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.model.connection.InternalDatabaseConfig;
|
||||
@@ -63,6 +63,7 @@ public class WebDatabaseConfig implements InternalDatabaseConfig {
|
||||
return user;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public String getPassword() {
|
||||
return password;
|
||||
@@ -84,4 +85,16 @@ public class WebDatabaseConfig implements InternalDatabaseConfig {
|
||||
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;
|
||||
}
|
||||
}
|
||||
+15
-17
@@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+5
-4
@@ -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() {
|
||||
|
||||
+3
-2
@@ -19,6 +19,7 @@ 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];
|
||||
|
||||
+62
-25
@@ -37,6 +37,7 @@ 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;
|
||||
@@ -130,12 +131,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);
|
||||
projectRegistries.put(projectId, project);
|
||||
}
|
||||
return project;
|
||||
@@ -256,18 +252,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);
|
||||
}
|
||||
@@ -581,7 +579,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 +611,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()) {
|
||||
@@ -628,6 +626,9 @@ public class LocalResourceController implements RMController {
|
||||
}
|
||||
Path newTargetPath = getTargetPath(projectId, normalizedNewResourcePath);
|
||||
validateResourcePath(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 +680,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 +768,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()
|
||||
@@ -822,7 +822,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,6 +836,27 @@ public class LocalResourceController implements RMController {
|
||||
}
|
||||
}
|
||||
|
||||
@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;
|
||||
}
|
||||
}
|
||||
|
||||
private void validateResourcePath(String resourcePath) throws DBException {
|
||||
var fullPath = Paths.get(resourcePath);
|
||||
for (Path path : fullPath) {
|
||||
@@ -1089,7 +1110,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);
|
||||
|
||||
@@ -1134,7 +1155,7 @@ public class LocalResourceController implements RMController {
|
||||
);
|
||||
}
|
||||
|
||||
private void fireRmProjectAddEvent(@NotNull RMProject project) throws DBException {
|
||||
private void fireRmProjectAddEvent(@NotNull RMProject project) {
|
||||
RMEventManager.fireEvent(
|
||||
new RMEvent(
|
||||
RMEvent.Action.RESOURCE_ADD,
|
||||
@@ -1264,4 +1285,20 @@ public class LocalResourceController implements RMController {
|
||||
}
|
||||
|
||||
|
||||
private class InternalWebProjectImpl extends BaseWebProjectImpl {
|
||||
public InternalWebProjectImpl(SessionContextImpl sessionContext, RMProject rmProject) {
|
||||
super(
|
||||
LocalResourceController.this.workspace,
|
||||
LocalResourceController.this,
|
||||
sessionContext,
|
||||
rmProject);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
protected DBPDataSourceRegistry createDataSourceRegistry() {
|
||||
return new DataSourceRegistry(this);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+7
-3
@@ -51,14 +51,14 @@ 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();
|
||||
@@ -72,6 +72,7 @@ public abstract class BaseWebSession extends AbstractSessionPersistent {
|
||||
return new WebUserContext(this.application, this.workspace);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public WebSessionWorkspace getWorkspace() {
|
||||
return workspace;
|
||||
}
|
||||
@@ -232,6 +233,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;
|
||||
}
|
||||
}
|
||||
|
||||
+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;
|
||||
}
|
||||
}
|
||||
+75
-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;
|
||||
@@ -68,15 +61,11 @@ 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;
|
||||
@@ -89,6 +78,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 {
|
||||
|
||||
@@ -105,12 +95,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<>();
|
||||
@@ -124,21 +112,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
|
||||
@@ -152,8 +153,8 @@ public class WebSession extends BaseWebSession
|
||||
}
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public DBPProject getSingletonProject() {
|
||||
@Nullable
|
||||
public WebSessionProjectImpl getSingletonProject() {
|
||||
return getWorkspace().getActiveProject();
|
||||
}
|
||||
|
||||
@@ -250,69 +251,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();
|
||||
@@ -333,7 +271,7 @@ public class WebSession extends BaseWebSession
|
||||
this.navigatorModel.dispose();
|
||||
this.navigatorModel = null;
|
||||
}
|
||||
this.connections.clear();
|
||||
this.globalProject = null;
|
||||
|
||||
loadProjects();
|
||||
|
||||
@@ -353,7 +291,6 @@ public class WebSession extends BaseWebSession
|
||||
// No anonymous mode in distributed apps
|
||||
return;
|
||||
}
|
||||
refreshAccessibleConnectionIds();
|
||||
try {
|
||||
RMController controller = getRmController();
|
||||
RMProject[] rmProjects = controller.listAccessibleProjects();
|
||||
@@ -373,57 +310,28 @@ 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) {
|
||||
WebSessionProjectImpl sessionProject;
|
||||
if (project.isGlobal()) {
|
||||
sessionProject = createGlobalProject(project);
|
||||
} else {
|
||||
sessionProject = new WebSessionProjectImpl(this, 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 WebSessionProjectImpl createGlobalProject(RMProject project) {
|
||||
globalProject = new WebSessionGlobalProjectImpl(this, project);
|
||||
globalProject.refreshAccessibleConnectionIds();
|
||||
return globalProject;
|
||||
}
|
||||
|
||||
private void resetSessionCache() throws DBCException {
|
||||
@@ -441,17 +349,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();
|
||||
@@ -465,7 +363,9 @@ public class WebSession extends BaseWebSession
|
||||
authAsAnonymousUser();
|
||||
} else if (getUserId() != null) {
|
||||
userContext.refreshPermissions();
|
||||
refreshAccessibleConnectionIds();
|
||||
if (globalProject != null) {
|
||||
globalProject.refreshAccessibleConnectionIds();
|
||||
}
|
||||
}
|
||||
|
||||
} catch (Exception e) {
|
||||
@@ -474,32 +374,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()) {
|
||||
@@ -558,75 +432,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 {
|
||||
@@ -810,7 +621,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;
|
||||
}
|
||||
@@ -826,7 +637,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);
|
||||
@@ -969,9 +780,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
|
||||
@@ -980,7 +794,7 @@ public class WebSession extends BaseWebSession
|
||||
|
||||
InstanceCreator<DBAAuthCredentials> credTypeAdapter = type -> credentials;
|
||||
Gson credGson = new GsonBuilder()
|
||||
.setLenient()
|
||||
.setStrictness(Strictness.LENIENT)
|
||||
.registerTypeAdapter(credentials.getClass(), credTypeAdapter)
|
||||
.create();
|
||||
|
||||
@@ -1062,12 +876,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);
|
||||
}
|
||||
@@ -1077,18 +896,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();
|
||||
}
|
||||
@@ -1113,10 +938,6 @@ public class WebSession extends BaseWebSession
|
||||
return;
|
||||
}
|
||||
deleteSessionProject(project);
|
||||
var projectConnections = project.getDataSourceRegistry().getDataSources();
|
||||
for (DBPDataSourceContainer c : projectConnections) {
|
||||
removeConnection(new WebConnectionInfo(this, c));
|
||||
}
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@@ -1133,6 +954,11 @@ public class WebSession extends BaseWebSession
|
||||
return getUserContext().getPreferenceStore();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public WebSessionGlobalProjectImpl getGlobalProject() {
|
||||
return globalProject;
|
||||
}
|
||||
|
||||
private class SessionProgressMonitor extends BaseProgressMonitor {
|
||||
@Override
|
||||
public void beginTask(String name, int totalWork) {
|
||||
@@ -1167,15 +993,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;
|
||||
|
||||
+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;
|
||||
|
||||
+1
@@ -46,6 +46,7 @@ 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);
|
||||
|
||||
|
||||
+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,147 @@
|
||||
/*
|
||||
* 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 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 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 WORK_DATA_FOLDER_NAME = ".work-data";
|
||||
|
||||
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);
|
||||
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) {
|
||||
// Make temp folder
|
||||
monitor.subTask("Create temp folder");
|
||||
tempFolder = workspace.getAbsolutePath().resolve(DBWConstants.WORK_DATA_FOLDER_NAME);
|
||||
}
|
||||
if (!Files.exists(tempFolder)) {
|
||||
try {
|
||||
Files.createDirectories(tempFolder);
|
||||
} catch (IOException e) {
|
||||
log.error("Can't create temp directory " + tempFolder, e);
|
||||
}
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
||||
@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);
|
||||
}
|
||||
}
|
||||
+87
-8
@@ -16,23 +16,102 @@
|
||||
*/
|
||||
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.eclipse.core.runtime.Platform;
|
||||
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.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
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;
|
||||
|
||||
public WebGlobalWorkspace(DBPPlatform platform) {
|
||||
super(platform,
|
||||
platform.getApplication().isMultiuser()
|
||||
? Path.of(getWorkspaceURI())
|
||||
: ((WebApplication) platform.getApplication()).getWorkspaceDirectory());
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static URI getWorkspaceURI() {
|
||||
String workspacePath = Platform.getInstanceLocation().getURL().toString();
|
||||
try {
|
||||
return new URI(workspacePath);
|
||||
} catch (URISyntaxException e) {
|
||||
throw new IllegalStateException("Workspace path is invalid: " + workspacePath, e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String initWorkspaceId() {
|
||||
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.getId().equals(projectName)) {
|
||||
return globalProject;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
-9
@@ -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;
|
||||
@@ -76,13 +74,6 @@ public class WebPlatformActivator extends Plugin {
|
||||
return preferences;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the workspace instance.
|
||||
*/
|
||||
public static IWorkspace getWorkspace() {
|
||||
return ResourcesPlugin.getWorkspace();
|
||||
}
|
||||
|
||||
protected void shutdownPlatform() {
|
||||
|
||||
}
|
||||
|
||||
+3
@@ -23,6 +23,9 @@ import org.jkiss.dbeaver.DBException;
|
||||
* Servlet service
|
||||
*/
|
||||
public interface DBWServiceBindingServlet<APPLICATION extends WebApplication> extends DBWServiceBinding {
|
||||
default boolean isApplicable(WebApplication application) {
|
||||
return true;
|
||||
}
|
||||
|
||||
void addServlets(APPLICATION application, DBWServletContext servletContext) throws DBException;
|
||||
}
|
||||
|
||||
+2
@@ -18,6 +18,7 @@ package io.cloudbeaver.service;
|
||||
|
||||
import io.cloudbeaver.model.app.WebAppConfiguration;
|
||||
import io.cloudbeaver.model.app.WebApplication;
|
||||
import io.cloudbeaver.model.app.WebServerConfiguration;
|
||||
import io.cloudbeaver.model.session.WebSession;
|
||||
import org.jkiss.code.NotNull;
|
||||
import org.jkiss.code.Nullable;
|
||||
@@ -31,6 +32,7 @@ public interface DBWServiceServerConfigurator extends DBWServiceBinding {
|
||||
void configureServer(
|
||||
@NotNull WebApplication application,
|
||||
@Nullable WebSession session,
|
||||
@NotNull WebServerConfiguration serverConfiguration,
|
||||
@NotNull WebAppConfiguration appConfig
|
||||
) throws DBException;
|
||||
|
||||
|
||||
-1
@@ -22,7 +22,6 @@ package io.cloudbeaver.service.sql;
|
||||
public class WebSQLConstants {
|
||||
|
||||
public static final String QUOTA_PROP_ROW_LIMIT = "sqlResultSetRowsLimit";
|
||||
public static final String QUOTA_PROP_MEMORY_LIMIT = "sqlResultSetMemoryLimit";
|
||||
public static final String QUOTA_PROP_QUERY_LIMIT = "sqlMaxRunningQueries";
|
||||
public static final String QUOTA_PROP_SQL_QUERY_TIMEOUT = "sqlQueryTimeout";
|
||||
public static final String QUOTA_PROP_TEXT_PREVIEW_MAX_LENGTH = "sqlTextPreviewMaxLength";
|
||||
|
||||
+9
@@ -22,6 +22,7 @@ import org.jkiss.dbeaver.model.DBPDataSource;
|
||||
import org.jkiss.dbeaver.model.DBUtils;
|
||||
import org.jkiss.dbeaver.model.data.DBDAttributeBinding;
|
||||
import org.jkiss.dbeaver.model.data.DBDRowIdentifier;
|
||||
import org.jkiss.dbeaver.model.exec.trace.DBCTrace;
|
||||
import org.jkiss.dbeaver.model.struct.*;
|
||||
|
||||
import java.util.HashSet;
|
||||
@@ -38,6 +39,7 @@ public class WebSQLResultsInfo {
|
||||
@NotNull
|
||||
private final String id;
|
||||
private DBDAttributeBinding[] attributes;
|
||||
private DBCTrace trace;
|
||||
private String queryText;
|
||||
|
||||
public WebSQLResultsInfo(@NotNull DBSDataContainer dataContainer, @NotNull String id) {
|
||||
@@ -132,4 +134,11 @@ public class WebSQLResultsInfo {
|
||||
(!(entity instanceof DBSDocumentContainer) && !entity.getDataSource().getInfo().isDynamicMetadata());
|
||||
}
|
||||
|
||||
public DBCTrace getTrace() {
|
||||
return trace;
|
||||
}
|
||||
|
||||
public void setTrace(@NotNull DBCTrace trace) {
|
||||
this.trace = trace;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,124 @@
|
||||
/*
|
||||
* 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.test;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.GsonBuilder;
|
||||
import org.jkiss.code.NotNull;
|
||||
import org.jkiss.code.Nullable;
|
||||
import org.jkiss.dbeaver.DBException;
|
||||
import org.jkiss.dbeaver.model.data.json.JSONUtils;
|
||||
import org.jkiss.dbeaver.utils.GeneralUtils;
|
||||
|
||||
import java.net.URI;
|
||||
import java.net.http.HttpClient;
|
||||
import java.net.http.HttpRequest;
|
||||
import java.net.http.HttpResponse;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* GraphQL client for tests.
|
||||
*/
|
||||
public class WebGQLClient {
|
||||
private static final Gson gson = new GsonBuilder()
|
||||
.setPrettyPrinting()
|
||||
.create();
|
||||
public static final String GQL_AUTHENTICATE = """
|
||||
query authLogin($provider: ID!, $configuration: ID, $credentials: Object, $linkUser: Boolean, $forceSessionsLogout: Boolean) {
|
||||
result: authLogin(
|
||||
provider: $provider
|
||||
configuration: $configuration
|
||||
credentials: $credentials
|
||||
linkUser: $linkUser
|
||||
forceSessionsLogout: $forceSessionsLogout
|
||||
) {
|
||||
authId
|
||||
authStatus
|
||||
}
|
||||
}""";
|
||||
|
||||
@NotNull
|
||||
private final HttpClient httpClient;
|
||||
@NotNull
|
||||
private final String apiUrl;
|
||||
|
||||
public WebGQLClient(@NotNull HttpClient httpClient, @NotNull String apiUrl) {
|
||||
this.httpClient = httpClient;
|
||||
this.apiUrl = apiUrl;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends GraphQL request without additional headers
|
||||
*
|
||||
* @param query GraphQL query
|
||||
* @param variables GraphQL query variables
|
||||
* @return GraphQL response
|
||||
*/
|
||||
@NotNull
|
||||
public <T> T sendQuery(@NotNull String query, @Nullable Map<String, Object> variables) throws Exception {
|
||||
return sendQueryWithHeaders(query, variables, List.of());
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends GraphQL request without additional headers
|
||||
*
|
||||
* @param query GraphQL query
|
||||
* @param variables GraphQL query variables
|
||||
* @param headers HTTP request headers
|
||||
* @return GraphQL response
|
||||
*/
|
||||
@NotNull
|
||||
public <T> T sendQueryWithHeaders(
|
||||
@NotNull String query,
|
||||
@Nullable Map<String, Object> variables,
|
||||
@NotNull List<String> headers
|
||||
) throws Exception {
|
||||
HttpRequest.Builder requestBuilder = HttpRequest.newBuilder()
|
||||
.uri(URI.create(apiUrl))
|
||||
.POST(HttpRequest.BodyPublishers.ofString(makeGQLRequest(query, variables)))
|
||||
.setHeader("TE-Client-Version", GeneralUtils.getMajorVersion())
|
||||
.header("Content-Type", "application/json");
|
||||
if (!headers.isEmpty()) {
|
||||
requestBuilder.headers(headers.toArray(String[]::new));
|
||||
}
|
||||
HttpRequest request = requestBuilder.build();
|
||||
HttpResponse<String> response = httpClient.send(request, HttpResponse.BodyHandlers.ofString());
|
||||
Map<String, Object> body = gson.fromJson(
|
||||
response.body(),
|
||||
JSONUtils.MAP_TYPE_TOKEN
|
||||
);
|
||||
if (body.containsKey("errors")) {
|
||||
String message = JSONUtils.getString(JSONUtils.getObjectList(body, "errors").get(0), "message");
|
||||
throw new DBException(message);
|
||||
}
|
||||
// graphql response will be in "data" key
|
||||
return (T) JSONUtils.getObject(body, "data").get("result");
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private String makeGQLRequest(@NotNull String text, @Nullable Map<String, Object> variables) {
|
||||
Map<String, Object> request = new LinkedHashMap<>();
|
||||
request.put("query", text);
|
||||
if (variables != null && !variables.isEmpty()) {
|
||||
request.put("variables", variables);
|
||||
}
|
||||
|
||||
return gson.toJson(request, Map.class);
|
||||
}
|
||||
}
|
||||
@@ -38,6 +38,8 @@ import org.jkiss.utils.CommonUtils;
|
||||
|
||||
import java.nio.file.Path;
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
public class WebAppUtils {
|
||||
private static final Log log = Log.getLog(WebAppUtils.class);
|
||||
@@ -199,7 +201,7 @@ public class WebAppUtils {
|
||||
path = path.concat("; SameSite=" + sameSite);
|
||||
}
|
||||
}
|
||||
|
||||
sessionCookie.setHttpOnly(true);
|
||||
sessionCookie.setPath(path);
|
||||
response.addCookie(sessionCookie);
|
||||
}
|
||||
@@ -269,4 +271,13 @@ public class WebAppUtils {
|
||||
}
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static String getFullServerUrl() {
|
||||
WebApplication application = WebAppUtils.getWebApplication();
|
||||
return Stream.of(application.getServerURL(), application.getRootURI())
|
||||
.map(WebAppUtils::removeSideSlashes)
|
||||
.filter(CommonUtils::isNotEmpty)
|
||||
.collect(Collectors.joining("/"));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+42
-5
@@ -18,9 +18,9 @@ package io.cloudbeaver.utils;
|
||||
|
||||
import io.cloudbeaver.DBWConstants;
|
||||
import io.cloudbeaver.DBWebException;
|
||||
import io.cloudbeaver.WebSessionProjectImpl;
|
||||
import io.cloudbeaver.model.WebConnectionInfo;
|
||||
import io.cloudbeaver.model.WebNetworkHandlerConfigInput;
|
||||
import io.cloudbeaver.model.app.WebApplication;
|
||||
import io.cloudbeaver.model.session.WebSession;
|
||||
import org.jkiss.code.NotNull;
|
||||
import org.jkiss.code.Nullable;
|
||||
@@ -37,6 +37,7 @@ import org.jkiss.utils.CommonUtils;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
|
||||
public class WebDataSourceUtils {
|
||||
|
||||
@@ -97,6 +98,10 @@ public class WebDataSourceUtils {
|
||||
private static void setSecureProperties(DBWHandlerConfiguration handlerConfig, WebNetworkHandlerConfigInput cfgInput, boolean ignoreNulls) {
|
||||
var secureProperties = cfgInput.getSecureProperties();
|
||||
if (secureProperties == null) {
|
||||
if (!handlerConfig.isSavePassword()) {
|
||||
// clear all secure properties from handler config
|
||||
handlerConfig.setSecureProperties(Map.of());
|
||||
}
|
||||
return;
|
||||
}
|
||||
for (var pr : secureProperties.entrySet()) {
|
||||
@@ -109,14 +114,22 @@ public class WebDataSourceUtils {
|
||||
|
||||
@Nullable
|
||||
public static DBPDataSourceContainer getLocalOrGlobalDataSource(
|
||||
WebApplication application, WebSession webSession, @Nullable String projectId, String connectionId
|
||||
WebSession webSession, @Nullable String projectId, String connectionId
|
||||
) throws DBWebException {
|
||||
DBPDataSourceContainer dataSource = null;
|
||||
if (!CommonUtils.isEmpty(connectionId)) {
|
||||
dataSource = webSession.getProjectById(projectId).getDataSourceRegistry().getDataSource(connectionId);
|
||||
if (dataSource == null && (webSession.hasPermission(DBWConstants.PERMISSION_ADMIN) || application.isConfigurationMode())) {
|
||||
WebSessionProjectImpl project = webSession.getProjectById(projectId);
|
||||
if (project == null) {
|
||||
throw new DBWebException("Project '" + projectId + "' not found");
|
||||
}
|
||||
dataSource = project.getDataSourceRegistry().getDataSource(connectionId);
|
||||
if (dataSource == null &&
|
||||
(webSession.hasPermission(DBWConstants.PERMISSION_ADMIN) || webSession.getApplication().isConfigurationMode())) {
|
||||
// If called for new connection in admin mode then this connection may absent in session registry yet
|
||||
dataSource = getGlobalDataSourceRegistry().getDataSource(connectionId);
|
||||
project = webSession.getGlobalProject();
|
||||
if (project != null) {
|
||||
dataSource = project.getDataSourceRegistry().getDataSource(connectionId);
|
||||
}
|
||||
}
|
||||
}
|
||||
return dataSource;
|
||||
@@ -153,4 +166,28 @@ public class WebDataSourceUtils {
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* The method that seeks for web connection in session cache by connection id.
|
||||
* Mostly used when project id is not defined.
|
||||
*/
|
||||
@NotNull
|
||||
public static WebConnectionInfo getWebConnectionInfo(
|
||||
@NotNull WebSession webSession,
|
||||
@Nullable String projectId,
|
||||
@NotNull String connectionId
|
||||
) throws DBWebException {
|
||||
if (projectId == null) {
|
||||
webSession.addWarningMessage("Project id is not defined in request. Try to find it from connection cache");
|
||||
// try to find connection in all accessible projects
|
||||
Optional<WebConnectionInfo> optional = webSession.getAccessibleProjects().stream()
|
||||
.flatMap(p -> p.getConnections().stream()) // get connection cache from web projects
|
||||
.filter(e -> e.getId().contains(connectionId))
|
||||
.findFirst();
|
||||
if (optional.isPresent()) {
|
||||
return optional.get();
|
||||
}
|
||||
}
|
||||
return webSession.getAccessibleProjectById(projectId).getWebConnectionInfo(connectionId);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,62 +17,15 @@
|
||||
|
||||
package io.cloudbeaver.utils;
|
||||
|
||||
import com.google.gson.GsonBuilder;
|
||||
import com.google.gson.reflect.TypeToken;
|
||||
import org.jkiss.dbeaver.model.data.json.JSONUtils;
|
||||
import org.jkiss.dbeaver.utils.GeneralUtils;
|
||||
|
||||
import java.io.File;
|
||||
import java.net.URI;
|
||||
import java.net.http.HttpClient;
|
||||
import java.net.http.HttpRequest;
|
||||
import java.net.http.HttpResponse;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public class WebTestUtils {
|
||||
|
||||
public static final String GQL_TEMPLATE_AUTH_LOGIN = "authLogin.json";
|
||||
|
||||
public static String readScriptTemplate(String templateName, Path scriptsPath) throws Exception {
|
||||
Path templatePath = new File(String.valueOf(scriptsPath), templateName).toPath();
|
||||
return Files.readString(templatePath);
|
||||
}
|
||||
|
||||
|
||||
public static Map<String, Object> doPost(String apiUrl, String input, HttpClient client) throws Exception {
|
||||
return doPostWithHeaders(apiUrl, input, client, List.of());
|
||||
}
|
||||
|
||||
public static Map<String, Object> doPostWithHeaders(
|
||||
String apiUrl,
|
||||
String input,
|
||||
HttpClient client,
|
||||
List<String> headers
|
||||
) throws Exception {
|
||||
HttpRequest.Builder requestBuilder = HttpRequest.newBuilder()
|
||||
.uri(URI.create(apiUrl))
|
||||
.POST(HttpRequest.BodyPublishers.ofString(input))
|
||||
.setHeader("TE-Client-Version", GeneralUtils.getMajorVersion())
|
||||
.header("Content-Type", "application/json");
|
||||
|
||||
if (!headers.isEmpty()) {
|
||||
requestBuilder.headers(headers.toArray(String[]::new));
|
||||
}
|
||||
HttpRequest request = requestBuilder.build();
|
||||
HttpResponse<String> response = client.send(request,
|
||||
HttpResponse.BodyHandlers.ofString());
|
||||
|
||||
return new GsonBuilder().create().fromJson(
|
||||
response.body(),
|
||||
new TypeToken<Map<String, Object>>() {
|
||||
}.getType()
|
||||
);
|
||||
}
|
||||
|
||||
public static boolean getServerStatus(HttpClient client, String apiUrl) {
|
||||
HttpRequest request = HttpRequest.newBuilder()
|
||||
.uri(URI.create(apiUrl))
|
||||
@@ -86,14 +39,4 @@ public class WebTestUtils {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public static Map<String, Object> authenticateUser(HttpClient client, Path scriptsPath, String apiUrl) throws Exception {
|
||||
String input = WebTestUtils.readScriptTemplate(GQL_TEMPLATE_AUTH_LOGIN, scriptsPath);
|
||||
Map<String, Object> map = WebTestUtils.doPost(apiUrl, input, client);
|
||||
Map<String, Object> data = JSONUtils.getObjectOrNull(map, "data");
|
||||
if (data != null) {
|
||||
return JSONUtils.getObjectOrNull(data, "authInfo");
|
||||
}
|
||||
return Collections.emptyMap();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,8 +3,8 @@ Bundle-ManifestVersion: 2
|
||||
Bundle-Vendor: DBeaver Corp
|
||||
Bundle-Name: Cloudbeaver Community Product
|
||||
Bundle-SymbolicName: io.cloudbeaver.product.ce;singleton:=true
|
||||
Bundle-Version: 24.1.0.qualifier
|
||||
Bundle-Release-Date: 20240603
|
||||
Bundle-Version: 24.2.2.qualifier
|
||||
Bundle-Release-Date: 20241007
|
||||
Bundle-RequiredExecutionEnvironment: JavaSE-17
|
||||
Bundle-ActivationPolicy: lazy
|
||||
Bundle-ClassPath: .
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
<relativePath>../</relativePath>
|
||||
</parent>
|
||||
<artifactId>io.cloudbeaver.product.ce</artifactId>
|
||||
<version>24.1.0-SNAPSHOT</version>
|
||||
<version>24.2.2-SNAPSHOT</version>
|
||||
<packaging>eclipse-plugin</packaging>
|
||||
|
||||
</project>
|
||||
|
||||
@@ -2,8 +2,8 @@ Manifest-Version: 1.0
|
||||
Bundle-ManifestVersion: 2
|
||||
Bundle-Name: Base JDBC drivers
|
||||
Bundle-SymbolicName: io.cloudbeaver.resources.drivers.base;singleton:=true
|
||||
Bundle-Version: 1.0.99.qualifier
|
||||
Bundle-Release-Date: 20240603
|
||||
Bundle-Version: 1.0.107.qualifier
|
||||
Bundle-Release-Date: 20241007
|
||||
Bundle-Vendor: DBeaver Corp
|
||||
Bundle-ActivationPolicy: lazy
|
||||
Automatic-Module-Name: io.cloudbeaver.resources.drivers.base
|
||||
|
||||
@@ -9,6 +9,6 @@
|
||||
<relativePath>../</relativePath>
|
||||
</parent>
|
||||
<artifactId>io.cloudbeaver.resources.drivers.base</artifactId>
|
||||
<version>1.0.99-SNAPSHOT</version>
|
||||
<version>1.0.107-SNAPSHOT</version>
|
||||
<packaging>eclipse-plugin</packaging>
|
||||
</project>
|
||||
|
||||
@@ -3,14 +3,13 @@ Bundle-ManifestVersion: 2
|
||||
Bundle-Vendor: DBeaver Corp
|
||||
Bundle-Name: Cloudbeaver Web Server
|
||||
Bundle-SymbolicName: io.cloudbeaver.server;singleton:=true
|
||||
Bundle-Version: 24.1.0.qualifier
|
||||
Bundle-Release-Date: 20240603
|
||||
Bundle-Version: 24.2.2.qualifier
|
||||
Bundle-Release-Date: 20241007
|
||||
Bundle-RequiredExecutionEnvironment: JavaSE-17
|
||||
Bundle-ActivationPolicy: lazy
|
||||
Bundle-Activator: io.cloudbeaver.server.CBPlatformActivator
|
||||
Bundle-ClassPath: .
|
||||
Require-Bundle: org.eclipse.core.runtime;visibility:=reexport,
|
||||
org.eclipse.core.resources;visibility:=reexport,
|
||||
org.apache.commons.jexl,
|
||||
org.jkiss.utils;visibility:=reexport,
|
||||
org.jkiss.dbeaver.model.sql;visibility:=reexport,
|
||||
@@ -29,8 +28,11 @@ Export-Package: io.cloudbeaver,
|
||||
io.cloudbeaver.server.events,
|
||||
io.cloudbeaver.server,
|
||||
io.cloudbeaver.server.actions,
|
||||
io.cloudbeaver.server.jetty,
|
||||
io.cloudbeaver.server.graphql,
|
||||
io.cloudbeaver.server.jobs,
|
||||
io.cloudbeaver.server.servlets,
|
||||
io.cloudbeaver.server.websockets,
|
||||
io.cloudbeaver.service,
|
||||
io.cloudbeaver.service.navigator,
|
||||
io.cloudbeaver.service.session,
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
<relativePath>../</relativePath>
|
||||
</parent>
|
||||
<artifactId>io.cloudbeaver.server</artifactId>
|
||||
<version>24.1.0-SNAPSHOT</version>
|
||||
<version>24.2.2-SNAPSHOT</version>
|
||||
<packaging>eclipse-plugin</packaging>
|
||||
|
||||
</project>
|
||||
|
||||
@@ -143,11 +143,13 @@ type ServerConfig {
|
||||
|
||||
licenseRequired: Boolean!
|
||||
licenseValid: Boolean!
|
||||
licenseStatus: String @since(version: "24.1.5")
|
||||
|
||||
sessionExpireTime: Int!
|
||||
localHostAddress: String
|
||||
|
||||
configurationMode: Boolean!
|
||||
# initializationMode: Boolean! @since(version: "24.1.5")
|
||||
developmentMode: Boolean!
|
||||
redirectOnFederatedAuth: Boolean!
|
||||
distributed: Boolean!
|
||||
@@ -229,6 +231,7 @@ type DriverInfo {
|
||||
enabled: Boolean!
|
||||
requiresServerName: Boolean
|
||||
requiresDatabaseName: Boolean
|
||||
useCustomPage: Boolean! @since(version: "24.1.2") # if host, port, database, server name fields are custom
|
||||
|
||||
licenseRequired: Boolean
|
||||
license: String
|
||||
@@ -248,6 +251,10 @@ type DriverInfo {
|
||||
# Driver parameters (map name->value)
|
||||
driverParameters: Object!
|
||||
|
||||
# Main driver properties
|
||||
# Contains info about main fields (host, port, database, server name)
|
||||
mainProperties: [ObjectPropertyInfo!]! @since(version: "24.1.2")
|
||||
|
||||
# Additional driver provider properties
|
||||
# These properties can be configured by user on main connection page
|
||||
# to provide important connection settings
|
||||
@@ -340,7 +347,10 @@ type ConnectionInfo {
|
||||
databaseName: String
|
||||
url: String
|
||||
|
||||
mainPropertyValues: Object @since(version: "24.1.2")
|
||||
|
||||
keepAliveInterval: Int!
|
||||
autocommit: Boolean
|
||||
|
||||
properties: Object
|
||||
|
||||
@@ -393,6 +403,8 @@ type ConnectionInfo {
|
||||
|
||||
projectId: ID!
|
||||
requiredAuth: String
|
||||
|
||||
tools: [String!]! @since(version: "24.1.3")
|
||||
}
|
||||
|
||||
type ConnectionFolderInfo {
|
||||
@@ -498,6 +510,10 @@ input ConnectionConfig {
|
||||
port: String
|
||||
serverName: String
|
||||
databaseName: String
|
||||
|
||||
# Host, port, serverName, databaseName are also stored in mainPropertyValues for custom pages
|
||||
mainPropertyValues: Object @since(version: "24.1.2")
|
||||
|
||||
# Connection url jdbc:{driver}://{host}[:{port}]/[{database}]
|
||||
url: String
|
||||
# Properties
|
||||
@@ -505,6 +521,7 @@ input ConnectionConfig {
|
||||
|
||||
# Keep-Alive interval
|
||||
keepAliveInterval: Int
|
||||
autocommit: Boolean
|
||||
|
||||
# Template connection
|
||||
template: Boolean
|
||||
|
||||
@@ -120,6 +120,7 @@ type SQLResultSet {
|
||||
# can't update data or load LOB file if hasRowIdentifier = false
|
||||
hasRowIdentifier: Boolean!
|
||||
hasChildrenCollection: Boolean!
|
||||
hasDynamicTrace: Boolean! @since(version: "24.1.2")
|
||||
isSupportsDataFilter: Boolean!
|
||||
}
|
||||
|
||||
@@ -211,6 +212,16 @@ type SQLScriptQuery {
|
||||
start: Int!
|
||||
end: Int!
|
||||
}
|
||||
|
||||
####################################################
|
||||
# Dynamic trace info
|
||||
####################################################
|
||||
type DynamicTraceProperty {
|
||||
name: String!
|
||||
value: String
|
||||
description: String
|
||||
}
|
||||
|
||||
####################################################
|
||||
# Query and Mutation
|
||||
####################################################
|
||||
@@ -402,6 +413,14 @@ extend type Mutation {
|
||||
|
||||
asyncSqlRowDataCountResult(taskId: ID!): Int!
|
||||
|
||||
@since(version: "24.1.2")
|
||||
sqlGetDynamicTrace(
|
||||
projectId: ID,
|
||||
connectionId: ID!,
|
||||
contextId: ID!,
|
||||
resultsId: ID!
|
||||
): [DynamicTraceProperty!]!
|
||||
|
||||
@since(version: "24.0.1")
|
||||
asyncSqlSetAutoCommit(
|
||||
projectId: ID!,
|
||||
|
||||
+27
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
/**
|
||||
* "Access denied" exception
|
||||
*/
|
||||
public class DBWebExceptionServerNotInitialized extends DBWebException {
|
||||
|
||||
public DBWebExceptionServerNotInitialized(String message) {
|
||||
super(message, ERROR_CODE_SERVER_NOT_INITIALIZED);
|
||||
}
|
||||
}
|
||||
@@ -33,4 +33,6 @@ public @interface WebAction {
|
||||
|
||||
boolean authRequired() default true;
|
||||
|
||||
boolean initializationRequired() default true;
|
||||
|
||||
}
|
||||
|
||||
@@ -21,19 +21,23 @@ import com.google.gson.GsonBuilder;
|
||||
import com.google.gson.InstanceCreator;
|
||||
import io.cloudbeaver.model.WebConnectionConfig;
|
||||
import io.cloudbeaver.model.WebNetworkHandlerConfigInput;
|
||||
import io.cloudbeaver.model.WebPropertyInfo;
|
||||
import io.cloudbeaver.model.config.CBAppConfig;
|
||||
import io.cloudbeaver.model.session.WebActionParameters;
|
||||
import io.cloudbeaver.model.session.WebSession;
|
||||
import io.cloudbeaver.registry.WebAuthProviderDescriptor;
|
||||
import io.cloudbeaver.registry.WebAuthProviderRegistry;
|
||||
import io.cloudbeaver.server.CBAppConfig;
|
||||
import io.cloudbeaver.server.CBApplication;
|
||||
import io.cloudbeaver.utils.WebAppUtils;
|
||||
import io.cloudbeaver.server.CBPlatform;
|
||||
import io.cloudbeaver.service.navigator.WebPropertyFilter;
|
||||
import io.cloudbeaver.utils.WebCommonUtils;
|
||||
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.DBConstants;
|
||||
import org.jkiss.dbeaver.model.DBPDataSourceContainer;
|
||||
import org.jkiss.dbeaver.model.DBPObject;
|
||||
import org.jkiss.dbeaver.model.access.DBAAuthCredentials;
|
||||
import org.jkiss.dbeaver.model.app.DBPDataSourceRegistry;
|
||||
import org.jkiss.dbeaver.model.app.DBPProject;
|
||||
@@ -44,6 +48,7 @@ import org.jkiss.dbeaver.model.navigator.DBNBrowseSettings;
|
||||
import org.jkiss.dbeaver.model.navigator.DBNModel;
|
||||
import org.jkiss.dbeaver.model.navigator.DBNProject;
|
||||
import org.jkiss.dbeaver.model.net.DBWHandlerConfiguration;
|
||||
import org.jkiss.dbeaver.model.preferences.DBPPropertyDescriptor;
|
||||
import org.jkiss.dbeaver.model.rm.RMProjectType;
|
||||
import org.jkiss.dbeaver.registry.DataSourceDescriptor;
|
||||
import org.jkiss.dbeaver.registry.DataSourceNavigatorSettings;
|
||||
@@ -52,10 +57,12 @@ import org.jkiss.dbeaver.registry.DataSourceProviderRegistry;
|
||||
import org.jkiss.dbeaver.registry.driver.DriverDescriptor;
|
||||
import org.jkiss.dbeaver.registry.network.NetworkHandlerDescriptor;
|
||||
import org.jkiss.dbeaver.registry.network.NetworkHandlerRegistry;
|
||||
import org.jkiss.dbeaver.runtime.properties.PropertyCollector;
|
||||
import org.jkiss.utils.CommonUtils;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* Various constants
|
||||
@@ -90,10 +97,6 @@ public class WebServiceUtils extends WebCommonUtils {
|
||||
return WebDataSourceUtils.getGlobalDataSourceRegistry();
|
||||
}
|
||||
|
||||
public static DBPDataSourceRegistry getGlobalRegistry(WebSession session) {
|
||||
return session.getProjectById(WebAppUtils.getGlobalProjectId()).getDataSourceRegistry();
|
||||
}
|
||||
|
||||
public static InputStream openStaticResource(String path) {
|
||||
return WebServiceUtils.class.getClassLoader().getResourceAsStream(path);
|
||||
}
|
||||
@@ -150,22 +153,7 @@ public class WebServiceUtils extends WebCommonUtils {
|
||||
}
|
||||
|
||||
public static void setConnectionConfiguration(DBPDriver driver, DBPConnectionConfiguration dsConfig, WebConnectionConfig config) {
|
||||
if (!CommonUtils.isEmpty(config.getUrl())) {
|
||||
dsConfig.setUrl(config.getUrl());
|
||||
} else {
|
||||
if (config.getHost() != null) {
|
||||
dsConfig.setHostName(config.getHost());
|
||||
}
|
||||
if (config.getPort() != null) {
|
||||
dsConfig.setHostPort(config.getPort());
|
||||
}
|
||||
if (config.getDatabaseName() != null) {
|
||||
dsConfig.setDatabaseName(config.getDatabaseName());
|
||||
}
|
||||
if (config.getServerName() != null) {
|
||||
dsConfig.setServerName(config.getServerName());
|
||||
}
|
||||
}
|
||||
setMainProperties(dsConfig, config);
|
||||
if (config.getProperties() != null) {
|
||||
Map<String, String> newProps = new LinkedHashMap<>();
|
||||
for (Map.Entry<String, Object> pe : config.getProperties().entrySet()) {
|
||||
@@ -185,6 +173,9 @@ public class WebServiceUtils extends WebCommonUtils {
|
||||
if (config.getKeepAliveInterval() >= 0) {
|
||||
dsConfig.setKeepAliveInterval(config.getKeepAliveInterval());
|
||||
}
|
||||
if (config.isDefaultAutoCommit() != null) {
|
||||
dsConfig.getBootstrap().setDefaultAutoCommit(config.isDefaultAutoCommit());
|
||||
}
|
||||
// Save provider props
|
||||
if (config.getProviderProperties() != null) {
|
||||
dsConfig.setProviderProperties(new LinkedHashMap<>());
|
||||
@@ -219,6 +210,40 @@ public class WebServiceUtils extends WebCommonUtils {
|
||||
}
|
||||
}
|
||||
|
||||
private static void setMainProperties(DBPConnectionConfiguration dsConfig, WebConnectionConfig config) {
|
||||
if (CommonUtils.isNotEmpty(config.getUrl())) {
|
||||
dsConfig.setUrl(config.getUrl());
|
||||
return;
|
||||
}
|
||||
if (config.getMainPropertyValues() != null) {
|
||||
for (Map.Entry<String, Object> e : config.getMainPropertyValues().entrySet()) {
|
||||
if (e.getValue() == null) {
|
||||
continue;
|
||||
}
|
||||
switch (e.getKey()) {
|
||||
case DBConstants.PROP_HOST -> dsConfig.setHostName(CommonUtils.toString(e.getValue()));
|
||||
case DBConstants.PROP_PORT -> dsConfig.setHostPort(CommonUtils.toString(e.getValue()));
|
||||
case DBConstants.PROP_DATABASE -> dsConfig.setDatabaseName(CommonUtils.toString(e.getValue()));
|
||||
case DBConstants.PROP_SERVER -> dsConfig.setServerName(CommonUtils.toString(e.getValue()));
|
||||
default -> throw new IllegalStateException("Unexpected value: " + e.getKey());
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (config.getHost() != null) {
|
||||
dsConfig.setHostName(config.getHost());
|
||||
}
|
||||
if (config.getPort() != null) {
|
||||
dsConfig.setHostPort(config.getPort());
|
||||
}
|
||||
if (config.getDatabaseName() != null) {
|
||||
dsConfig.setDatabaseName(config.getDatabaseName());
|
||||
}
|
||||
if (config.getServerName() != null) {
|
||||
dsConfig.setServerName(config.getServerName());
|
||||
}
|
||||
}
|
||||
|
||||
public static void saveAuthProperties(
|
||||
@NotNull DBPDataSourceContainer dataSourceContainer,
|
||||
@NotNull DBPConnectionConfiguration configuration,
|
||||
@@ -331,4 +356,44 @@ public class WebServiceUtils extends WebCommonUtils {
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns set of applicable ids of drivers.
|
||||
*/
|
||||
@NotNull
|
||||
public static Set<String> getApplicableDriversIds() {
|
||||
return CBPlatform.getInstance().getApplicableDrivers().stream()
|
||||
.map(DBPDriver::getId)
|
||||
.collect(Collectors.toSet());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns filtered properties collected from object.
|
||||
*/
|
||||
@NotNull
|
||||
public static WebPropertyInfo[] getObjectFilteredProperties(
|
||||
@NotNull WebSession session,
|
||||
@NotNull DBPObject object,
|
||||
@Nullable WebPropertyFilter filter
|
||||
) {
|
||||
PropertyCollector propertyCollector = new PropertyCollector(object, true);
|
||||
propertyCollector.setLocale(session.getLocale());
|
||||
propertyCollector.collectProperties();
|
||||
List<WebPropertyInfo> webProps = new ArrayList<>();
|
||||
for (DBPPropertyDescriptor prop : propertyCollector.getProperties()) {
|
||||
if (filter != null && !CommonUtils.isEmpty(filter.getIds()) && !filter.getIds().contains(CommonUtils.toString(prop.getId()))) {
|
||||
continue;
|
||||
}
|
||||
WebPropertyInfo webProperty = new WebPropertyInfo(session, prop, propertyCollector);
|
||||
if (filter != null) {
|
||||
if (!CommonUtils.isEmpty(filter.getFeatures()) && !webProperty.hasAnyFeature(filter.getFeatures())) {
|
||||
continue;
|
||||
}
|
||||
if (!CommonUtils.isEmpty(filter.getCategories()) && !filter.getCategories().contains(webProperty.getCategory())) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
webProps.add(webProperty);
|
||||
}
|
||||
return webProps.toArray(new WebPropertyInfo[0]);
|
||||
}
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user