mirror of
https://github.com/dbeaver/cloudbeaver.git
synced 2026-10-16 14:50:15 +08:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8155d328a2 | ||
|
|
6118e706e0 | ||
|
|
eab07b9e9f |
@@ -0,0 +1,35 @@
|
||||
// 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"
|
||||
}
|
||||
@@ -1,56 +0,0 @@
|
||||
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
|
||||
@@ -1,40 +0,0 @@
|
||||
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
|
||||
@@ -1,57 +0,0 @@
|
||||
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 }}"
|
||||
@@ -1,56 +0,0 @@
|
||||
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
|
||||
@@ -1,93 +0,0 @@
|
||||
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
|
||||
# });
|
||||
@@ -1,91 +0,0 @@
|
||||
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
|
||||
@@ -0,0 +1,76 @@
|
||||
name: frontend-cache
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: ["devel"]
|
||||
paths:
|
||||
- "webapp/**"
|
||||
|
||||
# Allows you to run this workflow manually from the Actions tab
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
build_and_test:
|
||||
runs-on: ubuntu-latest
|
||||
defaults:
|
||||
run:
|
||||
working-directory: ./webapp
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: "18"
|
||||
|
||||
- name: Get yarn cache directory path
|
||||
id: yarn-cache-dir-path
|
||||
run: echo "dir=$(yarn cache dir)" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: restore yarn cache
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: ${{ steps.yarn-cache-dir-path.outputs.dir }}
|
||||
key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-yarn-
|
||||
|
||||
- name: restore node_modules
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: "**/node_modules"
|
||||
key: ${{ runner.os }}-node_modules-${{ hashFiles('**/yarn.lock') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-node_modules-
|
||||
|
||||
- name: restore typescript cache
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: "**/packages/*/dist"
|
||||
key: ${{ runner.os }}-dist-${{ hashFiles('**/yarn.lock') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-dist-
|
||||
|
||||
- name: yarn install
|
||||
uses: borales/actions-yarn@v4
|
||||
with:
|
||||
dir: webapp
|
||||
cmd: install
|
||||
|
||||
- name: yarn lerna bootstrap
|
||||
uses: borales/actions-yarn@v4
|
||||
with:
|
||||
dir: webapp
|
||||
cmd: lerna bootstrap
|
||||
|
||||
- name: build
|
||||
uses: borales/actions-yarn@v4
|
||||
with:
|
||||
dir: webapp/packages/product-default
|
||||
cmd: build # build tyescript source code
|
||||
|
||||
- name: test
|
||||
uses: borales/actions-yarn@v4
|
||||
with:
|
||||
dir: webapp
|
||||
cmd: lerna run test # build packages that have test script
|
||||
@@ -0,0 +1,29 @@
|
||||
name: cleanup caches by a branch
|
||||
on:
|
||||
pull_request:
|
||||
types:
|
||||
- closed
|
||||
|
||||
jobs:
|
||||
cleanup:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Cleanup
|
||||
run: |
|
||||
gh extension install actions/gh-actions-cache
|
||||
|
||||
echo "Fetching list of cache key"
|
||||
cacheKeysForPR=$(gh actions-cache list -R $REPO -B $BRANCH -L 100 | cut -f 1 )
|
||||
|
||||
## Setting this to not fail the workflow while deleting cache keys.
|
||||
set +e
|
||||
echo "Deleting caches..."
|
||||
for cacheKey in $cacheKeysForPR
|
||||
do
|
||||
gh actions-cache delete $cacheKey -R $REPO -B $BRANCH --confirm
|
||||
done
|
||||
echo "Done"
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
REPO: ${{ github.repository }}
|
||||
BRANCH: refs/pull/${{ github.event.pull_request.number }}/merge
|
||||
@@ -1,45 +0,0 @@
|
||||
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"
|
||||
@@ -0,0 +1,80 @@
|
||||
name: frontend
|
||||
|
||||
on:
|
||||
# Triggers the workflow on push or pull request events but only for the "devel" branch
|
||||
pull_request:
|
||||
branches: ["devel"]
|
||||
paths:
|
||||
- "webapp/**" # monitor for the frontend changes
|
||||
|
||||
# Allows you to run this workflow manually from the Actions tab
|
||||
workflow_dispatch:
|
||||
|
||||
# Allows you to reuse workflows by referencing their YAML files
|
||||
workflow_call:
|
||||
|
||||
jobs:
|
||||
build_and_test:
|
||||
runs-on: ubuntu-latest
|
||||
defaults:
|
||||
run:
|
||||
working-directory: ./webapp
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: "20"
|
||||
|
||||
- name: Get yarn cache directory path
|
||||
id: yarn-cache-dir-path
|
||||
run: echo "dir=$(yarn cache dir)" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: restore yarn cache
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: ${{ steps.yarn-cache-dir-path.outputs.dir }}
|
||||
key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-yarn-
|
||||
|
||||
- name: restore node_modules
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: "**/node_modules"
|
||||
key: ${{ runner.os }}-node_modules-${{ hashFiles('**/yarn.lock') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-node_modules-
|
||||
|
||||
- name: restore typescript cache
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: "**/packages/*/dist"
|
||||
key: ${{ runner.os }}-dist-${{ hashFiles('**/yarn.lock') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-dist-
|
||||
|
||||
- name: yarn install
|
||||
uses: borales/actions-yarn@v4
|
||||
with:
|
||||
dir: webapp
|
||||
cmd: install
|
||||
|
||||
- name: yarn lerna bootstrap
|
||||
uses: borales/actions-yarn@v4
|
||||
with:
|
||||
dir: webapp
|
||||
cmd: lerna bootstrap
|
||||
|
||||
- name: build
|
||||
uses: borales/actions-yarn@v4
|
||||
with:
|
||||
dir: webapp/packages/product-default
|
||||
cmd: bundle
|
||||
|
||||
- name: test
|
||||
uses: borales/actions-yarn@v4
|
||||
with:
|
||||
dir: webapp
|
||||
cmd: lerna run test
|
||||
@@ -0,0 +1,17 @@
|
||||
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,6 +15,7 @@
|
||||
"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
+69
-53
@@ -4,7 +4,7 @@
|
||||
{
|
||||
"type": "chrome",
|
||||
"request": "launch",
|
||||
"name": "CloudBeaver CE",
|
||||
"name": "Chrome",
|
||||
"url": "http://localhost:8080",
|
||||
"webRoot": "${workspaceFolder}/..",
|
||||
"outFiles": [
|
||||
@@ -17,60 +17,76 @@
|
||||
},
|
||||
{
|
||||
"type": "java",
|
||||
"name": "CloudBeaver CE Server",
|
||||
"cwd": "${workspaceFolder}/../opt/cbce",
|
||||
"name": "CloudBeaver CE",
|
||||
"cwd": "${workspaceFolder}/workspace-dev-ce",
|
||||
"preLaunchTask": "Generate dev proprties for CBCE",
|
||||
"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",
|
||||
"-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"
|
||||
]
|
||||
"windows": {
|
||||
"type": "java",
|
||||
"name": "CloudBeaver CE",
|
||||
"request": "launch",
|
||||
"mainClass": "org.jkiss.dbeaver.launcher.DBeaverLauncher",
|
||||
"args": [
|
||||
"-product",
|
||||
"io.cloudbeaver.product.ce.product",
|
||||
"-configuration",
|
||||
"file:${workspaceFolder}/../eclipse/workspace/.metadata/.plugins/org.eclipse.pde.core/CloudbeaverServer.product/",
|
||||
"-dev",
|
||||
"file:${workspaceFolder}/../eclipse/workspace/.metadata/.plugins/org.eclipse.pde.core/CloudbeaverServer.product/dev.properties",
|
||||
"-os",
|
||||
"win32",
|
||||
"-ws",
|
||||
"win32",
|
||||
"-arch",
|
||||
"x86_64",
|
||||
"-nl",
|
||||
"en",
|
||||
"-showsplash",
|
||||
"-web-config",
|
||||
"conf/cloudbeaver.conf"
|
||||
],
|
||||
"vmArgs": [
|
||||
"-XX:+IgnoreUnrecognizedVMOptions",
|
||||
"--add-modules=ALL-SYSTEM",
|
||||
"-Xms64m",
|
||||
"-Xmx1024m",
|
||||
"-Declipse.pde.launch=true"
|
||||
]
|
||||
},
|
||||
"osx": {
|
||||
"type": "java",
|
||||
"name": "CloudBeaver CE",
|
||||
"request": "launch",
|
||||
"mainClass": "org.jkiss.dbeaver.launcher.DBeaverLauncher",
|
||||
"args": [
|
||||
"-product",
|
||||
"io.cloudbeaver.product.ce.product",
|
||||
"-configuration",
|
||||
"file:${workspaceFolder}/../eclipse/workspace/.metadata/.plugins/org.eclipse.pde.core/CloudbeaverServer.product/",
|
||||
"-dev",
|
||||
"file:${workspaceFolder}/../eclipse/workspace/.metadata/.plugins/org.eclipse.pde.core/CloudbeaverServer.product/dev.properties",
|
||||
"-os",
|
||||
"macosx",
|
||||
"-ws",
|
||||
"cocoa",
|
||||
"-arch",
|
||||
"aarch64",
|
||||
"-nl",
|
||||
"en",
|
||||
"-showsplash",
|
||||
"-web-config",
|
||||
"conf/cloudbeaver.conf"
|
||||
],
|
||||
"vmArgs": [
|
||||
"-XX:+IgnoreUnrecognizedVMOptions",
|
||||
"--add-modules=ALL-SYSTEM",
|
||||
"-Xms64m",
|
||||
"-Xmx1024m",
|
||||
"-Declipse.pde.launch=true",
|
||||
"-XstartOnFirstThread"
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
Vendored
+3
-1
@@ -9,7 +9,9 @@
|
||||
" *",
|
||||
" * Licensed under the Apache License, Version 2.0.",
|
||||
" * you may not use this file except in compliance with the License.",
|
||||
" */"
|
||||
" */",
|
||||
"",
|
||||
""
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
Vendored
+18
-15
@@ -17,7 +17,7 @@
|
||||
"type": "shell",
|
||||
"command": "yarn",
|
||||
"group": "test",
|
||||
"args": ["test"]
|
||||
"args": ["lerna", "run", "test", "--no-bail", "--stream", "--"]
|
||||
},
|
||||
{
|
||||
"label": "Run DevServer CE",
|
||||
@@ -34,16 +34,27 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"label": "Generate Eclipse PDE CloudBeaver",
|
||||
"label": "Generate dev proprties for CBCE",
|
||||
"type": "shell",
|
||||
"command": "mvn",
|
||||
"osx": {
|
||||
"command": "./generate_workspace.sh"
|
||||
"args": [
|
||||
"package",
|
||||
"-q",
|
||||
"exec:java",
|
||||
"-Dexec.args=-config ${workspaceFolder}/../idea-workspace-dbeaver/rcp-gen.properties -productFile ${workspaceFolder}/server/product/web-server/CloudbeaverServer.product -projectsFolder ${workspaceFolder}/../ -eclipse ${workspaceFolder}/../eclipse/Eclipse.app/Contents/Eclipse -output ${workspaceFolder}/../eclipse/workspace/.metadata/.plugins/org.eclipse.pde.core/CloudbeaverServer.product"
|
||||
]
|
||||
},
|
||||
"windows": {
|
||||
"command": "./generate_workspace.cmd"
|
||||
"args": [
|
||||
"package",
|
||||
"-q",
|
||||
"exec:java",
|
||||
"-Dexec.args=-config ${workspaceFolder}/../idea-workspace-dbeaver/rcp-gen.properties -productFile ${workspaceFolder}/server/product/web-server/CloudbeaverServer.product -projectsFolder ${workspaceFolder}/../ -eclipse ${workspaceFolder}/../eclipse/eclipse -output ${workspaceFolder}/../eclipse/workspace/.metadata/.plugins/org.eclipse.pde.core/CloudbeaverServer.product"
|
||||
]
|
||||
},
|
||||
"options": {
|
||||
"cwd": "${workspaceFolder}/../cloudbeaver"
|
||||
"cwd": "${workspaceFolder}/../idea-rcp-launch-config-generator"
|
||||
},
|
||||
"presentation": {
|
||||
"reveal": "silent",
|
||||
@@ -84,7 +95,7 @@
|
||||
{
|
||||
"label": "Yarn Install",
|
||||
"type": "shell",
|
||||
"command": "yarn install",
|
||||
"command": "yarn install; yarn lerna bootstrap",
|
||||
"args": [],
|
||||
"presentation": {
|
||||
"reveal": "silent",
|
||||
@@ -95,15 +106,7 @@
|
||||
{
|
||||
"label": "Update TS references CE",
|
||||
"type": "shell",
|
||||
"command": "yarn run update-ts-references",
|
||||
"options": {
|
||||
"cwd": "${workspaceFolder}/webapp"
|
||||
}
|
||||
},
|
||||
{
|
||||
"label": "Clean CE",
|
||||
"type": "shell",
|
||||
"command": "yarn clean",
|
||||
"command": "yarn lerna run update-ts-references --stream",
|
||||
"options": {
|
||||
"cwd": "${workspaceFolder}/webapp"
|
||||
}
|
||||
|
||||
@@ -1,16 +1,13 @@
|
||||
<img src="https://github.com/dbeaver/cloudbeaver/wiki/images/cloudbeaver-logo.png" align="right" width="250"/>
|
||||
|
||||
# CloudBeaver Community
|
||||
|
||||
Cloud Database Manager - Community Edition.
|
||||
CloudBeaver is a web server that provides a rich web interface. The server itself is a Java application, and the web part is written in TypeScript and React.
|
||||
It is free to use and open-source (licensed under [Apache 2](https://github.com/dbeaver/cloudbeaver/blob/devel/LICENSE) license).
|
||||
See our [WIKI](https://github.com/dbeaver/cloudbeaver/wiki) for more details.
|
||||
<img src="https://github.com/dbeaver/cloudbeaver/wiki/images/cloudbeaver-logo.png" width="250"/>
|
||||
|
||||
<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"/>
|
||||
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.
|
||||
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.
|
||||
|
||||

|
||||
|
||||
## Run in Docker
|
||||
|
||||
@@ -19,49 +16,47 @@ See our [WIKI](https://github.com/dbeaver/cloudbeaver/wiki) for more details.
|
||||
|
||||
## Demo server
|
||||
|
||||
You can see a live demo of CloudBeaver here: https://demo.cloudbeaver.io
|
||||
You can see live demo of CloudBeaver here: https://demo.cloudbeaver.io
|
||||
|
||||
[Database access instructions](https://github.com/dbeaver/cloudbeaver/wiki/Demo-Server)
|
||||
|
||||
## Changelog
|
||||
|
||||
### 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.0.1. 2024-03-25
|
||||
- Added support for two modes for committing changes to the database:
|
||||
- Auto-commit transfers all changes that you make immediately to the database;
|
||||
- Manual commit requires your confirmation before committing a change to the database or rolling it back.
|
||||
- Different bug fixes and enhancements have been made.
|
||||
|
||||
### 24.0.0. 2024-03-04
|
||||
- Changes since 23.3.0:
|
||||
|
||||
### 24.2.0. 2024-09-02
|
||||
### Changes since 24.1.0:
|
||||
- General:
|
||||
- French language support was added (thanks to [matthieukhl](https://github.com/matthieukhl))
|
||||
- Added the ability to close editor tabs with the middle mouse button
|
||||
- Added right-click support to open the context menu in the Metadata Editor
|
||||
- The list of forbidden characters for naming and renaming resource manager files has been updated, and now it includes the following characters: / : " \ ' <> | ? *
|
||||
- Application cookies security was improved
|
||||
- Authentication:
|
||||
- Improved LDAP authentication: added the ability to filter users via service account parameters and ability to specify custom unique user identifiers
|
||||
- Data Editor:
|
||||
- Added additional notifications about the restricted operations
|
||||
- Enhanced IPv6 and DateTime32 data representation for Clickhouse
|
||||
- Data editing was fixed for DuckDB
|
||||
- SQL Editor:
|
||||
- SQL Editor auto-completion was enhanced to get column, table, and function names faster
|
||||
- Fixed the dollar-quoted string parsing in the SQL Editor for PostgreSQL
|
||||
- Improved display of the long error messages in the SQL Editor and Log viewer
|
||||
- Changed the save script icon to a floppy disk for better recognition
|
||||
- Improved application behavior when closing a connection - open editors won't be closed on disconnect
|
||||
- Main updates:
|
||||
- Have enhanced security for unauthorised access;
|
||||
- Have added a password policy for the local authorization. Password parameters can be set in the configuration file;
|
||||
- Have added validation for mandatory fields in all forms to create and edit entities;
|
||||
- Have added an ability to edit the default preferences of the following parts: interface, tools and data viewer in the settings panel in the administrative part;
|
||||
- Performance:
|
||||
- Have improved the performance of the server.
|
||||
- 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))
|
||||
- Have added the ability to configure reverse proxy header name and redirect URL at logout. Now the administrator can configure all settings. The delimiter symbol for the reverse proxy team headers is configurable;
|
||||
- Preferences:
|
||||
- Have added the ability in the admin panel to customise copying in the Tables and Values panel for all users except the administrator;
|
||||
- Have added the ability to disable alias autocomplete for all users.
|
||||
- Access Management:
|
||||
- A default user group was added to the product. This group includes all users which could not be deleted.
|
||||
- Connections:
|
||||
- The 'Keep alive' option has been added to the connection settings to keep the connection active even in case of inactivity.
|
||||
- Data Editor:
|
||||
- Have added the ability to view decoded binary-type data in the Value panel;
|
||||
- Text wrap is activated by default for texts and BLOBs in the Values panel for better visibility. The user can switch to the one-line mode using a button on the toolbar;
|
||||
- Have added the ability to display full text for a string data type in the Value panel;
|
||||
- Have added the ability to display the OpenStreetMap layer with the coordinate system ESPG 4326.
|
||||
- Resource management:
|
||||
- Read-only scripts now have a padlock icon.
|
||||
- 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))
|
||||
|
||||
## Contribution
|
||||
As a community-driven open-source project, we warmly welcome contributions through GitHub pull requests.
|
||||
|
||||
[We are happy to reward](https://dbeaver.com/help-dbeaver/) our most active contributors every major sprint.
|
||||
The most significant contribution to our code for the major release 24.2.0 was made by:
|
||||
1. [matthieukhl](https://github.com/matthieukhl)
|
||||
- The DuckDB driver has been added;
|
||||
- Updated PostgreSQL driver to 42.5.2;
|
||||
- ClickHouse Legacy driver has been removed to prevent potential vulnerability issues;
|
||||
- Updated MariaDB driver to version 3.3.2.
|
||||
- Different bug fixes and enhancements have been made.
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
<dependency>
|
||||
<groupId>com.h2database</groupId>
|
||||
<artifactId>h2</artifactId>
|
||||
<version>2.1.214</version>
|
||||
<version>2.2.220</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
<build>
|
||||
|
||||
@@ -1,104 +0,0 @@
|
||||
{
|
||||
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: [
|
||||
|
||||
]
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,100 @@
|
||||
{
|
||||
server: {
|
||||
serverPort: 8978,
|
||||
|
||||
workspaceLocation: "workspace",
|
||||
contentRoot: "web",
|
||||
driversLocation: "drivers",
|
||||
|
||||
sslConfigurationPath:"${CLOUDBEAVER_SSL_CONF_PATH:workspace/.data/ssl-config.xml}",
|
||||
|
||||
rootURI: "/",
|
||||
serviceURI: "/api/",
|
||||
|
||||
productSettings: {
|
||||
# Global properties
|
||||
core.theming.theme: 'light',
|
||||
core.localization.localization: 'en',
|
||||
plugin.sql-editor.autoSave: true,
|
||||
plugin.sql-editor.disabled: false,
|
||||
# max size of the file that can be uploaded to the editor (in kilobytes)
|
||||
plugin.sql-editor.maxFileSize: 10240,
|
||||
plugin.log-viewer.disabled: false,
|
||||
plugin.log-viewer.logBatchSize: 1000,
|
||||
plugin.log-viewer.maxFailedRequests: 3,
|
||||
plugin.log-viewer.maxLogRecords: 2000,
|
||||
plugin.log-viewer.refreshTimeout: 3000,
|
||||
sql.proposals.insert.table.alias: PLAIN
|
||||
},
|
||||
|
||||
expireSessionAfterPeriod: 1800000,
|
||||
|
||||
develMode: false,
|
||||
|
||||
enableSecurityManager: false,
|
||||
|
||||
sm: {
|
||||
enableBruteForceProtection: "${CLOUDBEAVER_BRUTE_FORCE_PROTECTION_ENABLED:true}",
|
||||
maxFailedLogin: "${CLOUDBEAVER_MAX_FAILED_LOGINS:10}",
|
||||
minimumLoginTimeout: "${CLOUDBEAVER_MINIMUM_LOGIN_TIMEOUT:1}",
|
||||
blockLoginPeriod: "${CLOUDBEAVER_BLOCK_PERIOD:300}",
|
||||
passwordPolicy: {
|
||||
minLength: "${CLOUDBEAVER_POLICY_MIN_LENGTH:8}",
|
||||
requireMixedCase: "${CLOUDBEAVER_POLICY_REQUIRE_MIXED_CASE:true}",
|
||||
minNumberCount: "${CLOUDBEAVER_POLICY_MIN_NUMBER_COUNT:1}",
|
||||
minSymbolCount: "${CLOUDBEAVER_POLICY_MIN_SYMBOL_COUNT:0}"
|
||||
}
|
||||
},
|
||||
|
||||
database: {
|
||||
driver: "h2_embedded_v2",
|
||||
url: "jdbc:h2:${workspace}/.data/cb.h2v2.dat",
|
||||
initialDataConfiguration: "conf/initial-data.conf",
|
||||
pool: {
|
||||
minIdleConnections: 4,
|
||||
maxIdleConnections: 10,
|
||||
maxConnections: 100,
|
||||
validationQuery: "SELECT 1"
|
||||
}
|
||||
}
|
||||
|
||||
},
|
||||
app: {
|
||||
anonymousAccessEnabled: true,
|
||||
anonymousUserRole: "user",
|
||||
defaultUserTeam: "user",
|
||||
grantConnectionsAccessToAnonymousTeam: false,
|
||||
supportsCustomConnections: false,
|
||||
showReadOnlyConnectionInfo: false,
|
||||
systemVariablesResolvingEnabled: "${CLOUDBEAVER_SYSTEM_VARIABLES_RESOLVING_ENABLED:false}",
|
||||
|
||||
forwardProxy: false,
|
||||
|
||||
publicCredentialsSaveEnabled: true,
|
||||
adminCredentialsSaveEnabled: true,
|
||||
|
||||
resourceManagerEnabled: true,
|
||||
|
||||
resourceQuotas: {
|
||||
dataExportFileSizeLimit: 10000000,
|
||||
resourceManagerFileSizeLimit: 500000,
|
||||
sqlMaxRunningQueries: 100,
|
||||
sqlResultSetRowsLimit: 100000,
|
||||
sqlResultSetMemoryLimit: 2000000,
|
||||
sqlTextPreviewMaxLength: 4096,
|
||||
sqlBinaryPreviewMaxLength: 261120
|
||||
},
|
||||
enabledAuthProviders: [
|
||||
"local"
|
||||
],
|
||||
|
||||
disabledDrivers: [
|
||||
"sqlite:sqlite_jdbc",
|
||||
"h2:h2_embedded",
|
||||
"h2:h2_embedded_v2",
|
||||
"clickhouse:yandex_clickhouse"
|
||||
]
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
### Sample databases
|
||||
|
||||
Provides access to locally deployed SQLite sample database
|
||||
+39
@@ -0,0 +1,39 @@
|
||||
{
|
||||
"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
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"folders": {},
|
||||
"connections": {}
|
||||
}
|
||||
@@ -0,0 +1,95 @@
|
||||
{
|
||||
server: {
|
||||
serverPort: 8978,
|
||||
|
||||
workspaceLocation: "workspace",
|
||||
contentRoot: "web",
|
||||
driversLocation: "drivers",
|
||||
|
||||
rootURI: "/",
|
||||
serviceURI: "/api/",
|
||||
|
||||
productSettings: {
|
||||
# Global properties
|
||||
core.theming.theme: 'light',
|
||||
core.localization.localization: 'en',
|
||||
plugin.sql-editor.autoSave: true,
|
||||
plugin.sql-editor.disabled: false,
|
||||
# max size of the file that can be uploaded to the editor (in kilobytes)
|
||||
plugin.sql-editor.maxFileSize: 10240,
|
||||
plugin.log-viewer.disabled: false,
|
||||
plugin.log-viewer.logBatchSize: 1000,
|
||||
plugin.log-viewer.maxFailedRequests: 3,
|
||||
plugin.log-viewer.maxLogRecords: 2000,
|
||||
plugin.log-viewer.refreshTimeout: 3000,
|
||||
sql.proposals.insert.table.alias: PLAIN
|
||||
},
|
||||
|
||||
expireSessionAfterPeriod: 1800000,
|
||||
|
||||
develMode: false,
|
||||
|
||||
enableSecurityManager: false,
|
||||
|
||||
database: {
|
||||
driver:"h2_embedded_v2",
|
||||
url: "jdbc:h2:${workspace}/.data/cb.h2v2.dat",
|
||||
initialDataConfiguration: "conf/initial-data.conf",
|
||||
pool: {
|
||||
minIdleConnections: 4,
|
||||
maxIdleConnections: 10,
|
||||
maxConnections: 100,
|
||||
validationQuery: "SELECT 1"
|
||||
}
|
||||
},
|
||||
sm: {
|
||||
enableBruteForceProtection: "${CLOUDBEAVER_BRUTE_FORCE_PROTECTION_ENABLED:true}",
|
||||
maxFailedLogin: "${CLOUDBEAVER_MAX_FAILED_LOGINS:10}",
|
||||
minimumLoginTimeout: "${CLOUDBEAVER_MINIMUM_LOGIN_TIMEOUT:1}",
|
||||
blockLoginPeriod: "${CLOUDBEAVER_BLOCK_PERIOD:300}",
|
||||
passwordPolicy: {
|
||||
minLength: "${CLOUDBEAVER_POLICY_MIN_LENGTH:8}",
|
||||
requireMixedCase: "${CLOUDBEAVER_POLICY_REQUIRE_MIXED_CASE:true}",
|
||||
minNumberCount: "${CLOUDBEAVER_POLICY_MIN_NUMBER_COUNT:1}",
|
||||
minSymbolCount: "${CLOUDBEAVER_POLICY_MIN_SYMBOL_COUNT:0}"
|
||||
}
|
||||
}
|
||||
|
||||
},
|
||||
app: {
|
||||
anonymousAccessEnabled: true,
|
||||
anonymousUserRole: "user",
|
||||
grantConnectionsAccessToAnonymousTeam: false,
|
||||
supportsCustomConnections: false,
|
||||
showReadOnlyConnectionInfo: false,
|
||||
systemVariablesResolvingEnabled: "${CLOUDBEAVER_SYSTEM_VARIABLES_RESOLVING_ENABLED:false}",
|
||||
|
||||
forwardProxy: false,
|
||||
|
||||
publicCredentialsSaveEnabled: true,
|
||||
adminCredentialsSaveEnabled: true,
|
||||
|
||||
resourceManagerEnabled: true,
|
||||
|
||||
resourceQuotas: {
|
||||
dataExportFileSizeLimit: 10000000,
|
||||
resourceManagerFileSizeLimit: 500000,
|
||||
sqlMaxRunningQueries: 100,
|
||||
sqlResultSetRowsLimit: 100000,
|
||||
sqlResultSetMemoryLimit: 2000000,
|
||||
sqlTextPreviewMaxLength: 4096,
|
||||
sqlBinaryPreviewMaxLength: 261120
|
||||
},
|
||||
enabledAuthProviders: [
|
||||
"local"
|
||||
],
|
||||
|
||||
disabledDrivers: [
|
||||
"h2:h2_embedded",
|
||||
"h2:h2_embedded_v2",
|
||||
"clickhouse:yandex_clickhouse"
|
||||
]
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
Binary file not shown.
@@ -0,0 +1,6 @@
|
||||
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
|
||||
+20
-1
@@ -2,6 +2,12 @@
|
||||
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
|
||||
@@ -10,6 +16,7 @@ mkdir ./cloudbeaver
|
||||
mkdir ./cloudbeaver/server
|
||||
mkdir ./cloudbeaver/conf
|
||||
mkdir ./cloudbeaver/workspace
|
||||
mkdir ./cloudbeaver/web
|
||||
|
||||
echo "Pull cloudbeaver platform"
|
||||
|
||||
@@ -37,8 +44,20 @@ 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 ../config/GlobalConfiguration/.dbeaver/data-sources.json cloudbeaver/conf/initial-data-sources.conf
|
||||
cp -rp "${CONFIGURATION_PATH}"/GlobalConfiguration/.dbeaver/data-sources.json cloudbeaver/conf/initial-data-sources.conf
|
||||
cp -p "${CONFIGURATION_PATH}"/*.conf cloudbeaver/conf/
|
||||
mv drivers cloudbeaver
|
||||
|
||||
echo "End of backend build"
|
||||
@@ -3,25 +3,15 @@ set -Eeuo pipefail
|
||||
|
||||
echo "Build static content"
|
||||
|
||||
mkdir ./cloudbeaver/web
|
||||
|
||||
cd ../../cloudbeaver/webapp
|
||||
|
||||
yarn
|
||||
cd ./packages/product-default
|
||||
yarn run bundle
|
||||
|
||||
yarn lerna run bootstrap
|
||||
yarn lerna run bundle --no-bail --stream --scope=@cloudbeaver/product-default #-- -- --env source-map
|
||||
if [[ "$?" -ne 0 ]] ; then
|
||||
echo 'Application build failed'; exit $rc
|
||||
fi
|
||||
|
||||
cd ../../
|
||||
yarn test
|
||||
|
||||
if [[ "$?" -ne 0 ]] ; then
|
||||
echo 'Frontend tests failed'; exit $rc
|
||||
fi
|
||||
|
||||
cd ../deploy
|
||||
|
||||
echo "Copy static content"
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
@call build.bat ..\config\sample-databases\SQLiteConfiguration ..\config\sample-databases\db
|
||||
Executable
+9
@@ -0,0 +1,9 @@
|
||||
#!/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
|
||||
+11
-2
@@ -1,6 +1,11 @@
|
||||
@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
|
||||
|
||||
@@ -35,9 +40,13 @@ 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 ..\config\DefaultConfiguration\GlobalConfiguration\.dbeaver\data-sources.json cloudbeaver\conf\initial-data-sources.conf >NUL
|
||||
copy %CONFIGURATION_PATH%\GlobalConfiguration\.dbeaver\data-sources.json cloudbeaver\conf\initial-data-sources.conf >NUL
|
||||
copy %CONFIGURATION_PATH%\*.conf cloudbeaver\conf >NUL
|
||||
|
||||
move drivers cloudbeaver >NUL
|
||||
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
#!/bin/bash
|
||||
set -Eeuo pipefail
|
||||
|
||||
#command line arguments
|
||||
CONFIGURATION_PATH="../config/sample-databases/DefaultConfiguration"
|
||||
SAMPLE_DATABASE_PATH=""
|
||||
|
||||
source build-backend.sh
|
||||
source build-frontend.sh
|
||||
@@ -1,12 +1,10 @@
|
||||
FROM ubuntu:24.04
|
||||
FROM ubuntu:23.10
|
||||
|
||||
MAINTAINER DBeaver Corp, devops@dbeaver.com
|
||||
|
||||
ENV DEBIAN_FRONTEND=noninteractive
|
||||
|
||||
RUN set -eux; \
|
||||
apt-get update; \
|
||||
apt-get install -y --no-install-recommends \
|
||||
DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \
|
||||
# curl required for historical reasons, see https://github.com/adoptium/containers/issues/255
|
||||
curl \
|
||||
wget \
|
||||
@@ -45,3 +43,10 @@ RUN set -eux; \
|
||||
|
||||
### Patch java security
|
||||
COPY java.security* ${JAVA_HOME}/conf/security/java.security
|
||||
|
||||
COPY cloudbeaver /opt/cloudbeaver
|
||||
|
||||
EXPOSE 8978
|
||||
RUN find /opt/cloudbeaver -type d -exec chmod 775 {} \;
|
||||
WORKDIR /opt/cloudbeaver/
|
||||
ENTRYPOINT ["./run-server.sh"]
|
||||
@@ -1,13 +0,0 @@
|
||||
FROM dbeaver/base-java
|
||||
|
||||
MAINTAINER DBeaver Corp, devops@dbeaver.com
|
||||
|
||||
RUN apt-get update; \
|
||||
apt-get upgrade -y;
|
||||
|
||||
COPY cloudbeaver /opt/cloudbeaver
|
||||
|
||||
EXPOSE 8978
|
||||
RUN find /opt/cloudbeaver -type d -exec chmod 775 {} \;
|
||||
WORKDIR /opt/cloudbeaver/
|
||||
ENTRYPOINT ["./run-server.sh"]
|
||||
@@ -1,3 +1,3 @@
|
||||
cd ..
|
||||
docker build -t dbeaver/cloudbeaver:dev . --file ./docker/cloudbeaver-ce/Dockerfile
|
||||
docker build -t dbeaver/cloudbeaver:dev . --file ./docker/Dockerfile
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@ echo "Starting Cloudbeaver Server"
|
||||
&& [ ! -f "workspace/GlobalConfiguration/.dbeaver/data-sources.json" ] \
|
||||
&& cp conf/initial-data-sources.conf workspace/GlobalConfiguration/.dbeaver/data-sources.json
|
||||
|
||||
exec java ${JAVA_OPTS} \
|
||||
java ${JAVA_OPTS} \
|
||||
-Dfile.encoding=UTF-8 \
|
||||
--add-modules=ALL-SYSTEM \
|
||||
--add-opens=java.base/java.io=ALL-UNNAMED \
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
[program:cloudbeaver]
|
||||
directory=/opt/cloudbeaver/
|
||||
command=/opt/cloudbeaver/run-server.sh
|
||||
stopasgroup=true
|
||||
killasgroup=true
|
||||
stopsignal=INT
|
||||
autostart=true
|
||||
autorestart=true
|
||||
redirect_stderr=true
|
||||
stdout_logfile=/var/log/cloudbeaver-server.log
|
||||
@@ -1,3 +0,0 @@
|
||||
SET mypath=%~dp0
|
||||
|
||||
.\..\idea-rcp-launch-config-generator\runGenerator.cmd -f %mypath%
|
||||
@@ -1 +0,0 @@
|
||||
./../idea-rcp-launch-config-generator/runGenerator.sh -f "$PWD"
|
||||
@@ -1,3 +1,4 @@
|
||||
{
|
||||
"projects": ["../dbeaver-common", "../dbeaver", "../cloudbeaver/server"],
|
||||
"targetPlatform": "./org.jkiss.cloudbeaver.tp.target"
|
||||
}
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<?pde version="3.8"?>
|
||||
<target name="org.jkiss.cloudbeaver.tp">
|
||||
<target name="org.jkiss.tp.cloudbeaver">
|
||||
<locations>
|
||||
<location path="../dbeaver-workspace/dependencies/plugins" type="Directory"/>
|
||||
<location path="../eclipse/Eclipse.app/Contents/Eclipse/plugins" type="Directory"/>
|
||||
<location path="../eclipse/eclipse/plugins" type="Directory"/>
|
||||
</locations>
|
||||
</target>
|
||||
@@ -1,33 +0,0 @@
|
||||
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.62.qualifier
|
||||
Bundle-Release-Date: 20241007
|
||||
Bundle-Version: 1.0.50.qualifier
|
||||
Bundle-Release-Date: 20240408
|
||||
Bundle-RequiredExecutionEnvironment: JavaSE-17
|
||||
Bundle-ActivationPolicy: lazy
|
||||
Bundle-ClassPath: .
|
||||
@@ -18,19 +18,16 @@ 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-api;bundle-version:="6.0.0";visibility:=reexport
|
||||
jakarta.servlet;visibility:=reexport
|
||||
Export-Package: io.cloudbeaver,
|
||||
io.cloudbeaver.auth,
|
||||
io.cloudbeaver.auth.provider,
|
||||
io.cloudbeaver.auth.provider.fa,
|
||||
io.cloudbeaver.auth.provider.local,
|
||||
io.cloudbeaver.auth.provisioning,
|
||||
io.cloudbeaver.websocket,
|
||||
io.cloudbeaver.model,
|
||||
io.cloudbeaver.model.app,
|
||||
io.cloudbeaver.model.config,
|
||||
io.cloudbeaver.model.fs,
|
||||
io.cloudbeaver.model.log,
|
||||
io.cloudbeaver.model.rm,
|
||||
io.cloudbeaver.model.rm.local,
|
||||
io.cloudbeaver.model.rm.lock,
|
||||
@@ -43,7 +40,6 @@ 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.62-SNAPSHOT</version>
|
||||
<version>1.0.50-SNAPSHOT</version>
|
||||
<packaging>eclipse-plugin</packaging>
|
||||
|
||||
</project>
|
||||
|
||||
@@ -16,21 +16,23 @@
|
||||
*/
|
||||
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 abstract class BaseWebProjectImpl extends BaseProjectImpl implements RMControllerProvider {
|
||||
public class BaseWebProjectImpl extends BaseProjectImpl implements RMControllerProvider {
|
||||
|
||||
@NotNull
|
||||
private final RMProject project;
|
||||
@@ -38,18 +40,21 @@ public abstract class BaseWebProjectImpl extends BaseProjectImpl implements RMCo
|
||||
@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 RMProject project,
|
||||
@NotNull DataSourceFilter dataSourceFilter
|
||||
) {
|
||||
super(workspace, sessionContext);
|
||||
this.resourceController = resourceController;
|
||||
this.path = RMUtils.getProjectPath(project);
|
||||
this.project = project;
|
||||
this.dataSourceFilter = dataSourceFilter;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@@ -86,6 +91,12 @@ public abstract class BaseWebProjectImpl extends BaseProjectImpl implements RMCo
|
||||
return path;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public IProject getEclipseProject() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isOpen() {
|
||||
return true;
|
||||
@@ -101,6 +112,11 @@ public abstract class BaseWebProjectImpl extends BaseProjectImpl implements RMCo
|
||||
return false;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public RMProject getRmProject() {
|
||||
return this.project;
|
||||
}
|
||||
|
||||
/**
|
||||
* Method for Bulk Update of resources properties paths
|
||||
*
|
||||
|
||||
@@ -35,7 +35,6 @@ 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.sql.SQLState;
|
||||
import org.jkiss.dbeaver.model.DBPDataSource;
|
||||
import org.jkiss.dbeaver.utils.GeneralUtils;
|
||||
import org.jkiss.utils.CommonUtils;
|
||||
|
||||
@@ -38,7 +38,6 @@ 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";
|
||||
@@ -66,8 +65,12 @@ public class DBWebException extends DBException implements GraphQLError {
|
||||
this.webErrorCode = errorCode;
|
||||
}
|
||||
|
||||
public DBWebException(Throwable cause) {
|
||||
super(null, cause);
|
||||
public DBWebException(Throwable cause, DBPDataSource dataSource) {
|
||||
super(cause, dataSource);
|
||||
}
|
||||
|
||||
public DBWebException(String message, Throwable cause, DBPDataSource dataSource) {
|
||||
super(makeMessage(message, cause), cause, dataSource);
|
||||
}
|
||||
|
||||
public String getWebErrorCode() {
|
||||
@@ -125,14 +128,14 @@ public class DBWebException extends DBException implements GraphQLError {
|
||||
//stString = stString.substring(divPos + 1).trim();
|
||||
}
|
||||
extensions.put("stackTrace", stString.trim());
|
||||
int errorCode = SQLState.getCodeFromException(this);
|
||||
int errorCode = getErrorCode();
|
||||
if (errorCode != ERROR_CODE_NONE) {
|
||||
extensions.put("errorCode", errorCode);
|
||||
}
|
||||
if (!CommonUtils.isEmpty(webErrorCode)) {
|
||||
extensions.put("webErrorCode", webErrorCode);
|
||||
}
|
||||
String databaseState = SQLState.getStateFromException(this);
|
||||
String databaseState = getDatabaseState();
|
||||
if (databaseState != null) {
|
||||
extensions.put("databaseState", databaseState);
|
||||
}
|
||||
|
||||
+1
-8
@@ -29,7 +29,6 @@ import org.jkiss.dbeaver.model.app.DBPProject;
|
||||
import org.jkiss.dbeaver.model.connection.DBPConnectionConfiguration;
|
||||
import org.jkiss.dbeaver.model.connection.DBPDriver;
|
||||
import org.jkiss.dbeaver.model.net.DBWNetworkProfile;
|
||||
import org.jkiss.dbeaver.model.preferences.DBPPreferenceStore;
|
||||
import org.jkiss.dbeaver.model.runtime.DBRProgressMonitor;
|
||||
import org.jkiss.dbeaver.model.secret.DBSSecretController;
|
||||
import org.jkiss.dbeaver.model.struct.DBSObjectFilter;
|
||||
@@ -261,7 +260,7 @@ public class WebDataSourceRegistryProxy implements DBPDataSourceRegistry, DataSo
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeAuthProfile(@NotNull DBAAuthProfile profile) {
|
||||
public void removeAuthProfile(DBAAuthProfile profile) {
|
||||
dataSourceRegistry.removeAuthProfile(profile);
|
||||
}
|
||||
|
||||
@@ -323,12 +322,6 @@ public class WebDataSourceRegistryProxy implements DBPDataSourceRegistry, DataSo
|
||||
return dataSourceRegistry.getTemporaryFolders();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public DBPPreferenceStore getPreferenceStore() {
|
||||
return dataSourceRegistry.getPreferenceStore();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean loadDataSources(
|
||||
@NotNull List<DBPDataSourceConfigurationStorage> storages,
|
||||
|
||||
-36
@@ -1,36 +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;
|
||||
|
||||
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()
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -23,26 +23,22 @@ import org.jkiss.dbeaver.Log;
|
||||
import org.jkiss.dbeaver.model.app.DBPDataSourceRegistry;
|
||||
import org.jkiss.dbeaver.model.app.DBPWorkspace;
|
||||
import org.jkiss.dbeaver.model.auth.SMSessionContext;
|
||||
import org.jkiss.dbeaver.model.preferences.DBPPreferenceStore;
|
||||
import org.jkiss.dbeaver.model.rm.RMController;
|
||||
import org.jkiss.dbeaver.model.rm.RMProject;
|
||||
import org.jkiss.dbeaver.model.task.DBTTaskManager;
|
||||
import org.jkiss.dbeaver.registry.rm.DataSourceRegistryRM;
|
||||
import org.jkiss.dbeaver.runtime.DBWorkbench;
|
||||
|
||||
public abstract class WebProjectImpl extends BaseWebProjectImpl {
|
||||
private static final Log log = Log.getLog(WebProjectImpl.class);
|
||||
@NotNull
|
||||
protected final DBPPreferenceStore preferenceStore;
|
||||
|
||||
public WebProjectImpl(
|
||||
@NotNull DBPWorkspace workspace,
|
||||
@NotNull RMController resourceController,
|
||||
@NotNull SMSessionContext sessionContext,
|
||||
@NotNull RMProject project,
|
||||
@NotNull DBPPreferenceStore preferenceStore
|
||||
@NotNull DataSourceFilter dataSourceFilter
|
||||
) {
|
||||
super(workspace, resourceController, sessionContext, project);
|
||||
this.preferenceStore = preferenceStore;
|
||||
super(workspace, resourceController, sessionContext, project, dataSourceFilter);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@@ -70,24 +66,13 @@ 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),
|
||||
getDataSourceFilter()
|
||||
new DataSourceRegistryRM(this, getResourceController()),
|
||||
dataSourceFilter
|
||||
);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public DataSourceFilter getDataSourceFilter() {
|
||||
return (ds) -> true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
-118
@@ -1,118 +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;
|
||||
|
||||
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,41 +16,18 @@
|
||||
*/
|
||||
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 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
|
||||
) {
|
||||
super(
|
||||
webSession.getWorkspace(),
|
||||
webSession.getRmController(),
|
||||
webSession.getSessionContext(),
|
||||
project,
|
||||
webSession.getUserPreferenceStore()
|
||||
);
|
||||
private final WebSession webSession;
|
||||
|
||||
public WebSessionProjectImpl(@NotNull WebSession webSession, @NotNull RMProject project, @NotNull DataSourceFilter dataSourceFilter) {
|
||||
super(webSession.getWorkspace(), webSession.getRmController(), webSession.getSessionContext(), project, dataSourceFilter);
|
||||
this.webSession = webSession;
|
||||
}
|
||||
|
||||
@@ -59,156 +36,4 @@ public class WebSessionProjectImpl extends WebProjectImpl {
|
||||
public DBNModel getNavigatorModel() {
|
||||
return webSession.getNavigatorModel();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
protected DBPDataSourceRegistry createDataSourceRegistry() {
|
||||
DBPDataSourceRegistry dataSourceRegistry = super.createDataSourceRegistry();
|
||||
dataSourceRegistry.setAuthCredentialsProvider(webSession);
|
||||
return dataSourceRegistry;
|
||||
}
|
||||
|
||||
private synchronized void addDataSourcesToCache() {
|
||||
if (registryIsLoaded) {
|
||||
return;
|
||||
}
|
||||
getDataSourceRegistry().getDataSources().forEach(this::addConnection);
|
||||
Throwable lastError = getDataSourceRegistry().getLastError();
|
||||
if (lastError != null) {
|
||||
webSession.addSessionError(lastError);
|
||||
log.error("Error refreshing connections from project '" + getId() + "'", lastError);
|
||||
}
|
||||
registryIsLoaded = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispose() {
|
||||
super.dispose();
|
||||
Map<String, WebConnectionInfo> conCopy;
|
||||
synchronized (this.connections) {
|
||||
conCopy = new HashMap<>(this.connections);
|
||||
this.connections.clear();
|
||||
}
|
||||
|
||||
for (WebConnectionInfo connectionInfo : conCopy.values()) {
|
||||
if (connectionInfo.isConnected()) {
|
||||
new DisconnectJob(connectionInfo.getDataSourceContainer()).schedule();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns web connection info from cache (if exists).
|
||||
*/
|
||||
@Nullable
|
||||
public WebConnectionInfo findWebConnectionInfo(@NotNull String connectionId) {
|
||||
synchronized (connections) {
|
||||
return connections.get(connectionId);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns web connection info from cache, adds it to cache if not present.
|
||||
* Throws exception if connection is not found.
|
||||
*/
|
||||
@NotNull
|
||||
public WebConnectionInfo getWebConnectionInfo(@NotNull String connectionId) throws DBWebException {
|
||||
WebConnectionInfo connectionInfo = findWebConnectionInfo(connectionId);
|
||||
if (connectionInfo != null) {
|
||||
return connectionInfo;
|
||||
}
|
||||
DBPDataSourceContainer dataSource = getDataSourceRegistry().getDataSource(connectionId);
|
||||
if (dataSource != null) {
|
||||
return addConnection(dataSource);
|
||||
}
|
||||
throw new DBWebException("Connection '%s' not found".formatted(connectionId));
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds connection to project cache.
|
||||
*/
|
||||
@NotNull
|
||||
public synchronized WebConnectionInfo addConnection(@NotNull DBPDataSourceContainer dataSourceContainer) {
|
||||
WebConnectionInfo connection = new WebConnectionInfo(webSession, dataSourceContainer);
|
||||
synchronized (connections) {
|
||||
connections.put(dataSourceContainer.getId(), connection);
|
||||
}
|
||||
return connection;
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes connection from project cache.
|
||||
*/
|
||||
public void removeConnection(@NotNull DBPDataSourceContainer dataSourceContainer) {
|
||||
WebConnectionInfo webConnectionInfo = connections.get(dataSourceContainer.getId());
|
||||
if (webConnectionInfo != null) {
|
||||
webConnectionInfo.clearCache();
|
||||
synchronized (connections) {
|
||||
connections.remove(dataSourceContainer.getId());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads connection from registry if they are not loaded.
|
||||
*
|
||||
* @return connections from cache.
|
||||
*/
|
||||
public List<WebConnectionInfo> getConnections() {
|
||||
if (!registryIsLoaded) {
|
||||
addDataSourcesToCache();
|
||||
registryIsLoaded = true;
|
||||
}
|
||||
synchronized (connections) {
|
||||
return new ArrayList<>(connections.values());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* updates data sources based on event in web session
|
||||
*
|
||||
* @param dataSourceIds list of updated connections
|
||||
* @param type type of event
|
||||
*/
|
||||
public synchronized boolean updateProjectDataSources(@NotNull List<String> dataSourceIds, @NotNull WSEventType type) {
|
||||
var sendDataSourceUpdatedEvent = false;
|
||||
DBPDataSourceRegistry registry = getDataSourceRegistry();
|
||||
// save old connections
|
||||
var oldDataSources = dataSourceIds.stream()
|
||||
.map(registry::getDataSource)
|
||||
.filter(Objects::nonNull)
|
||||
.collect(Collectors.toMap(
|
||||
DBPDataSourceContainer::getId,
|
||||
ds -> new DataSourceDescriptor((DataSourceDescriptor) ds, ds.getRegistry())
|
||||
));
|
||||
if (type == WSEventType.DATASOURCE_CREATED || type == WSEventType.DATASOURCE_UPDATED) {
|
||||
registry.refreshConfig(dataSourceIds);
|
||||
}
|
||||
for (String dsId : dataSourceIds) {
|
||||
DataSourceDescriptor ds = (DataSourceDescriptor) registry.getDataSource(dsId);
|
||||
if (ds == null) {
|
||||
continue;
|
||||
}
|
||||
switch (type) {
|
||||
case DATASOURCE_CREATED -> {
|
||||
addConnection(ds);
|
||||
sendDataSourceUpdatedEvent = true;
|
||||
}
|
||||
case DATASOURCE_UPDATED -> // if settings were changed we need to send event
|
||||
sendDataSourceUpdatedEvent |= !ds.equalSettings(oldDataSources.get(dsId));
|
||||
case DATASOURCE_DELETED -> {
|
||||
WebDataSourceUtils.disconnectDataSource(webSession, ds);
|
||||
if (registry instanceof DBPDataSourceRegistryCache dsrc) {
|
||||
dsrc.removeDataSourceFromList(ds);
|
||||
}
|
||||
removeConnection(ds);
|
||||
sendDataSourceUpdatedEvent = true;
|
||||
}
|
||||
default -> {
|
||||
}
|
||||
}
|
||||
}
|
||||
return sendDataSourceUpdatedEvent;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,7 +25,6 @@ public interface CBAuthConstants {
|
||||
String CB_AUTH_ID_REQUEST_PARAM = "authId";
|
||||
String CB_AUTO_LOGIN_REQUEST_PARAM = "autoLogin";
|
||||
String CB_REDIRECT_URL_REQUEST_PARAM = "redirectUrl";
|
||||
String CB_FORCE_SESSIONS_LOGOUT = "forceSessionsLogout";
|
||||
|
||||
// Default max idle time (10 minutes)
|
||||
long MAX_SESSION_IDLE_TIME = 10 * 60 * 1000;
|
||||
|
||||
-3
@@ -46,9 +46,6 @@ public interface SMAuthProviderFederated extends SMSignOutLinkProvider {
|
||||
@Nullable
|
||||
String getAcsLink(String id, @NotNull Map<String, Object> providerConfig) throws DBException;
|
||||
|
||||
@Nullable
|
||||
String getEntityIdLink(String id, @NotNull Map<String, Object> providerConfig) throws DBException;
|
||||
|
||||
@Nullable
|
||||
default String getRedirectLink(String id, @NotNull Map<String, Object> providerConfig) throws DBException {
|
||||
return null;
|
||||
|
||||
-79
@@ -1,79 +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.auth.provider.fa;
|
||||
|
||||
import org.jkiss.code.NotNull;
|
||||
import org.jkiss.code.Nullable;
|
||||
import org.jkiss.dbeaver.model.auth.*;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.Map;
|
||||
|
||||
public abstract class AbstractSessionExternal implements SMSessionExternal {
|
||||
|
||||
@NotNull
|
||||
protected final Map<String, Object> authParameters;
|
||||
@NotNull
|
||||
protected final SMSession parentSession;
|
||||
@NotNull
|
||||
protected final SMAuthSpace space;
|
||||
|
||||
protected AbstractSessionExternal(
|
||||
@NotNull SMSession parentSession,
|
||||
@NotNull SMAuthSpace space,
|
||||
@NotNull Map<String, Object> authParameters
|
||||
) {
|
||||
this.parentSession = parentSession;
|
||||
this.space = space;
|
||||
this.authParameters = authParameters;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public SMAuthSpace getSessionSpace() {
|
||||
return space;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public SMSessionContext getSessionContext() {
|
||||
return this.parentSession.getSessionContext();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public SMSessionPrincipal getSessionPrincipal() {
|
||||
return parentSession.getSessionPrincipal();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public LocalDateTime getSessionStart() {
|
||||
return parentSession.getSessionStart();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
// do nothing
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Object> getAuthParameters() {
|
||||
return authParameters;
|
||||
}
|
||||
}
|
||||
+12
-59
@@ -24,11 +24,12 @@ 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.*;
|
||||
import org.jkiss.dbeaver.model.admin.sessions.DBAServerSessionManager;
|
||||
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.app.DBPProject;
|
||||
import org.jkiss.dbeaver.model.connection.DBPAuthModelDescriptor;
|
||||
import org.jkiss.dbeaver.model.connection.DBPConnectionConfiguration;
|
||||
@@ -39,7 +40,6 @@ 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,17 +56,6 @@ 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;
|
||||
@@ -254,25 +243,22 @@ public class WebConnectionInfo {
|
||||
List<String> features = new ArrayList<>();
|
||||
|
||||
if (dataSourceContainer.isConnected()) {
|
||||
features.add(FEATURE_CONNECTED);
|
||||
if (!getTools().isEmpty()) {
|
||||
features.add(FEATURE_HAS_TOOLS);
|
||||
}
|
||||
features.add("connected");
|
||||
}
|
||||
if (dataSourceContainer.isHidden()) {
|
||||
features.add(FEATURE_VIRTUAL);
|
||||
features.add("virtual");
|
||||
}
|
||||
if (dataSourceContainer.isTemporary()) {
|
||||
features.add(FEATURE_TEMPORARY);
|
||||
features.add("temporary");
|
||||
}
|
||||
if (dataSourceContainer.isConnectionReadOnly()) {
|
||||
features.add(FEATURE_READ_ONLY);
|
||||
features.add("readOnly");
|
||||
}
|
||||
if (dataSourceContainer.isProvided()) {
|
||||
features.add(FEATURE_PROVIDED);
|
||||
features.add("provided");
|
||||
}
|
||||
if (dataSourceContainer.isManageable()) {
|
||||
features.add(FEATURE_MANAGEABLE);
|
||||
features.add("manageable");
|
||||
}
|
||||
|
||||
return features.toArray(new String[0]);
|
||||
@@ -398,16 +384,6 @@ 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();
|
||||
@@ -445,10 +421,10 @@ public class WebConnectionInfo {
|
||||
|
||||
private boolean hasProjectPermission(RMProjectPermission projectPermission) {
|
||||
DBPProject project = dataSourceContainer.getProject();
|
||||
if (!(project instanceof WebProjectImpl webProject)) {
|
||||
if (!(project instanceof WebProjectImpl)) {
|
||||
return false;
|
||||
}
|
||||
return SMUtils.hasProjectPermission(session, webProject.getRMProject(), projectPermission);
|
||||
return SMUtils.hasProjectPermission(session, ((WebProjectImpl) project).getRmProject(), projectPermission);
|
||||
}
|
||||
|
||||
private boolean canViewReadOnlyConnections() {
|
||||
@@ -472,15 +448,6 @@ 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()
|
||||
@@ -489,18 +456,4 @@ 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,13 +53,11 @@ 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
|
||||
@@ -74,7 +72,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);
|
||||
@@ -96,12 +94,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,18 +28,22 @@ import java.io.StringWriter;
|
||||
*/
|
||||
public class WebServerError {
|
||||
|
||||
private final String message;
|
||||
private final String stackTrace;
|
||||
private final String errorType;
|
||||
private final String errorCode;
|
||||
private String message;
|
||||
private String stackTrace;
|
||||
private String errorType;
|
||||
private String errorCode;
|
||||
|
||||
public WebServerError(Throwable ex) {
|
||||
this.message = ex.getMessage();
|
||||
StringWriter buf = new StringWriter();
|
||||
ex.printStackTrace(new PrintWriter(buf, true));
|
||||
this.stackTrace = buf.toString();
|
||||
errorCode = String.valueOf(SQLState.getCodeFromException(ex));
|
||||
errorType = ex instanceof DBQuotaException ? DBWebException.ERROR_CODE_QUOTA_EXCEEDED : null;
|
||||
if (ex instanceof DBException) {
|
||||
errorCode = String.valueOf(((DBException) ex).getErrorCode());
|
||||
}
|
||||
if (ex instanceof DBQuotaException) {
|
||||
errorType = DBWebException.ERROR_CODE_QUOTA_EXCEEDED;
|
||||
}
|
||||
}
|
||||
|
||||
public String getMessage() {
|
||||
|
||||
+144
@@ -0,0 +1,144 @@
|
||||
/*
|
||||
* 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();
|
||||
}
|
||||
}
|
||||
}
|
||||
+2
-5
@@ -18,16 +18,13 @@ 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> {
|
||||
|
||||
@NotNull
|
||||
public Gson getGson() {
|
||||
protected Gson getGson() {
|
||||
return getGsonBuilder().create();
|
||||
}
|
||||
|
||||
|
||||
-8
@@ -33,14 +33,11 @@ public abstract class BaseWebAppConfiguration implements WebAppConfiguration {
|
||||
protected boolean resourceManagerEnabled;
|
||||
protected boolean showReadOnlyConnectionInfo;
|
||||
protected String[] enabledFeatures;
|
||||
protected String[] disabledBetaFeatures;
|
||||
|
||||
|
||||
public BaseWebAppConfiguration() {
|
||||
this.plugins = new LinkedHashMap<>();
|
||||
this.resourceManagerEnabled = true;
|
||||
this.enabledFeatures = null;
|
||||
this.disabledBetaFeatures = new String[0];
|
||||
this.showReadOnlyConnectionInfo = false;
|
||||
}
|
||||
|
||||
@@ -49,7 +46,6 @@ public abstract class BaseWebAppConfiguration implements WebAppConfiguration {
|
||||
this.defaultUserTeam = src.defaultUserTeam;
|
||||
this.resourceManagerEnabled = src.resourceManagerEnabled;
|
||||
this.enabledFeatures = src.enabledFeatures;
|
||||
this.disabledBetaFeatures = src.disabledBetaFeatures;
|
||||
this.showReadOnlyConnectionInfo = src.showReadOnlyConnectionInfo;
|
||||
}
|
||||
|
||||
@@ -108,8 +104,4 @@ public abstract class BaseWebAppConfiguration implements WebAppConfiguration {
|
||||
public boolean isShowReadOnlyConnectionInfo() {
|
||||
return showReadOnlyConnectionInfo;
|
||||
}
|
||||
|
||||
public String[] getDisabledBetaFeatures() {
|
||||
return disabledBetaFeatures;
|
||||
}
|
||||
}
|
||||
|
||||
+45
-26
@@ -16,7 +16,13 @@
|
||||
*/
|
||||
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;
|
||||
@@ -24,16 +30,17 @@ import org.jkiss.code.Nullable;
|
||||
import org.jkiss.dbeaver.DBException;
|
||||
import org.jkiss.dbeaver.Log;
|
||||
import org.jkiss.dbeaver.model.DBFileController;
|
||||
import org.jkiss.dbeaver.model.app.DBPPlatform;
|
||||
import org.jkiss.dbeaver.model.app.DBPWorkspace;
|
||||
import org.jkiss.dbeaver.model.auth.SMCredentialsProvider;
|
||||
import org.jkiss.dbeaver.model.auth.SMSessionContext;
|
||||
import org.jkiss.dbeaver.model.data.json.JSONUtils;
|
||||
import org.jkiss.dbeaver.model.impl.app.ApplicationRegistry;
|
||||
import org.jkiss.dbeaver.model.impl.app.BaseApplicationImpl;
|
||||
import org.jkiss.dbeaver.model.impl.app.BaseWorkspaceImpl;
|
||||
import org.jkiss.dbeaver.model.rm.RMController;
|
||||
import org.jkiss.dbeaver.model.rm.RMProject;
|
||||
import org.jkiss.dbeaver.model.secret.DBSSecretController;
|
||||
import org.jkiss.dbeaver.model.websocket.event.WSEventController;
|
||||
import org.jkiss.dbeaver.registry.BaseApplicationImpl;
|
||||
import org.jkiss.dbeaver.runtime.IVariableResolver;
|
||||
import org.jkiss.dbeaver.utils.GeneralUtils;
|
||||
import org.jkiss.dbeaver.utils.RuntimeUtils;
|
||||
@@ -54,9 +61,14 @@ 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(
|
||||
@@ -90,7 +102,16 @@ public abstract class BaseWebApplication extends BaseApplicationImpl implements
|
||||
|
||||
protected boolean loadServerConfiguration() throws DBException {
|
||||
Path configFilePath = getMainConfigurationFilePath().toAbsolutePath();
|
||||
Path configFolder = configFilePath.getParent();
|
||||
|
||||
// Configure logging
|
||||
Path logbackConfigPath = getLogbackConfigPath(configFolder);
|
||||
|
||||
if (logbackConfigPath == null) {
|
||||
System.err.println("Can't find slf4j configuration file in " + configFilePath.getParent());
|
||||
} else {
|
||||
System.setProperty("logback.configurationFile", logbackConfigPath.toString());
|
||||
}
|
||||
Log.setLogHandler(new SLF4JLogHandler());
|
||||
|
||||
// Load config file
|
||||
@@ -155,6 +176,19 @@ 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.
|
||||
@@ -173,9 +207,7 @@ public abstract class BaseWebApplication extends BaseApplicationImpl implements
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public static void patchConfigurationWithProperties(
|
||||
Map<String, Object> configProps, IVariableResolver varResolver
|
||||
) {
|
||||
public static void patchConfigurationWithProperties(Map<String, Object> configProps, IVariableResolver varResolver) {
|
||||
for (Map.Entry<String, Object> entry : configProps.entrySet()) {
|
||||
Object propValue = entry.getValue();
|
||||
if (propValue instanceof String) {
|
||||
@@ -210,27 +242,14 @@ public abstract class BaseWebApplication extends BaseApplicationImpl implements
|
||||
protected abstract void startServer() throws DBException;
|
||||
|
||||
@Override
|
||||
public synchronized String getApplicationInstanceId() throws DBException {
|
||||
if (instanceId == null) {
|
||||
try {
|
||||
byte[] macAddress = RuntimeUtils.getLocalMacAddress();
|
||||
instanceId = String.join(
|
||||
"_",
|
||||
ApplicationRegistry.getInstance().getApplication().getId(),
|
||||
getWorkspaceIdProperty(), // workspace id is read from property file
|
||||
CommonUtils.toHexString(macAddress),
|
||||
CommonUtils.toString(getServerPort())
|
||||
);
|
||||
} catch (Exception e) {
|
||||
throw new DBException("Error during generation instance id generation", e);
|
||||
}
|
||||
public String getApplicationInstanceId() throws DBException {
|
||||
try {
|
||||
byte[] macAddress = RuntimeUtils.getLocalMacAddress();
|
||||
String appId = ApplicationRegistry.getInstance().getApplication().getId();
|
||||
return appId + "_" + CommonUtils.toHexString(macAddress) + getServerPort();
|
||||
} catch (Exception e) {
|
||||
throw new DBException("Error during generation instance id generation", e);
|
||||
}
|
||||
return instanceId;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public String getWorkspaceIdProperty() throws DBException {
|
||||
return BaseWorkspaceImpl.readWorkspaceIdProperty();
|
||||
}
|
||||
|
||||
public String getApplicationId() {
|
||||
|
||||
+10
-9
@@ -16,6 +16,9 @@
|
||||
*/
|
||||
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;
|
||||
@@ -24,13 +27,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
|
||||
@@ -38,10 +41,6 @@ import java.util.Map;
|
||||
public interface WebApplication extends DBPApplication {
|
||||
boolean isConfigurationMode();
|
||||
|
||||
default boolean isInitializationMode() {
|
||||
return false;
|
||||
}
|
||||
|
||||
WebAppConfiguration getAppConfiguration();
|
||||
|
||||
WebServerConfiguration getServerConfiguration();
|
||||
@@ -54,6 +53,12 @@ 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;
|
||||
@@ -90,8 +95,4 @@ public interface WebApplication extends DBPApplication {
|
||||
int getServerPort();
|
||||
|
||||
boolean isLicenseRequired();
|
||||
|
||||
default void getStatusInfo(Map<String, Object> infoMap) {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
-10
@@ -16,12 +16,9 @@
|
||||
*/
|
||||
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.
|
||||
@@ -34,11 +31,4 @@ public interface WebServerConfigurationController<T extends WebServerConfigurati
|
||||
*/
|
||||
void loadServerConfiguration(Path configPath) throws DBException;
|
||||
|
||||
@NotNull
|
||||
default Map<String, Object> getOriginalConfigurationProperties() {
|
||||
return Map.of();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
Gson getGson();
|
||||
}
|
||||
|
||||
+28
-26
@@ -1,18 +1,18 @@
|
||||
/*
|
||||
* DBeaver - Universal Database Manager
|
||||
* Copyright (C) 2010-2024 DBeaver Corp and others
|
||||
* Copyright (C) 2010-2024 DBeaver Corp
|
||||
*
|
||||
* 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
|
||||
* All Rights Reserved.
|
||||
*
|
||||
* 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.
|
||||
* NOTICE: All information contained herein is, and remains
|
||||
* the property of DBeaver Corp and its suppliers, if any.
|
||||
* The intellectual and technical concepts contained
|
||||
* herein are proprietary to DBeaver Corp and its suppliers
|
||||
* and may be covered by U.S. and Foreign Patents,
|
||||
* patents in process, and are protected by trade secret or copyright law.
|
||||
* Dissemination of this information or reproduction of this material
|
||||
* is strictly forbidden unless prior written permission is obtained
|
||||
* from DBeaver Corp.
|
||||
*/
|
||||
|
||||
package io.cloudbeaver.model.rm;
|
||||
@@ -20,6 +20,7 @@ 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;
|
||||
@@ -36,6 +37,7 @@ 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;
|
||||
|
||||
@@ -91,8 +93,8 @@ public class DBNResourceManagerProject extends DBNAbstractResourceManagerNode {
|
||||
}
|
||||
|
||||
@Override
|
||||
public DBNResourceManagerResource[] getChildren(@NotNull DBRProgressMonitor monitor) throws DBException {
|
||||
if (children == null && !monitor.isForceCacheUsage()) {
|
||||
public DBNResourceManagerResource[] getChildren(DBRProgressMonitor monitor) throws DBException {
|
||||
if (children == null) {
|
||||
List<DBNResourceManagerResource> rfList = new ArrayList<>();
|
||||
for (RMResource resource : getResourceController().listResources(
|
||||
project.getId(), null, null, true, false, false)) {
|
||||
@@ -121,15 +123,21 @@ public class DBNResourceManagerProject extends DBNAbstractResourceManagerNode {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public DBPProject getOwnerProjectOrNull() {
|
||||
public String toString() {
|
||||
return getNodeDisplayName();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public DBPProject getOwnerProject() {
|
||||
List<? extends DBPProject> globalProjects = getModel().getModelProjects();
|
||||
if (globalProjects != null) {
|
||||
for (DBPProject modelProject : globalProjects) {
|
||||
if (CommonUtils.equalObjects(modelProject.getId(), project.getId())) {
|
||||
return modelProject;
|
||||
}
|
||||
if (globalProjects == null) {
|
||||
return null;
|
||||
}
|
||||
for (DBPProject modelProject : globalProjects) {
|
||||
if (CommonUtils.equalObjects(modelProject.getId(), project.getId())) {
|
||||
return modelProject;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
@@ -140,10 +148,4 @@ 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();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+15
-16
@@ -1,18 +1,18 @@
|
||||
/*
|
||||
* DBeaver - Universal Database Manager
|
||||
* Copyright (C) 2010-2024 DBeaver Corp and others
|
||||
* Copyright (C) 2010-2024 DBeaver Corp
|
||||
*
|
||||
* 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
|
||||
* All Rights Reserved.
|
||||
*
|
||||
* 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.
|
||||
* NOTICE: All information contained herein is, and remains
|
||||
* the property of DBeaver Corp and its suppliers, if any.
|
||||
* The intellectual and technical concepts contained
|
||||
* herein are proprietary to DBeaver Corp and its suppliers
|
||||
* and may be covered by U.S. and Foreign Patents,
|
||||
* patents in process, and are protected by trade secret or copyright law.
|
||||
* Dissemination of this information or reproduction of this material
|
||||
* is strictly forbidden unless prior written permission is obtained
|
||||
* from DBeaver Corp.
|
||||
*/
|
||||
|
||||
package io.cloudbeaver.model.rm;
|
||||
@@ -110,8 +110,8 @@ public class DBNResourceManagerResource extends DBNAbstractResourceManagerNode {
|
||||
}
|
||||
|
||||
@Override
|
||||
public DBNNode[] getChildren(@NotNull DBRProgressMonitor monitor) throws DBException {
|
||||
if (children == null && !monitor.isForceCacheUsage()) {
|
||||
public DBNNode[] getChildren(DBRProgressMonitor monitor) throws DBException {
|
||||
if (children == null) {
|
||||
List<DBNResourceManagerResource> rfList = new ArrayList<>();
|
||||
for (RMResource resource : getResourceController().listResources(
|
||||
getResourceProject().getId(), getResourceFolder(), null, true, false, false)) {
|
||||
@@ -203,10 +203,9 @@ public class DBNResourceManagerResource extends DBNAbstractResourceManagerNode {
|
||||
return resource;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public DBPProject getOwnerProjectOrNull() {
|
||||
return getParentNode().getOwnerProjectOrNull();
|
||||
public DBPProject getOwnerProject() {
|
||||
return getParentNode().getOwnerProject();
|
||||
}
|
||||
|
||||
public RMResource getResource() {
|
||||
|
||||
+15
-21
@@ -1,25 +1,24 @@
|
||||
/*
|
||||
* DBeaver - Universal Database Manager
|
||||
* Copyright (C) 2010-2024 DBeaver Corp and others
|
||||
* Copyright (C) 2010-2024 DBeaver Corp
|
||||
*
|
||||
* 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
|
||||
* All Rights Reserved.
|
||||
*
|
||||
* 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.
|
||||
* NOTICE: All information contained herein is, and remains
|
||||
* the property of DBeaver Corp and its suppliers, if any.
|
||||
* The intellectual and technical concepts contained
|
||||
* herein are proprietary to DBeaver Corp and its suppliers
|
||||
* and may be covered by U.S. and Foreign Patents,
|
||||
* patents in process, and are protected by trade secret or copyright law.
|
||||
* Dissemination of this information or reproduction of this material
|
||||
* is strictly forbidden unless prior written permission is obtained
|
||||
* from DBeaver Corp.
|
||||
*/
|
||||
|
||||
package io.cloudbeaver.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;
|
||||
@@ -82,8 +81,8 @@ public class DBNResourceManagerRoot extends DBNNode implements DBPHiddenObject,
|
||||
}
|
||||
|
||||
@Override
|
||||
public DBNResourceManagerProject[] getChildren(@NotNull DBRProgressMonitor monitor) throws DBException {
|
||||
if (projects == null && !monitor.isForceCacheUsage()) {
|
||||
public DBNResourceManagerProject[] getChildren(DBRProgressMonitor monitor) throws DBException {
|
||||
if (projects == null) {
|
||||
List<? extends DBPProject> projectList = getParentNode().getModel().getModelProjects();
|
||||
if (CommonUtils.isEmpty(projectList)) {
|
||||
return new DBNResourceManagerProject[0];
|
||||
@@ -155,13 +154,8 @@ public class DBNResourceManagerRoot extends DBNNode implements DBPHiddenObject,
|
||||
return;
|
||||
}
|
||||
var projectNode = getProjectNode(project);
|
||||
var rmResourcePath = Arrays.stream(resourcePath.split("/"))
|
||||
.filter(CommonUtils::isNotEmpty)
|
||||
.toList();
|
||||
projectNode.ifPresent(
|
||||
dbnResourceManagerProject -> dbnResourceManagerProject.removeChildResourceNode(new ArrayDeque<>(
|
||||
rmResourcePath))
|
||||
);
|
||||
var rmResourcePath = Arrays.asList(resourcePath.split("/"));
|
||||
projectNode.ifPresent(dbnResourceManagerProject -> dbnResourceManagerProject.removeChildResourceNode(new ArrayDeque<>(rmResourcePath)));
|
||||
}
|
||||
|
||||
@NotNull
|
||||
|
||||
+25
-62
@@ -37,7 +37,6 @@ 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;
|
||||
@@ -131,7 +130,12 @@ public class LocalResourceController implements RMController {
|
||||
if (project == null || refresh) {
|
||||
SessionContextImpl sessionContext = new SessionContextImpl(null);
|
||||
RMProject rmProject = makeProjectFromId(projectId, false);
|
||||
project = new InternalWebProjectImpl(sessionContext, rmProject);
|
||||
project = new BaseWebProjectImpl(
|
||||
workspace,
|
||||
this,
|
||||
sessionContext,
|
||||
rmProject,
|
||||
(container) -> true);
|
||||
projectRegistries.put(projectId, project);
|
||||
}
|
||||
return project;
|
||||
@@ -252,20 +256,18 @@ public class LocalResourceController implements RMController {
|
||||
return new RMProject[0];
|
||||
}
|
||||
var projects = new ArrayList<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);
|
||||
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);
|
||||
}
|
||||
return projects.stream()
|
||||
.filter(Objects::nonNull)
|
||||
.toArray(RMProject[]::new);
|
||||
} catch (IOException e) {
|
||||
throw new DBException("Error reading shared projects", e);
|
||||
}
|
||||
@@ -579,7 +581,7 @@ public class LocalResourceController implements RMController {
|
||||
@NotNull String resourcePath,
|
||||
boolean isFolder
|
||||
) throws DBException {
|
||||
try (var ignoredLock = lockController.lockProject(projectId, "createResource")) {
|
||||
try (var projectLock = lockController.lockProject(projectId, "createResource")) {
|
||||
validateResourcePath(resourcePath);
|
||||
Path targetPath = getTargetPath(projectId, resourcePath);
|
||||
if (Files.exists(targetPath)) {
|
||||
@@ -611,7 +613,7 @@ public class LocalResourceController implements RMController {
|
||||
@NotNull String oldResourcePath,
|
||||
@NotNull String newResourcePath
|
||||
) throws DBException {
|
||||
try (var ignoredLock = lockController.lockProject(projectId, "moveResource")) {
|
||||
try (var projectLock = lockController.lockProject(projectId, "moveResource")) {
|
||||
var normalizedOldResourcePath = CommonUtils.normalizeResourcePath(oldResourcePath);
|
||||
var normalizedNewResourcePath = CommonUtils.normalizeResourcePath(newResourcePath);
|
||||
if (log.isDebugEnabled()) {
|
||||
@@ -626,9 +628,6 @@ 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) {
|
||||
@@ -680,10 +679,11 @@ public class LocalResourceController implements RMController {
|
||||
|
||||
@Override
|
||||
public void deleteResource(@NotNull String projectId, @NotNull String resourcePath, boolean recursive) throws DBException {
|
||||
try (var ignoredLock = lockController.lockProject(projectId, "deleteResource")) {
|
||||
try (var projectLock = 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 ignoredLock = lockController.lockProject(projectId, "setResourceContents")) {
|
||||
try (var lock = 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 ignoredLock = lockController.lockProject(projectId, "resourcePropertyUpdate")) {
|
||||
try (var projectLock = lockController.lockProject(projectId, "resourcePropertyUpdate")) {
|
||||
validateResourcePath(resourcePath);
|
||||
BaseWebProjectImpl webProject = getWebProject(projectId, false);
|
||||
doFileWriteOperation(projectId, webProject.getMetadataFilePath(),
|
||||
@@ -836,27 +836,6 @@ 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) {
|
||||
@@ -1110,7 +1089,7 @@ public class LocalResourceController implements RMController {
|
||||
);
|
||||
}
|
||||
if (readProperties) {
|
||||
final BaseProjectImpl project = getWebProject(projectId, true);
|
||||
final BaseProjectImpl project = (BaseProjectImpl) getWebProject(projectId, true);
|
||||
final String resourcePath = getProjectRelativePath(projectId, path);
|
||||
final Map<String, Object> properties = project.getResourceProperties(resourcePath);
|
||||
|
||||
@@ -1155,7 +1134,7 @@ public class LocalResourceController implements RMController {
|
||||
);
|
||||
}
|
||||
|
||||
private void fireRmProjectAddEvent(@NotNull RMProject project) {
|
||||
private void fireRmProjectAddEvent(@NotNull RMProject project) throws DBException {
|
||||
RMEventManager.fireEvent(
|
||||
new RMEvent(
|
||||
RMEvent.Action.RESOURCE_ADD,
|
||||
@@ -1285,20 +1264,4 @@ 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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+8
-22
@@ -51,14 +51,14 @@ public abstract class BaseWebSession extends AbstractSessionPersistent {
|
||||
@NotNull
|
||||
protected final WebUserContext userContext;
|
||||
@NotNull
|
||||
protected final WebApplication application;
|
||||
protected final WebAuthApplication 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 WebApplication application) throws DBException {
|
||||
public BaseWebSession(@NotNull String id, @NotNull WebAuthApplication application) throws DBException {
|
||||
this.id = id;
|
||||
this.application = application;
|
||||
this.createTime = System.currentTimeMillis();
|
||||
@@ -72,7 +72,6 @@ public abstract class BaseWebSession extends AbstractSessionPersistent {
|
||||
return new WebUserContext(this.application, this.workspace);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public WebSessionWorkspace getWorkspace() {
|
||||
return workspace;
|
||||
}
|
||||
@@ -174,24 +173,14 @@ public abstract class BaseWebSession extends AbstractSessionPersistent {
|
||||
@Override
|
||||
public void close() {
|
||||
super.close();
|
||||
cleanUpSession(true);
|
||||
}
|
||||
|
||||
public void close(boolean clearTokens, boolean sendSessionExpiredEvent) {
|
||||
cleanUpSession(sendSessionExpiredEvent);
|
||||
}
|
||||
|
||||
private void cleanUpSession(boolean sendSessionExpiredEvent) {
|
||||
var sessionExpiredEvent = new WSSessionExpiredEvent();
|
||||
application.getEventController().addEvent(new WSEventDeleteTempFile(getSessionId()));
|
||||
synchronized (sessionEventHandlers) {
|
||||
var sessionExpiredEvent = new WSSessionExpiredEvent();
|
||||
for (CBWebSessionEventHandler sessionEventHandler : sessionEventHandlers) {
|
||||
if (sendSessionExpiredEvent) {
|
||||
try {
|
||||
sessionEventHandler.handleWebSessionEvent(sessionExpiredEvent);
|
||||
} catch (DBException e) {
|
||||
log.warn("Failed to send session expiration event", e);
|
||||
}
|
||||
try {
|
||||
sessionEventHandler.handleWebSessionEvent(sessionExpiredEvent);
|
||||
} catch (DBException e) {
|
||||
log.warn("Failed to send session expiration event", e);
|
||||
}
|
||||
sessionEventHandler.close();
|
||||
}
|
||||
@@ -233,9 +222,6 @@ public abstract class BaseWebSession extends AbstractSessionPersistent {
|
||||
|
||||
@Property
|
||||
public long getRemainingTime() {
|
||||
if (application instanceof WebAuthApplication authApplication) {
|
||||
return authApplication.getMaxSessionIdleTime() + lastAccessTime - System.currentTimeMillis();
|
||||
}
|
||||
return Integer.MAX_VALUE;
|
||||
return application.getMaxSessionIdleTime() + lastAccessTime - System.currentTimeMillis();
|
||||
}
|
||||
}
|
||||
|
||||
+11
-11
@@ -1,18 +1,18 @@
|
||||
/*
|
||||
* DBeaver - Universal Database Manager
|
||||
* Copyright (C) 2010-2024 DBeaver Corp and others
|
||||
* Copyright (C) 2010-2024 DBeaver Corp
|
||||
*
|
||||
* 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
|
||||
* All Rights Reserved.
|
||||
*
|
||||
* 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.
|
||||
* NOTICE: All information contained herein is, and remains
|
||||
* the property of DBeaver Corp and its suppliers, if any.
|
||||
* The intellectual and technical concepts contained
|
||||
* herein are proprietary to DBeaver Corp and its suppliers
|
||||
* and may be covered by U.S. and Foreign Patents,
|
||||
* patents in process, and are protected by trade secret or copyright law.
|
||||
* Dissemination of this information or reproduction of this material
|
||||
* is strictly forbidden unless prior written permission is obtained
|
||||
* from DBeaver Corp.
|
||||
*/
|
||||
package io.cloudbeaver.model.session;
|
||||
|
||||
|
||||
-57
@@ -1,57 +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.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;
|
||||
}
|
||||
}
|
||||
+258
-100
@@ -19,18 +19,21 @@ package io.cloudbeaver.model.session;
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.GsonBuilder;
|
||||
import com.google.gson.InstanceCreator;
|
||||
import com.google.gson.Strictness;
|
||||
import io.cloudbeaver.*;
|
||||
import io.cloudbeaver.DBWConstants;
|
||||
import io.cloudbeaver.DBWebException;
|
||||
import io.cloudbeaver.DataSourceFilter;
|
||||
import io.cloudbeaver.WebProjectImpl;
|
||||
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;
|
||||
@@ -40,8 +43,12 @@ 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;
|
||||
@@ -49,7 +56,6 @@ import org.jkiss.dbeaver.model.fs.DBFFileSystemManager;
|
||||
import org.jkiss.dbeaver.model.meta.Association;
|
||||
import org.jkiss.dbeaver.model.meta.Property;
|
||||
import org.jkiss.dbeaver.model.navigator.DBNModel;
|
||||
import org.jkiss.dbeaver.model.preferences.DBPPreferenceStore;
|
||||
import org.jkiss.dbeaver.model.rm.RMController;
|
||||
import org.jkiss.dbeaver.model.rm.RMProject;
|
||||
import org.jkiss.dbeaver.model.rm.RMProjectType;
|
||||
@@ -61,11 +67,15 @@ import org.jkiss.dbeaver.model.runtime.ProxyProgressMonitor;
|
||||
import org.jkiss.dbeaver.model.security.SMAdminController;
|
||||
import org.jkiss.dbeaver.model.security.SMConstants;
|
||||
import org.jkiss.dbeaver.model.security.SMController;
|
||||
import org.jkiss.dbeaver.model.security.SMObjectType;
|
||||
import org.jkiss.dbeaver.model.security.user.SMObjectPermissions;
|
||||
import org.jkiss.dbeaver.model.sql.DBQuotaException;
|
||||
import org.jkiss.dbeaver.model.websocket.event.MessageType;
|
||||
import org.jkiss.dbeaver.model.websocket.event.WSEventType;
|
||||
import org.jkiss.dbeaver.model.websocket.event.WSSessionLogUpdatedEvent;
|
||||
import org.jkiss.dbeaver.registry.DataSourceDescriptor;
|
||||
import org.jkiss.dbeaver.runtime.DBWorkbench;
|
||||
import org.jkiss.dbeaver.runtime.jobs.DisconnectJob;
|
||||
import org.jkiss.utils.CommonUtils;
|
||||
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
@@ -78,7 +88,6 @@ 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 {
|
||||
|
||||
@@ -95,10 +104,12 @@ public class WebSession extends BaseWebSession
|
||||
private String lastRemoteAddr;
|
||||
private String lastRemoteUserAgent;
|
||||
|
||||
private Set<String> accessibleConnectionIds = Collections.emptySet();
|
||||
|
||||
private String locale;
|
||||
private boolean cacheExpired;
|
||||
|
||||
protected WebSessionGlobalProjectImpl globalProject;
|
||||
private final Map<String, WebConnectionInfo> connections = new HashMap<>();
|
||||
private final List<WebServerMessage> sessionMessages = new ArrayList<>();
|
||||
|
||||
private final Map<String, WebAsyncTaskInfo> asyncTasks = new HashMap<>();
|
||||
@@ -112,34 +123,21 @@ public class WebSession extends BaseWebSession
|
||||
private final Map<String, DBWSessionHandler> sessionHandlers;
|
||||
|
||||
public WebSession(
|
||||
@NotNull WebHttpRequestInfo requestInfo,
|
||||
@NotNull HttpServletRequest request,
|
||||
@NotNull WebAuthApplication application,
|
||||
@NotNull Map<String, DBWSessionHandler> sessionHandlers
|
||||
) throws DBException {
|
||||
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);
|
||||
super(request.getSession().getId(), 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
|
||||
@@ -153,8 +151,8 @@ public class WebSession extends BaseWebSession
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public WebSessionProjectImpl getSingletonProject() {
|
||||
@NotNull
|
||||
public DBPProject getSingletonProject() {
|
||||
return getWorkspace().getActiveProject();
|
||||
}
|
||||
|
||||
@@ -251,6 +249,69 @@ 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();
|
||||
@@ -271,7 +332,7 @@ public class WebSession extends BaseWebSession
|
||||
this.navigatorModel.dispose();
|
||||
this.navigatorModel = null;
|
||||
}
|
||||
this.globalProject = null;
|
||||
this.connections.clear();
|
||||
|
||||
loadProjects();
|
||||
|
||||
@@ -291,6 +352,7 @@ public class WebSession extends BaseWebSession
|
||||
// No anonymous mode in distributed apps
|
||||
return;
|
||||
}
|
||||
refreshAccessibleConnectionIds();
|
||||
try {
|
||||
RMController controller = getRmController();
|
||||
RMProject[] rmProjects = controller.listAccessibleProjects();
|
||||
@@ -310,28 +372,57 @@ public class WebSession extends BaseWebSession
|
||||
}
|
||||
}
|
||||
|
||||
private WebSessionProjectImpl createWebProject(RMProject project) {
|
||||
WebSessionProjectImpl sessionProject;
|
||||
if (project.isGlobal()) {
|
||||
sessionProject = createGlobalProject(project);
|
||||
} else {
|
||||
sessionProject = new WebSessionProjectImpl(this, project);
|
||||
}
|
||||
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);
|
||||
// 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;
|
||||
}
|
||||
|
||||
protected WebSessionProjectImpl createGlobalProject(RMProject project) {
|
||||
globalProject = new WebSessionGlobalProjectImpl(this, project);
|
||||
globalProject.refreshAccessibleConnectionIds();
|
||||
return globalProject;
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
private void resetSessionCache() throws DBCException {
|
||||
@@ -349,7 +440,17 @@ public class WebSession extends BaseWebSession
|
||||
}
|
||||
|
||||
private void resetNavigationModel() {
|
||||
getWorkspace().getProjects().forEach(WebSessionProjectImpl::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();
|
||||
}
|
||||
}
|
||||
|
||||
if (this.navigatorModel != null) {
|
||||
this.navigatorModel.dispose();
|
||||
@@ -363,9 +464,7 @@ public class WebSession extends BaseWebSession
|
||||
authAsAnonymousUser();
|
||||
} else if (getUserId() != null) {
|
||||
userContext.refreshPermissions();
|
||||
if (globalProject != null) {
|
||||
globalProject.refreshAccessibleConnectionIds();
|
||||
}
|
||||
refreshAccessibleConnectionIds();
|
||||
}
|
||||
|
||||
} catch (Exception e) {
|
||||
@@ -374,6 +473,32 @@ 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()) {
|
||||
@@ -432,12 +557,75 @@ public class WebSession extends BaseWebSession
|
||||
}
|
||||
}
|
||||
|
||||
public synchronized void updateSessionParameters(WebHttpRequestInfo requestInfo) {
|
||||
this.lastRemoteAddr = requestInfo.getLastRemoteAddress();
|
||||
this.lastRemoteUserAgent = requestInfo.getLastRemoteUserAgent();
|
||||
public synchronized void updateSessionParameters(HttpServletRequest request) {
|
||||
this.lastRemoteAddr = request.getRemoteAddr();
|
||||
this.lastRemoteUserAgent = request.getHeader("User-Agent");
|
||||
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 {
|
||||
@@ -455,25 +643,6 @@ public class WebSession extends BaseWebSession
|
||||
super.close();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close(boolean clearTokens, boolean sendSessionExpiredEvent) {
|
||||
try {
|
||||
resetNavigationModel();
|
||||
resetSessionCache();
|
||||
} catch (Throwable e) {
|
||||
log.error(e);
|
||||
}
|
||||
if (clearTokens) {
|
||||
try {
|
||||
clearAuthTokens();
|
||||
} catch (Exception e) {
|
||||
log.error("Error closing web session tokens");
|
||||
}
|
||||
}
|
||||
this.userContext.setUser(null);
|
||||
super.close(clearTokens, sendSessionExpiredEvent);
|
||||
}
|
||||
|
||||
private List<WebAuthInfo> clearAuthTokens() throws DBException {
|
||||
ArrayList<WebAuthInfo> tokensCopy;
|
||||
synchronized (authTokens) {
|
||||
@@ -621,7 +790,7 @@ public class WebSession extends BaseWebSession
|
||||
synchronized (attributes) {
|
||||
Object value = attributes.get(name);
|
||||
if (value instanceof PersistentAttribute persistentAttribute) {
|
||||
value = persistentAttribute.value();
|
||||
value = persistentAttribute.getValue();
|
||||
}
|
||||
return (T) value;
|
||||
}
|
||||
@@ -637,7 +806,7 @@ public class WebSession extends BaseWebSession
|
||||
synchronized (attributes) {
|
||||
Object value = attributes.get(name);
|
||||
if (value instanceof PersistentAttribute persistentAttribute) {
|
||||
value = persistentAttribute.value();
|
||||
value = persistentAttribute.getValue();
|
||||
}
|
||||
if (value == null) {
|
||||
value = creator.apply(null);
|
||||
@@ -780,12 +949,9 @@ public class WebSession extends BaseWebSession
|
||||
}
|
||||
configuration.setRuntimeAttribute(RUNTIME_PARAM_AUTH_INFOS, getAllAuthInfo());
|
||||
|
||||
WebSessionProjectImpl project = getProjectById(dataSourceContainer.getProject().getId());
|
||||
if (project != null) {
|
||||
WebConnectionInfo webConnectionInfo = project.findWebConnectionInfo(dataSourceContainer.getId());
|
||||
if (webConnectionInfo != null) {
|
||||
WebDataSourceUtils.saveCredentialsInDataSource(webConnectionInfo, dataSourceContainer, configuration);
|
||||
}
|
||||
WebConnectionInfo webConnectionInfo = findWebConnectionInfo(dataSourceContainer.getProject().getId(), dataSourceContainer.getId());
|
||||
if (webConnectionInfo != null) {
|
||||
WebDataSourceUtils.saveCredentialsInDataSource(webConnectionInfo, dataSourceContainer, configuration);
|
||||
}
|
||||
|
||||
// uncommented because we had the problem with non-native auth models
|
||||
@@ -794,7 +960,7 @@ public class WebSession extends BaseWebSession
|
||||
|
||||
InstanceCreator<DBAAuthCredentials> credTypeAdapter = type -> credentials;
|
||||
Gson credGson = new GsonBuilder()
|
||||
.setStrictness(Strictness.LENIENT)
|
||||
.setLenient()
|
||||
.registerTypeAdapter(credentials.getClass(), credTypeAdapter)
|
||||
.create();
|
||||
|
||||
@@ -876,17 +1042,12 @@ public class WebSession extends BaseWebSession
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public WebSessionProjectImpl getProjectById(@Nullable String projectId) {
|
||||
public WebProjectImpl getProjectById(@Nullable String projectId) {
|
||||
return getWorkspace().getProjectById(projectId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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;
|
||||
public WebProjectImpl getAccessibleProjectById(@Nullable String projectId) throws DBWebException {
|
||||
WebProjectImpl project = null;
|
||||
if (projectId != null) {
|
||||
project = getWorkspace().getProjectById(projectId);
|
||||
}
|
||||
@@ -896,24 +1057,18 @@ public class WebSession extends BaseWebSession
|
||||
return project;
|
||||
}
|
||||
|
||||
public List<WebSessionProjectImpl> getAccessibleProjects() {
|
||||
public List<WebProjectImpl> getAccessibleProjects() {
|
||||
return getWorkspace().getProjects();
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds project to session cache and navigator tree.
|
||||
*/
|
||||
public void addSessionProject(@NotNull WebSessionProjectImpl project) {
|
||||
public void addSessionProject(@NotNull WebProjectImpl project) {
|
||||
getWorkspace().addProject(project);
|
||||
if (navigatorModel != null) {
|
||||
navigatorModel.getRoot().addProject(project, false);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes project from session cache and navigator tree.
|
||||
*/
|
||||
public void deleteSessionProject(@Nullable WebSessionProjectImpl project) {
|
||||
public void deleteSessionProject(@Nullable WebProjectImpl project) {
|
||||
if (project != null) {
|
||||
project.dispose();
|
||||
}
|
||||
@@ -938,6 +1093,10 @@ public class WebSession extends BaseWebSession
|
||||
return;
|
||||
}
|
||||
deleteSessionProject(project);
|
||||
var projectConnections = project.getDataSourceRegistry().getDataSources();
|
||||
for (DBPDataSourceContainer c : projectConnections) {
|
||||
removeConnection(new WebConnectionInfo(this, c));
|
||||
}
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@@ -949,16 +1108,6 @@ public class WebSession extends BaseWebSession
|
||||
return project.getFileSystemManager();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public DBPPreferenceStore getUserPreferenceStore() {
|
||||
return getUserContext().getPreferenceStore();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public WebSessionGlobalProjectImpl getGlobalProject() {
|
||||
return globalProject;
|
||||
}
|
||||
|
||||
private class SessionProgressMonitor extends BaseProgressMonitor {
|
||||
@Override
|
||||
public void beginTask(String name, int totalWork) {
|
||||
@@ -993,6 +1142,15 @@ public class WebSession extends BaseWebSession
|
||||
}
|
||||
}
|
||||
|
||||
private record PersistentAttribute(Object value) {
|
||||
private static class PersistentAttribute {
|
||||
private final Object value;
|
||||
|
||||
public PersistentAttribute(Object value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public Object getValue() {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
-3
@@ -50,9 +50,6 @@ public class WebSessionEventsFilter {
|
||||
}
|
||||
|
||||
public boolean isEventAllowed(WSEvent event) {
|
||||
if (event.isForceProcessed()) {
|
||||
return true;
|
||||
}
|
||||
if (!subscribedEventTopics.isEmpty() && !subscribedEventTopics.contains(event.getTopicId())
|
||||
) {
|
||||
return false;
|
||||
|
||||
-72
@@ -1,72 +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.session;
|
||||
|
||||
import org.jkiss.code.NotNull;
|
||||
import org.jkiss.dbeaver.model.impl.preferences.AbstractUserPreferenceStore;
|
||||
import org.jkiss.dbeaver.model.preferences.DBPPreferenceStore;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Map;
|
||||
|
||||
public class WebSessionPreferenceStore extends AbstractUserPreferenceStore {
|
||||
public WebSessionPreferenceStore(
|
||||
@NotNull DBPPreferenceStore parentStore
|
||||
) {
|
||||
super(parentStore);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public Map<String, Object> getCustomUserParameters() {
|
||||
return userPreferences;
|
||||
}
|
||||
|
||||
// to avoid redundant sm api call
|
||||
public void updatePreferenceValues(@NotNull Map<String, Object> newValues) {
|
||||
userPreferences.putAll(newValues);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void setUserPreference(String name, Object value) {
|
||||
throw new RuntimeException("Not implemented");
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDefaultString(String name) {
|
||||
return parentStore.getDefaultString(name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isDefault(String name) {
|
||||
return !userPreferences.containsKey(name) && parentStore.isDefault(name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean needsSaving() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setToDefault(String name) {
|
||||
throw new RuntimeException("Not implemented");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void save() throws IOException {
|
||||
throw new RuntimeException("Not implemented");
|
||||
}
|
||||
}
|
||||
+15
-26
@@ -16,12 +16,11 @@
|
||||
*/
|
||||
package io.cloudbeaver.model.session;
|
||||
|
||||
import io.cloudbeaver.WebSessionProjectImpl;
|
||||
import io.cloudbeaver.WebProjectImpl;
|
||||
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;
|
||||
@@ -39,8 +38,8 @@ public class WebSessionWorkspace implements DBPWorkspace {
|
||||
|
||||
private final BaseWebSession session;
|
||||
private final SessionContextImpl workspaceAuthContext;
|
||||
private final List<WebSessionProjectImpl> accessibleProjects = new ArrayList<>();
|
||||
private WebSessionProjectImpl activeProject;
|
||||
private final List<WebProjectImpl> accessibleProjects = new ArrayList<>();
|
||||
private WebProjectImpl activeProject;
|
||||
|
||||
public WebSessionWorkspace(BaseWebSession session) {
|
||||
this.session = session;
|
||||
@@ -82,20 +81,20 @@ public class WebSessionWorkspace implements DBPWorkspace {
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public List<WebSessionProjectImpl> getProjects() {
|
||||
public List<WebProjectImpl> getProjects() {
|
||||
return accessibleProjects;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public WebSessionProjectImpl getActiveProject() {
|
||||
public DBPProject getActiveProject() {
|
||||
return activeProject;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public WebSessionProjectImpl getProject(@NotNull String projectName) {
|
||||
for (WebSessionProjectImpl project : accessibleProjects) {
|
||||
public WebProjectImpl getProject(@NotNull String projectName) {
|
||||
for (WebProjectImpl project : accessibleProjects) {
|
||||
if (project.getName().equals(projectName)) {
|
||||
return project;
|
||||
}
|
||||
@@ -105,11 +104,11 @@ public class WebSessionWorkspace implements DBPWorkspace {
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public WebSessionProjectImpl getProjectById(@NotNull String projectId) {
|
||||
public WebProjectImpl getProjectById(@NotNull String projectId) {
|
||||
if (projectId == null) {
|
||||
return activeProject;
|
||||
}
|
||||
for (WebSessionProjectImpl project : accessibleProjects) {
|
||||
for (WebProjectImpl project : accessibleProjects) {
|
||||
if (project.getId().equals(projectId)) {
|
||||
return project;
|
||||
}
|
||||
@@ -123,36 +122,26 @@ public class WebSessionWorkspace implements DBPWorkspace {
|
||||
return workspaceAuthContext;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initializeProjects() {
|
||||
// noop
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispose() {
|
||||
clearProjects();
|
||||
}
|
||||
|
||||
@Override
|
||||
public DBPImage getResourceIcon(DBPAdaptable resourceAdapter) {
|
||||
return null;
|
||||
public void setActiveProject(DBPProject activeProject) {
|
||||
this.activeProject = (WebProjectImpl) activeProject;
|
||||
}
|
||||
|
||||
public void setActiveProject(WebSessionProjectImpl activeProject) {
|
||||
this.activeProject = activeProject;
|
||||
}
|
||||
|
||||
void addProject(WebSessionProjectImpl project) {
|
||||
void addProject(WebProjectImpl project) {
|
||||
accessibleProjects.add(project);
|
||||
}
|
||||
|
||||
void removeProject(WebSessionProjectImpl project) {
|
||||
void removeProject(WebProjectImpl project) {
|
||||
accessibleProjects.remove(project);
|
||||
}
|
||||
|
||||
void clearProjects() {
|
||||
if (!this.accessibleProjects.isEmpty()) {
|
||||
for (WebSessionProjectImpl project : accessibleProjects) {
|
||||
for (WebProjectImpl project : accessibleProjects) {
|
||||
project.dispose();
|
||||
}
|
||||
this.activeProject = null;
|
||||
|
||||
+4
-15
@@ -36,17 +36,18 @@ import org.jkiss.dbeaver.model.secret.DBSSecretController;
|
||||
import org.jkiss.dbeaver.model.security.SMAdminController;
|
||||
import org.jkiss.dbeaver.model.security.SMController;
|
||||
import org.jkiss.dbeaver.model.security.user.SMAuthPermissions;
|
||||
import org.jkiss.dbeaver.runtime.DBWorkbench;
|
||||
import org.jkiss.utils.CommonUtils;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
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);
|
||||
|
||||
@@ -65,7 +66,6 @@ public class WebUserContext implements SMCredentialsProvider {
|
||||
private RMController rmController;
|
||||
private DBFileController fileController;
|
||||
private Set<String> accessibleProjectIds = new HashSet<>();
|
||||
private final WebSessionPreferenceStore preferenceStore;
|
||||
|
||||
public WebUserContext(WebApplication application, DBPWorkspace workspace) throws DBException {
|
||||
this.application = application;
|
||||
@@ -73,7 +73,6 @@ public class WebUserContext implements SMCredentialsProvider {
|
||||
this.securityController = application.createSecurityController(this);
|
||||
this.rmController = application.createResourceController(this, workspace);
|
||||
this.fileController = application.createFileController(this);
|
||||
this.preferenceStore = new WebSessionPreferenceStore(DBWorkbench.getPlatform().getPreferenceStore());
|
||||
setUserPermissions(getDefaultPermissions());
|
||||
}
|
||||
|
||||
@@ -117,13 +116,7 @@ public class WebUserContext implements SMCredentialsProvider {
|
||||
setRefreshToken(smRefreshToken);
|
||||
setUserPermissions(smAuthPermissions.getPermissions());
|
||||
this.adminSecurityController = application.getAdminSecurityController(this);
|
||||
this.rmController = application.createResourceController(this, workspace);
|
||||
if (isSessionChanged) {
|
||||
if (smAuthPermissions.getUserId() != null) {
|
||||
this.preferenceStore.updateAllUserPreferences(securityController.getCurrentUserParameters());
|
||||
} else {
|
||||
this.preferenceStore.updateAllUserPreferences(Map.of());
|
||||
}
|
||||
this.smSessionId = smAuthPermissions.getSessionId();
|
||||
setUser(smAuthPermissions.getUserId() == null ? null : new WebUser(securityController.getCurrentUser()));
|
||||
refreshAccessibleProjects();
|
||||
@@ -258,8 +251,4 @@ public class WebUserContext implements SMCredentialsProvider {
|
||||
private void setRefreshToken(@Nullable String refreshToken) {
|
||||
this.refreshToken = refreshToken;
|
||||
}
|
||||
|
||||
public WebSessionPreferenceStore getPreferenceStore() {
|
||||
return preferenceStore;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -70,7 +70,6 @@ public class WebUser {
|
||||
return Collections.emptyMap();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public String[] getTeams() {
|
||||
return user.getUserTeams();
|
||||
}
|
||||
|
||||
+1
-9
@@ -80,7 +80,7 @@ public class WebAuthProviderConfiguration {
|
||||
}
|
||||
|
||||
private String buildRedirectUrl(String baseUrl) {
|
||||
return baseUrl + "?" + CBAuthConstants.CB_REDIRECT_URL_REQUEST_PARAM + "=" + WebAppUtils.getFullServerUrl();
|
||||
return baseUrl + "?" + CBAuthConstants.CB_REDIRECT_URL_REQUEST_PARAM + "=" + WebAppUtils.getWebApplication().getServerURL();
|
||||
}
|
||||
|
||||
@Property
|
||||
@@ -109,14 +109,6 @@ 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();
|
||||
|
||||
@@ -1,147 +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.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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,85 +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.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);
|
||||
}
|
||||
}
|
||||
+7
-90
@@ -16,102 +16,19 @@
|
||||
*/
|
||||
package io.cloudbeaver.server;
|
||||
|
||||
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.eclipse.core.resources.IWorkspace;
|
||||
import org.jkiss.dbeaver.model.app.DBPPlatform;
|
||||
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;
|
||||
import org.jkiss.dbeaver.registry.EclipseWorkspaceImpl;
|
||||
|
||||
/**
|
||||
* Web global workspace.
|
||||
* <p>
|
||||
* Basically just a wrapper around Eclipse workspace.
|
||||
*/
|
||||
public class WebGlobalWorkspace extends BaseWorkspaceImpl {
|
||||
public class WebGlobalWorkspace extends EclipseWorkspaceImpl {
|
||||
|
||||
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());
|
||||
public WebGlobalWorkspace(DBPPlatform platform, IWorkspace eclipseWorkspace) {
|
||||
super(platform, eclipseWorkspace);
|
||||
}
|
||||
|
||||
@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
|
||||
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,6 +16,8 @@
|
||||
*/
|
||||
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;
|
||||
@@ -74,6 +76,13 @@ public class WebPlatformActivator extends Plugin {
|
||||
return preferences;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the workspace instance.
|
||||
*/
|
||||
public static IWorkspace getWorkspace() {
|
||||
return ResourcesPlugin.getWorkspace();
|
||||
}
|
||||
|
||||
protected void shutdownPlatform() {
|
||||
|
||||
}
|
||||
|
||||
-3
@@ -23,9 +23,6 @@ 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,7 +18,6 @@ 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;
|
||||
@@ -32,7 +31,6 @@ public interface DBWServiceServerConfigurator extends DBWServiceBinding {
|
||||
void configureServer(
|
||||
@NotNull WebApplication application,
|
||||
@Nullable WebSession session,
|
||||
@NotNull WebServerConfiguration serverConfiguration,
|
||||
@NotNull WebAppConfiguration appConfig
|
||||
) throws DBException;
|
||||
|
||||
|
||||
+1
@@ -22,6 +22,7 @@ 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,7 +22,6 @@ 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;
|
||||
@@ -39,7 +38,6 @@ 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) {
|
||||
@@ -134,11 +132,4 @@ public class WebSQLResultsInfo {
|
||||
(!(entity instanceof DBSDocumentContainer) && !entity.getDataSource().getInfo().isDynamicMetadata());
|
||||
}
|
||||
|
||||
public DBCTrace getTrace() {
|
||||
return trace;
|
||||
}
|
||||
|
||||
public void setTrace(@NotNull DBCTrace trace) {
|
||||
this.trace = trace;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,124 +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.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,8 +38,6 @@ 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);
|
||||
@@ -76,25 +74,17 @@ public class WebAppUtils {
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public static Map<String, Object> mergeConfigurations(
|
||||
Map<String, Object> priorityConfiguration,
|
||||
Map<String, Object> additional
|
||||
) {
|
||||
public static Map<String, Object> mergeConfigurations(Map<String, Object> origin, Map<String, Object> additional) {
|
||||
var resultConfig = new HashMap<String, Object>();
|
||||
Set<String> rootKeys = new HashSet<>(priorityConfiguration.keySet());
|
||||
Set<String> rootKeys = new HashSet<>(origin.keySet());
|
||||
rootKeys.addAll(additional.keySet());
|
||||
|
||||
for (var rootKey : rootKeys) {
|
||||
var originValue = priorityConfiguration.get(rootKey);
|
||||
var originValue = origin.get(rootKey);
|
||||
var additionalValue = additional.get(rootKey);
|
||||
|
||||
if (originValue == null || additionalValue == null) {
|
||||
Object resultValue = null;
|
||||
if (additional.containsKey(rootKey)) {
|
||||
resultValue = additionalValue;
|
||||
} else if (originValue != null) {
|
||||
resultValue = originValue;
|
||||
}
|
||||
var resultValue = originValue != null ? originValue : additionalValue;
|
||||
resultConfig.put(rootKey, resultValue);
|
||||
continue;
|
||||
}
|
||||
@@ -140,10 +130,6 @@ public class WebAppUtils {
|
||||
if (!(oldValue instanceof String)) {
|
||||
return newValue;
|
||||
}
|
||||
//new value already contains variable pattern
|
||||
if (newValue instanceof String newStringValue && GeneralUtils.isVariablePattern(newStringValue)) {
|
||||
return newValue;
|
||||
}
|
||||
String value = (String) oldValue;
|
||||
if (!GeneralUtils.isVariablePattern(value)) {
|
||||
return newValue;
|
||||
@@ -201,7 +187,7 @@ public class WebAppUtils {
|
||||
path = path.concat("; SameSite=" + sameSite);
|
||||
}
|
||||
}
|
||||
sessionCookie.setHttpOnly(true);
|
||||
|
||||
sessionCookie.setPath(path);
|
||||
response.addCookie(sessionCookie);
|
||||
}
|
||||
@@ -271,13 +257,4 @@ 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("/"));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+5
-42
@@ -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,7 +37,6 @@ import org.jkiss.utils.CommonUtils;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
|
||||
public class WebDataSourceUtils {
|
||||
|
||||
@@ -98,10 +97,6 @@ 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()) {
|
||||
@@ -114,22 +109,14 @@ public class WebDataSourceUtils {
|
||||
|
||||
@Nullable
|
||||
public static DBPDataSourceContainer getLocalOrGlobalDataSource(
|
||||
WebSession webSession, @Nullable String projectId, String connectionId
|
||||
WebApplication application, WebSession webSession, @Nullable String projectId, String connectionId
|
||||
) throws DBWebException {
|
||||
DBPDataSourceContainer dataSource = null;
|
||||
if (!CommonUtils.isEmpty(connectionId)) {
|
||||
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())) {
|
||||
dataSource = webSession.getProjectById(projectId).getDataSourceRegistry().getDataSource(connectionId);
|
||||
if (dataSource == null && (webSession.hasPermission(DBWConstants.PERMISSION_ADMIN) || application.isConfigurationMode())) {
|
||||
// If called for new connection in admin mode then this connection may absent in session registry yet
|
||||
project = webSession.getGlobalProject();
|
||||
if (project != null) {
|
||||
dataSource = project.getDataSourceRegistry().getDataSource(connectionId);
|
||||
}
|
||||
dataSource = getGlobalDataSourceRegistry().getDataSource(connectionId);
|
||||
}
|
||||
}
|
||||
return dataSource;
|
||||
@@ -166,28 +153,4 @@ 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,15 +17,62 @@
|
||||
|
||||
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))
|
||||
@@ -39,4 +86,14 @@ 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.2.2.qualifier
|
||||
Bundle-Release-Date: 20241007
|
||||
Bundle-Version: 24.0.2.qualifier
|
||||
Bundle-Release-Date: 20240408
|
||||
Bundle-RequiredExecutionEnvironment: JavaSE-17
|
||||
Bundle-ActivationPolicy: lazy
|
||||
Bundle-ClassPath: .
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
<relativePath>../</relativePath>
|
||||
</parent>
|
||||
<artifactId>io.cloudbeaver.product.ce</artifactId>
|
||||
<version>24.2.2-SNAPSHOT</version>
|
||||
<version>24.0.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.107.qualifier
|
||||
Bundle-Release-Date: 20241007
|
||||
Bundle-Version: 1.0.95.qualifier
|
||||
Bundle-Release-Date: 20240408
|
||||
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.107-SNAPSHOT</version>
|
||||
<version>1.0.95-SNAPSHOT</version>
|
||||
<packaging>eclipse-plugin</packaging>
|
||||
</project>
|
||||
|
||||
@@ -3,13 +3,14 @@ Bundle-ManifestVersion: 2
|
||||
Bundle-Vendor: DBeaver Corp
|
||||
Bundle-Name: Cloudbeaver Web Server
|
||||
Bundle-SymbolicName: io.cloudbeaver.server;singleton:=true
|
||||
Bundle-Version: 24.2.2.qualifier
|
||||
Bundle-Release-Date: 20241007
|
||||
Bundle-Version: 24.0.2.qualifier
|
||||
Bundle-Release-Date: 20240408
|
||||
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,
|
||||
@@ -25,14 +26,9 @@ Export-Package: io.cloudbeaver,
|
||||
io.cloudbeaver.model,
|
||||
io.cloudbeaver.model.user,
|
||||
io.cloudbeaver.registry,
|
||||
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,
|
||||
|
||||
@@ -70,7 +70,7 @@
|
||||
<eventHandler class="io.cloudbeaver.server.events.WSDeleteTempFileHandler">
|
||||
<topic id="cb_delete_temp_folder"/>
|
||||
</eventHandler>
|
||||
<eventHandler class="io.cloudbeaver.server.events.WSUserEventHandler">
|
||||
<eventHandler class="io.cloudbeaver.server.events.WSUserDeletedEventHandler">
|
||||
<topic id="cb_user"/>
|
||||
</eventHandler>
|
||||
</extension>
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user