diff --git a/.ci/openshift-ci/Dockerfile b/.ci/openshift-ci/Dockerfile index 718fd06652..7111a3dfa5 100644 --- a/.ci/openshift-ci/Dockerfile +++ b/.ci/openshift-ci/Dockerfile @@ -17,7 +17,7 @@ USER 0 # Install yq, kubectl, chectl cli used by olm/olm.sh script. # hadolint ignore=DL3041 -RUN dnf install -y -q --allowerasing --nobest nodejs-devel nodejs-libs psmisc python3-pip jq golang httpd-tools \ +RUN dnf install -y -q --allowerasing --nobest nodejs-devel nodejs-libs psmisc python3-pip jq yq golang httpd-tools \ # already installed or installed as deps: openssl openssl-devel ca-certificates make cmake cpp gcc gcc-c++ zlib zlib-devel brotli brotli-devel python3 nodejs-packaging && \ dnf update -y && dnf clean all && \ diff --git a/.ci/openshift-ci/ca.crt b/.ci/openshift-ci/ca.crt new file mode 100644 index 0000000000..66dfa1513c --- /dev/null +++ b/.ci/openshift-ci/ca.crt @@ -0,0 +1,19 @@ +-----BEGIN CERTIFICATE----- +MIIDDDCCAfSgAwIBAgIBATANBgkqhkiG9w0BAQsFADAmMSQwIgYDVQQDDBtpbmdy +ZXNzLW9wZXJhdG9yQDE3MDI5MTczMzkwHhcNMjMxMjE4MTYzNTM4WhcNMjUxMjE3 +MTYzNTM5WjAmMSQwIgYDVQQDDBtpbmdyZXNzLW9wZXJhdG9yQDE3MDI5MTczMzkw +ggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDDVfcIyqKLYcrmieDOCvAq +RKFR0PXqjl54DyfhkP7TC/rA4WMaJgYamYNNRcy4rOb+i3P1yepeVlXj+4gka41W +fh1QghqDGPpQTF7I8SDhnz3qdQymgSSkrxBWOGgJZRG3Gd50CNeh+ADdNlOxzZhJ +c1HI9/36Zp0M7RHLNwrYzKf/Scrcl3t/Ps4KHKsNtjNygnPSTjGwza5TTTAvGQ+h +3+c+bKJHZp6VFoEpubmBfCG8x+vJwBNI+YauXwti9EqFutnAbOiQ4aOvqQYINngV +OQTOg3xnifnooaR2iZPOtxPLUDMydHP9sDVNGLICpnKcFN6x5JKq1fAuwOAMRMQr +AgMBAAGjRTBDMA4GA1UdDwEB/wQEAwICpDASBgNVHRMBAf8ECDAGAQH/AgEAMB0G +A1UdDgQWBBRQfZkM6C20GT1YiTiS694vRPP2MDANBgkqhkiG9w0BAQsFAAOCAQEA +qd9ug2Bo2s3CG0qjNtk19WYYAuwkEMZU9L/FbSfmPkkOTbrZB71ofZbVan8Dx9/W +SfbkN1eJQNxd7QIDlExKEW2ufJIMCDe6haLHV8x+z3fISHg3wMUCPtadLG/exafy +agfj8PQgnJ1sbA/fKl0rqcVYuIihZ0DdMe28Yj92BFcs5+TXzVYzjyeIGonUZrCR +LtW2CxV7Ngue9pkWGsqQSlRwzp3knYfZxeQLneZcGlsSV4r9LOKIAA/MhBGgpwVc +zrh7L93jajHD1AD0sRdUQp4VVhysXVmMMSAjaLkqHsvSyJp7yKJOmrts6a274qbU +J1E6cXqtfF0NwRo2D9BouQ== +-----END CERTIFICATE----- diff --git a/.ci/openshift-ci/common.sh b/.ci/openshift-ci/common.sh index fc35495781..277ed2fe36 100644 --- a/.ci/openshift-ci/common.sh +++ b/.ci/openshift-ci/common.sh @@ -32,6 +32,8 @@ export PRIVATE_REPO_WORKSPACE_NAME=${PRIVATE_REPO_WORKSPACE_NAME:-"private-repo- export PUBLIC_PROJECT_NAME=${PUBLIC_PROJECT_NAME:-"public-repo"} export PRIVATE_PROJECT_NAME=${PRIVATE_PROJECT_NAME:-"private-repo"} export YAML_FILE_NAME=${YAML_FILE_NAME:-"devfile.yaml"} +export CUSTOM_CONFIG_MAP_NAME=${CUSTOM_CONFIG_MAP_NAME:-"custom-ca-certificates"} +export GIT_SSL_CONFIG_MAP_NAME=${GIT_SSL_CONFIG_MAP_NAME:-"che-self-signed-cert"} provisionOpenShiftOAuthUser() { echo -e "[INFO] Provisioning Openshift OAuth user" @@ -52,18 +54,38 @@ provisionOpenShiftOAuthUser() { done } +configureGitSelfSignedCertificate() { + echo "[INFO] Configure self-signed certificate for Git provider" + oc adm new-project ${CHE_NAMESPACE} + oc project ${CHE_NAMESPACE} + + echo -e "[INFO] Create ConfigMap with the required TLS certificate" + oc create configmap ${CUSTOM_CONFIG_MAP_NAME} --from-file=.ci/openshift-ci/ca.crt + oc label configmap ${CUSTOM_CONFIG_MAP_NAME} app.kubernetes.io/part-of=che.eclipse.org app.kubernetes.io/component=ca-bundle + + echo "[INFO] Create ConfigMap to support Git repositories with self-signed certificates" + oc create configmap ${GIT_SSL_CONFIG_MAP_NAME} --from-file=.ci/openshift-ci/ca.crt --from-literal=githost=$GIT_PROVIDER_URL + oc label configmap ${GIT_SSL_CONFIG_MAP_NAME} app.kubernetes.io/part-of=che.eclipse.org +} + createCustomResourcesFile() { cat > custom-resources.yaml <<-END apiVersion: org.eclipse.che/v2 spec: devEnvironments: - maxNumberOfRunningWorkspacesPerUser: 10000 + maxNumberOfRunningWorkspacesPerUser: 10000 END echo "Generated custom resources file" cat custom-resources.yaml } +patchCustomResourcesFile() { + yq -y '.spec.devEnvironments.trustedCerts += {"gitTrustedCertsConfigMapName": "'${GIT_SSL_CONFIG_MAP_NAME}'"}' custom-resources.yaml -i + + cat custom-resources.yaml +} + deployChe() { chectl server:deploy --cheimage=$CHE_SERVER_IMAGE \ --che-operator-cr-patch-yaml=custom-resources.yaml \ @@ -101,6 +123,15 @@ testFactoryResolverNoPatOAuth() { echo "[INFO] Check factory resolver for public repository with NO PAT/OAuth setup" testFactoryResolverResponse $1 200 + echo "[INFO] Check factory resolver for private repository with NO PAT/OAuth setup" + testFactoryResolverResponse $2 500 +} + +# check that raw devfile url factory resolver returns correct value without any PAT/OAuth setup +testFactoryResolverNoPatOAuthRaw() { + echo "[INFO] Check factory resolver for public repository with NO PAT/OAuth setup" + testFactoryResolverResponse $1 200 + echo "[INFO] Check factory resolver for private repository with NO PAT/OAuth setup" testFactoryResolverResponse $2 400 } @@ -195,6 +226,87 @@ setupSSHKeyPairs() { oc apply -f ssh-secret.yaml -n ${USER_CHE_NAMESPACE} } +# Only for GitLab server administrator users +createOAuthApplicationGitLabServer() { + ADMIN_ACCESS_TOKEN=$1 + + CHE_URL=https://$(oc get route -n ${CHE_NAMESPACE} che -o jsonpath='{.spec.host}') + + echo "[INFO] Create OAuth Application" + response=$(curl -k -X POST \ + ${GIT_PROVIDER_URL}/api/v4/applications \ + -H "PRIVATE-TOKEN: ${ADMIN_ACCESS_TOKEN}" \ + -d "name=${APPLICATION_NAME}" \ + -d "redirect_uri=${CHE_URL}/api/oauth/callback" \ + -d "scopes=api write_repository openid") + + echo "[INFO] Response of the created OAuth Application" + + OAUTH_ID=$(echo "$response" | jq -r '.id') + APPLICATION_ID=$(echo "$response" | jq -r '.application_id') + APPLICATION_SECRET=$(echo "$response" | jq -r '.secret') + + echo "[INFO] OAuth ID: ${OAUTH_ID}" + echo "[INFO] Application ID: ${APPLICATION_ID}" + echo "[INFO] Application Secret: ${APPLICATION_SECRET}" +} + +# Only for GitLab server administrator users +deleteOAuthApplicationGitLabServer() { + OAUTH_ID=$1 + ADMIN_ACCESS_TOKEN=$2 + + echo "[INFO] Delete OAuth Application" + curl -i -k -X DELETE \ + ${GIT_PROVIDER_URL}/api/v4/applications/${OAUTH_ID} \ + -H "PRIVATE-TOKEN: ${ADMIN_ACCESS_TOKEN}" +} + +# Only for GitLab server +revokeAuthorizedOAuthApplication() { + APPLICATION_ID=$1 + APPLICATION_SECRET=$2 + + echo "[INFO] Revoke authorized OAuth application" + oc project ${USER_CHE_NAMESPACE} + OAUTH_TOKEN_NAME=$(oc get secret | grep 'personal-access-token'| awk 'NR==1 { print $1 }') + echo "[INFO] OAuth token name: "$OAUTH_TOKEN_NAME + OAUTH_TOKEN=$(oc get secret $OAUTH_TOKEN_NAME -o jsonpath='{.data.token}' | base64 -d) + echo "[INFO] Oauth token: "$OAUTH_TOKEN + + curl -i -k -X POST \ + ${GIT_PROVIDER_URL}/oauth/revoke \ + -d "client_id=${APPLICATION_ID}" \ + -d "client_secret=${APPLICATION_SECRET}" \ + -d "token=${OAUTH_TOKEN}" +} + +# Only for GitLab server +setupOAuthSecret() { + APPLICATION_ID=$1 + APPLICATION_SECRET=$2 + + echo "[INFO] Setup OAuth Secret" + oc login -u=${OCP_ADMIN_USER_NAME} -p=${OCP_LOGIN_PASSWORD} --insecure-skip-tls-verify=false + oc project ${CHE_NAMESPACE} + SERVER_POD=$(oc get pod -l component=che | grep "che" | awk 'NR==1 { print $1 }') + + ENCODED_APP_ID=$(echo -n "${APPLICATION_ID}" | base64 -w 0) + ENCODED_APP_SECRET=$(echo -n "${APPLICATION_SECRET}" | base64 -w 0) + cat .ci/openshift-ci/oauth-secret.yaml > oauth-secret.yaml + + # patch the oauth-secret.yaml file + sed -i "s#git-provider-url#${GIT_PROVIDER_URL}#g" oauth-secret.yaml + sed -i "s#encoded-application-id#${ENCODED_APP_ID}#g" oauth-secret.yaml + sed -i "s#encoded-application-secret#${ENCODED_APP_SECRET}#g" oauth-secret.yaml + + cat oauth-secret.yaml + oc apply -f oauth-secret.yaml -n ${CHE_NAMESPACE} + + echo "[INFO] Wait updating deployment after create OAuth secret" + oc wait --for=delete pod/${SERVER_POD} --timeout=120s +} + runTestWorkspaceWithGitRepoUrl() { WS_NAME=$1 PROJECT_NAME=$2 @@ -260,8 +372,46 @@ deleteTestWorkspace() { oc delete dw ${WS_NAME} -n ${OCP_USER_NAMESPACE} } +startOAuthFactoryTest() { + CHE_URL=https://$(oc get route -n ${CHE_NAMESPACE} che -o jsonpath='{.spec.host}') + # patch oauth-factory-test.yaml + cat .ci/openshift-ci/pod-oauth-factory-test.yaml > oauth-factory-test.yaml + sed -i "s#CHE_URL#${CHE_URL}#g" oauth-factory-test.yaml + sed -i "s#CHE-NAMESPACE#${CHE_NAMESPACE}#g" oauth-factory-test.yaml + sed -i "s#OCP_USER_NAME#${OCP_NON_ADMIN_USER_NAME}#g" oauth-factory-test.yaml + sed -i "s#OCP_USER_PASSWORD#${OCP_LOGIN_PASSWORD}#g" oauth-factory-test.yaml + sed -i "s#FACTORY_REPO_URL#${PRIVATE_REPO_URL}#g" oauth-factory-test.yaml + sed -i "s#GIT_BRANCH#${GIT_REPO_BRANCH}#g" oauth-factory-test.yaml + sed -i "s#GIT_PROVIDER_TYPE#${GIT_PROVIDER_TYPE}#g" oauth-factory-test.yaml + sed -i "s#GIT_PROVIDER_USER_NAME#${GIT_PROVIDER_LOGIN}#g" oauth-factory-test.yaml + sed -i "s#GIT_PROVIDER_USER_PASSWORD#${GIT_PROVIDER_PASSWORD}#g" oauth-factory-test.yaml + + echo "[INFO] Applying the following patched OAuth Factory Test Pod:" + cat oauth-factory-test.yaml + echo "[INFO] --------------------------------------------------" + oc apply -f oauth-factory-test.yaml + # wait for the pod to start + n=0 + while [ $n -le 120 ] + do + PHASE=$(oc get pod -n ${CHE_NAMESPACE} ${TEST_POD_NAME} \ + --template='{{ .status.phase }}') + if [[ ${PHASE} == "Running" ]]; then + echo "[INFO] Smoke test started successfully." + return + fi + + sleep 5 + n=$(( n+1 )) + done + + echo "[ERROR] Failed to start smoke test." + exit 1 +} + # Catch the finish of the job and write logs in artifacts. catchFinish() { + echo "[INFO] Terminate the process after finish the test script." local RESULT=$? killProcessByPort if [ "$RESULT" != "0" ]; then @@ -278,8 +428,37 @@ collectEclipseCheLogs() { mkdir -p ${ARTIFACTS_DIR}/che-logs # Collect all Eclipse Che logs and cluster CR - chectl server:logs -n $CHE_NAMESPACE --directory ${ARTIFACTS_DIR}/che-logs --telemetry off + oc login -u=${OCP_ADMIN_USER_NAME} -p=${OCP_LOGIN_PASSWORD} --insecure-skip-tls-verify=false oc get checluster -o yaml -n $CHE_NAMESPACE > "${ARTIFACTS_DIR}/che-cluster.yaml" + chectl server:logs -n $CHE_NAMESPACE --directory ${ARTIFACTS_DIR}/che-logs --telemetry off +} + +collectLogs() { + echo "[INFO] Waiting until oauth test pod finished" + oc logs -n ${CHE_NAMESPACE} ${TEST_POD_NAME} -c oauth-test -f + sleep 3 + + # Download artifacts + set +e + echo "[INFO] Collect all Eclipse Che logs and cluster CR." + collectEclipseCheLogs + + echo "[INFO] Downloading test report." + mkdir -p ${ARTIFACTS_DIR}/e2e + oc rsync -n ${CHE_NAMESPACE} ${TEST_POD_NAME}:/tmp/e2e/report/ ${ARTIFACTS_DIR}/e2e -c download-reports + oc exec -n ${CHE_NAMESPACE} ${TEST_POD_NAME} -c download-reports -- touch /tmp/done + + # Revoke and delete the OAuth application + revokeAuthorizedOAuthApplication ${APPLICATION_ID} ${APPLICATION_SECRET} + deleteOAuthApplicationGitLabServer ${OAUTH_ID} ${ADMIN_ACCESS_TOKEN} + set -e + + EXIT_CODE=$(oc logs -n ${CHE_NAMESPACE} ${TEST_POD_NAME} -c oauth-test | grep EXIT_CODE) + if [[ ${EXIT_CODE} != "+ EXIT_CODE=0" ]]; then + echo "[ERROR] Factory OAuth test failed. Job failed." + exit 1 + fi + echo "[INFO] Job completed successfully." } testCloneGitRepoNoProjectExists() { @@ -317,3 +496,17 @@ setupTestEnvironment() { forwardPortToService initUserNamespace ${OCP_USER_NAME} } + +setupTestEnvironmentOAuthFlow() { + ADMIN_ACCESS_TOKEN=$1 + APPLICATION_ID=$2 + APPLICATION_SECRET=$3 + + provisionOpenShiftOAuthUser + configureGitSelfSignedCertificate + createCustomResourcesFile + patchCustomResourcesFile + deployChe + createOAuthApplicationGitLabServer ${ADMIN_ACCESS_TOKEN} ${APPLICATION_NAME} + setupOAuthSecret ${APPLICATION_ID} ${APPLICATION_SECRET} +} diff --git a/.ci/openshift-ci/oauth-secret.yaml b/.ci/openshift-ci/oauth-secret.yaml new file mode 100644 index 0000000000..f00f9c6cdf --- /dev/null +++ b/.ci/openshift-ci/oauth-secret.yaml @@ -0,0 +1,15 @@ +kind: Secret +apiVersion: v1 +metadata: + name: gitlab-oauth-secret + namespace: eclipse-che + labels: + app.kubernetes.io/part-of: che.eclipse.org + app.kubernetes.io/component: oauth-scm-configuration + annotations: + che.eclipse.org/oauth-scm-server: gitlab + che.eclipse.org/scm-server-endpoint: git-provider-url +type: Opaque +data: + id: encoded-application-id + secret: encoded-application-secret diff --git a/.ci/openshift-ci/pod-oauth-factory-test.yaml b/.ci/openshift-ci/pod-oauth-factory-test.yaml new file mode 100644 index 0000000000..aeb0b380cd --- /dev/null +++ b/.ci/openshift-ci/pod-oauth-factory-test.yaml @@ -0,0 +1,89 @@ +apiVersion: v1 +kind: Pod +metadata: + name: oauth-factory-test + namespace: CHE-NAMESPACE +spec: + volumes: + - name: test-run-results + - name: ffmpeg-video + - name: dshm + emptyDir: + medium: Memory + containers: + # container containing the tests + - name: oauth-test + image: quay.io/eclipse/che-e2e:next + imagePullPolicy: Always + env: + - name: VIDEO_RECORDING + value: "true" + - name: TEST_SUITE + value: "test" + - name: TS_SELENIUM_EDITOR + value: "che-code" + - name: USERSTORY + value: "Factory" + - name: NODE_TLS_REJECT_UNAUTHORIZED + value: "0" + - name: TS_SELENIUM_BASE_URL + value: "CHE_URL" + - name: TS_SELENIUM_LOG_LEVEL + value: "TRACE" + - name: TS_SELENIUM_OCP_USERNAME + value: "OCP_USER_NAME" + - name: TS_SELENIUM_OCP_PASSWORD + value: "OCP_USER_PASSWORD" + - name: TS_SELENIUM_VALUE_OPENSHIFT_OAUTH + value: "true" + - name: TS_SELENIUM_FACTORY_GIT_REPO_URL + value: "FACTORY_REPO_URL" + - name: TS_SELENIUM_FACTORY_GIT_REPO_BRANCH + value: "GIT_BRANCH" + - name: TS_SELENIUM_FACTORY_GIT_PROVIDER + value: "GIT_PROVIDER_TYPE" + - name: TS_SELENIUM_GIT_PROVIDER_OAUTH + value: "true" + - name: TS_SELENIUM_GIT_PROVIDER_USERNAME + value: "GIT_PROVIDER_USER_NAME" + - name: TS_SELENIUM_GIT_PROVIDER_PASSWORD + value: "GIT_PROVIDER_USER_PASSWORD" + - name: TS_OCP_LOGIN_PAGE_PROVIDER_TITLE + value: "htpasswd" + - name: DELETE_WORKSPACE_ON_FAILED_TEST + value: "true" + - name: TS_SELENIUM_START_WORKSPACE_TIMEOUT + value: "360000" + - name: TS_IDE_LOAD_TIMEOUT + value: "40000" + volumeMounts: + - name: test-run-results + mountPath: /tmp/e2e/report/ + - name: ffmpeg-video + mountPath: /tmp/ffmpeg_report/ + - name: dshm + mountPath: /dev/shm + resources: + requests: + memory: "3Gi" + cpu: "2" + limits: + memory: "4Gi" + cpu: "2" + # Download results + - name: download-reports + image: eeacms/rsync + imagePullPolicy: IfNotPresent + volumeMounts: + - name: test-run-results + mountPath: /tmp/e2e/report/ + - name: ffmpeg-video + mountPath: /tmp/ffmpeg_report/ + resources: + command: ["sh"] + args: + [ + "-c", + "while true; if [[ -f /tmp/done ]]; then exit 0; fi; do sleep 1; done", + ] + restartPolicy: Never diff --git a/.ci/openshift-ci/test-azure-no-pat-oauth-flow.sh b/.ci/openshift-ci/test-azure-no-pat-oauth-flow.sh index a6268176ca..58c51470f4 100644 --- a/.ci/openshift-ci/test-azure-no-pat-oauth-flow.sh +++ b/.ci/openshift-ci/test-azure-no-pat-oauth-flow.sh @@ -26,8 +26,7 @@ source "${SCRIPT_DIR}"/common.sh trap "catchFinish" EXIT SIGINT setupTestEnvironment ${OCP_NON_ADMIN_USER_NAME} -# due to the issue https://github.com/eclipse/che/issues/22469 -# testFactoryResolverNoPatOAuth ${PUBLIC_REPO_URL} ${PRIVATE_REPO_URL} +testFactoryResolverNoPatOAuth ${PUBLIC_REPO_URL} ${PRIVATE_REPO_URL} testCloneGitRepoProjectShouldExists ${PUBLIC_REPO_WORKSPACE_NAME} ${PUBLIC_PROJECT_NAME} ${PUBLIC_REPO_URL} ${USER_CHE_NAMESPACE} deleteTestWorkspace ${PUBLIC_REPO_WORKSPACE_NAME} ${USER_CHE_NAMESPACE} testCloneGitRepoNoProjectExists ${PRIVATE_REPO_WORKSPACE_NAME} ${PRIVATE_PROJECT_NAME} ${PRIVATE_REPO_URL} ${USER_CHE_NAMESPACE} diff --git a/.ci/openshift-ci/test-bitbucket-no-pat-oauth-flow-raw-devfile-url.sh b/.ci/openshift-ci/test-bitbucket-no-pat-oauth-flow-raw-devfile-url.sh index 240b20183e..ce8651bebb 100644 --- a/.ci/openshift-ci/test-bitbucket-no-pat-oauth-flow-raw-devfile-url.sh +++ b/.ci/openshift-ci/test-bitbucket-no-pat-oauth-flow-raw-devfile-url.sh @@ -26,7 +26,7 @@ source "${SCRIPT_DIR}"/common.sh trap "catchFinish" EXIT SIGINT setupTestEnvironment ${OCP_NON_ADMIN_USER_NAME} -testFactoryResolverNoPatOAuth ${PUBLIC_REPO_RAW_PATH_URL} ${PRIVATE_REPO_RAW_PATH_URL} +testFactoryResolverNoPatOAuthRaw ${PUBLIC_REPO_RAW_PATH_URL} ${PRIVATE_REPO_RAW_PATH_URL} testCloneGitRepoProjectShouldExists ${PUBLIC_REPO_WORKSPACE_NAME} ${PUBLIC_PROJECT_NAME} ${PUBLIC_REPO_RAW_PATH_URL} ${USER_CHE_NAMESPACE} deleteTestWorkspace ${PUBLIC_REPO_WORKSPACE_NAME} ${USER_CHE_NAMESPACE} diff --git a/.ci/openshift-ci/test-github-no-pat-oauth-flow-raw-devfile-url.sh b/.ci/openshift-ci/test-github-no-pat-oauth-flow-raw-devfile-url.sh index a4bddfe32c..747cb9c5d3 100644 --- a/.ci/openshift-ci/test-github-no-pat-oauth-flow-raw-devfile-url.sh +++ b/.ci/openshift-ci/test-github-no-pat-oauth-flow-raw-devfile-url.sh @@ -26,7 +26,7 @@ source "${SCRIPT_DIR}"/common.sh trap "catchFinish" EXIT SIGINT setupTestEnvironment ${OCP_NON_ADMIN_USER_NAME} -testFactoryResolverNoPatOAuth ${PUBLIC_REPO_RAW_PATH_URL} ${PRIVATE_REPO_RAW_PATH_URL} +testFactoryResolverNoPatOAuthRaw ${PUBLIC_REPO_RAW_PATH_URL} ${PRIVATE_REPO_RAW_PATH_URL} testCloneGitRepoNoProjectExists ${PUBLIC_REPO_WORKSPACE_NAME} ${PUBLIC_PROJECT_NAME} ${PUBLIC_REPO_RAW_PATH_URL} ${USER_CHE_NAMESPACE} deleteTestWorkspace ${PUBLIC_REPO_WORKSPACE_NAME} ${USER_CHE_NAMESPACE} diff --git a/.ci/openshift-ci/test-gitlab-no-pat-oauth-flow-raw-devfile-url.sh b/.ci/openshift-ci/test-gitlab-no-pat-oauth-flow-raw-devfile-url.sh index b12a21369e..265390055e 100644 --- a/.ci/openshift-ci/test-gitlab-no-pat-oauth-flow-raw-devfile-url.sh +++ b/.ci/openshift-ci/test-gitlab-no-pat-oauth-flow-raw-devfile-url.sh @@ -26,7 +26,7 @@ source "${SCRIPT_DIR}"/common.sh trap "catchFinish" EXIT SIGINT setupTestEnvironment ${OCP_NON_ADMIN_USER_NAME} -testFactoryResolverNoPatOAuth ${PUBLIC_REPO_RAW_PATH_URL} ${PRIVATE_REPO_RAW_PATH_URL} +testFactoryResolverNoPatOAuthRaw ${PUBLIC_REPO_RAW_PATH_URL} ${PRIVATE_REPO_RAW_PATH_URL} testCloneGitRepoNoProjectExists ${PUBLIC_REPO_WORKSPACE_NAME} ${PUBLIC_PROJECT_NAME} ${PUBLIC_REPO_RAW_PATH_URL} ${USER_CHE_NAMESPACE} deleteTestWorkspace ${PUBLIC_REPO_WORKSPACE_NAME} ${USER_CHE_NAMESPACE} diff --git a/.ci/openshift-ci/test-gitlab-with-oauth-setup-flow.sh b/.ci/openshift-ci/test-gitlab-with-oauth-setup-flow.sh new file mode 100644 index 0000000000..dd0b3c3e00 --- /dev/null +++ b/.ci/openshift-ci/test-gitlab-with-oauth-setup-flow.sh @@ -0,0 +1,37 @@ +#!/bin/bash +# +# Copyright (c) 2023 Red Hat, Inc. +# This program and the accompanying materials are made +# available under the terms of the Eclipse Public License 2.0 +# which is available at https://www.eclipse.org/legal/epl-2.0/ +# +# SPDX-License-Identifier: EPL-2.0 +# +# Contributors: +# Red Hat, Inc. - initial API and implementation +# + +# exit immediately when a command fails +set -ex +# only exit with zero if all commands of the pipeline exit successfully +set -o pipefail + +export TEST_POD_NAME=${TEST_POD_NAME:-"oauth-factory-test"} +export GIT_PROVIDER_TYPE=${GIT_PROVIDER_TYPE:-"gitlab"} +export GIT_PROVIDER_URL=${GIT_PROVIDER_URL:-"https://gitlab-gitlab-system.apps.git.crw-qe.com"} +export GIT_PROVIDER_LOGIN=${GIT_PROVIDER_LOGIN:-"admin-user"} +export PRIVATE_REPO_URL=${PRIVATE_REPO_URL:-"https://gitlab-gitlab-system.apps.git.crw-qe.com/admin-user/private-repo.git"} +export GIT_REPO_BRANCH=${PRIVATE_REPO_BRANCH:-"main"} +export APPLICATION_NAME=${APPLICATION_NAME:-"TestApp"} +export OAUTH_ID="" +export APPLICATION_ID="" +export APPLICATION_SECRET="" + +# import common test functions +SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )" +source "${SCRIPT_DIR}"/common.sh + +trap "collectLogs" EXIT SIGINT + +setupTestEnvironmentOAuthFlow ${ADMIN_ACCESS_TOKEN} ${APPLICATION_NAME} ${APPLICATION_ID} ${APPLICATION_SECRET} +startOAuthFactoryTest diff --git a/.github/workflows/build-pr-check.yml b/.github/workflows/build-pr-check.yml index d2eaf447d3..ea75d678bf 100644 --- a/.github/workflows/build-pr-check.yml +++ b/.github/workflows/build-pr-check.yml @@ -31,23 +31,23 @@ jobs: cache: 'maven' - name: Login to docker.io if: github.event_name == 'pull_request' - uses: docker/login-action@v2 + uses: redhat-actions/podman-login@v1 with: username: ${{ secrets.DOCKERHUB_USERNAME }} password: ${{ secrets.DOCKERHUB_PASSWORD }} registry: docker.io - name: Login to quay.io if: github.event_name == 'pull_request' - uses: docker/login-action@v2 + uses: redhat-actions/podman-login@v1 with: username: ${{ secrets.QUAY_USERNAME }} password: ${{ secrets.QUAY_PASSWORD }} registry: quay.io - name: Build with Maven run: mvn -B clean install -U -Pintegration - - name: Build docker images + - name: Build images if: github.event_name == 'pull_request' run: ./build/build.sh --tag:${{ env.PR_IMAGE_TAG }} - - name: Push docker images + - name: Push images if: github.event_name == 'pull_request' - run: docker push quay.io/eclipse/che-server:${{ env.PR_IMAGE_TAG }} + run: podman push quay.io/eclipse/che-server:${{ env.PR_IMAGE_TAG }} diff --git a/.github/workflows/next-build.yml b/.github/workflows/next-build.yml index 2a6a464cc0..b95aa18644 100644 --- a/.github/workflows/next-build.yml +++ b/.github/workflows/next-build.yml @@ -29,28 +29,28 @@ jobs: java-version: '11' cache: 'maven' - name: Login to docker.io - uses: docker/login-action@v2 + uses: redhat-actions/podman-login@v1 with: username: ${{ secrets.DOCKERHUB_USERNAME }} password: ${{ secrets.DOCKERHUB_PASSWORD }} registry: docker.io - name: Login to quay.io - uses: docker/login-action@v2 + uses: redhat-actions/podman-login@v1 with: username: ${{ secrets.QUAY_USERNAME }} password: ${{ secrets.QUAY_PASSWORD }} registry: quay.io - name: Build with Maven run: mvn -B clean install -U -Pintegration - - name: Build docker images + - name: Build images id: build run: | echo "short_sha1=$(git rev-parse --short HEAD)" >> $GITHUB_OUTPUT ./build/build.sh --tag:next --sha-tag - name: Push docker images run: | - docker push quay.io/eclipse/che-server:next - docker push quay.io/eclipse/che-server:${{ steps.build.outputs.short_sha1 }} + podman push quay.io/eclipse/che-server:next + podman push quay.io/eclipse/che-server:${{ steps.build.outputs.short_sha1 }} - name: Create failure MM message if: ${{ failure() }} run: | diff --git a/README.md b/README.md index fa53d3968a..b37639601c 100644 --- a/README.md +++ b/README.md @@ -32,3 +32,7 @@ Other modules are deprecated and will be removed in the future. The Eclipse Che community is globally reachable through public chat rooms, mailing list and weekly calls. See the Eclipse Che Documentation about [how you can join our community](https://www.eclipse.org/che/docs/stable/overview/introduction-to-eclipse-che/#_joining_the_community). + +## Builds + +* [![release latest stable](https://github.com/eclipse-che/che-server/actions/workflows/release.yml/badge.svg)](https://github.com/eclipse-che/che-server/actions/workflows/release.yml) diff --git a/assembly/assembly-che-tomcat/pom.xml b/assembly/assembly-che-tomcat/pom.xml index fa8382d4c0..538c85313c 100644 --- a/assembly/assembly-che-tomcat/pom.xml +++ b/assembly/assembly-che-tomcat/pom.xml @@ -17,7 +17,7 @@ che-assembly-parent org.eclipse.che - 7.78.0-SNAPSHOT + 7.80.0-SNAPSHOT assembly-che-tomcat jar diff --git a/assembly/assembly-main/pom.xml b/assembly/assembly-main/pom.xml index 340edd6b4e..9c489b58fb 100644 --- a/assembly/assembly-main/pom.xml +++ b/assembly/assembly-main/pom.xml @@ -17,7 +17,7 @@ che-assembly-parent org.eclipse.che - 7.78.0-SNAPSHOT + 7.80.0-SNAPSHOT assembly-main pom diff --git a/assembly/assembly-root-war/pom.xml b/assembly/assembly-root-war/pom.xml index 094cbae01c..160c6290bb 100644 --- a/assembly/assembly-root-war/pom.xml +++ b/assembly/assembly-root-war/pom.xml @@ -17,7 +17,7 @@ che-assembly-parent org.eclipse.che - 7.78.0-SNAPSHOT + 7.80.0-SNAPSHOT assembly-root-war war diff --git a/assembly/assembly-swagger-war/pom.xml b/assembly/assembly-swagger-war/pom.xml index 44148f0816..3a49be76c7 100644 --- a/assembly/assembly-swagger-war/pom.xml +++ b/assembly/assembly-swagger-war/pom.xml @@ -17,7 +17,7 @@ che-assembly-parent org.eclipse.che - 7.78.0-SNAPSHOT + 7.80.0-SNAPSHOT assembly-swagger-war war diff --git a/assembly/assembly-wsmaster-war/pom.xml b/assembly/assembly-wsmaster-war/pom.xml index 3fbae77f1d..636b5e63ae 100644 --- a/assembly/assembly-wsmaster-war/pom.xml +++ b/assembly/assembly-wsmaster-war/pom.xml @@ -17,7 +17,7 @@ che-assembly-parent org.eclipse.che - 7.78.0-SNAPSHOT + 7.80.0-SNAPSHOT assembly-wsmaster-war war diff --git a/assembly/assembly-wsmaster-war/src/main/webapp/WEB-INF/classes/che/che.properties b/assembly/assembly-wsmaster-war/src/main/webapp/WEB-INF/classes/che/che.properties index 8c660ec936..8ee1f1f10b 100644 --- a/assembly/assembly-wsmaster-war/src/main/webapp/WEB-INF/classes/che/che.properties +++ b/assembly/assembly-wsmaster-war/src/main/webapp/WEB-INF/classes/che/che.properties @@ -663,3 +663,16 @@ che.oauth2.gitlab.clientid_filepath=NULL # Location of the file with GitLab client secret. che.oauth2.gitlab.clientsecret_filepath=NULL + +### Advanced authorization +# Comma separated list of users allowed to access Che. +che.infra.kubernetes.advanced_authorization.allow_users=NULL + +# Comma separated list of groups of users allowed to access Che. +che.infra.kubernetes.advanced_authorization.allow_groups=NULL + +# Comma separated list of users denied to access Che. +che.infra.kubernetes.advanced_authorization.deny_users=NULL + +# Comma separated list of groups of users denied to access Che. +che.infra.kubernetes.advanced_authorization.deny_groups=NULL diff --git a/assembly/pom.xml b/assembly/pom.xml index 4424129b74..cd42593280 100644 --- a/assembly/pom.xml +++ b/assembly/pom.xml @@ -17,7 +17,7 @@ che-server org.eclipse.che - 7.78.0-SNAPSHOT + 7.80.0-SNAPSHOT ../pom.xml che-assembly-parent diff --git a/build/build.sh b/build/build.sh index 63b872faac..261b61f711 100755 --- a/build/build.sh +++ b/build/build.sh @@ -15,6 +15,7 @@ IMAGE_ALIASES=${IMAGE_ALIASES:-} ERROR=${ERROR:-} DIR=${DIR:-} SHA_TAG=${SHA_TAG:-} +BUILDER=${BUILDER:-} skip_tests() { if [ $SKIP_TESTS = "true" ]; then @@ -49,6 +50,7 @@ init() { ARGS="" OPTIONS="" DOCKERFILE="" + BUILD_COMMAND="build" BUILD_ARGS="" while [ $# -gt 0 ]; do @@ -104,6 +106,37 @@ build() { DIR=$(cd "$(dirname "$0")"; pwd) fi + BUILD_COMAMAND="build" + if [ -z $BUILDER ]; then + echo "BUILDER is not specified, trying with podman" + BUILDER=$(command -v podman || true) + if [[ ! -x $BUILDER ]]; then + echo "[WARNING] podman is not installed, trying with buildah" + BUILDER=$(command -v buildah || true) + if [[ ! -x $BUILDER ]]; then + echo "[WARNING] buildah is not installed, trying with docker" + BUILDER=$(command -v docker || true) + if [[ ! -x $BUILDER ]]; then + echo "[ERROR] This script requires podman, buildah or docker to be installed. Must abort!"; exit 1 + fi + else + BUILD_COMMAND="bud" + fi + fi + else + if [[ ! -x $(command -v "$BUILDER" || true) ]]; then + echo "Builder $BUILDER is missing. Aborting."; exit 1 + fi + if [[ $BUILDER =~ "docker" || $BUILDER =~ "podman" ]]; then + if [[ ! $($BUILDER ps) ]]; then + echo "Builder $BUILDER is not functioning. Aborting."; exit 1 + fi + fi + if [[ $BUILDER =~ "buildah" ]]; then + BUILD_COMMAND="bud" + fi + fi + # If Dockerfile is empty, build all Dockerfiles if [ -z ${DOCKERFILE} ]; then DOCKERFILES_TO_BUILD="$(ls ${DIR}/Dockerfile*)" @@ -138,14 +171,14 @@ build_image() { -e "s;\${BUILD_PREFIX};${PREFIX};" \ -e "s;\${BUILD_TAG};${TAG};" \ > ${DIR}/.Dockerfile - cd "${DIR}" && docker build -f ${DIR}/.Dockerfile -t ${IMAGE_NAME} ${BUILD_ARGS} . + cd "${DIR}" && "${BUILDER}" "${BUILD_COMMAND}" -f ${DIR}/.Dockerfile -t ${IMAGE_NAME} ${BUILD_ARGS} . DOCKER_BUILD_STATUS=$? rm ${DIR}/.Dockerfile if [ $DOCKER_BUILD_STATUS -eq 0 ]; then printf "Build of ${BLUE}${IMAGE_NAME} ${GREEN}[OK]${NC}\n" if [ ! -z "${SHA_TAG}" ]; then SHA_IMAGE_NAME=${ORGANIZATION}/${PREFIX}-${NAME}:${SHA_TAG} - docker tag ${IMAGE_NAME} ${SHA_IMAGE_NAME} + "${BUILDER}" tag ${IMAGE_NAME} ${SHA_IMAGE_NAME} DOCKER_TAG_STATUS=$? if [ $DOCKER_TAG_STATUS -eq 0 ]; then printf "Re-tagging with SHA based tag ${BLUE}${SHA_IMAGE_NAME} ${GREEN}[OK]${NC}\n" @@ -157,7 +190,7 @@ build_image() { if [ ! -z "${IMAGE_ALIASES}" ]; then for TMP_IMAGE_NAME in ${IMAGE_ALIASES} do - docker tag ${IMAGE_NAME} ${TMP_IMAGE_NAME}:${TAG} + "${BUILDER}" tag ${IMAGE_NAME} ${TMP_IMAGE_NAME}:${TAG} DOCKER_TAG_STATUS=$? if [ $DOCKER_TAG_STATUS -eq 0 ]; then printf " /alias ${BLUE}${TMP_IMAGE_NAME}:${TAG}${NC} ${GREEN}[OK]${NC}\n" @@ -175,32 +208,6 @@ build_image() { fi } -check_docker() { - if ! docker ps > /dev/null 2>&1; then - output=$(docker ps) - printf "${RED}Docker not installed properly: ${output}${NC}\n" - exit 1 - fi -} - -docker_exec() { - if has_docker_for_windows_client; then - MSYS_NO_PATHCONV=1 docker.exe "$@" - else - "$(which docker)" "$@" - fi -} - -has_docker_for_windows_client() { - GLOBAL_HOST_ARCH=$(docker version --format {{.Client}}) - - if [[ "${GLOBAL_HOST_ARCH}" = *"windows"* ]]; then - return 0 - else - return 1 - fi -} - get_full_path() { echo "$(cd "$(dirname "${1}")"; pwd)/$(basename "$1")" } diff --git a/core/che-core-api-core/pom.xml b/core/che-core-api-core/pom.xml index bd307a07f5..209830f827 100644 --- a/core/che-core-api-core/pom.xml +++ b/core/che-core-api-core/pom.xml @@ -17,7 +17,7 @@ che-core-parent org.eclipse.che.core - 7.78.0-SNAPSHOT + 7.80.0-SNAPSHOT che-core-api-core jar diff --git a/core/che-core-api-dto-maven-plugin/pom.xml b/core/che-core-api-dto-maven-plugin/pom.xml index 26432e9587..aca2194175 100644 --- a/core/che-core-api-dto-maven-plugin/pom.xml +++ b/core/che-core-api-dto-maven-plugin/pom.xml @@ -17,7 +17,7 @@ che-core-parent org.eclipse.che.core - 7.78.0-SNAPSHOT + 7.80.0-SNAPSHOT che-core-api-dto-maven-plugin maven-plugin diff --git a/core/che-core-api-dto/pom.xml b/core/che-core-api-dto/pom.xml index ae97709040..f150679f83 100644 --- a/core/che-core-api-dto/pom.xml +++ b/core/che-core-api-dto/pom.xml @@ -17,7 +17,7 @@ che-core-parent org.eclipse.che.core - 7.78.0-SNAPSHOT + 7.80.0-SNAPSHOT che-core-api-dto jar diff --git a/core/che-core-api-model/pom.xml b/core/che-core-api-model/pom.xml index db8011e9d3..ee019585b3 100644 --- a/core/che-core-api-model/pom.xml +++ b/core/che-core-api-model/pom.xml @@ -17,7 +17,7 @@ che-core-parent org.eclipse.che.core - 7.78.0-SNAPSHOT + 7.80.0-SNAPSHOT che-core-api-model jar diff --git a/core/che-core-db-vendor-h2/pom.xml b/core/che-core-db-vendor-h2/pom.xml index f29460ffaa..0d56fdcb6e 100644 --- a/core/che-core-db-vendor-h2/pom.xml +++ b/core/che-core-db-vendor-h2/pom.xml @@ -17,7 +17,7 @@ che-core-parent org.eclipse.che.core - 7.78.0-SNAPSHOT + 7.80.0-SNAPSHOT che-core-db-vendor-h2 Che Core :: Commons :: DB :: Vendor H2 diff --git a/core/che-core-db-vendor-mysql/pom.xml b/core/che-core-db-vendor-mysql/pom.xml index d0bd283b04..997d903ebf 100644 --- a/core/che-core-db-vendor-mysql/pom.xml +++ b/core/che-core-db-vendor-mysql/pom.xml @@ -17,7 +17,7 @@ che-core-parent org.eclipse.che.core - 7.78.0-SNAPSHOT + 7.80.0-SNAPSHOT che-core-db-vendor-mysql Che Core :: Commons :: DB :: Vendor MySQL diff --git a/core/che-core-db-vendor-postgresql/pom.xml b/core/che-core-db-vendor-postgresql/pom.xml index c64d84aca7..3331064e4f 100644 --- a/core/che-core-db-vendor-postgresql/pom.xml +++ b/core/che-core-db-vendor-postgresql/pom.xml @@ -17,7 +17,7 @@ che-core-parent org.eclipse.che.core - 7.78.0-SNAPSHOT + 7.80.0-SNAPSHOT che-core-db-vendor-postgresql Che Core :: Commons :: DB :: Vendor PostgreSQL diff --git a/core/che-core-db/pom.xml b/core/che-core-db/pom.xml index df8f440857..4b696df1ba 100644 --- a/core/che-core-db/pom.xml +++ b/core/che-core-db/pom.xml @@ -17,7 +17,7 @@ che-core-parent org.eclipse.che.core - 7.78.0-SNAPSHOT + 7.80.0-SNAPSHOT che-core-db Che Core :: Commons :: DB diff --git a/core/che-core-logback/pom.xml b/core/che-core-logback/pom.xml index fcb65ded4a..6f28ae7638 100644 --- a/core/che-core-logback/pom.xml +++ b/core/che-core-logback/pom.xml @@ -17,7 +17,7 @@ che-core-parent org.eclipse.che.core - 7.78.0-SNAPSHOT + 7.80.0-SNAPSHOT che-core-logback jar diff --git a/core/che-core-metrics-core/pom.xml b/core/che-core-metrics-core/pom.xml index cde83fe775..d0af53f87f 100644 --- a/core/che-core-metrics-core/pom.xml +++ b/core/che-core-metrics-core/pom.xml @@ -17,7 +17,7 @@ che-core-parent org.eclipse.che.core - 7.78.0-SNAPSHOT + 7.80.0-SNAPSHOT che-core-metrics-core Che Core :: Commons :: Metrics :: Core diff --git a/core/che-core-tracing-core/pom.xml b/core/che-core-tracing-core/pom.xml index 3217f84288..5b651c7c8f 100644 --- a/core/che-core-tracing-core/pom.xml +++ b/core/che-core-tracing-core/pom.xml @@ -17,7 +17,7 @@ che-core-parent org.eclipse.che.core - 7.78.0-SNAPSHOT + 7.80.0-SNAPSHOT che-core-tracing-core Che Core :: Commons :: Tracing :: Core diff --git a/core/che-core-tracing-metrics/pom.xml b/core/che-core-tracing-metrics/pom.xml index f7548ed423..ee4be49274 100644 --- a/core/che-core-tracing-metrics/pom.xml +++ b/core/che-core-tracing-metrics/pom.xml @@ -17,7 +17,7 @@ che-core-parent org.eclipse.che.core - 7.78.0-SNAPSHOT + 7.80.0-SNAPSHOT che-core-tracing-metrics Che Core :: Commons :: Tracing :: Metrics diff --git a/core/che-core-tracing-web/pom.xml b/core/che-core-tracing-web/pom.xml index 25fe454327..8fcd0a4a4a 100644 --- a/core/che-core-tracing-web/pom.xml +++ b/core/che-core-tracing-web/pom.xml @@ -17,7 +17,7 @@ che-core-parent org.eclipse.che.core - 7.78.0-SNAPSHOT + 7.80.0-SNAPSHOT che-core-tracing-web Che Core :: Commons :: Tracing :: Web diff --git a/core/che-core-typescript-dto-maven-plugin/pom.xml b/core/che-core-typescript-dto-maven-plugin/pom.xml index 5ab93f5a97..d08a072470 100644 --- a/core/che-core-typescript-dto-maven-plugin/pom.xml +++ b/core/che-core-typescript-dto-maven-plugin/pom.xml @@ -17,7 +17,7 @@ che-core-parent org.eclipse.che.core - 7.78.0-SNAPSHOT + 7.80.0-SNAPSHOT che-core-typescript-dto-maven-plugin maven-plugin diff --git a/core/commons/che-core-commons-annotations/pom.xml b/core/commons/che-core-commons-annotations/pom.xml index cd04d3f3e1..ecd23f430d 100644 --- a/core/commons/che-core-commons-annotations/pom.xml +++ b/core/commons/che-core-commons-annotations/pom.xml @@ -17,7 +17,7 @@ che-core-commons-parent org.eclipse.che.core - 7.78.0-SNAPSHOT + 7.80.0-SNAPSHOT che-core-commons-annotations jar diff --git a/core/commons/che-core-commons-inject/pom.xml b/core/commons/che-core-commons-inject/pom.xml index 635d88e3f4..a4ef7445e5 100644 --- a/core/commons/che-core-commons-inject/pom.xml +++ b/core/commons/che-core-commons-inject/pom.xml @@ -17,7 +17,7 @@ che-core-commons-parent org.eclipse.che.core - 7.78.0-SNAPSHOT + 7.80.0-SNAPSHOT che-core-commons-inject jar diff --git a/core/commons/che-core-commons-j2ee/pom.xml b/core/commons/che-core-commons-j2ee/pom.xml index 47054a5fb2..83c322a9f8 100644 --- a/core/commons/che-core-commons-j2ee/pom.xml +++ b/core/commons/che-core-commons-j2ee/pom.xml @@ -17,7 +17,7 @@ che-core-commons-parent org.eclipse.che.core - 7.78.0-SNAPSHOT + 7.80.0-SNAPSHOT che-core-commons-j2ee jar diff --git a/core/commons/che-core-commons-json/pom.xml b/core/commons/che-core-commons-json/pom.xml index 7ee7e1875c..9082749af3 100644 --- a/core/commons/che-core-commons-json/pom.xml +++ b/core/commons/che-core-commons-json/pom.xml @@ -17,7 +17,7 @@ che-core-commons-parent org.eclipse.che.core - 7.78.0-SNAPSHOT + 7.80.0-SNAPSHOT che-core-commons-json jar diff --git a/core/commons/che-core-commons-lang/pom.xml b/core/commons/che-core-commons-lang/pom.xml index 07e111f353..d8589b1bb6 100644 --- a/core/commons/che-core-commons-lang/pom.xml +++ b/core/commons/che-core-commons-lang/pom.xml @@ -17,7 +17,7 @@ che-core-commons-parent org.eclipse.che.core - 7.78.0-SNAPSHOT + 7.80.0-SNAPSHOT che-core-commons-lang jar diff --git a/core/commons/che-core-commons-lang/src/main/java/org/eclipse/che/commons/lang/StringUtils.java b/core/commons/che-core-commons-lang/src/main/java/org/eclipse/che/commons/lang/StringUtils.java index a83fde764f..407d921293 100644 --- a/core/commons/che-core-commons-lang/src/main/java/org/eclipse/che/commons/lang/StringUtils.java +++ b/core/commons/che-core-commons-lang/src/main/java/org/eclipse/che/commons/lang/StringUtils.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012-2018 Red Hat, Inc. + * Copyright (c) 2012-2023 Red Hat, Inc. * This program and the accompanying materials are made * available under the terms of the Eclipse Public License 2.0 * which is available at https://www.eclipse.org/legal/epl-2.0/ @@ -11,6 +11,13 @@ */ package org.eclipse.che.commons.lang; +import static com.google.common.base.Strings.isNullOrEmpty; + +import com.google.common.base.Splitter; +import com.google.common.collect.Sets; +import java.util.Collections; +import java.util.Set; + /** Set of useful String methods */ public class StringUtils { @@ -91,4 +98,13 @@ public class StringUtils { } return -1; } + + /** Parse string to set of strings. String should be comma separated. Whitespaces are trimmed. */ + public static Set strToSet(String str) { + if (!isNullOrEmpty(str)) { + return Sets.newHashSet(Splitter.on(",").trimResults().omitEmptyStrings().split(str)); + } else { + return Collections.emptySet(); + } + } } diff --git a/core/commons/che-core-commons-observability/pom.xml b/core/commons/che-core-commons-observability/pom.xml index 97960812ee..9c3fdb33b8 100644 --- a/core/commons/che-core-commons-observability/pom.xml +++ b/core/commons/che-core-commons-observability/pom.xml @@ -17,7 +17,7 @@ che-core-commons-parent org.eclipse.che.core - 7.78.0-SNAPSHOT + 7.80.0-SNAPSHOT che-core-commons-observability Che Core :: Commons :: Tracing and Monitoring wrapper diff --git a/core/commons/che-core-commons-schedule/pom.xml b/core/commons/che-core-commons-schedule/pom.xml index ee0bbf7f47..e5807dac65 100644 --- a/core/commons/che-core-commons-schedule/pom.xml +++ b/core/commons/che-core-commons-schedule/pom.xml @@ -17,7 +17,7 @@ che-core-commons-parent org.eclipse.che.core - 7.78.0-SNAPSHOT + 7.80.0-SNAPSHOT che-core-commons-schedule jar diff --git a/core/commons/che-core-commons-test/pom.xml b/core/commons/che-core-commons-test/pom.xml index 39a3dbe8f4..6f4fda3890 100644 --- a/core/commons/che-core-commons-test/pom.xml +++ b/core/commons/che-core-commons-test/pom.xml @@ -17,7 +17,7 @@ che-core-commons-parent org.eclipse.che.core - 7.78.0-SNAPSHOT + 7.80.0-SNAPSHOT che-core-commons-test jar diff --git a/core/commons/che-core-commons-tracing/pom.xml b/core/commons/che-core-commons-tracing/pom.xml index 6c9d8d5d45..9a36a5bd01 100644 --- a/core/commons/che-core-commons-tracing/pom.xml +++ b/core/commons/che-core-commons-tracing/pom.xml @@ -17,7 +17,7 @@ che-core-commons-parent org.eclipse.che.core - 7.78.0-SNAPSHOT + 7.80.0-SNAPSHOT che-core-commons-tracing Che Core :: Commons :: Tracing diff --git a/core/commons/pom.xml b/core/commons/pom.xml index 339e0421ca..aac0219c21 100644 --- a/core/commons/pom.xml +++ b/core/commons/pom.xml @@ -17,7 +17,7 @@ che-core-parent org.eclipse.che.core - 7.78.0-SNAPSHOT + 7.80.0-SNAPSHOT ../pom.xml che-core-commons-parent diff --git a/core/pom.xml b/core/pom.xml index defd9d7fa6..1f85dc9ab1 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -17,7 +17,7 @@ che-server org.eclipse.che - 7.78.0-SNAPSHOT + 7.80.0-SNAPSHOT ../pom.xml org.eclipse.che.core diff --git a/devfile.yaml b/devfile.yaml index 79a7b2509a..9171a43bae 100644 --- a/devfile.yaml +++ b/devfile.yaml @@ -13,13 +13,23 @@ components: - name: m2 volume: {} commands: - - id: build + - id: buildsources exec: - label: "1. Build" + label: "1. Build sources" component: tools workingDir: ${PROJECT_SOURCE} commandLine: | - mvn clean install -DskipTests + mvn clean install -V -e -Pfast -DskipTests -Dskip-validate-sources -Denforcer.skip=true + group: + kind: build + isDefault: true + - id: buildimage + exec: + label: "2. Build image" + component: tools + workingDir: ${PROJECT_SOURCE} + commandLine: | + ./build/build.sh group: kind: build isDefault: true diff --git a/infrastructures/infrastructure-distributed/pom.xml b/infrastructures/infrastructure-distributed/pom.xml index 6d1e82eead..7c5887d761 100644 --- a/infrastructures/infrastructure-distributed/pom.xml +++ b/infrastructures/infrastructure-distributed/pom.xml @@ -17,7 +17,7 @@ che-infrastructures-parent org.eclipse.che.infrastructure - 7.78.0-SNAPSHOT + 7.80.0-SNAPSHOT infrastructure-distributed jar diff --git a/infrastructures/infrastructure-factory/pom.xml b/infrastructures/infrastructure-factory/pom.xml index f7a2939cbc..9af71b982b 100644 --- a/infrastructures/infrastructure-factory/pom.xml +++ b/infrastructures/infrastructure-factory/pom.xml @@ -17,7 +17,7 @@ che-infrastructures-parent org.eclipse.che.infrastructure - 7.78.0-SNAPSHOT + 7.80.0-SNAPSHOT infrastructure-factory jar diff --git a/infrastructures/infrastructure-factory/src/main/java/org/eclipse/che/api/factory/server/scm/kubernetes/KubernetesAuthorisationRequestManager.java b/infrastructures/infrastructure-factory/src/main/java/org/eclipse/che/api/factory/server/scm/kubernetes/KubernetesAuthorisationRequestManager.java index 51dd6e6258..44356d006d 100644 --- a/infrastructures/infrastructure-factory/src/main/java/org/eclipse/che/api/factory/server/scm/kubernetes/KubernetesAuthorisationRequestManager.java +++ b/infrastructures/infrastructure-factory/src/main/java/org/eclipse/che/api/factory/server/scm/kubernetes/KubernetesAuthorisationRequestManager.java @@ -90,7 +90,10 @@ public class KubernetesAuthorisationRequestManager implements AuthorisationReque Map> params = getQueryParametersFromState(getState(requestUrl)); errorValues = errorValues == null ? uriInfo.getQueryParameters().get("error") : errorValues; if (errorValues != null && errorValues.contains("access_denied")) { - store(getParameter(params, "oauth_provider")); + String oauthProvider = getParameter(params, "oauth_provider"); + if (!isNullOrEmpty(oauthProvider)) { + store(oauthProvider); + } } } diff --git a/infrastructures/infrastructure-factory/src/main/java/org/eclipse/che/api/factory/server/scm/kubernetes/KubernetesGitCredentialManager.java b/infrastructures/infrastructure-factory/src/main/java/org/eclipse/che/api/factory/server/scm/kubernetes/KubernetesGitCredentialManager.java index 3f31c451c6..6092ee357f 100644 --- a/infrastructures/infrastructure-factory/src/main/java/org/eclipse/che/api/factory/server/scm/kubernetes/KubernetesGitCredentialManager.java +++ b/infrastructures/infrastructure-factory/src/main/java/org/eclipse/che/api/factory/server/scm/kubernetes/KubernetesGitCredentialManager.java @@ -23,6 +23,7 @@ import static org.eclipse.che.workspace.infrastructure.kubernetes.provision.secr import static org.eclipse.che.workspace.infrastructure.kubernetes.provision.secret.KubernetesSecretAnnotationNames.DEV_WORKSPACE_PREFIX; import com.google.common.collect.ImmutableMap; +import com.google.common.net.PercentEscaper; import io.fabric8.kubernetes.api.model.ObjectMeta; import io.fabric8.kubernetes.api.model.ObjectMetaBuilder; import io.fabric8.kubernetes.api.model.Secret; @@ -173,10 +174,12 @@ public class KubernetesGitCredentialManager implements GitCredentialManager { * field. */ private String getUsernameSegment(PersonalAccessToken personalAccessToken) { + // Special characters are not allowed in URL username segment, so we need to escape them. + PercentEscaper percentEscaper = new PercentEscaper("", false); return personalAccessToken.getScmTokenName().startsWith(OAUTH_2_PREFIX) ? "oauth2" : isNullOrEmpty(personalAccessToken.getScmOrganization()) - ? personalAccessToken.getScmUserName() + ? percentEscaper.escape(personalAccessToken.getScmUserName()) : "username"; } diff --git a/infrastructures/infrastructure-metrics/pom.xml b/infrastructures/infrastructure-metrics/pom.xml index dfaab257fc..7b3f5b6d55 100644 --- a/infrastructures/infrastructure-metrics/pom.xml +++ b/infrastructures/infrastructure-metrics/pom.xml @@ -17,7 +17,7 @@ che-infrastructures-parent org.eclipse.che.infrastructure - 7.78.0-SNAPSHOT + 7.80.0-SNAPSHOT ../pom.xml infrastructure-metrics diff --git a/infrastructures/infrastructure-permission/pom.xml b/infrastructures/infrastructure-permission/pom.xml index f427a76f3c..39121ce998 100644 --- a/infrastructures/infrastructure-permission/pom.xml +++ b/infrastructures/infrastructure-permission/pom.xml @@ -17,7 +17,7 @@ che-infrastructures-parent org.eclipse.che.infrastructure - 7.78.0-SNAPSHOT + 7.80.0-SNAPSHOT infrastructure-permission Infrastructure :: Kubernetes Permissions diff --git a/infrastructures/kubernetes/pom.xml b/infrastructures/kubernetes/pom.xml index 2a5e7eb791..ee89732885 100644 --- a/infrastructures/kubernetes/pom.xml +++ b/infrastructures/kubernetes/pom.xml @@ -17,7 +17,7 @@ che-infrastructures-parent org.eclipse.che.infrastructure - 7.78.0-SNAPSHOT + 7.80.0-SNAPSHOT infrastructure-kubernetes Infrastructure :: Kubernetes diff --git a/infrastructures/kubernetes/src/main/java/org/eclipse/che/workspace/infrastructure/kubernetes/KubernetesInfraModule.java b/infrastructures/kubernetes/src/main/java/org/eclipse/che/workspace/infrastructure/kubernetes/KubernetesInfraModule.java index 1b5678ef1e..8fcc1be4fc 100644 --- a/infrastructures/kubernetes/src/main/java/org/eclipse/che/workspace/infrastructure/kubernetes/KubernetesInfraModule.java +++ b/infrastructures/kubernetes/src/main/java/org/eclipse/che/workspace/infrastructure/kubernetes/KubernetesInfraModule.java @@ -36,6 +36,9 @@ import org.eclipse.che.api.workspace.server.spi.provision.env.CheApiInternalEnvV import org.eclipse.che.api.workspace.server.wsplugins.ChePluginsApplier; import org.eclipse.che.api.workspace.shared.Constants; import org.eclipse.che.workspace.infrastructure.kubernetes.api.server.KubernetesNamespaceService; +import org.eclipse.che.workspace.infrastructure.kubernetes.authorization.AuthorizationChecker; +import org.eclipse.che.workspace.infrastructure.kubernetes.authorization.KubernetesAuthorizationCheckerImpl; +import org.eclipse.che.workspace.infrastructure.kubernetes.authorization.PermissionsCleaner; import org.eclipse.che.workspace.infrastructure.kubernetes.cache.jpa.JpaKubernetesRuntimeCacheModule; import org.eclipse.che.workspace.infrastructure.kubernetes.devfile.DockerimageComponentToWorkspaceApplier; import org.eclipse.che.workspace.infrastructure.kubernetes.devfile.KubernetesComponentToWorkspaceApplier; @@ -110,6 +113,9 @@ public class KubernetesInfraModule extends AbstractModule { namespaceConfigurators.addBinding().to(SshKeysConfigurator.class); namespaceConfigurators.addBinding().to(GitconfigUserDataConfigurator.class); + bind(AuthorizationChecker.class).to(KubernetesAuthorizationCheckerImpl.class); + bind(PermissionsCleaner.class).asEagerSingleton(); + bind(KubernetesNamespaceService.class); MapBinder factories = diff --git a/infrastructures/kubernetes/src/main/java/org/eclipse/che/workspace/infrastructure/kubernetes/authorization/AuthorizationChecker.java b/infrastructures/kubernetes/src/main/java/org/eclipse/che/workspace/infrastructure/kubernetes/authorization/AuthorizationChecker.java new file mode 100644 index 0000000000..73b238c1de --- /dev/null +++ b/infrastructures/kubernetes/src/main/java/org/eclipse/che/workspace/infrastructure/kubernetes/authorization/AuthorizationChecker.java @@ -0,0 +1,20 @@ +/* + * Copyright (c) 2012-2023 Red Hat, Inc. + * This program and the accompanying materials are made + * available under the terms of the Eclipse Public License 2.0 + * which is available at https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * Red Hat, Inc. - initial API and implementation + */ +package org.eclipse.che.workspace.infrastructure.kubernetes.authorization; + +import org.eclipse.che.api.workspace.server.spi.InfrastructureException; + +/** This {@link AuthorizationChecker} checks if user is allowed to use Che. */ +public interface AuthorizationChecker { + + boolean isAuthorized(String username) throws InfrastructureException; +} diff --git a/infrastructures/kubernetes/src/main/java/org/eclipse/che/workspace/infrastructure/kubernetes/authorization/AuthorizationException.java b/infrastructures/kubernetes/src/main/java/org/eclipse/che/workspace/infrastructure/kubernetes/authorization/AuthorizationException.java new file mode 100644 index 0000000000..eb08dbe73b --- /dev/null +++ b/infrastructures/kubernetes/src/main/java/org/eclipse/che/workspace/infrastructure/kubernetes/authorization/AuthorizationException.java @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2012-2023 Red Hat, Inc. + * This program and the accompanying materials are made + * available under the terms of the Eclipse Public License 2.0 + * which is available at https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * Red Hat, Inc. - initial API and implementation + */ +package org.eclipse.che.workspace.infrastructure.kubernetes.authorization; + +import org.eclipse.che.api.workspace.server.spi.InfrastructureException; +import org.eclipse.che.api.workspace.server.spi.RuntimeInfrastructure; + +/** + * An exception thrown by {@link RuntimeInfrastructure} and related components. Indicates that a + * user is not authorized to use Che. + * + * @author Anatolii Bazko + */ +public class AuthorizationException extends InfrastructureException { + public AuthorizationException(String message) { + super(message); + } +} diff --git a/infrastructures/kubernetes/src/main/java/org/eclipse/che/workspace/infrastructure/kubernetes/authorization/KubernetesAuthorizationCheckerImpl.java b/infrastructures/kubernetes/src/main/java/org/eclipse/che/workspace/infrastructure/kubernetes/authorization/KubernetesAuthorizationCheckerImpl.java new file mode 100644 index 0000000000..16f9893ea5 --- /dev/null +++ b/infrastructures/kubernetes/src/main/java/org/eclipse/che/workspace/infrastructure/kubernetes/authorization/KubernetesAuthorizationCheckerImpl.java @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2012-2023 Red Hat, Inc. + * This program and the accompanying materials are made + * available under the terms of the Eclipse Public License 2.0 + * which is available at https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * Red Hat, Inc. - initial API and implementation + */ +package org.eclipse.che.workspace.infrastructure.kubernetes.authorization; + +import static org.eclipse.che.commons.lang.StringUtils.strToSet; + +import java.util.Set; +import javax.inject.Inject; +import javax.inject.Named; +import javax.inject.Singleton; +import org.eclipse.che.commons.annotation.Nullable; + +/** This {@link KubernetesAuthorizationCheckerImpl} checks if user is allowed to use Che. */ +@Singleton +public class KubernetesAuthorizationCheckerImpl implements AuthorizationChecker { + + private final Set allowUsers; + private final Set denyUsers; + + @Inject + public KubernetesAuthorizationCheckerImpl( + @Nullable @Named("che.infra.kubernetes.advanced_authorization.allow_users") String allowUsers, + @Nullable @Named("che.infra.kubernetes.advanced_authorization.deny_users") String denyUsers) { + this.allowUsers = strToSet(allowUsers); + this.denyUsers = strToSet(denyUsers); + } + + public boolean isAuthorized(String username) { + return isAllowedUser(username) && !isDeniedUser(username); + } + + private boolean isAllowedUser(String username) { + return allowUsers.isEmpty() || allowUsers.contains(username); + } + + private boolean isDeniedUser(String username) { + return !denyUsers.isEmpty() && denyUsers.contains(username); + } +} diff --git a/infrastructures/kubernetes/src/main/java/org/eclipse/che/workspace/infrastructure/kubernetes/authorization/PermissionsCleaner.java b/infrastructures/kubernetes/src/main/java/org/eclipse/che/workspace/infrastructure/kubernetes/authorization/PermissionsCleaner.java new file mode 100644 index 0000000000..8b4eacaf98 --- /dev/null +++ b/infrastructures/kubernetes/src/main/java/org/eclipse/che/workspace/infrastructure/kubernetes/authorization/PermissionsCleaner.java @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2012-2023 Red Hat, Inc. + * This program and the accompanying materials are made + * available under the terms of the Eclipse Public License 2.0 + * which is available at https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * Red Hat, Inc. - initial API and implementation + */ +package org.eclipse.che.workspace.infrastructure.kubernetes.authorization; + +import static org.eclipse.che.commons.lang.StringUtils.strToSet; + +import io.fabric8.kubernetes.client.KubernetesClient; +import java.util.Set; +import javax.inject.Inject; +import javax.inject.Named; +import javax.inject.Singleton; +import org.eclipse.che.api.workspace.server.spi.InfrastructureException; +import org.eclipse.che.commons.annotation.Nullable; +import org.eclipse.che.workspace.infrastructure.kubernetes.CheServerKubernetesClientFactory; + +/** This {@link PermissionsCleaner} cleans up all user's permissions. */ +@Singleton +public class PermissionsCleaner { + + private final Set userClusterRoles; + private final CheServerKubernetesClientFactory cheServerKubernetesClientFactory; + + @Inject + public PermissionsCleaner( + @Nullable @Named("che.infra.kubernetes.user_cluster_roles") String userClusterRoles, + CheServerKubernetesClientFactory cheServerKubernetesClientFactory) { + this.cheServerKubernetesClientFactory = cheServerKubernetesClientFactory; + this.userClusterRoles = strToSet(userClusterRoles); + } + + public void cleanUp(String namespaceName) throws InfrastructureException { + KubernetesClient client = cheServerKubernetesClientFactory.create(); + for (String userClusterRole : userClusterRoles) { + client.rbac().roleBindings().inNamespace(namespaceName).withName(userClusterRole).delete(); + } + } +} diff --git a/infrastructures/kubernetes/src/main/java/org/eclipse/che/workspace/infrastructure/kubernetes/namespace/KubernetesNamespaceFactory.java b/infrastructures/kubernetes/src/main/java/org/eclipse/che/workspace/infrastructure/kubernetes/namespace/KubernetesNamespaceFactory.java index a76a7b4207..319d1b8770 100644 --- a/infrastructures/kubernetes/src/main/java/org/eclipse/che/workspace/infrastructure/kubernetes/namespace/KubernetesNamespaceFactory.java +++ b/infrastructures/kubernetes/src/main/java/org/eclipse/che/workspace/infrastructure/kubernetes/namespace/KubernetesNamespaceFactory.java @@ -57,6 +57,9 @@ import org.eclipse.che.inject.ConfigurationException; import org.eclipse.che.workspace.infrastructure.kubernetes.CheServerKubernetesClientFactory; import org.eclipse.che.workspace.infrastructure.kubernetes.api.server.impls.KubernetesNamespaceMetaImpl; import org.eclipse.che.workspace.infrastructure.kubernetes.api.shared.KubernetesNamespaceMeta; +import org.eclipse.che.workspace.infrastructure.kubernetes.authorization.AuthorizationChecker; +import org.eclipse.che.workspace.infrastructure.kubernetes.authorization.AuthorizationException; +import org.eclipse.che.workspace.infrastructure.kubernetes.authorization.PermissionsCleaner; import org.eclipse.che.workspace.infrastructure.kubernetes.namespace.configurator.NamespaceConfigurator; import org.eclipse.che.workspace.infrastructure.kubernetes.util.KubernetesSharedPool; import org.slf4j.Logger; @@ -98,6 +101,8 @@ public class KubernetesNamespaceFactory { private final PreferenceManager preferenceManager; protected final Set namespaceConfigurators; protected final KubernetesSharedPool sharedPool; + protected final AuthorizationChecker authorizationChecker; + protected final PermissionsCleaner permissionsCleaner; @Inject public KubernetesNamespaceFactory( @@ -110,7 +115,9 @@ public class KubernetesNamespaceFactory { Set namespaceConfigurators, CheServerKubernetesClientFactory cheServerKubernetesClientFactory, PreferenceManager preferenceManager, - KubernetesSharedPool sharedPool) + KubernetesSharedPool sharedPool, + AuthorizationChecker authorizationChecker, + PermissionsCleaner permissionsCleaner) throws ConfigurationException { this.namespaceCreationAllowed = namespaceCreationAllowed; this.cheServerKubernetesClientFactory = cheServerKubernetesClientFactory; @@ -120,6 +127,8 @@ public class KubernetesNamespaceFactory { this.labelNamespaces = labelNamespaces; this.annotateNamespaces = annotateNamespaces; this.namespaceConfigurators = ImmutableSet.copyOf(namespaceConfigurators); + this.authorizationChecker = authorizationChecker; + this.permissionsCleaner = permissionsCleaner; //noinspection UnstableApiUsage Splitter.MapSplitter csvMapSplitter = Splitter.on(",").withKeyValueSeparator("="); @@ -281,6 +290,9 @@ public class KubernetesNamespaceFactory { var subject = EnvironmentContext.getCurrent().getSubject(); var userName = subject.getUserName(); + + validateAuthorization(namespace.getName(), userName); + NamespaceResolutionContext resolutionCtx = new NamespaceResolutionContext(identity.getWorkspaceId(), subject.getUserId(), userName); Map namespaceAnnotationsEvaluated = @@ -573,6 +585,27 @@ public class KubernetesNamespaceFactory { } } + protected void validateAuthorization(String namespaceName, String username) + throws InfrastructureException { + if (!authorizationChecker.isAuthorized(username)) { + try { + permissionsCleaner.cleanUp(namespaceName); + } catch (InfrastructureException | KubernetesClientException e) { + LOG.error( + "Failed to clean up permissions for user '{}' in namespace '{}'. Cause: {}", + username, + namespaceName, + e.getMessage(), + e); + } + + throw new AuthorizationException( + format( + "User '%s' is not authorized to create a project. Please contact your system administrator.", + username)); + } + } + protected String evalPlaceholders(String namespace, NamespaceResolutionContext ctx) { checkArgument(!isNullOrEmpty(namespace)); String evaluated = namespace; diff --git a/infrastructures/kubernetes/src/test/java/org/eclipse/che/workspace/infrastructure/kubernetes/authorization/KubernetesAuthorizationCheckerTest.java b/infrastructures/kubernetes/src/test/java/org/eclipse/che/workspace/infrastructure/kubernetes/authorization/KubernetesAuthorizationCheckerTest.java new file mode 100644 index 0000000000..3c5d0a5b4a --- /dev/null +++ b/infrastructures/kubernetes/src/test/java/org/eclipse/che/workspace/infrastructure/kubernetes/authorization/KubernetesAuthorizationCheckerTest.java @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2012-2023 Red Hat, Inc. + * This program and the accompanying materials are made + * available under the terms of the Eclipse Public License 2.0 + * which is available at https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * Red Hat, Inc. - initial API and implementation + */ +package org.eclipse.che.workspace.infrastructure.kubernetes.authorization; + +import org.eclipse.che.api.workspace.server.spi.InfrastructureException; +import org.mockito.testng.MockitoTestNGListener; +import org.testng.Assert; +import org.testng.annotations.DataProvider; +import org.testng.annotations.Listeners; +import org.testng.annotations.Test; + +@Listeners(MockitoTestNGListener.class) +public class KubernetesAuthorizationCheckerTest { + @Test(dataProvider = "advancedAuthorizationData") + public void advancedAuthorization( + String testUserName, String allowedUsers, String deniedUsers, boolean expectedIsAuthorized) + throws InfrastructureException { + // give + AuthorizationChecker authorizationChecker = + new KubernetesAuthorizationCheckerImpl(allowedUsers, deniedUsers); + + // when + boolean isAuthorized = authorizationChecker.isAuthorized(testUserName); + + // then + Assert.assertEquals(isAuthorized, expectedIsAuthorized); + } + + @DataProvider + public static Object[][] advancedAuthorizationData() { + return new Object[][] { + {"user1", "", "", true}, + {"user1", "user1", "", true}, + {"user1", "user1", "user2", true}, + {"user1", "user1", "user1", false}, + {"user2", "user1", "", false}, + {"user2", "user1", "user2", false}, + }; + } +} diff --git a/infrastructures/kubernetes/src/test/java/org/eclipse/che/workspace/infrastructure/kubernetes/namespace/KubernetesNamespaceFactoryTest.java b/infrastructures/kubernetes/src/test/java/org/eclipse/che/workspace/infrastructure/kubernetes/namespace/KubernetesNamespaceFactoryTest.java index 061c06aaf9..689481e017 100644 --- a/infrastructures/kubernetes/src/test/java/org/eclipse/che/workspace/infrastructure/kubernetes/namespace/KubernetesNamespaceFactoryTest.java +++ b/infrastructures/kubernetes/src/test/java/org/eclipse/che/workspace/infrastructure/kubernetes/namespace/KubernetesNamespaceFactoryTest.java @@ -87,6 +87,8 @@ import org.eclipse.che.inject.ConfigurationException; import org.eclipse.che.workspace.infrastructure.kubernetes.CheServerKubernetesClientFactory; import org.eclipse.che.workspace.infrastructure.kubernetes.api.server.impls.KubernetesNamespaceMetaImpl; import org.eclipse.che.workspace.infrastructure.kubernetes.api.shared.KubernetesNamespaceMeta; +import org.eclipse.che.workspace.infrastructure.kubernetes.authorization.AuthorizationChecker; +import org.eclipse.che.workspace.infrastructure.kubernetes.authorization.PermissionsCleaner; import org.eclipse.che.workspace.infrastructure.kubernetes.namespace.configurator.NamespaceConfigurator; import org.eclipse.che.workspace.infrastructure.kubernetes.namespace.configurator.PreferencesConfigMapConfigurator; import org.eclipse.che.workspace.infrastructure.kubernetes.namespace.configurator.WorkspaceServiceAccountConfigurator; @@ -126,6 +128,8 @@ public class KubernetesNamespaceFactoryTest { private KubernetesClient k8sClient; @Mock private PreferenceManager preferenceManager; @Mock Appender mockedAppender; + @Mock AuthorizationChecker authorizationChecker; + @Mock PermissionsCleaner permissionsCleaner; @Mock private NonNamespaceOperation namespaceOperation; @@ -151,6 +155,7 @@ public class KubernetesNamespaceFactoryTest { lenient().when(namespaceOperation.withName(any())).thenReturn(namespaceResource); lenient().when(namespaceResource.get()).thenReturn(mock(Namespace.class)); + lenient().when(authorizationChecker.isAuthorized(anyString())).thenReturn(true); lenient().doReturn(namespaceListResource).when(namespaceOperation).withLabels(anyMap()); lenient().when(namespaceListResource.list()).thenReturn(namespaceList); @@ -179,7 +184,9 @@ public class KubernetesNamespaceFactoryTest { emptySet(), cheServerKubernetesClientFactory, preferenceManager, - pool); + pool, + authorizationChecker, + permissionsCleaner); namespaceFactory.checkIfNamespaceIsAllowed("jondoe-che"); } @@ -204,7 +211,9 @@ public class KubernetesNamespaceFactoryTest { emptySet(), cheServerKubernetesClientFactory, preferenceManager, - pool); + pool, + authorizationChecker, + permissionsCleaner); namespaceFactory.checkIfNamespaceIsAllowed("any-namespace"); } @@ -222,7 +231,9 @@ public class KubernetesNamespaceFactoryTest { emptySet(), cheServerKubernetesClientFactory, preferenceManager, - pool); + pool, + authorizationChecker, + permissionsCleaner); assertEquals("che-kube-admin", namespaceFactory.normalizeNamespaceName("kube:admin")); } @@ -245,7 +256,9 @@ public class KubernetesNamespaceFactoryTest { emptySet(), cheServerKubernetesClientFactory, preferenceManager, - pool); + pool, + authorizationChecker, + permissionsCleaner); namespaceFactory.checkIfNamespaceIsAllowed("any-namespace"); } @@ -265,7 +278,9 @@ public class KubernetesNamespaceFactoryTest { emptySet(), cheServerKubernetesClientFactory, preferenceManager, - pool); + pool, + authorizationChecker, + permissionsCleaner); } @Test @@ -313,7 +328,9 @@ public class KubernetesNamespaceFactoryTest { emptySet(), cheServerKubernetesClientFactory, preferenceManager, - pool); + pool, + authorizationChecker, + permissionsCleaner); EnvironmentContext.getCurrent().setSubject(new SubjectImpl("jondoe", "123", null, false)); // when @@ -354,7 +371,9 @@ public class KubernetesNamespaceFactoryTest { emptySet(), cheServerKubernetesClientFactory, preferenceManager, - pool); + pool, + authorizationChecker, + permissionsCleaner); EnvironmentContext.getCurrent().setSubject(new SubjectImpl("jondoe", "123", null, false)); // when @@ -382,7 +401,9 @@ public class KubernetesNamespaceFactoryTest { emptySet(), cheServerKubernetesClientFactory, preferenceManager, - pool); + pool, + authorizationChecker, + permissionsCleaner); // when namespaceFactory.list(); @@ -413,7 +434,9 @@ public class KubernetesNamespaceFactoryTest { emptySet(), cheServerKubernetesClientFactory, preferenceManager, - pool); + pool, + authorizationChecker, + permissionsCleaner); List availableNamespaces = namespaceFactory.list(); assertEquals(availableNamespaces.size(), 1); @@ -439,7 +462,9 @@ public class KubernetesNamespaceFactoryTest { emptySet(), cheServerKubernetesClientFactory, preferenceManager, - pool); + pool, + authorizationChecker, + permissionsCleaner); List availableNamespaces = namespaceFactory.list(); assertEquals(availableNamespaces.size(), 1); @@ -467,7 +492,9 @@ public class KubernetesNamespaceFactoryTest { Set.of(new PreferencesConfigMapConfigurator(cheServerKubernetesClientFactory)), cheServerKubernetesClientFactory, preferenceManager, - pool)); + pool, + authorizationChecker, + permissionsCleaner)); KubernetesNamespace toReturnNamespace = mock(KubernetesNamespace.class); when(toReturnNamespace.getName()).thenReturn("namespaceName"); doReturn(toReturnNamespace).when(namespaceFactory).doCreateNamespaceAccess(any(), any()); @@ -508,7 +535,9 @@ public class KubernetesNamespaceFactoryTest { namespaceConfigurators, cheServerKubernetesClientFactory, preferenceManager, - pool)); + pool, + authorizationChecker, + permissionsCleaner)); EnvironmentContext.getCurrent().setSubject(new SubjectImpl("jondoe", "123", null, false)); KubernetesNamespace toReturnNamespace = mock(KubernetesNamespace.class); @@ -543,7 +572,9 @@ public class KubernetesNamespaceFactoryTest { emptySet(), cheServerKubernetesClientFactory, preferenceManager, - pool)); + pool, + authorizationChecker, + permissionsCleaner)); KubernetesNamespace toReturnNamespace = mock(KubernetesNamespace.class); prepareNamespace(toReturnNamespace); doReturn(toReturnNamespace).when(namespaceFactory).doCreateNamespaceAccess(any(), any()); @@ -575,7 +606,9 @@ public class KubernetesNamespaceFactoryTest { emptySet(), cheServerKubernetesClientFactory, preferenceManager, - pool)); + pool, + authorizationChecker, + permissionsCleaner)); KubernetesNamespace toReturnNamespace = mock(KubernetesNamespace.class); prepareNamespace(toReturnNamespace); doReturn(toReturnNamespace).when(namespaceFactory).doCreateNamespaceAccess(any(), any()); @@ -608,7 +641,9 @@ public class KubernetesNamespaceFactoryTest { emptySet(), cheServerKubernetesClientFactory, preferenceManager, - pool); + pool, + authorizationChecker, + permissionsCleaner); throwOnTryToGetNamespaceByName( "jondoe-che", new KubernetesClientException("connection refused")); @@ -631,7 +666,9 @@ public class KubernetesNamespaceFactoryTest { emptySet(), cheServerKubernetesClientFactory, preferenceManager, - pool); + pool, + authorizationChecker, + permissionsCleaner); throwOnTryToGetNamespacesList(new KubernetesClientException("connection refused")); namespaceFactory.list(); @@ -659,7 +696,9 @@ public class KubernetesNamespaceFactoryTest { emptySet(), cheServerKubernetesClientFactory, preferenceManager, - pool)); + pool, + authorizationChecker, + permissionsCleaner)); KubernetesNamespace toReturnNamespace = mock(KubernetesNamespace.class); prepareNamespace(toReturnNamespace); doReturn(toReturnNamespace).when(namespaceFactory).doCreateNamespaceAccess(any(), any()); @@ -688,7 +727,9 @@ public class KubernetesNamespaceFactoryTest { emptySet(), cheServerKubernetesClientFactory, preferenceManager, - pool)); + pool, + authorizationChecker, + permissionsCleaner)); KubernetesNamespace toReturnNamespace = mock(KubernetesNamespace.class); prepareNamespace(toReturnNamespace); doReturn(toReturnNamespace).when(namespaceFactory).doCreateNamespaceAccess(any(), any()); @@ -723,7 +764,9 @@ public class KubernetesNamespaceFactoryTest { Set.of(serviceAccountCfg), cheServerKubernetesClientFactory, preferenceManager, - pool)); + pool, + authorizationChecker, + permissionsCleaner)); KubernetesNamespace toReturnNamespace = mock(KubernetesNamespace.class); prepareNamespace(toReturnNamespace); when(toReturnNamespace.getName()).thenReturn("workspace123"); @@ -761,7 +804,9 @@ public class KubernetesNamespaceFactoryTest { Set.of(serviceAccountConfigurator), cheServerKubernetesClientFactory, preferenceManager, - pool)); + pool, + authorizationChecker, + permissionsCleaner)); KubernetesNamespace toReturnNamespace = mock(KubernetesNamespace.class); prepareNamespace(toReturnNamespace); when(toReturnNamespace.getName()).thenReturn("workspace123"); @@ -836,7 +881,9 @@ public class KubernetesNamespaceFactoryTest { Set.of(serviceAccountConfigurator), cheServerKubernetesClientFactory, preferenceManager, - pool)); + pool, + authorizationChecker, + permissionsCleaner)); KubernetesNamespace toReturnNamespace = mock(KubernetesNamespace.class); prepareNamespace(toReturnNamespace); when(toReturnNamespace.getName()).thenReturn("workspace123"); @@ -881,7 +928,9 @@ public class KubernetesNamespaceFactoryTest { "serviceAccount", "", cheServerKubernetesClientFactory)), cheServerKubernetesClientFactory, preferenceManager, - pool)); + pool, + authorizationChecker, + permissionsCleaner)); KubernetesNamespace toReturnNamespace = mock(KubernetesNamespace.class); prepareNamespace(toReturnNamespace); when(toReturnNamespace.getName()).thenReturn("workspace123"); @@ -945,7 +994,9 @@ public class KubernetesNamespaceFactoryTest { emptySet(), cheServerKubernetesClientFactory, preferenceManager, - pool); + pool, + authorizationChecker, + permissionsCleaner); WorkspaceImpl workspace = new WorkspaceImplBuilder().setId("workspace123").setAttributes(emptyMap()).build(); @@ -968,7 +1019,9 @@ public class KubernetesNamespaceFactoryTest { emptySet(), cheServerKubernetesClientFactory, preferenceManager, - pool); + pool, + authorizationChecker, + permissionsCleaner); Map prefs = new HashMap<>(); prefs.put(WORKSPACE_INFRASTRUCTURE_NAMESPACE_ATTRIBUTE, "che-123"); @@ -996,7 +1049,9 @@ public class KubernetesNamespaceFactoryTest { emptySet(), cheServerKubernetesClientFactory, preferenceManager, - pool); + pool, + authorizationChecker, + permissionsCleaner); Map prefs = new HashMap<>(); // returned but ignored @@ -1025,7 +1080,9 @@ public class KubernetesNamespaceFactoryTest { emptySet(), cheServerKubernetesClientFactory, preferenceManager, - pool); + pool, + authorizationChecker, + permissionsCleaner); Map prefs = new HashMap<>(); // returned but ignored @@ -1054,7 +1111,9 @@ public class KubernetesNamespaceFactoryTest { emptySet(), cheServerKubernetesClientFactory, preferenceManager, - pool)); + pool, + authorizationChecker, + permissionsCleaner)); doReturn(empty()).when(namespaceFactory).fetchNamespace(anyString()); String namespace = @@ -1079,7 +1138,9 @@ public class KubernetesNamespaceFactoryTest { emptySet(), cheServerKubernetesClientFactory, preferenceManager, - pool); + pool, + authorizationChecker, + permissionsCleaner); WorkspaceImpl workspace = new WorkspaceImplBuilder() @@ -1106,7 +1167,9 @@ public class KubernetesNamespaceFactoryTest { emptySet(), cheServerKubernetesClientFactory, preferenceManager, - pool); + pool, + authorizationChecker, + permissionsCleaner); WorkspaceImpl workspace = new WorkspaceImplBuilder() @@ -1154,7 +1217,9 @@ public class KubernetesNamespaceFactoryTest { emptySet(), cheServerKubernetesClientFactory, preferenceManager, - pool); + pool, + authorizationChecker, + permissionsCleaner); String namespace = namespaceFactory.evaluateNamespaceName( @@ -1178,7 +1243,9 @@ public class KubernetesNamespaceFactoryTest { emptySet(), cheServerKubernetesClientFactory, preferenceManager, - pool)); + pool, + authorizationChecker, + permissionsCleaner)); KubernetesNamespace toReturnNamespace = mock(KubernetesNamespace.class); prepareNamespace(toReturnNamespace); when(toReturnNamespace.getName()).thenReturn("jondoe-che"); @@ -1215,7 +1282,9 @@ public class KubernetesNamespaceFactoryTest { emptySet(), cheServerKubernetesClientFactory, preferenceManager, - pool)); + pool, + authorizationChecker, + permissionsCleaner)); KubernetesNamespace toReturnNamespace = mock(KubernetesNamespace.class); prepareNamespace(toReturnNamespace); when(toReturnNamespace.getName()).thenReturn("jondoe-cha-cha-cha"); @@ -1251,7 +1320,9 @@ public class KubernetesNamespaceFactoryTest { emptySet(), cheServerKubernetesClientFactory, preferenceManager, - pool)); + pool, + authorizationChecker, + permissionsCleaner)); KubernetesNamespace toReturnNamespace = mock(KubernetesNamespace.class); prepareNamespace(toReturnNamespace); when(toReturnNamespace.getName()).thenReturn("jondoe-cha-cha-cha"); @@ -1299,7 +1370,9 @@ public class KubernetesNamespaceFactoryTest { emptySet(), cheServerKubernetesClientFactory, preferenceManager, - pool); + pool, + authorizationChecker, + permissionsCleaner); EnvironmentContext.getCurrent().setSubject(new SubjectImpl("jondoe", "123", null, false)); namespaceFactory.list(); @@ -1322,7 +1395,9 @@ public class KubernetesNamespaceFactoryTest { emptySet(), cheServerKubernetesClientFactory, preferenceManager, - pool)); + pool, + authorizationChecker, + permissionsCleaner)); EnvironmentContext.getCurrent().setSubject(new SubjectImpl("jondoe", "123", null, false)); KubernetesNamespace toReturnNamespace = mock(KubernetesNamespace.class); prepareNamespace(toReturnNamespace); @@ -1351,7 +1426,9 @@ public class KubernetesNamespaceFactoryTest { emptySet(), cheServerKubernetesClientFactory, preferenceManager, - pool); + pool, + authorizationChecker, + permissionsCleaner); assertEquals(expected, namespaceFactory.normalizeNamespaceName(raw)); } @@ -1368,7 +1445,9 @@ public class KubernetesNamespaceFactoryTest { emptySet(), cheServerKubernetesClientFactory, preferenceManager, - pool); + pool, + authorizationChecker, + permissionsCleaner); assertEquals( 63, diff --git a/infrastructures/openshift/pom.xml b/infrastructures/openshift/pom.xml index 838bacde8f..576b62a706 100644 --- a/infrastructures/openshift/pom.xml +++ b/infrastructures/openshift/pom.xml @@ -17,7 +17,7 @@ che-infrastructures-parent org.eclipse.che.infrastructure - 7.78.0-SNAPSHOT + 7.80.0-SNAPSHOT infrastructure-openshift Infrastructure :: OpenShift @@ -161,6 +161,17 @@ mockwebserver test + + io.fabric8 + kubernetes-server-mock + test + + + junit-jupiter-api + org.junit.jupiter + + + io.fabric8 openshift-server-mock diff --git a/infrastructures/openshift/src/main/java/org/eclipse/che/workspace/infrastructure/openshift/OpenShiftInfraModule.java b/infrastructures/openshift/src/main/java/org/eclipse/che/workspace/infrastructure/openshift/OpenShiftInfraModule.java index 3b7ab5c538..4ec75404dd 100644 --- a/infrastructures/openshift/src/main/java/org/eclipse/che/workspace/infrastructure/openshift/OpenShiftInfraModule.java +++ b/infrastructures/openshift/src/main/java/org/eclipse/che/workspace/infrastructure/openshift/OpenShiftInfraModule.java @@ -40,6 +40,8 @@ import org.eclipse.che.workspace.infrastructure.kubernetes.KubernetesClientTermi import org.eclipse.che.workspace.infrastructure.kubernetes.KubernetesEnvironmentProvisioner; import org.eclipse.che.workspace.infrastructure.kubernetes.StartSynchronizerFactory; import org.eclipse.che.workspace.infrastructure.kubernetes.api.server.KubernetesNamespaceService; +import org.eclipse.che.workspace.infrastructure.kubernetes.authorization.AuthorizationChecker; +import org.eclipse.che.workspace.infrastructure.kubernetes.authorization.PermissionsCleaner; import org.eclipse.che.workspace.infrastructure.kubernetes.cache.jpa.JpaKubernetesRuntimeCacheModule; import org.eclipse.che.workspace.infrastructure.kubernetes.devfile.DockerimageComponentToWorkspaceApplier; import org.eclipse.che.workspace.infrastructure.kubernetes.devfile.KubernetesComponentToWorkspaceApplier; @@ -80,6 +82,7 @@ import org.eclipse.che.workspace.infrastructure.kubernetes.wsplugins.PluginBroke import org.eclipse.che.workspace.infrastructure.kubernetes.wsplugins.SidecarToolingProvisioner; import org.eclipse.che.workspace.infrastructure.kubernetes.wsplugins.brokerphases.BrokerEnvironmentFactory; import org.eclipse.che.workspace.infrastructure.kubernetes.wsplugins.events.BrokerService; +import org.eclipse.che.workspace.infrastructure.openshift.authorization.OpenShiftAuthorizationCheckerImpl; import org.eclipse.che.workspace.infrastructure.openshift.devfile.OpenshiftComponentToWorkspaceApplier; import org.eclipse.che.workspace.infrastructure.openshift.environment.OpenShiftEnvironment; import org.eclipse.che.workspace.infrastructure.openshift.environment.OpenShiftEnvironmentFactory; @@ -118,6 +121,9 @@ public class OpenShiftInfraModule extends AbstractModule { namespaceConfigurators.addBinding().to(SshKeysConfigurator.class); namespaceConfigurators.addBinding().to(GitconfigUserDataConfigurator.class); + bind(AuthorizationChecker.class).to(OpenShiftAuthorizationCheckerImpl.class); + bind(PermissionsCleaner.class).asEagerSingleton(); + bind(KubernetesNamespaceService.class); MapBinder factories = diff --git a/infrastructures/openshift/src/main/java/org/eclipse/che/workspace/infrastructure/openshift/authorization/OpenShiftAuthorizationCheckerImpl.java b/infrastructures/openshift/src/main/java/org/eclipse/che/workspace/infrastructure/openshift/authorization/OpenShiftAuthorizationCheckerImpl.java new file mode 100644 index 0000000000..a606e4f512 --- /dev/null +++ b/infrastructures/openshift/src/main/java/org/eclipse/che/workspace/infrastructure/openshift/authorization/OpenShiftAuthorizationCheckerImpl.java @@ -0,0 +1,97 @@ +/* + * Copyright (c) 2012-2023 Red Hat, Inc. + * This program and the accompanying materials are made + * available under the terms of the Eclipse Public License 2.0 + * which is available at https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * Red Hat, Inc. - initial API and implementation + */ +package org.eclipse.che.workspace.infrastructure.openshift.authorization; + +import static org.eclipse.che.commons.lang.StringUtils.strToSet; + +import io.fabric8.kubernetes.client.KubernetesClient; +import io.fabric8.openshift.api.model.Group; +import java.util.Set; +import javax.inject.Inject; +import javax.inject.Named; +import javax.inject.Singleton; +import org.eclipse.che.api.workspace.server.spi.InfrastructureException; +import org.eclipse.che.commons.annotation.Nullable; +import org.eclipse.che.workspace.infrastructure.kubernetes.CheServerKubernetesClientFactory; +import org.eclipse.che.workspace.infrastructure.kubernetes.authorization.AuthorizationChecker; + +/** This {@link OpenShiftAuthorizationCheckerImpl} checks if user is allowed to use Che. */ +@Singleton +public class OpenShiftAuthorizationCheckerImpl implements AuthorizationChecker { + + private final CheServerKubernetesClientFactory cheServerKubernetesClientFactory; + + private final Set allowUsers; + private final Set allowGroups; + private final Set denyUsers; + private final Set denyGroups; + + @Inject + public OpenShiftAuthorizationCheckerImpl( + @Nullable @Named("che.infra.kubernetes.advanced_authorization.allow_users") String allowUsers, + @Nullable @Named("che.infra.kubernetes.advanced_authorization.allow_groups") + String allowGroups, + @Nullable @Named("che.infra.kubernetes.advanced_authorization.deny_users") String denyUsers, + @Nullable @Named("che.infra.kubernetes.advanced_authorization.deny_groups") String denyGroups, + CheServerKubernetesClientFactory cheServerKubernetesClientFactory) { + this.allowUsers = strToSet(allowUsers); + this.allowGroups = strToSet(allowGroups); + this.denyUsers = strToSet(denyUsers); + this.denyGroups = strToSet(denyGroups); + this.cheServerKubernetesClientFactory = cheServerKubernetesClientFactory; + } + + public boolean isAuthorized(String username) throws InfrastructureException { + return isAllowedUser(cheServerKubernetesClientFactory.create(), username) + && !isDeniedUser(cheServerKubernetesClientFactory.create(), username); + } + + private boolean isAllowedUser(KubernetesClient client, String username) { + // All users from all groups are allowed by default + if (allowUsers.isEmpty() && allowGroups.isEmpty()) { + return true; + } + + if (allowUsers.contains(username)) { + return true; + } + + for (String groupName : allowGroups) { + Group group = client.resources(Group.class).withName(groupName).get(); + if (group != null && group.getUsers().contains(username)) { + return true; + } + } + + return false; + } + + private boolean isDeniedUser(KubernetesClient client, String username) { + // All users from all groups are allowed by default + if (denyUsers.isEmpty() && denyGroups.isEmpty()) { + return false; + } + + if (denyUsers.contains(username)) { + return true; + } + + for (String groupName : denyGroups) { + Group group = client.resources(Group.class).withName(groupName).get(); + if (group != null && group.getUsers().contains(username)) { + return true; + } + } + + return false; + } +} diff --git a/infrastructures/openshift/src/main/java/org/eclipse/che/workspace/infrastructure/openshift/project/OpenShiftProjectFactory.java b/infrastructures/openshift/src/main/java/org/eclipse/che/workspace/infrastructure/openshift/project/OpenShiftProjectFactory.java index 7ae64d5574..bc1976b2f3 100644 --- a/infrastructures/openshift/src/main/java/org/eclipse/che/workspace/infrastructure/openshift/project/OpenShiftProjectFactory.java +++ b/infrastructures/openshift/src/main/java/org/eclipse/che/workspace/infrastructure/openshift/project/OpenShiftProjectFactory.java @@ -40,6 +40,8 @@ import org.eclipse.che.commons.env.EnvironmentContext; import org.eclipse.che.workspace.infrastructure.kubernetes.CheServerKubernetesClientFactory; import org.eclipse.che.workspace.infrastructure.kubernetes.api.server.impls.KubernetesNamespaceMetaImpl; import org.eclipse.che.workspace.infrastructure.kubernetes.api.shared.KubernetesNamespaceMeta; +import org.eclipse.che.workspace.infrastructure.kubernetes.authorization.AuthorizationChecker; +import org.eclipse.che.workspace.infrastructure.kubernetes.authorization.PermissionsCleaner; import org.eclipse.che.workspace.infrastructure.kubernetes.namespace.KubernetesNamespaceFactory; import org.eclipse.che.workspace.infrastructure.kubernetes.namespace.configurator.NamespaceConfigurator; import org.eclipse.che.workspace.infrastructure.kubernetes.util.KubernetesSharedPool; @@ -81,6 +83,8 @@ public class OpenShiftProjectFactory extends KubernetesNamespaceFactory { CheServerOpenshiftClientFactory cheServerOpenshiftClientFactory, PreferenceManager preferenceManager, KubernetesSharedPool sharedPool, + AuthorizationChecker authorizationChecker, + PermissionsCleaner permissionsCleaner, @Nullable @Named("che.infra.openshift.oauth_identity_provider") String oAuthIdentityProvider) { super( @@ -93,7 +97,9 @@ public class OpenShiftProjectFactory extends KubernetesNamespaceFactory { namespaceConfigurators, cheServerKubernetesClientFactory, preferenceManager, - sharedPool); + sharedPool, + authorizationChecker, + permissionsCleaner); this.initWithCheServerSa = initWithCheServerSa; this.cheServerKubernetesClientFactory = cheServerKubernetesClientFactory; this.cheServerOpenshiftClientFactory = cheServerOpenshiftClientFactory; @@ -105,6 +111,9 @@ public class OpenShiftProjectFactory extends KubernetesNamespaceFactory { OpenShiftProject osProject = get(identity); var subject = EnvironmentContext.getCurrent().getSubject(); var userName = subject.getUserName(); + + validateAuthorization(osProject.getName(), userName); + NamespaceResolutionContext resolutionCtx = new NamespaceResolutionContext(identity.getWorkspaceId(), subject.getUserId(), userName); Map namespaceAnnotationsEvaluated = diff --git a/infrastructures/openshift/src/test/java/org/eclipse/che/workspace/infrastructure/openshift/authorization/OpenShiftAuthorizationCheckerTest.java b/infrastructures/openshift/src/test/java/org/eclipse/che/workspace/infrastructure/openshift/authorization/OpenShiftAuthorizationCheckerTest.java new file mode 100644 index 0000000000..f826cf4002 --- /dev/null +++ b/infrastructures/openshift/src/test/java/org/eclipse/che/workspace/infrastructure/openshift/authorization/OpenShiftAuthorizationCheckerTest.java @@ -0,0 +1,119 @@ +/* + * Copyright (c) 2012-2023 Red Hat, Inc. + * This program and the accompanying materials are made + * available under the terms of the Eclipse Public License 2.0 + * which is available at https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * Red Hat, Inc. - initial API and implementation + */ +package org.eclipse.che.workspace.infrastructure.openshift.authorization; + +import static org.mockito.Mockito.*; + +import io.fabric8.kubernetes.api.model.ObjectMetaBuilder; +import io.fabric8.kubernetes.client.KubernetesClient; +import io.fabric8.kubernetes.client.server.mock.KubernetesServer; +import io.fabric8.openshift.api.model.Group; +import java.util.Collections; +import java.util.List; +import org.eclipse.che.api.workspace.server.spi.InfrastructureException; +import org.eclipse.che.workspace.infrastructure.kubernetes.CheServerKubernetesClientFactory; +import org.mockito.Mock; +import org.mockito.testng.MockitoTestNGListener; +import org.testng.Assert; +import org.testng.annotations.BeforeMethod; +import org.testng.annotations.DataProvider; +import org.testng.annotations.Listeners; +import org.testng.annotations.Test; + +@Listeners(MockitoTestNGListener.class) +public class OpenShiftAuthorizationCheckerTest { + + @Mock private CheServerKubernetesClientFactory clientFactory; + private KubernetesClient client; + private KubernetesServer serverMock; + + @BeforeMethod + public void setUp() throws InfrastructureException { + serverMock = new KubernetesServer(true, true); + serverMock.before(); + client = spy(serverMock.getClient()); + lenient().when(clientFactory.create()).thenReturn(client); + } + + @Test(dataProvider = "advancedAuthorizationData") + public void advancedAuthorization( + String testUserName, + List groups, + String allowedUsers, + String allowedGroups, + String deniedUsers, + String deniedGroups, + boolean expectedIsAuthorized) + throws InfrastructureException { + // give + OpenShiftAuthorizationCheckerImpl authorizationChecker = + new OpenShiftAuthorizationCheckerImpl( + allowedUsers, allowedGroups, deniedUsers, deniedGroups, clientFactory); + groups.forEach(group -> client.resources(Group.class).create(group)); + + // when + boolean isAuthorized = authorizationChecker.isAuthorized(testUserName); + + // then + Assert.assertEquals(isAuthorized, expectedIsAuthorized); + } + + @DataProvider + public static Object[][] advancedAuthorizationData() { + Group groupWithUser1 = + new Group( + "v1", + "Group", + new ObjectMetaBuilder().withName("groupWithUser1").build(), + List.of("user1")); + Group groupWithUser2 = + new Group( + "v1", + "Group", + new ObjectMetaBuilder().withName("groupWithUser2").build(), + List.of("user2")); + + return new Object[][] { + {"user1", Collections.emptyList(), "", "", "", "", true}, + {"user1", Collections.emptyList(), "user1", "", "", "", true}, + {"user1", Collections.emptyList(), "user1", "", "user2", "", true}, + {"user1", List.of(groupWithUser2), "user1", "", "", "groupWithUser2", true}, + {"user1", List.of(groupWithUser1), "", "groupWithUser1", "", "", true}, + {"user2", List.of(groupWithUser1), "user2", "groupWithUser1", "", "", true}, + { + "user1", + List.of(groupWithUser1, groupWithUser2), + "", + "groupWithUser1", + "", + "groupWithUser2", + true + }, + {"user1", Collections.emptyList(), "user1", "", "user1", "", false}, + {"user2", Collections.emptyList(), "user1", "", "", "", false}, + {"user2", Collections.emptyList(), "user1", "", "user2", "", false}, + {"user2", List.of(groupWithUser1), "", "groupWithUser1", "", "", false}, + {"user1", Collections.emptyList(), "", "", "user1", "", false}, + {"user1", List.of(groupWithUser1), "", "", "", "groupWithUser1", false}, + {"user1", List.of(groupWithUser1), "", "groupWithUser1", "", "groupWithUser1", false}, + { + "user2", + List.of(groupWithUser1, groupWithUser2), + "", + "groupWithUser1", + "", + "groupWithUser2", + false + }, + }; + } +} diff --git a/infrastructures/openshift/src/test/java/org/eclipse/che/workspace/infrastructure/openshift/project/OpenShiftProjectFactoryTest.java b/infrastructures/openshift/src/test/java/org/eclipse/che/workspace/infrastructure/openshift/project/OpenShiftProjectFactoryTest.java index 93f2d36876..c620aab5d8 100644 --- a/infrastructures/openshift/src/test/java/org/eclipse/che/workspace/infrastructure/openshift/project/OpenShiftProjectFactoryTest.java +++ b/infrastructures/openshift/src/test/java/org/eclipse/che/workspace/infrastructure/openshift/project/OpenShiftProjectFactoryTest.java @@ -71,6 +71,8 @@ import org.eclipse.che.commons.subject.SubjectImpl; import org.eclipse.che.inject.ConfigurationException; import org.eclipse.che.workspace.infrastructure.kubernetes.CheServerKubernetesClientFactory; import org.eclipse.che.workspace.infrastructure.kubernetes.api.shared.KubernetesNamespaceMeta; +import org.eclipse.che.workspace.infrastructure.kubernetes.authorization.AuthorizationChecker; +import org.eclipse.che.workspace.infrastructure.kubernetes.authorization.PermissionsCleaner; import org.eclipse.che.workspace.infrastructure.kubernetes.namespace.KubernetesConfigsMaps; import org.eclipse.che.workspace.infrastructure.kubernetes.namespace.KubernetesSecrets; import org.eclipse.che.workspace.infrastructure.kubernetes.namespace.configurator.NamespaceConfigurator; @@ -113,6 +115,8 @@ public class OpenShiftProjectFactoryTest { @Mock private WorkspaceManager workspaceManager; @Mock private PreferenceManager preferenceManager; @Mock private KubernetesSharedPool pool; + @Mock private AuthorizationChecker authorizationChecker; + @Mock private PermissionsCleaner permissionsCleaner; @Mock private ProjectOperation projectOperation; @@ -134,6 +138,7 @@ public class OpenShiftProjectFactoryTest { lenient().when(cheServerOpenshiftClientFactory.createOC()).thenReturn(osClient); lenient().when(cheServerKubernetesClientFactory.create()).thenReturn(osClient); lenient().when(osClient.projects()).thenReturn(projectOperation); + lenient().when(authorizationChecker.isAuthorized(anyString())).thenReturn(true); lenient() .when(workspaceManager.getWorkspace(any())) @@ -174,6 +179,8 @@ public class OpenShiftProjectFactoryTest { cheServerOpenshiftClientFactory, preferenceManager, pool, + authorizationChecker, + permissionsCleaner, NO_OAUTH_IDENTITY_PROVIDER); projectFactory.checkIfNamespaceIsAllowed(USER_NAME + "-che"); @@ -204,6 +211,8 @@ public class OpenShiftProjectFactoryTest { cheServerOpenshiftClientFactory, preferenceManager, pool, + authorizationChecker, + permissionsCleaner, NO_OAUTH_IDENTITY_PROVIDER); try { projectFactory.checkIfNamespaceIsAllowed("any-namespace"); @@ -234,6 +243,8 @@ public class OpenShiftProjectFactoryTest { cheServerOpenshiftClientFactory, preferenceManager, pool, + authorizationChecker, + permissionsCleaner, NO_OAUTH_IDENTITY_PROVIDER); } @@ -269,6 +280,8 @@ public class OpenShiftProjectFactoryTest { cheServerOpenshiftClientFactory, preferenceManager, pool, + authorizationChecker, + permissionsCleaner, NO_OAUTH_IDENTITY_PROVIDER); EnvironmentContext.getCurrent().setSubject(new SubjectImpl("jondoe", "123", null, false)); @@ -305,6 +318,8 @@ public class OpenShiftProjectFactoryTest { cheServerOpenshiftClientFactory, preferenceManager, pool, + authorizationChecker, + permissionsCleaner, NO_OAUTH_IDENTITY_PROVIDER); EnvironmentContext.getCurrent().setSubject(new SubjectImpl("jondoe", "u123", null, false)); @@ -337,6 +352,8 @@ public class OpenShiftProjectFactoryTest { cheServerOpenshiftClientFactory, preferenceManager, pool, + authorizationChecker, + permissionsCleaner, NO_OAUTH_IDENTITY_PROVIDER); // when @@ -379,6 +396,8 @@ public class OpenShiftProjectFactoryTest { cheServerOpenshiftClientFactory, preferenceManager, pool, + authorizationChecker, + permissionsCleaner, NO_OAUTH_IDENTITY_PROVIDER); List availableNamespaces = projectFactory.list(); @@ -415,6 +434,8 @@ public class OpenShiftProjectFactoryTest { cheServerOpenshiftClientFactory, preferenceManager, pool, + authorizationChecker, + permissionsCleaner, NO_OAUTH_IDENTITY_PROVIDER); List availableNamespaces = projectFactory.list(); @@ -451,6 +472,8 @@ public class OpenShiftProjectFactoryTest { cheServerOpenshiftClientFactory, preferenceManager, pool, + authorizationChecker, + permissionsCleaner, NO_OAUTH_IDENTITY_PROVIDER); projectFactory.list(); @@ -477,6 +500,8 @@ public class OpenShiftProjectFactoryTest { cheServerOpenshiftClientFactory, preferenceManager, pool, + authorizationChecker, + permissionsCleaner, NO_OAUTH_IDENTITY_PROVIDER); projectFactory.list(); @@ -508,6 +533,8 @@ public class OpenShiftProjectFactoryTest { cheServerOpenshiftClientFactory, preferenceManager, pool, + authorizationChecker, + permissionsCleaner, NO_OAUTH_IDENTITY_PROVIDER)); OpenShiftProject toReturnProject = mock(OpenShiftProject.class); prepareProject(toReturnProject); @@ -542,6 +569,8 @@ public class OpenShiftProjectFactoryTest { cheServerOpenshiftClientFactory, preferenceManager, pool, + authorizationChecker, + permissionsCleaner, NO_OAUTH_IDENTITY_PROVIDER)); OpenShiftProject toReturnProject = mock(OpenShiftProject.class); doReturn(toReturnProject).when(projectFactory).doCreateProjectAccess(any(), any()); @@ -584,6 +613,8 @@ public class OpenShiftProjectFactoryTest { cheServerOpenshiftClientFactory, preferenceManager, pool, + authorizationChecker, + permissionsCleaner, NO_OAUTH_IDENTITY_PROVIDER)); OpenShiftProject toReturnProject = mock(OpenShiftProject.class); prepareProject(toReturnProject); @@ -628,6 +659,8 @@ public class OpenShiftProjectFactoryTest { cheServerOpenshiftClientFactory, preferenceManager, pool, + authorizationChecker, + permissionsCleaner, OAUTH_IDENTITY_PROVIDER)); OpenShiftProject toReturnProject = mock(OpenShiftProject.class); when(toReturnProject.getName()).thenReturn("workspace123"); @@ -677,6 +710,8 @@ public class OpenShiftProjectFactoryTest { cheServerOpenshiftClientFactory, preferenceManager, pool, + authorizationChecker, + permissionsCleaner, NO_OAUTH_IDENTITY_PROVIDER); String namespace = @@ -709,6 +744,8 @@ public class OpenShiftProjectFactoryTest { cheServerOpenshiftClientFactory, preferenceManager, pool, + authorizationChecker, + permissionsCleaner, NO_OAUTH_IDENTITY_PROVIDER); EnvironmentContext.getCurrent().setSubject(new SubjectImpl("jondoe", "123", null, false)); projectFactory.list(); @@ -735,6 +772,8 @@ public class OpenShiftProjectFactoryTest { cheServerOpenshiftClientFactory, preferenceManager, pool, + authorizationChecker, + permissionsCleaner, NO_OAUTH_IDENTITY_PROVIDER)); EnvironmentContext.getCurrent().setSubject(new SubjectImpl("jondoe", "123", null, false)); OpenShiftProject toReturnProject = mock(OpenShiftProject.class); @@ -775,6 +814,8 @@ public class OpenShiftProjectFactoryTest { cheServerOpenshiftClientFactory, preferenceManager, pool, + authorizationChecker, + permissionsCleaner, NO_OAUTH_IDENTITY_PROVIDER)); EnvironmentContext.getCurrent().setSubject(new SubjectImpl("jondoe", "123", null, false)); diff --git a/infrastructures/pom.xml b/infrastructures/pom.xml index 9c3bd28f05..b806517a6a 100644 --- a/infrastructures/pom.xml +++ b/infrastructures/pom.xml @@ -17,7 +17,7 @@ che-server org.eclipse.che - 7.78.0-SNAPSHOT + 7.80.0-SNAPSHOT ../pom.xml org.eclipse.che.infrastructure diff --git a/make-release.sh b/make-release.sh index 34a2075733..572dbfeffe 100755 --- a/make-release.sh +++ b/make-release.sh @@ -37,23 +37,6 @@ loadMvnSettingsGpgKey() { gpg --version } -# comment out: why do we think we need nodejs installed for a java build? -# installDebDeps(){ -# set +x -# curl -sL https://deb.nodesource.com/setup_18.x | sudo -E bash - -# sudo apt-get install -y nodejs -# } - -# comment out: already installed via GH action so no need to do it again -# installMaven(){ -# set -x -# mkdir -p /opt/apache-maven && curl -sSL https://downloads.apache.org/maven/maven-3/3.8.8/binaries/apache-maven-3.8.8-bin.tar.gz | tar -xz --strip=1 -C /opt/apache-maven -# export M2_HOME="/opt/apache-maven" -# export PATH="/opt/apache-maven/bin:${PATH}" -# mvn -version || die_with "mvn not found in path: ${PATH} !" -# set +x -# } - evaluateCheVariables() { echo "Che version: ${CHE_VERSION}" # derive branch from version @@ -280,6 +263,7 @@ releaseTypescriptDto() { } buildImages() { + export BUILDER="docker" echo "Going to build docker images" set -e set -o pipefail @@ -392,14 +376,8 @@ updateImageTagsInCheServer() { popd >/dev/null } -# already installed via GH action so no need to do it again -# installMaven - loadMvnSettingsGpgKey -# why do we think we need nodejs installed for a java build? -# installDebDeps - set -x setupGitconfig diff --git a/multiuser/api/che-multiuser-api-authentication-commons/pom.xml b/multiuser/api/che-multiuser-api-authentication-commons/pom.xml index 828b9f2638..056071e5bd 100644 --- a/multiuser/api/che-multiuser-api-authentication-commons/pom.xml +++ b/multiuser/api/che-multiuser-api-authentication-commons/pom.xml @@ -17,7 +17,7 @@ che-multiuser-api org.eclipse.che.multiuser - 7.78.0-SNAPSHOT + 7.80.0-SNAPSHOT che-multiuser-api-authentication-commons jar diff --git a/multiuser/api/che-multiuser-api-authorization-impl/pom.xml b/multiuser/api/che-multiuser-api-authorization-impl/pom.xml index e13a4293e1..6a099aa969 100644 --- a/multiuser/api/che-multiuser-api-authorization-impl/pom.xml +++ b/multiuser/api/che-multiuser-api-authorization-impl/pom.xml @@ -17,7 +17,7 @@ che-multiuser-api org.eclipse.che.multiuser - 7.78.0-SNAPSHOT + 7.80.0-SNAPSHOT che-multiuser-api-authorization-impl jar diff --git a/multiuser/api/che-multiuser-api-authorization/pom.xml b/multiuser/api/che-multiuser-api-authorization/pom.xml index 6489866515..ac31785c1c 100644 --- a/multiuser/api/che-multiuser-api-authorization/pom.xml +++ b/multiuser/api/che-multiuser-api-authorization/pom.xml @@ -17,7 +17,7 @@ che-multiuser-api org.eclipse.che.multiuser - 7.78.0-SNAPSHOT + 7.80.0-SNAPSHOT che-multiuser-api-authorization jar diff --git a/multiuser/api/che-multiuser-api-organization-shared/pom.xml b/multiuser/api/che-multiuser-api-organization-shared/pom.xml index f49bd87666..058af2f96a 100644 --- a/multiuser/api/che-multiuser-api-organization-shared/pom.xml +++ b/multiuser/api/che-multiuser-api-organization-shared/pom.xml @@ -17,7 +17,7 @@ che-multiuser-api org.eclipse.che.multiuser - 7.78.0-SNAPSHOT + 7.80.0-SNAPSHOT che-multiuser-api-organization-shared jar diff --git a/multiuser/api/che-multiuser-api-organization/pom.xml b/multiuser/api/che-multiuser-api-organization/pom.xml index 6eb1d14d72..a367287e6f 100644 --- a/multiuser/api/che-multiuser-api-organization/pom.xml +++ b/multiuser/api/che-multiuser-api-organization/pom.xml @@ -17,7 +17,7 @@ che-multiuser-api org.eclipse.che.multiuser - 7.78.0-SNAPSHOT + 7.80.0-SNAPSHOT che-multiuser-api-organization jar diff --git a/multiuser/api/che-multiuser-api-permission-shared/pom.xml b/multiuser/api/che-multiuser-api-permission-shared/pom.xml index b896b04058..6f46afdd65 100644 --- a/multiuser/api/che-multiuser-api-permission-shared/pom.xml +++ b/multiuser/api/che-multiuser-api-permission-shared/pom.xml @@ -17,7 +17,7 @@ che-multiuser-api org.eclipse.che.multiuser - 7.78.0-SNAPSHOT + 7.80.0-SNAPSHOT che-multiuser-api-permission-shared jar diff --git a/multiuser/api/che-multiuser-api-permission/pom.xml b/multiuser/api/che-multiuser-api-permission/pom.xml index 0d21100a08..8f8ad56e69 100644 --- a/multiuser/api/che-multiuser-api-permission/pom.xml +++ b/multiuser/api/che-multiuser-api-permission/pom.xml @@ -17,7 +17,7 @@ che-multiuser-api org.eclipse.che.multiuser - 7.78.0-SNAPSHOT + 7.80.0-SNAPSHOT che-multiuser-api-permission jar diff --git a/multiuser/api/che-multiuser-api-resource-shared/pom.xml b/multiuser/api/che-multiuser-api-resource-shared/pom.xml index f4946efb7b..f9c4ee5eba 100644 --- a/multiuser/api/che-multiuser-api-resource-shared/pom.xml +++ b/multiuser/api/che-multiuser-api-resource-shared/pom.xml @@ -17,7 +17,7 @@ che-multiuser-api org.eclipse.che.multiuser - 7.78.0-SNAPSHOT + 7.80.0-SNAPSHOT che-multiuser-api-resource-shared jar diff --git a/multiuser/api/che-multiuser-api-resource/pom.xml b/multiuser/api/che-multiuser-api-resource/pom.xml index 84e61a8984..6fab8ec33f 100644 --- a/multiuser/api/che-multiuser-api-resource/pom.xml +++ b/multiuser/api/che-multiuser-api-resource/pom.xml @@ -17,7 +17,7 @@ che-multiuser-api org.eclipse.che.multiuser - 7.78.0-SNAPSHOT + 7.80.0-SNAPSHOT che-multiuser-api-resource jar diff --git a/multiuser/api/che-multiuser-api-workspace-activity/pom.xml b/multiuser/api/che-multiuser-api-workspace-activity/pom.xml index 3a8da66959..c9d21472fd 100644 --- a/multiuser/api/che-multiuser-api-workspace-activity/pom.xml +++ b/multiuser/api/che-multiuser-api-workspace-activity/pom.xml @@ -17,7 +17,7 @@ che-multiuser-api org.eclipse.che.multiuser - 7.78.0-SNAPSHOT + 7.80.0-SNAPSHOT che-multiuser-api-workspace-activity jar diff --git a/multiuser/api/pom.xml b/multiuser/api/pom.xml index 5e758d46d1..e7d1244231 100644 --- a/multiuser/api/pom.xml +++ b/multiuser/api/pom.xml @@ -17,7 +17,7 @@ che-multiuser-parent org.eclipse.che.multiuser - 7.78.0-SNAPSHOT + 7.80.0-SNAPSHOT ../pom.xml che-multiuser-api diff --git a/multiuser/integration-tests/che-multiuser-cascade-removal/pom.xml b/multiuser/integration-tests/che-multiuser-cascade-removal/pom.xml index 892cead1a8..1b9e815254 100644 --- a/multiuser/integration-tests/che-multiuser-cascade-removal/pom.xml +++ b/multiuser/integration-tests/che-multiuser-cascade-removal/pom.xml @@ -17,7 +17,7 @@ che-multiuser-integration-tests org.eclipse.che.multiuser - 7.78.0-SNAPSHOT + 7.80.0-SNAPSHOT che-multiuser-cascade-removal jar diff --git a/multiuser/integration-tests/che-multiuser-mysql-tck/pom.xml b/multiuser/integration-tests/che-multiuser-mysql-tck/pom.xml index 6ca9beb521..d51165aee5 100644 --- a/multiuser/integration-tests/che-multiuser-mysql-tck/pom.xml +++ b/multiuser/integration-tests/che-multiuser-mysql-tck/pom.xml @@ -17,7 +17,7 @@ che-multiuser-integration-tests org.eclipse.che.multiuser - 7.78.0-SNAPSHOT + 7.80.0-SNAPSHOT che-multiuser-mysql-tck jar diff --git a/multiuser/integration-tests/che-multiuser-postgresql-tck/pom.xml b/multiuser/integration-tests/che-multiuser-postgresql-tck/pom.xml index 21ebdbcf8b..a0f890d73e 100644 --- a/multiuser/integration-tests/che-multiuser-postgresql-tck/pom.xml +++ b/multiuser/integration-tests/che-multiuser-postgresql-tck/pom.xml @@ -17,7 +17,7 @@ che-multiuser-integration-tests org.eclipse.che.multiuser - 7.78.0-SNAPSHOT + 7.80.0-SNAPSHOT che-multiuser-postgresql-tck jar diff --git a/multiuser/integration-tests/pom.xml b/multiuser/integration-tests/pom.xml index b2da0e5c98..4d22615179 100644 --- a/multiuser/integration-tests/pom.xml +++ b/multiuser/integration-tests/pom.xml @@ -17,7 +17,7 @@ che-multiuser-parent org.eclipse.che.multiuser - 7.78.0-SNAPSHOT + 7.80.0-SNAPSHOT che-multiuser-integration-tests pom diff --git a/multiuser/keycloak/che-multiuser-keycloak-server/pom.xml b/multiuser/keycloak/che-multiuser-keycloak-server/pom.xml index 486bc22594..0f8344dd83 100644 --- a/multiuser/keycloak/che-multiuser-keycloak-server/pom.xml +++ b/multiuser/keycloak/che-multiuser-keycloak-server/pom.xml @@ -17,7 +17,7 @@ che-multiuser-keycloak org.eclipse.che.multiuser - 7.78.0-SNAPSHOT + 7.80.0-SNAPSHOT che-multiuser-keycloak-server jar diff --git a/multiuser/keycloak/che-multiuser-keycloak-shared/pom.xml b/multiuser/keycloak/che-multiuser-keycloak-shared/pom.xml index 01c3abaa4f..176bb3d001 100644 --- a/multiuser/keycloak/che-multiuser-keycloak-shared/pom.xml +++ b/multiuser/keycloak/che-multiuser-keycloak-shared/pom.xml @@ -17,7 +17,7 @@ che-multiuser-keycloak org.eclipse.che.multiuser - 7.78.0-SNAPSHOT + 7.80.0-SNAPSHOT che-multiuser-keycloak-shared jar diff --git a/multiuser/keycloak/che-multiuser-keycloak-token-provider/pom.xml b/multiuser/keycloak/che-multiuser-keycloak-token-provider/pom.xml index b9b440c410..5a10a1b2e3 100644 --- a/multiuser/keycloak/che-multiuser-keycloak-token-provider/pom.xml +++ b/multiuser/keycloak/che-multiuser-keycloak-token-provider/pom.xml @@ -17,7 +17,7 @@ che-multiuser-keycloak org.eclipse.che.multiuser - 7.78.0-SNAPSHOT + 7.80.0-SNAPSHOT che-multiuser-keycloak-token-provider Che Multiuser :: Keycloak Token Provider diff --git a/multiuser/keycloak/che-multiuser-keycloak-user-remover/pom.xml b/multiuser/keycloak/che-multiuser-keycloak-user-remover/pom.xml index 15fcc4a09f..fd7e76f228 100644 --- a/multiuser/keycloak/che-multiuser-keycloak-user-remover/pom.xml +++ b/multiuser/keycloak/che-multiuser-keycloak-user-remover/pom.xml @@ -17,7 +17,7 @@ che-multiuser-keycloak org.eclipse.che.multiuser - 7.78.0-SNAPSHOT + 7.80.0-SNAPSHOT che-multiuser-keycloak-user-remover jar diff --git a/multiuser/keycloak/pom.xml b/multiuser/keycloak/pom.xml index 60afdd4440..e76f557289 100644 --- a/multiuser/keycloak/pom.xml +++ b/multiuser/keycloak/pom.xml @@ -17,7 +17,7 @@ che-multiuser-parent org.eclipse.che.multiuser - 7.78.0-SNAPSHOT + 7.80.0-SNAPSHOT ../pom.xml che-multiuser-keycloak diff --git a/multiuser/machine-auth/che-multiuser-machine-authentication-shared/pom.xml b/multiuser/machine-auth/che-multiuser-machine-authentication-shared/pom.xml index 0ca5cad608..19e17765d9 100644 --- a/multiuser/machine-auth/che-multiuser-machine-authentication-shared/pom.xml +++ b/multiuser/machine-auth/che-multiuser-machine-authentication-shared/pom.xml @@ -17,7 +17,7 @@ che-multiuser-machine-auth org.eclipse.che.multiuser - 7.78.0-SNAPSHOT + 7.80.0-SNAPSHOT che-multiuser-machine-authentication-shared jar diff --git a/multiuser/machine-auth/che-multiuser-machine-authentication/pom.xml b/multiuser/machine-auth/che-multiuser-machine-authentication/pom.xml index 96783eb91d..2a05ed9b21 100644 --- a/multiuser/machine-auth/che-multiuser-machine-authentication/pom.xml +++ b/multiuser/machine-auth/che-multiuser-machine-authentication/pom.xml @@ -17,7 +17,7 @@ che-multiuser-machine-auth org.eclipse.che.multiuser - 7.78.0-SNAPSHOT + 7.80.0-SNAPSHOT che-multiuser-machine-authentication jar diff --git a/multiuser/machine-auth/pom.xml b/multiuser/machine-auth/pom.xml index 0b19a1bf33..a8ab7d6c79 100644 --- a/multiuser/machine-auth/pom.xml +++ b/multiuser/machine-auth/pom.xml @@ -17,7 +17,7 @@ che-multiuser-parent org.eclipse.che.multiuser - 7.78.0-SNAPSHOT + 7.80.0-SNAPSHOT ../pom.xml che-multiuser-machine-auth diff --git a/multiuser/oidc/pom.xml b/multiuser/oidc/pom.xml index 7ebaf5c178..8501538bf4 100644 --- a/multiuser/oidc/pom.xml +++ b/multiuser/oidc/pom.xml @@ -17,7 +17,7 @@ che-multiuser-parent org.eclipse.che.multiuser - 7.78.0-SNAPSHOT + 7.80.0-SNAPSHOT che-multiuser-oidc jar diff --git a/multiuser/permission/che-multiuser-permission-devfile/pom.xml b/multiuser/permission/che-multiuser-permission-devfile/pom.xml index 5ce89c76df..471f0e6026 100644 --- a/multiuser/permission/che-multiuser-permission-devfile/pom.xml +++ b/multiuser/permission/che-multiuser-permission-devfile/pom.xml @@ -17,7 +17,7 @@ che-multiuser-permission org.eclipse.che.multiuser - 7.78.0-SNAPSHOT + 7.80.0-SNAPSHOT che-multiuser-permission-devfile Che Multiuser :: Devfile Permissions diff --git a/multiuser/permission/che-multiuser-permission-logger/pom.xml b/multiuser/permission/che-multiuser-permission-logger/pom.xml index fbcfd63b82..dc33262183 100644 --- a/multiuser/permission/che-multiuser-permission-logger/pom.xml +++ b/multiuser/permission/che-multiuser-permission-logger/pom.xml @@ -17,7 +17,7 @@ che-multiuser-permission org.eclipse.che.multiuser - 7.78.0-SNAPSHOT + 7.80.0-SNAPSHOT che-multiuser-permission-logger Che Multiuser :: Logger Permissions diff --git a/multiuser/permission/che-multiuser-permission-resource/pom.xml b/multiuser/permission/che-multiuser-permission-resource/pom.xml index 4feecaa9df..9ce7af990b 100644 --- a/multiuser/permission/che-multiuser-permission-resource/pom.xml +++ b/multiuser/permission/che-multiuser-permission-resource/pom.xml @@ -17,7 +17,7 @@ che-multiuser-permission org.eclipse.che.multiuser - 7.78.0-SNAPSHOT + 7.80.0-SNAPSHOT che-multiuser-permission-resource Che Multiuser :: Resource :: Permissions diff --git a/multiuser/permission/che-multiuser-permission-system/pom.xml b/multiuser/permission/che-multiuser-permission-system/pom.xml index e8e15a4965..cb00919009 100644 --- a/multiuser/permission/che-multiuser-permission-system/pom.xml +++ b/multiuser/permission/che-multiuser-permission-system/pom.xml @@ -17,7 +17,7 @@ che-multiuser-permission org.eclipse.che.multiuser - 7.78.0-SNAPSHOT + 7.80.0-SNAPSHOT che-multiuser-permission-system Che Multiuser :: System Permissions diff --git a/multiuser/permission/che-multiuser-permission-user/pom.xml b/multiuser/permission/che-multiuser-permission-user/pom.xml index 86d7321142..0ef5395f03 100644 --- a/multiuser/permission/che-multiuser-permission-user/pom.xml +++ b/multiuser/permission/che-multiuser-permission-user/pom.xml @@ -17,7 +17,7 @@ che-multiuser-permission org.eclipse.che.multiuser - 7.78.0-SNAPSHOT + 7.80.0-SNAPSHOT che-multiuser-permission-user Che Multiuser :: User Permissions diff --git a/multiuser/permission/che-multiuser-permission-workspace-activity/pom.xml b/multiuser/permission/che-multiuser-permission-workspace-activity/pom.xml index 4802b2f9e1..e5a5656722 100644 --- a/multiuser/permission/che-multiuser-permission-workspace-activity/pom.xml +++ b/multiuser/permission/che-multiuser-permission-workspace-activity/pom.xml @@ -17,7 +17,7 @@ che-multiuser-permission org.eclipse.che.multiuser - 7.78.0-SNAPSHOT + 7.80.0-SNAPSHOT che-multiuser-permission-workspace-activity Che Multiuser :: Workspace Activity Permissions diff --git a/multiuser/permission/che-multiuser-permission-workspace/pom.xml b/multiuser/permission/che-multiuser-permission-workspace/pom.xml index a3ad272e51..d26547640b 100644 --- a/multiuser/permission/che-multiuser-permission-workspace/pom.xml +++ b/multiuser/permission/che-multiuser-permission-workspace/pom.xml @@ -17,7 +17,7 @@ che-multiuser-permission org.eclipse.che.multiuser - 7.78.0-SNAPSHOT + 7.80.0-SNAPSHOT che-multiuser-permission-workspace Che Multiuser :: Workspace Permissions diff --git a/multiuser/permission/pom.xml b/multiuser/permission/pom.xml index 0a9c38d138..9f5c9a5dfb 100644 --- a/multiuser/permission/pom.xml +++ b/multiuser/permission/pom.xml @@ -17,7 +17,7 @@ che-multiuser-parent org.eclipse.che.multiuser - 7.78.0-SNAPSHOT + 7.80.0-SNAPSHOT ../pom.xml che-multiuser-permission diff --git a/multiuser/personal-account/pom.xml b/multiuser/personal-account/pom.xml index 37ca8df3cd..4fd6900ae1 100644 --- a/multiuser/personal-account/pom.xml +++ b/multiuser/personal-account/pom.xml @@ -17,7 +17,7 @@ che-multiuser-parent org.eclipse.che.multiuser - 7.78.0-SNAPSHOT + 7.80.0-SNAPSHOT che-multiuser-personal-account jar diff --git a/multiuser/pom.xml b/multiuser/pom.xml index 760ddd0e34..8cdeb1284b 100644 --- a/multiuser/pom.xml +++ b/multiuser/pom.xml @@ -17,7 +17,7 @@ che-server org.eclipse.che - 7.78.0-SNAPSHOT + 7.80.0-SNAPSHOT ../pom.xml org.eclipse.che.multiuser diff --git a/multiuser/sql-schema/pom.xml b/multiuser/sql-schema/pom.xml index 0fa3a3d360..461833167a 100644 --- a/multiuser/sql-schema/pom.xml +++ b/multiuser/sql-schema/pom.xml @@ -17,7 +17,7 @@ che-multiuser-parent org.eclipse.che.multiuser - 7.78.0-SNAPSHOT + 7.80.0-SNAPSHOT ../pom.xml che-multiuser-sql-schema diff --git a/pom.xml b/pom.xml index 31a0b7ca47..80fdb868e3 100644 --- a/pom.xml +++ b/pom.xml @@ -16,7 +16,7 @@ 4.0.0 org.eclipse.che che-server - 7.78.0-SNAPSHOT + 7.80.0-SNAPSHOT pom Che Server Eclipse Che Server @@ -99,7 +99,7 @@ Red Hat, Inc. - initial API and implementation ${project.version} - 7.78.0-SNAPSHOT + 7.80.0-SNAPSHOT 1.0-beta2 Red Hat, Inc. diff --git a/typescript-dto/dto-pom.xml b/typescript-dto/dto-pom.xml index 9604a4ebfd..f3d502fe9b 100644 --- a/typescript-dto/dto-pom.xml +++ b/typescript-dto/dto-pom.xml @@ -23,7 +23,7 @@ pom Che TypeScript DTO - 7.78.0-SNAPSHOT + 7.80.0-SNAPSHOT diff --git a/wsmaster/che-core-api-account/pom.xml b/wsmaster/che-core-api-account/pom.xml index 8d8541d785..928160455a 100644 --- a/wsmaster/che-core-api-account/pom.xml +++ b/wsmaster/che-core-api-account/pom.xml @@ -17,7 +17,7 @@ che-master-parent org.eclipse.che.core - 7.78.0-SNAPSHOT + 7.80.0-SNAPSHOT che-core-api-account Che Core :: API :: Account diff --git a/wsmaster/che-core-api-auth-azure-devops/pom.xml b/wsmaster/che-core-api-auth-azure-devops/pom.xml index 88ffbe7a50..917fba3057 100644 --- a/wsmaster/che-core-api-auth-azure-devops/pom.xml +++ b/wsmaster/che-core-api-auth-azure-devops/pom.xml @@ -17,7 +17,7 @@ che-master-parent org.eclipse.che.core - 7.78.0-SNAPSHOT + 7.80.0-SNAPSHOT che-core-api-auth-azure-devops jar diff --git a/wsmaster/che-core-api-auth-bitbucket/pom.xml b/wsmaster/che-core-api-auth-bitbucket/pom.xml index 705f8b049a..eee767c18d 100644 --- a/wsmaster/che-core-api-auth-bitbucket/pom.xml +++ b/wsmaster/che-core-api-auth-bitbucket/pom.xml @@ -17,7 +17,7 @@ che-master-parent org.eclipse.che.core - 7.78.0-SNAPSHOT + 7.80.0-SNAPSHOT che-core-api-auth-bitbucket jar diff --git a/wsmaster/che-core-api-auth-bitbucket/src/main/java/org/eclipse/che/security/oauth1/BitbucketServerOAuthAuthenticator.java b/wsmaster/che-core-api-auth-bitbucket/src/main/java/org/eclipse/che/security/oauth1/BitbucketServerOAuthAuthenticator.java index 39934e2bd6..64c1da387a 100644 --- a/wsmaster/che-core-api-auth-bitbucket/src/main/java/org/eclipse/che/security/oauth1/BitbucketServerOAuthAuthenticator.java +++ b/wsmaster/che-core-api-auth-bitbucket/src/main/java/org/eclipse/che/security/oauth1/BitbucketServerOAuthAuthenticator.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012-2021 Red Hat, Inc. + * Copyright (c) 2012-2023 Red Hat, Inc. * This program and the accompanying materials are made * available under the terms of the Eclipse Public License 2.0 * which is available at https://www.eclipse.org/legal/epl-2.0/ @@ -21,6 +21,7 @@ import com.google.inject.Singleton; @Singleton public class BitbucketServerOAuthAuthenticator extends OAuthAuthenticator { public static final String AUTHENTICATOR_NAME = "bitbucket-server"; + private final String bitbucketEndpoint; private final String apiEndpoint; public BitbucketServerOAuthAuthenticator( @@ -33,6 +34,7 @@ public class BitbucketServerOAuthAuthenticator extends OAuthAuthenticator { apiEndpoint + "/oauth/1.0/callback", null, privateKey); + this.bitbucketEndpoint = bitbucketEndpoint; this.apiEndpoint = apiEndpoint; } @@ -48,4 +50,9 @@ public class BitbucketServerOAuthAuthenticator extends OAuthAuthenticator { + AUTHENTICATOR_NAME + "&request_method=POST&signature_method=rsa"; } + + @Override + public String getEndpointUrl() { + return bitbucketEndpoint; + } } diff --git a/wsmaster/che-core-api-auth-bitbucket/src/main/java/org/eclipse/che/security/oauth1/NoopOAuthAuthenticator.java b/wsmaster/che-core-api-auth-bitbucket/src/main/java/org/eclipse/che/security/oauth1/NoopOAuthAuthenticator.java index 2042e60e6e..0a9901fa7d 100644 --- a/wsmaster/che-core-api-auth-bitbucket/src/main/java/org/eclipse/che/security/oauth1/NoopOAuthAuthenticator.java +++ b/wsmaster/che-core-api-auth-bitbucket/src/main/java/org/eclipse/che/security/oauth1/NoopOAuthAuthenticator.java @@ -51,4 +51,9 @@ public class NoopOAuthAuthenticator extends OAuthAuthenticator { public String getLocalAuthenticateUrl() { return "Noop URL"; } + + @Override + public String getEndpointUrl() { + return "Noop URL"; + } } diff --git a/wsmaster/che-core-api-auth-github-common/pom.xml b/wsmaster/che-core-api-auth-github-common/pom.xml index 573b88721a..c31c7d8048 100644 --- a/wsmaster/che-core-api-auth-github-common/pom.xml +++ b/wsmaster/che-core-api-auth-github-common/pom.xml @@ -17,7 +17,7 @@ che-master-parent org.eclipse.che.core - 7.78.0-SNAPSHOT + 7.80.0-SNAPSHOT che-core-api-auth-github-common jar diff --git a/wsmaster/che-core-api-auth-github/pom.xml b/wsmaster/che-core-api-auth-github/pom.xml index b6e00a21ca..5093a6a3a1 100644 --- a/wsmaster/che-core-api-auth-github/pom.xml +++ b/wsmaster/che-core-api-auth-github/pom.xml @@ -17,7 +17,7 @@ che-master-parent org.eclipse.che.core - 7.78.0-SNAPSHOT + 7.80.0-SNAPSHOT che-core-api-auth-github jar diff --git a/wsmaster/che-core-api-auth-gitlab/pom.xml b/wsmaster/che-core-api-auth-gitlab/pom.xml index 1ac0d1a37c..bb6ef14a10 100644 --- a/wsmaster/che-core-api-auth-gitlab/pom.xml +++ b/wsmaster/che-core-api-auth-gitlab/pom.xml @@ -17,7 +17,7 @@ che-master-parent org.eclipse.che.core - 7.78.0-SNAPSHOT + 7.80.0-SNAPSHOT che-core-api-auth-gitlab jar diff --git a/wsmaster/che-core-api-auth-openshift/pom.xml b/wsmaster/che-core-api-auth-openshift/pom.xml index f1772d318b..e033fc58e5 100644 --- a/wsmaster/che-core-api-auth-openshift/pom.xml +++ b/wsmaster/che-core-api-auth-openshift/pom.xml @@ -17,7 +17,7 @@ che-master-parent org.eclipse.che.core - 7.78.0-SNAPSHOT + 7.80.0-SNAPSHOT che-core-api-auth-openshift jar diff --git a/wsmaster/che-core-api-auth-shared/pom.xml b/wsmaster/che-core-api-auth-shared/pom.xml index a9fef22eec..d16200966c 100644 --- a/wsmaster/che-core-api-auth-shared/pom.xml +++ b/wsmaster/che-core-api-auth-shared/pom.xml @@ -17,7 +17,7 @@ che-master-parent org.eclipse.che.core - 7.78.0-SNAPSHOT + 7.80.0-SNAPSHOT che-core-api-auth-shared jar diff --git a/wsmaster/che-core-api-auth/pom.xml b/wsmaster/che-core-api-auth/pom.xml index df6e4c3740..067e33e55e 100644 --- a/wsmaster/che-core-api-auth/pom.xml +++ b/wsmaster/che-core-api-auth/pom.xml @@ -17,7 +17,7 @@ che-master-parent org.eclipse.che.core - 7.78.0-SNAPSHOT + 7.80.0-SNAPSHOT che-core-api-auth jar diff --git a/wsmaster/che-core-api-auth/src/main/java/org/eclipse/che/security/oauth/EmbeddedOAuthAPI.java b/wsmaster/che-core-api-auth/src/main/java/org/eclipse/che/security/oauth/EmbeddedOAuthAPI.java index 2ba960cb0e..2620f753d1 100644 --- a/wsmaster/che-core-api-auth/src/main/java/org/eclipse/che/security/oauth/EmbeddedOAuthAPI.java +++ b/wsmaster/che-core-api-auth/src/main/java/org/eclipse/che/security/oauth/EmbeddedOAuthAPI.java @@ -60,7 +60,8 @@ public class EmbeddedOAuthAPI implements OAuthAPI { @Named("che.auth.access_denied_error_page") protected String errorPage; - @Inject protected OAuthAuthenticatorProvider providers; + @Inject protected OAuthAuthenticatorProvider oauth2Providers; + @Inject protected org.eclipse.che.security.oauth1.OAuthAuthenticatorProvider oauth1Providers; private String redirectAfterLogin; @Override @@ -126,7 +127,10 @@ public class EmbeddedOAuthAPI implements OAuthAPI { Set result = new HashSet<>(); final UriBuilder uriBuilder = uriInfo.getBaseUriBuilder().clone().path(OAuthAuthenticationService.class); - for (String name : providers.getRegisteredProviderNames()) { + Set registeredProviderNames = + new HashSet<>(oauth2Providers.getRegisteredProviderNames()); + registeredProviderNames.addAll(oauth1Providers.getRegisteredProviderNames()); + for (String name : registeredProviderNames) { final List links = new LinkedList<>(); links.add( LinksHelper.createLink( @@ -147,11 +151,14 @@ public class EmbeddedOAuthAPI implements OAuthAPI { .withName("mode") .withRequired(true) .withDefaultValue("federated_login"))); - OAuthAuthenticator authenticator = providers.getAuthenticator(name); + OAuthAuthenticator authenticator = oauth2Providers.getAuthenticator(name); result.add( newDto(OAuthAuthenticatorDescriptor.class) .withName(name) - .withEndpointUrl(authenticator.getEndpointUrl()) + .withEndpointUrl( + authenticator != null + ? authenticator.getEndpointUrl() + : oauth1Providers.getAuthenticator(name).getEndpointUrl()) .withLinks(links)); } return result; @@ -193,7 +200,7 @@ public class EmbeddedOAuthAPI implements OAuthAPI { } protected OAuthAuthenticator getAuthenticator(String oauthProviderName) throws NotFoundException { - OAuthAuthenticator oauth = providers.getAuthenticator(oauthProviderName); + OAuthAuthenticator oauth = oauth2Providers.getAuthenticator(oauthProviderName); if (oauth == null) { LOG.warn("Unsupported OAuth provider {} ", oauthProviderName); throw new NotFoundException("Unsupported OAuth provider " + oauthProviderName); diff --git a/wsmaster/che-core-api-auth/src/main/java/org/eclipse/che/security/oauth1/OAuthAuthenticator.java b/wsmaster/che-core-api-auth/src/main/java/org/eclipse/che/security/oauth1/OAuthAuthenticator.java index 863dece3c5..3abef8aa94 100644 --- a/wsmaster/che-core-api-auth/src/main/java/org/eclipse/che/security/oauth1/OAuthAuthenticator.java +++ b/wsmaster/che-core-api-auth/src/main/java/org/eclipse/che/security/oauth1/OAuthAuthenticator.java @@ -243,6 +243,13 @@ public abstract class OAuthAuthenticator { */ public abstract String getLocalAuthenticateUrl(); + /** + * Get endpoint URL. + * + * @return provider's endpoint URL + */ + public abstract String getEndpointUrl(); + /** * Compute the Authorization header to sign the OAuth 1 request. * diff --git a/wsmaster/che-core-api-auth/src/main/java/org/eclipse/che/security/oauth1/OAuthAuthenticatorProvider.java b/wsmaster/che-core-api-auth/src/main/java/org/eclipse/che/security/oauth1/OAuthAuthenticatorProvider.java index 4cc3d30ba2..334ec25e66 100644 --- a/wsmaster/che-core-api-auth/src/main/java/org/eclipse/che/security/oauth1/OAuthAuthenticatorProvider.java +++ b/wsmaster/che-core-api-auth/src/main/java/org/eclipse/che/security/oauth1/OAuthAuthenticatorProvider.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012-2018 Red Hat, Inc. + * Copyright (c) 2012-2023 Red Hat, Inc. * This program and the accompanying materials are made * available under the terms of the Eclipse Public License 2.0 * which is available at https://www.eclipse.org/legal/epl-2.0/ @@ -11,6 +11,8 @@ */ package org.eclipse.che.security.oauth1; +import static java.util.stream.Collectors.toUnmodifiableSet; + import java.util.HashMap; import java.util.Map; import java.util.Set; @@ -44,4 +46,15 @@ public class OAuthAuthenticatorProvider { public OAuthAuthenticator getAuthenticator(String oauthProviderName) { return oAuthAuthenticators.get(oauthProviderName); } + + /** + * Gets registered OAuth1 provider names + * + * @return set of registered OAuth1 provider names + */ + public Set getRegisteredProviderNames() { + return oAuthAuthenticators.keySet().stream() + .filter(key -> !"Noop".equals(key)) + .collect(toUnmodifiableSet()); + } } diff --git a/wsmaster/che-core-api-auth/src/test/java/org/eclipse/che/security/oauth/EmbeddedOAuthAPITest.java b/wsmaster/che-core-api-auth/src/test/java/org/eclipse/che/security/oauth/EmbeddedOAuthAPITest.java index 53670aa6e4..54056d84f4 100644 --- a/wsmaster/che-core-api-auth/src/test/java/org/eclipse/che/security/oauth/EmbeddedOAuthAPITest.java +++ b/wsmaster/che-core-api-auth/src/test/java/org/eclipse/che/security/oauth/EmbeddedOAuthAPITest.java @@ -20,11 +20,14 @@ import static org.mockito.Mockito.when; import static org.testng.Assert.assertEquals; import jakarta.ws.rs.core.Response; +import jakarta.ws.rs.core.UriBuilder; import jakarta.ws.rs.core.UriInfo; import java.lang.reflect.Field; import java.net.URI; +import java.util.Set; import org.eclipse.che.api.auth.shared.dto.OAuthToken; import org.eclipse.che.api.core.NotFoundException; +import org.eclipse.che.security.oauth.shared.dto.OAuthAuthenticatorDescriptor; import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.testng.MockitoTestNGListener; @@ -35,7 +38,8 @@ import org.testng.annotations.Test; @Listeners(value = MockitoTestNGListener.class) public class EmbeddedOAuthAPITest { - @Mock OAuthAuthenticatorProvider providers; + @Mock OAuthAuthenticatorProvider oauth2Providers; + @Mock org.eclipse.che.security.oauth1.OAuthAuthenticatorProvider oauth1Providers; @InjectMocks EmbeddedOAuthAPI embeddedOAuthAPI; @@ -51,7 +55,7 @@ public class EmbeddedOAuthAPITest { String provider = "myprovider"; String token = "token123"; OAuthAuthenticator authenticator = mock(OAuthAuthenticator.class); - when(providers.getAuthenticator(eq(provider))).thenReturn(authenticator); + when(oauth2Providers.getAuthenticator(eq(provider))).thenReturn(authenticator); when(authenticator.getToken(anyString())).thenReturn(newDto(OAuthToken.class).withToken(token)); @@ -60,6 +64,26 @@ public class EmbeddedOAuthAPITest { assertEquals(result.getToken(), token); } + @Test + public void shouldGetRegisteredAuthenticators() throws Exception { + // given + UriInfo uriInfo = mock(UriInfo.class); + when(uriInfo.getBaseUriBuilder()).thenReturn(UriBuilder.fromUri("http://eclipse.che")); + when(oauth2Providers.getRegisteredProviderNames()).thenReturn(Set.of("github")); + when(oauth1Providers.getRegisteredProviderNames()).thenReturn(Set.of("bitbucket")); + org.eclipse.che.security.oauth1.OAuthAuthenticator authenticator = + mock(org.eclipse.che.security.oauth1.OAuthAuthenticator.class); + when(oauth2Providers.getAuthenticator("github")).thenReturn(mock(OAuthAuthenticator.class)); + when(oauth1Providers.getAuthenticator("bitbucket")).thenReturn(authenticator); + + // when + Set registeredAuthenticators = + embeddedOAuthAPI.getRegisteredAuthenticators(uriInfo); + + // then + assertEquals(registeredAuthenticators.size(), 2); + } + @Test public void shouldEncodeRejectErrorForRedirectUrl() throws Exception { // given diff --git a/wsmaster/che-core-api-auth/src/test/java/org/eclipse/che/security/oauth1/OAuthAuthenticatorTest.java b/wsmaster/che-core-api-auth/src/test/java/org/eclipse/che/security/oauth1/OAuthAuthenticatorTest.java index ea1304ecae..9310afed5d 100644 --- a/wsmaster/che-core-api-auth/src/test/java/org/eclipse/che/security/oauth1/OAuthAuthenticatorTest.java +++ b/wsmaster/che-core-api-auth/src/test/java/org/eclipse/che/security/oauth1/OAuthAuthenticatorTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012-2021 Red Hat, Inc. + * Copyright (c) 2012-2023 Red Hat, Inc. * This program and the accompanying materials are made * available under the terms of the Eclipse Public License 2.0 * which is available at https://www.eclipse.org/legal/epl-2.0/ @@ -62,6 +62,11 @@ public class OAuthAuthenticatorTest { public String getLocalAuthenticateUrl() { return null; } + + @Override + public String getEndpointUrl() { + return null; + } }; } diff --git a/wsmaster/che-core-api-devfile-shared/pom.xml b/wsmaster/che-core-api-devfile-shared/pom.xml index ad7f0fbbf7..167889cf76 100644 --- a/wsmaster/che-core-api-devfile-shared/pom.xml +++ b/wsmaster/che-core-api-devfile-shared/pom.xml @@ -17,7 +17,7 @@ che-master-parent org.eclipse.che.core - 7.78.0-SNAPSHOT + 7.80.0-SNAPSHOT che-core-api-devfile-shared jar diff --git a/wsmaster/che-core-api-devfile/pom.xml b/wsmaster/che-core-api-devfile/pom.xml index aebf666c0c..c07aee9afa 100644 --- a/wsmaster/che-core-api-devfile/pom.xml +++ b/wsmaster/che-core-api-devfile/pom.xml @@ -17,7 +17,7 @@ che-master-parent org.eclipse.che.core - 7.78.0-SNAPSHOT + 7.80.0-SNAPSHOT che-core-api-devfile jar diff --git a/wsmaster/che-core-api-factory-azure-devops/pom.xml b/wsmaster/che-core-api-factory-azure-devops/pom.xml index 9bfdafdb3d..6f19f85e2c 100644 --- a/wsmaster/che-core-api-factory-azure-devops/pom.xml +++ b/wsmaster/che-core-api-factory-azure-devops/pom.xml @@ -17,7 +17,7 @@ che-master-parent org.eclipse.che.core - 7.78.0-SNAPSHOT + 7.80.0-SNAPSHOT che-core-api-factory-azure-devops jar diff --git a/wsmaster/che-core-api-factory-bitbucket-server/pom.xml b/wsmaster/che-core-api-factory-bitbucket-server/pom.xml index b5258e90e7..a747839402 100644 --- a/wsmaster/che-core-api-factory-bitbucket-server/pom.xml +++ b/wsmaster/che-core-api-factory-bitbucket-server/pom.xml @@ -17,7 +17,7 @@ che-master-parent org.eclipse.che.core - 7.78.0-SNAPSHOT + 7.80.0-SNAPSHOT che-core-api-factory-bitbucket-server jar diff --git a/wsmaster/che-core-api-factory-bitbucket-server/src/main/java/org/eclipse/che/api/factory/server/bitbucket/BitbucketServerPersonalAccessTokenFetcher.java b/wsmaster/che-core-api-factory-bitbucket-server/src/main/java/org/eclipse/che/api/factory/server/bitbucket/BitbucketServerPersonalAccessTokenFetcher.java index b22878ee63..2e40d9e409 100644 --- a/wsmaster/che-core-api-factory-bitbucket-server/src/main/java/org/eclipse/che/api/factory/server/bitbucket/BitbucketServerPersonalAccessTokenFetcher.java +++ b/wsmaster/che-core-api-factory-bitbucket-server/src/main/java/org/eclipse/che/api/factory/server/bitbucket/BitbucketServerPersonalAccessTokenFetcher.java @@ -131,8 +131,7 @@ public class BitbucketServerPersonalAccessTokenFetcher implements PersonalAccess } try { BitbucketPersonalAccessToken bitbucketPersonalAccessToken = - bitbucketServerApiClient.getPersonalAccessToken( - Long.valueOf(personalAccessToken.getScmTokenId())); + bitbucketServerApiClient.getPersonalAccessToken(personalAccessToken.getScmTokenId()); return Optional.of(DEFAULT_TOKEN_SCOPE.equals(bitbucketPersonalAccessToken.getPermissions())); } catch (ScmItemNotFoundException e) { return Optional.of(Boolean.FALSE); @@ -167,7 +166,7 @@ public class BitbucketServerPersonalAccessTokenFetcher implements PersonalAccess } // Token is added by OAuth. Token id is available. BitbucketPersonalAccessToken bitbucketPersonalAccessToken = - bitbucketServerApiClient.getPersonalAccessToken(Long.valueOf(params.getScmTokenId())); + bitbucketServerApiClient.getPersonalAccessToken(params.getScmTokenId()); return Optional.of( Pair.of( DEFAULT_TOKEN_SCOPE.equals(bitbucketPersonalAccessToken.getPermissions()) diff --git a/wsmaster/che-core-api-factory-bitbucket-server/src/main/java/org/eclipse/che/api/factory/server/bitbucket/HttpBitbucketServerApiClient.java b/wsmaster/che-core-api-factory-bitbucket-server/src/main/java/org/eclipse/che/api/factory/server/bitbucket/HttpBitbucketServerApiClient.java index 41571fbef3..e7fa8ddc9c 100644 --- a/wsmaster/che-core-api-factory-bitbucket-server/src/main/java/org/eclipse/che/api/factory/server/bitbucket/HttpBitbucketServerApiClient.java +++ b/wsmaster/che-core-api-factory-bitbucket-server/src/main/java/org/eclipse/che/api/factory/server/bitbucket/HttpBitbucketServerApiClient.java @@ -113,59 +113,20 @@ public class HttpBitbucketServerApiClient implements BitbucketServerApiClient { @Override public BitbucketUser getUser(String token) throws ScmItemNotFoundException, ScmUnauthorizedException, ScmCommunicationException { - return getUser(getUserSlug(token), Optional.of(token)); + return getUser(Optional.of(token)); } @Override public BitbucketUser getUser() throws ScmItemNotFoundException, ScmUnauthorizedException, ScmCommunicationException { - return getUser(getUserSlug(), Optional.empty()); - } - - private BitbucketUser getUser(String slug, Optional token) - throws ScmItemNotFoundException, ScmUnauthorizedException, ScmCommunicationException { - URI uri; - try { - uri = serverUri.resolve("./rest/api/1.0/users/" + slug); - } catch (IllegalArgumentException e) { - // if the slug contains invalid characters (space for example) then the URI will be invalid - throw new ScmCommunicationException(e.getMessage(), e); - } - - HttpRequest request = - HttpRequest.newBuilder(uri) - .headers( - "Authorization", - token.isPresent() - ? "Bearer " + token.get() - : computeAuthorizationHeader("GET", uri.toString())) - .timeout(DEFAULT_HTTP_TIMEOUT) - .build(); - - try { - LOG.trace("executeRequest={}", request); - return executeRequest( - httpClient, - request, - inputStream -> { - try { - String result = - CharStreams.toString(new InputStreamReader(inputStream, Charsets.UTF_8)); - return OM.readValue(result, BitbucketUser.class); - } catch (IOException e) { - throw new UncheckedIOException(e); - } - }); - } catch (ScmBadRequestException e) { - throw new ScmCommunicationException(e.getMessage(), e); - } + return getUser(Optional.empty()); } @Override public List getUsers() throws ScmBadRequestException, ScmUnauthorizedException, ScmCommunicationException { try { - return doGetItems(BitbucketUser.class, "./rest/api/1.0/users", null); + return doGetItems(Optional.empty(), BitbucketUser.class, "./rest/api/1.0/users", null); } catch (ScmItemNotFoundException e) { throw new ScmCommunicationException(e.getMessage(), e); } @@ -175,14 +136,14 @@ public class HttpBitbucketServerApiClient implements BitbucketServerApiClient { public List getUsers(String filter) throws ScmBadRequestException, ScmUnauthorizedException, ScmCommunicationException { try { - return doGetItems(BitbucketUser.class, "./rest/api/1.0/users", filter); + return doGetItems(Optional.empty(), BitbucketUser.class, "./rest/api/1.0/users", filter); } catch (ScmItemNotFoundException e) { throw new ScmCommunicationException(e.getMessage(), e); } } @Override - public void deletePersonalAccessTokens(Long tokenId) + public void deletePersonalAccessTokens(String tokenId) throws ScmItemNotFoundException, ScmUnauthorizedException, ScmCommunicationException { URI uri = serverUri.resolve("./rest/access-tokens/1.0/users/" + getUserSlug() + "/" + tokenId); HttpRequest request = @@ -266,6 +227,7 @@ public class HttpBitbucketServerApiClient implements BitbucketServerApiClient { throws ScmItemNotFoundException, ScmUnauthorizedException, ScmCommunicationException { try { return doGetItems( + Optional.empty(), BitbucketPersonalAccessToken.class, "./rest/access-tokens/1.0/users/" + getUserSlug(), null); @@ -275,7 +237,7 @@ public class HttpBitbucketServerApiClient implements BitbucketServerApiClient { } @Override - public BitbucketPersonalAccessToken getPersonalAccessToken(Long tokenId) + public BitbucketPersonalAccessToken getPersonalAccessToken(String tokenId) throws ScmItemNotFoundException, ScmUnauthorizedException, ScmCommunicationException { URI uri = serverUri.resolve("./rest/access-tokens/1.0/users/" + getUserSlug() + "/" + tokenId); HttpRequest request = @@ -307,17 +269,12 @@ public class HttpBitbucketServerApiClient implements BitbucketServerApiClient { } } - private String getUserSlug(String token) - throws ScmItemNotFoundException, ScmCommunicationException, ScmUnauthorizedException { - return getUserSlug(Optional.of(token)); - } - private String getUserSlug() throws ScmItemNotFoundException, ScmCommunicationException, ScmUnauthorizedException { - return getUserSlug(Optional.empty()); + return getUser(Optional.empty()).getSlug(); } - private String getUserSlug(Optional token) + private BitbucketUser getUser(Optional token) throws ScmCommunicationException, ScmUnauthorizedException, ScmItemNotFoundException { URI uri; try { @@ -337,36 +294,59 @@ public class HttpBitbucketServerApiClient implements BitbucketServerApiClient { .timeout(DEFAULT_HTTP_TIMEOUT) .build(); + String username; try { LOG.trace("executeRequest={}", request); - return executeRequest( - httpClient, - request, - inputStream -> { - try { - return CharStreams.toString(new InputStreamReader(inputStream, Charsets.UTF_8)); - } catch (IOException e) { - throw new UncheckedIOException(e); - } - }); + username = + executeRequest( + httpClient, + request, + inputStream -> { + try { + return CharStreams.toString(new InputStreamReader(inputStream, Charsets.UTF_8)); + } catch (IOException e) { + throw new UncheckedIOException(e); + } + }); + } catch (ScmBadRequestException e) { + throw new ScmCommunicationException(e.getMessage(), e); + } + + // Only authenticated users can do the request below, so we must ensure that the username is not + // empty + if (isNullOrEmpty(username)) { + throw buildScmUnauthorizedException(); + } + + try { + List users = + doGetItems(token, BitbucketUser.class, "./rest/api/1.0/users", username); + // The result can contain multiple users, but we only want the one with the correct username + // See https://docs.atlassian.com/bitbucket-server/rest/7.9.0/bitbucket-rest.html#idp434 + return users.stream() + .filter(user -> user.getName().equals(username)) + .findFirst() + .orElseThrow( + () -> new ScmItemNotFoundException("User '" + username + "' not found in Bitbucket")); } catch (ScmBadRequestException e) { throw new ScmCommunicationException(e.getMessage(), e); } } - private List doGetItems(Class tClass, String api, String filter) + private List doGetItems(Optional token, Class tClass, String api, String filter) throws ScmUnauthorizedException, ScmCommunicationException, ScmBadRequestException, ScmItemNotFoundException { - Page currentPage = doGetPage(tClass, api, 0, 25, filter); + Page currentPage = doGetPage(token, tClass, api, 0, 25, filter); List result = new ArrayList<>(currentPage.getValues()); while (!currentPage.isLastPage()) { - currentPage = doGetPage(tClass, api, currentPage.getNextPageStart(), 25, filter); + currentPage = doGetPage(token, tClass, api, currentPage.getNextPageStart(), 25, filter); result.addAll(currentPage.getValues()); } return result; } - private Page doGetPage(Class tClass, String api, int start, int limit, String filter) + private Page doGetPage( + Optional token, Class tClass, String api, int start, int limit, String filter) throws ScmUnauthorizedException, ScmBadRequestException, ScmCommunicationException, ScmItemNotFoundException { String suffix = api + "?start=" + start + "&limit=" + limit; @@ -377,7 +357,11 @@ public class HttpBitbucketServerApiClient implements BitbucketServerApiClient { URI uri = serverUri.resolve(suffix); HttpRequest request = HttpRequest.newBuilder(uri) - .headers("Authorization", computeAuthorizationHeader("GET", uri.toString())) + .headers( + "Authorization", + token.isPresent() + ? "Bearer " + token.get() + : computeAuthorizationHeader("GET", uri.toString())) .timeout(DEFAULT_HTTP_TIMEOUT) .build(); LOG.trace("executeRequest={}", request); diff --git a/wsmaster/che-core-api-factory-bitbucket-server/src/main/java/org/eclipse/che/api/factory/server/bitbucket/server/BitbucketPersonalAccessToken.java b/wsmaster/che-core-api-factory-bitbucket-server/src/main/java/org/eclipse/che/api/factory/server/bitbucket/server/BitbucketPersonalAccessToken.java index 92e9531f8c..dd9ebb32dd 100644 --- a/wsmaster/che-core-api-factory-bitbucket-server/src/main/java/org/eclipse/che/api/factory/server/bitbucket/server/BitbucketPersonalAccessToken.java +++ b/wsmaster/che-core-api-factory-bitbucket-server/src/main/java/org/eclipse/che/api/factory/server/bitbucket/server/BitbucketPersonalAccessToken.java @@ -17,7 +17,7 @@ import java.util.Set; @JsonIgnoreProperties(ignoreUnknown = true) public class BitbucketPersonalAccessToken { - private long id; + private String id; private long createdDate; private long lastAuthenticated; private int expiryDays; @@ -35,7 +35,7 @@ public class BitbucketPersonalAccessToken { public BitbucketPersonalAccessToken() {} public BitbucketPersonalAccessToken( - long id, + String id, long createdDate, long lastAuthenticated, int expiryDays, @@ -53,11 +53,11 @@ public class BitbucketPersonalAccessToken { this.permissions = permissions; } - public long getId() { + public String getId() { return id; } - public void setId(long id) { + public void setId(String id) { this.id = id; } @@ -146,7 +146,7 @@ public class BitbucketPersonalAccessToken { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; BitbucketPersonalAccessToken that = (BitbucketPersonalAccessToken) o; - return id == that.id + return id.equals(that.id) && createdDate == that.createdDate && lastAuthenticated == that.lastAuthenticated && expiryDays == that.expiryDays diff --git a/wsmaster/che-core-api-factory-bitbucket-server/src/main/java/org/eclipse/che/api/factory/server/bitbucket/server/BitbucketServerApiClient.java b/wsmaster/che-core-api-factory-bitbucket-server/src/main/java/org/eclipse/che/api/factory/server/bitbucket/server/BitbucketServerApiClient.java index 399e112aa6..e399d3f603 100644 --- a/wsmaster/che-core-api-factory-bitbucket-server/src/main/java/org/eclipse/che/api/factory/server/bitbucket/server/BitbucketServerApiClient.java +++ b/wsmaster/che-core-api-factory-bitbucket-server/src/main/java/org/eclipse/che/api/factory/server/bitbucket/server/BitbucketServerApiClient.java @@ -69,7 +69,7 @@ public interface BitbucketServerApiClient { * @throws ScmUnauthorizedException * @throws ScmCommunicationException */ - void deletePersonalAccessTokens(Long tokenId) + void deletePersonalAccessTokens(String tokenId) throws ScmItemNotFoundException, ScmUnauthorizedException, ScmCommunicationException; /** @@ -103,6 +103,6 @@ public interface BitbucketServerApiClient { * @return - Bitbucket personal access token. * @throws ScmCommunicationException */ - BitbucketPersonalAccessToken getPersonalAccessToken(Long tokenId) + BitbucketPersonalAccessToken getPersonalAccessToken(String tokenId) throws ScmItemNotFoundException, ScmUnauthorizedException, ScmCommunicationException; } diff --git a/wsmaster/che-core-api-factory-bitbucket-server/src/main/java/org/eclipse/che/api/factory/server/bitbucket/server/NoopBitbucketServerApiClient.java b/wsmaster/che-core-api-factory-bitbucket-server/src/main/java/org/eclipse/che/api/factory/server/bitbucket/server/NoopBitbucketServerApiClient.java index 600ecc3f1c..d6cfd3efa3 100644 --- a/wsmaster/che-core-api-factory-bitbucket-server/src/main/java/org/eclipse/che/api/factory/server/bitbucket/server/NoopBitbucketServerApiClient.java +++ b/wsmaster/che-core-api-factory-bitbucket-server/src/main/java/org/eclipse/che/api/factory/server/bitbucket/server/NoopBitbucketServerApiClient.java @@ -57,7 +57,7 @@ public class NoopBitbucketServerApiClient implements BitbucketServerApiClient { } @Override - public void deletePersonalAccessTokens(Long tokenId) + public void deletePersonalAccessTokens(String tokenId) throws ScmItemNotFoundException, ScmUnauthorizedException, ScmCommunicationException { throw new RuntimeException( "The fallback noop api client cannot be used for real operation. Make sure Bitbucket OAuth1 is properly configured."); @@ -77,7 +77,7 @@ public class NoopBitbucketServerApiClient implements BitbucketServerApiClient { } @Override - public BitbucketPersonalAccessToken getPersonalAccessToken(Long tokenId) + public BitbucketPersonalAccessToken getPersonalAccessToken(String tokenId) throws ScmItemNotFoundException, ScmUnauthorizedException, ScmCommunicationException { throw new RuntimeException("Invalid usage of BitbucketServerApi"); } diff --git a/wsmaster/che-core-api-factory-bitbucket-server/src/test/java/org/eclipse/che/api/factory/server/bitbucket/BitbucketServerPersonalAccessTokenFetcherTest.java b/wsmaster/che-core-api-factory-bitbucket-server/src/test/java/org/eclipse/che/api/factory/server/bitbucket/BitbucketServerPersonalAccessTokenFetcherTest.java index acdfa4d90c..c917555e11 100644 --- a/wsmaster/che-core-api-factory-bitbucket-server/src/test/java/org/eclipse/che/api/factory/server/bitbucket/BitbucketServerPersonalAccessTokenFetcherTest.java +++ b/wsmaster/che-core-api-factory-bitbucket-server/src/test/java/org/eclipse/che/api/factory/server/bitbucket/BitbucketServerPersonalAccessTokenFetcherTest.java @@ -78,7 +78,7 @@ public class BitbucketServerPersonalAccessTokenFetcherTest { "User User", "user-name", 32423523, "NORMAL", true, "user-slug", "user@users.com"); bitbucketPersonalAccessToken = new BitbucketPersonalAccessToken( - 234234, + "234234", 234345345, 23534534, 90, @@ -88,7 +88,7 @@ public class BitbucketServerPersonalAccessTokenFetcherTest { ImmutableSet.of("PROJECT_WRITE", "REPO_WRITE")); bitbucketPersonalAccessToken2 = new BitbucketPersonalAccessToken( - 3647456, + "3647456", 234345345, 23534534, 90, @@ -98,7 +98,7 @@ public class BitbucketServerPersonalAccessTokenFetcherTest { ImmutableSet.of("REPO_READ")); bitbucketPersonalAccessToken3 = new BitbucketPersonalAccessToken( - 132423, + "132423", 234345345, 23534534, 90, @@ -222,7 +222,7 @@ public class BitbucketServerPersonalAccessTokenFetcherTest { // given when(personalAccessTokenParams.getScmProviderUrl()).thenReturn(someBitbucketURL); when(personalAccessTokenParams.getScmTokenId()) - .thenReturn(Long.toString(bitbucketPersonalAccessToken.getId())); + .thenReturn(bitbucketPersonalAccessToken.getId()); when(bitbucketServerApiClient.isConnected(eq(someBitbucketURL))).thenReturn(true); when(bitbucketServerApiClient.getPersonalAccessToken(eq(bitbucketPersonalAccessToken.getId()))) .thenReturn(bitbucketPersonalAccessToken); diff --git a/wsmaster/che-core-api-factory-bitbucket-server/src/test/java/org/eclipse/che/api/factory/server/bitbucket/HttpBitbucketServerApiClientTest.java b/wsmaster/che-core-api-factory-bitbucket-server/src/test/java/org/eclipse/che/api/factory/server/bitbucket/HttpBitbucketServerApiClientTest.java index e0ff3f415d..9d019932e0 100644 --- a/wsmaster/che-core-api-factory-bitbucket-server/src/test/java/org/eclipse/che/api/factory/server/bitbucket/HttpBitbucketServerApiClientTest.java +++ b/wsmaster/che-core-api-factory-bitbucket-server/src/test/java/org/eclipse/che/api/factory/server/bitbucket/HttpBitbucketServerApiClientTest.java @@ -122,6 +122,16 @@ public class HttpBitbucketServerApiClientTest { .withHeader("Content-Type", "application/json; charset=utf-8") .withBodyFile("bitbucket/rest/api/1.0/users/ksmster/response.json"))); + stubFor( + get(urlPathEqualTo("/rest/api/1.0/users")) + .withHeader(HttpHeaders.AUTHORIZATION, equalTo(AUTHORIZATION_TOKEN)) + .withQueryParam("start", equalTo("0")) + .withQueryParam("limit", equalTo("25")) + .willReturn( + aResponse() + .withHeader("Content-Type", "application/json; charset=utf-8") + .withBodyFile("bitbucket/rest/api/1.0/users/filtered/response.json"))); + BitbucketUser user = bitbucketServer.getUser(); assertNotNull(user); } @@ -219,6 +229,16 @@ public class HttpBitbucketServerApiClientTest { .withHeader("Content-Type", "application/json; charset=utf-8") .withBodyFile("bitbucket/rest/access-tokens/1.0/users/ksmster/response.json"))); + stubFor( + get(urlPathEqualTo("/rest/api/1.0/users")) + .withHeader(HttpHeaders.AUTHORIZATION, equalTo(AUTHORIZATION_TOKEN)) + .withQueryParam("start", equalTo("0")) + .withQueryParam("limit", equalTo("25")) + .willReturn( + aResponse() + .withHeader("Content-Type", "application/json; charset=utf-8") + .withBodyFile("bitbucket/rest/api/1.0/users/filtered/response.json"))); + List page = bitbucketServer.getPersonalAccessTokens().stream() .map(BitbucketPersonalAccessToken::getName) @@ -237,9 +257,18 @@ public class HttpBitbucketServerApiClientTest { .withHeader(HttpHeaders.AUTHORIZATION, equalTo(AUTHORIZATION_TOKEN)) .withHeader(HttpHeaders.ACCEPT, equalTo(MediaType.APPLICATION_JSON)) .withHeader(HttpHeaders.CONTENT_TYPE, equalTo(MediaType.APPLICATION_JSON)) - .withHeader(HttpHeaders.CONTENT_LENGTH, equalTo("149")) + .withHeader(HttpHeaders.CONTENT_LENGTH, equalTo("152")) .willReturn( ok().withBodyFile("bitbucket/rest/access-tokens/1.0/users/ksmster/newtoken.json"))); + stubFor( + get(urlPathEqualTo("/rest/api/1.0/users")) + .withHeader(HttpHeaders.AUTHORIZATION, equalTo(AUTHORIZATION_TOKEN)) + .withQueryParam("start", equalTo("0")) + .withQueryParam("limit", equalTo("25")) + .willReturn( + aResponse() + .withHeader("Content-Type", "application/json; charset=utf-8") + .withBodyFile("bitbucket/rest/api/1.0/users/filtered/response.json"))); // when BitbucketPersonalAccessToken result = @@ -262,8 +291,18 @@ public class HttpBitbucketServerApiClientTest { .withHeader(HttpHeaders.CONTENT_TYPE, equalTo(MediaType.APPLICATION_JSON)) .willReturn(aResponse().withStatus(204))); + stubFor( + get(urlPathEqualTo("/rest/api/1.0/users")) + .withHeader(HttpHeaders.AUTHORIZATION, equalTo(AUTHORIZATION_TOKEN)) + .withQueryParam("start", equalTo("0")) + .withQueryParam("limit", equalTo("25")) + .willReturn( + aResponse() + .withHeader("Content-Type", "application/json; charset=utf-8") + .withBodyFile("bitbucket/rest/api/1.0/users/filtered/response.json"))); + // when - bitbucketServer.deletePersonalAccessTokens(5L); + bitbucketServer.deletePersonalAccessTokens("5"); } @Test @@ -278,8 +317,18 @@ public class HttpBitbucketServerApiClientTest { .willReturn( ok().withBodyFile("bitbucket/rest/access-tokens/1.0/users/ksmster/newtoken.json"))); + stubFor( + get(urlPathEqualTo("/rest/api/1.0/users")) + .withHeader(HttpHeaders.AUTHORIZATION, equalTo(AUTHORIZATION_TOKEN)) + .withQueryParam("start", equalTo("0")) + .withQueryParam("limit", equalTo("25")) + .willReturn( + aResponse() + .withHeader("Content-Type", "application/json; charset=utf-8") + .withBodyFile("bitbucket/rest/api/1.0/users/filtered/response.json"))); + // when - BitbucketPersonalAccessToken result = bitbucketServer.getPersonalAccessToken(5L); + BitbucketPersonalAccessToken result = bitbucketServer.getPersonalAccessToken("5"); // then assertNotNull(result); assertEquals(result.getToken(), "MTU4OTEwNTMyOTA5Ohc88HcY8k7gWOzl2mP5TtdtY5Qs"); @@ -297,7 +346,18 @@ public class HttpBitbucketServerApiClientTest { .willReturn(notFound())); // when - bitbucketServer.getPersonalAccessToken(5L); + bitbucketServer.getPersonalAccessToken("5"); + } + + @Test(expectedExceptions = ScmUnauthorizedException.class) + public void shouldBeAbleToThrowScmUnauthorizedExceptionOnGetUser() + throws ScmCommunicationException, ScmUnauthorizedException, ScmItemNotFoundException { + // given + stubFor( + get(urlEqualTo("/plugins/servlet/applinks/whoami")).willReturn(aResponse().withBody(""))); + + // when + bitbucketServer.getUser(); } @Test(expectedExceptions = ScmUnauthorizedException.class) @@ -310,9 +370,18 @@ public class HttpBitbucketServerApiClientTest { .withHeader(HttpHeaders.AUTHORIZATION, equalTo(AUTHORIZATION_TOKEN)) .withHeader(HttpHeaders.ACCEPT, equalTo(MediaType.APPLICATION_JSON)) .willReturn(unauthorized())); + stubFor( + get(urlPathEqualTo("/rest/api/1.0/users")) + .withHeader(HttpHeaders.AUTHORIZATION, equalTo(AUTHORIZATION_TOKEN)) + .withQueryParam("start", equalTo("0")) + .withQueryParam("limit", equalTo("25")) + .willReturn( + aResponse() + .withHeader("Content-Type", "application/json; charset=utf-8") + .withBodyFile("bitbucket/rest/api/1.0/users/filtered/response.json"))); // when - bitbucketServer.getPersonalAccessToken(5L); + bitbucketServer.getPersonalAccessToken("5"); } @Test( @@ -331,7 +400,7 @@ public class HttpBitbucketServerApiClientTest { wireMockServer.url("/"), new NoopOAuthAuthenticator(), oAuthAPI, apiEndpoint); // when - localServer.getPersonalAccessToken(5L); + localServer.getPersonalAccessToken("5"); } @Test @@ -354,6 +423,16 @@ public class HttpBitbucketServerApiClientTest { .withHeader("Content-Type", "application/json; charset=utf-8") .withBodyFile("bitbucket/rest/api/1.0/users/ksmster/response.json"))); + stubFor( + get(urlPathEqualTo("/rest/api/1.0/users")) + .withHeader(HttpHeaders.AUTHORIZATION, equalTo("Bearer token")) + .withQueryParam("start", equalTo("0")) + .withQueryParam("limit", equalTo("25")) + .willReturn( + aResponse() + .withHeader("Content-Type", "application/json; charset=utf-8") + .withBodyFile("bitbucket/rest/api/1.0/users/filtered/response.json"))); + // when bitbucketServer.getUser(); diff --git a/wsmaster/che-core-api-factory-bitbucket/pom.xml b/wsmaster/che-core-api-factory-bitbucket/pom.xml index 079451acc4..ff9d3fe4c6 100644 --- a/wsmaster/che-core-api-factory-bitbucket/pom.xml +++ b/wsmaster/che-core-api-factory-bitbucket/pom.xml @@ -17,7 +17,7 @@ che-master-parent org.eclipse.che.core - 7.78.0-SNAPSHOT + 7.80.0-SNAPSHOT che-core-api-factory-bitbucket jar diff --git a/wsmaster/che-core-api-factory-git-ssh/pom.xml b/wsmaster/che-core-api-factory-git-ssh/pom.xml index 49672beb64..45dc955db1 100644 --- a/wsmaster/che-core-api-factory-git-ssh/pom.xml +++ b/wsmaster/che-core-api-factory-git-ssh/pom.xml @@ -17,7 +17,7 @@ che-master-parent org.eclipse.che.core - 7.78.0-SNAPSHOT + 7.80.0-SNAPSHOT che-core-api-factory-git-ssh jar diff --git a/wsmaster/che-core-api-factory-github-common/pom.xml b/wsmaster/che-core-api-factory-github-common/pom.xml index 00c7f7a51f..abdbe3d912 100644 --- a/wsmaster/che-core-api-factory-github-common/pom.xml +++ b/wsmaster/che-core-api-factory-github-common/pom.xml @@ -17,7 +17,7 @@ che-master-parent org.eclipse.che.core - 7.78.0-SNAPSHOT + 7.80.0-SNAPSHOT che-core-api-factory-github-common jar diff --git a/wsmaster/che-core-api-factory-github/pom.xml b/wsmaster/che-core-api-factory-github/pom.xml index eec88c8e65..1040c097a0 100644 --- a/wsmaster/che-core-api-factory-github/pom.xml +++ b/wsmaster/che-core-api-factory-github/pom.xml @@ -17,7 +17,7 @@ che-master-parent org.eclipse.che.core - 7.78.0-SNAPSHOT + 7.80.0-SNAPSHOT che-core-api-factory-github jar diff --git a/wsmaster/che-core-api-factory-gitlab/pom.xml b/wsmaster/che-core-api-factory-gitlab/pom.xml index eb03eb3d7b..69087766a4 100644 --- a/wsmaster/che-core-api-factory-gitlab/pom.xml +++ b/wsmaster/che-core-api-factory-gitlab/pom.xml @@ -17,7 +17,7 @@ che-master-parent org.eclipse.che.core - 7.78.0-SNAPSHOT + 7.80.0-SNAPSHOT che-core-api-factory-gitlab jar diff --git a/wsmaster/che-core-api-factory-shared/pom.xml b/wsmaster/che-core-api-factory-shared/pom.xml index 556d2fa2bb..41c1ed4482 100644 --- a/wsmaster/che-core-api-factory-shared/pom.xml +++ b/wsmaster/che-core-api-factory-shared/pom.xml @@ -17,7 +17,7 @@ che-master-parent org.eclipse.che.core - 7.78.0-SNAPSHOT + 7.80.0-SNAPSHOT che-core-api-factory-shared jar diff --git a/wsmaster/che-core-api-factory/pom.xml b/wsmaster/che-core-api-factory/pom.xml index f481e4c1dc..a8747d3c86 100644 --- a/wsmaster/che-core-api-factory/pom.xml +++ b/wsmaster/che-core-api-factory/pom.xml @@ -17,7 +17,7 @@ che-master-parent org.eclipse.che.core - 7.78.0-SNAPSHOT + 7.80.0-SNAPSHOT che-core-api-factory jar diff --git a/wsmaster/che-core-api-factory/src/main/java/org/eclipse/che/api/factory/server/RawDevfileUrlFactoryParameterResolver.java b/wsmaster/che-core-api-factory/src/main/java/org/eclipse/che/api/factory/server/RawDevfileUrlFactoryParameterResolver.java index 89ccea9aec..1e6b133832 100644 --- a/wsmaster/che-core-api-factory/src/main/java/org/eclipse/che/api/factory/server/RawDevfileUrlFactoryParameterResolver.java +++ b/wsmaster/che-core-api-factory/src/main/java/org/eclipse/che/api/factory/server/RawDevfileUrlFactoryParameterResolver.java @@ -22,6 +22,7 @@ import java.net.URI; import java.net.URISyntaxException; import java.net.URL; import java.util.Map; +import java.util.regex.Pattern; import javax.inject.Inject; import org.eclipse.che.api.core.ApiException; import org.eclipse.che.api.core.BadRequestException; @@ -40,6 +41,7 @@ public class RawDevfileUrlFactoryParameterResolver extends BaseFactoryParameterR implements FactoryParametersResolver { private static final String PROVIDER_NAME = "raw-devfile-url"; + private static final Pattern PATTERN = Pattern.compile("^https?://.*\\.ya?ml(\\?token=.*)?$"); protected final URLFactoryBuilder urlFactoryBuilder; protected final URLFetcher urlFetcher; @@ -62,7 +64,7 @@ public class RawDevfileUrlFactoryParameterResolver extends BaseFactoryParameterR @Override public boolean accept(Map factoryParameters) { String url = factoryParameters.get(URL_PARAMETER_NAME); - return !isNullOrEmpty(url) && (url.endsWith(".yaml") || url.endsWith(".yml")); + return !isNullOrEmpty(url) && PATTERN.matcher(url).matches(); } @Override diff --git a/wsmaster/che-core-api-factory/src/main/java/org/eclipse/che/api/factory/server/scm/AuthorizingFileContentProvider.java b/wsmaster/che-core-api-factory/src/main/java/org/eclipse/che/api/factory/server/scm/AuthorizingFileContentProvider.java index 1bb4fbcfa1..a4d3a45742 100644 --- a/wsmaster/che-core-api-factory/src/main/java/org/eclipse/che/api/factory/server/scm/AuthorizingFileContentProvider.java +++ b/wsmaster/che-core-api-factory/src/main/java/org/eclipse/che/api/factory/server/scm/AuthorizingFileContentProvider.java @@ -13,6 +13,7 @@ package org.eclipse.che.api.factory.server.scm; import static com.google.common.base.Strings.isNullOrEmpty; import static org.eclipse.che.api.factory.server.scm.PersonalAccessTokenFetcher.OAUTH_2_PREFIX; +import static org.eclipse.che.api.factory.server.scm.exception.ExceptionMessages.getDevfileConnectionErrorMessage; import java.io.FileNotFoundException; import java.io.IOException; @@ -123,7 +124,7 @@ public class AuthorizingFileContentProvider } } throw new DevfileException( - "Could not reach devfile at " + "`" + exception.getMessage() + "`", exception); + getDevfileConnectionErrorMessage(exception.getMessage()), exception); } } diff --git a/wsmaster/che-core-api-factory/src/main/java/org/eclipse/che/api/factory/server/scm/exception/ExceptionMessages.java b/wsmaster/che-core-api-factory/src/main/java/org/eclipse/che/api/factory/server/scm/exception/ExceptionMessages.java new file mode 100644 index 0000000000..6035050dbd --- /dev/null +++ b/wsmaster/che-core-api-factory/src/main/java/org/eclipse/che/api/factory/server/scm/exception/ExceptionMessages.java @@ -0,0 +1,18 @@ +/* + * Copyright (c) 2012-2023 Red Hat, Inc. + * This program and the accompanying materials are made + * available under the terms of the Eclipse Public License 2.0 + * which is available at https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * Red Hat, Inc. - initial API and implementation + */ +package org.eclipse.che.api.factory.server.scm.exception; + +public class ExceptionMessages { + public static String getDevfileConnectionErrorMessage(String location) { + return String.format("Could not reach devfile at %s", location); + } +} diff --git a/wsmaster/che-core-api-factory/src/main/java/org/eclipse/che/api/factory/server/urlfactory/URLFactoryBuilder.java b/wsmaster/che-core-api-factory/src/main/java/org/eclipse/che/api/factory/server/urlfactory/URLFactoryBuilder.java index 648d769780..f35564a220 100644 --- a/wsmaster/che-core-api-factory/src/main/java/org/eclipse/che/api/factory/server/urlfactory/URLFactoryBuilder.java +++ b/wsmaster/che-core-api-factory/src/main/java/org/eclipse/che/api/factory/server/urlfactory/URLFactoryBuilder.java @@ -13,6 +13,7 @@ package org.eclipse.che.api.factory.server.urlfactory; import static com.google.common.base.Strings.isNullOrEmpty; import static org.eclipse.che.api.factory.server.ApiExceptionMapper.toApiException; +import static org.eclipse.che.api.factory.server.scm.exception.ExceptionMessages.getDevfileConnectionErrorMessage; import static org.eclipse.che.api.factory.shared.Constants.CURRENT_VERSION; import static org.eclipse.che.api.workspace.server.devfile.Constants.CURRENT_API_VERSION; import static org.eclipse.che.api.workspace.shared.Constants.WORKSPACE_TOOLING_EDITOR_ATTRIBUTE; @@ -29,6 +30,7 @@ import javax.inject.Named; import javax.inject.Singleton; import org.eclipse.che.api.core.ApiException; import org.eclipse.che.api.core.BadRequestException; +import org.eclipse.che.api.factory.server.scm.exception.ScmUnauthorizedException; import org.eclipse.che.api.factory.server.urlfactory.RemoteFactoryUrl.DevfileLocation; import org.eclipse.che.api.factory.shared.dto.FactoryDevfileV2Dto; import org.eclipse.che.api.factory.shared.dto.FactoryDto; @@ -130,7 +132,9 @@ public class URLFactoryBuilder { continue; } catch (DevfileException e) { LOG.debug("Unexpected devfile exception: {}", e.getMessage()); - throw toApiException(e, location); + throw e.getCause() instanceof ScmUnauthorizedException + ? toApiException(e, location) + : new ApiException(e.getMessage()); } if (isNullOrEmpty(devfileYamlContent)) { return Optional.empty(); @@ -142,7 +146,7 @@ public class URLFactoryBuilder { try { devfileVersionDetector.devfileVersion(parsedDevfile); } catch (DevfileException e) { - throw new ApiException("Failed to fetch devfile"); + throw new ApiException(getDevfileConnectionErrorMessage(devfileLocation)); } return Optional.of( createFactory(parsedDevfile, overrideProperties, fileContentProvider, location)); diff --git a/wsmaster/che-core-api-factory/src/test/java/org/eclipse/che/api/factory/server/RawDevfileUrlFactoryParameterResolverTest.java b/wsmaster/che-core-api-factory/src/test/java/org/eclipse/che/api/factory/server/RawDevfileUrlFactoryParameterResolverTest.java index 12bed43bc5..287ea55bd5 100644 --- a/wsmaster/che-core-api-factory/src/test/java/org/eclipse/che/api/factory/server/RawDevfileUrlFactoryParameterResolverTest.java +++ b/wsmaster/che-core-api-factory/src/test/java/org/eclipse/che/api/factory/server/RawDevfileUrlFactoryParameterResolverTest.java @@ -12,6 +12,7 @@ package org.eclipse.che.api.factory.server; import static java.lang.String.format; +import static java.util.Collections.singletonMap; import static org.eclipse.che.api.factory.shared.Constants.URL_PARAMETER_NAME; import static org.eclipse.che.api.workspace.server.devfile.Constants.EDITOR_COMPONENT_TYPE; import static org.eclipse.che.api.workspace.server.devfile.Constants.KUBERNETES_COMPONENT_TYPE; @@ -28,7 +29,6 @@ import static org.testng.Assert.fail; import static org.testng.AssertJUnit.assertEquals; import static org.testng.AssertJUnit.assertTrue; -import java.util.Collections; import java.util.HashMap; import java.util.Map; import org.eclipse.che.api.core.BadRequestException; @@ -162,12 +162,11 @@ public class RawDevfileUrlFactoryParameterResolverTest { } } - @Test(dataProvider = "devfileNames") - public void shouldAcceptRawDevfileUrl(String devfileName) { + @Test(dataProvider = "devfileUrls") + public void shouldAcceptRawDevfileUrl(String url) { // when boolean result = - rawDevfileUrlFactoryParameterResolver.accept( - Collections.singletonMap(URL_PARAMETER_NAME, "https://host/path/" + devfileName)); + rawDevfileUrlFactoryParameterResolver.accept(singletonMap(URL_PARAMETER_NAME, url)); // then assertTrue(result); @@ -178,7 +177,7 @@ public class RawDevfileUrlFactoryParameterResolverTest { // when boolean result = rawDevfileUrlFactoryParameterResolver.accept( - Collections.singletonMap(URL_PARAMETER_NAME, "https://host/user/repo.git")); + singletonMap(URL_PARAMETER_NAME, "https://host/user/repo.git")); // then assertFalse(result); @@ -196,8 +195,17 @@ public class RawDevfileUrlFactoryParameterResolverTest { }; } - @DataProvider(name = "devfileNames") - private Object[] devfileNames() { - return new String[] {"devfile.yaml", ".devfile.yaml", "any-name.yaml", "any-name.yml"}; + @DataProvider(name = "devfileUrls") + private Object[] devfileUrls() { + return new String[] { + "https://host/path/devfile.yaml", + "https://host/path/.devfile.yaml", + "https://host/path/any-name.yaml", + "https://host/path/any-name.yml", + "https://host/path/devfile.yaml?token=TOKEN123", + "https://host/path/.devfile.yaml?token=TOKEN123", + "https://host/path/any-name.yaml?token=TOKEN123", + "https://host/path/any-name.yml?token=TOKEN123" + }; } } diff --git a/wsmaster/che-core-api-factory/src/test/java/org/eclipse/che/api/factory/server/urlfactory/URLFactoryBuilderTest.java b/wsmaster/che-core-api-factory/src/test/java/org/eclipse/che/api/factory/server/urlfactory/URLFactoryBuilderTest.java index 324eb6fa79..062d0e9820 100644 --- a/wsmaster/che-core-api-factory/src/test/java/org/eclipse/che/api/factory/server/urlfactory/URLFactoryBuilderTest.java +++ b/wsmaster/che-core-api-factory/src/test/java/org/eclipse/che/api/factory/server/urlfactory/URLFactoryBuilderTest.java @@ -441,7 +441,8 @@ public class URLFactoryBuilderTest { } })); - when(fileContentProvider.fetchContent(anyString())).thenThrow(new DevfileException("", cause)); + when(fileContentProvider.fetchContent(anyString())) + .thenThrow(new DevfileException(expectedMessage, cause)); // when try { @@ -450,14 +451,14 @@ public class URLFactoryBuilderTest { } catch (ApiException e) { assertTrue(e.getClass().isAssignableFrom(expectedClass)); assertEquals(e.getMessage(), expectedMessage); - if (e.getServiceError() instanceof ExtendedError) + if ("SCM Authentication required".equals(e.getMessage())) assertEquals(((ExtendedError) e.getServiceError()).getAttributes(), expectedAttributes); } } @Test( expectedExceptions = ApiException.class, - expectedExceptionsMessageRegExp = "Failed to fetch devfile") + expectedExceptionsMessageRegExp = "Could not reach devfile at location") public void shouldThrowErrorOnUnsupportedDevfileContent() throws ApiException, DevfileException, IOException { JsonNode jsonNode = mock(JsonNode.class); diff --git a/wsmaster/che-core-api-logger-shared/pom.xml b/wsmaster/che-core-api-logger-shared/pom.xml index 194063e525..d72e92f12f 100644 --- a/wsmaster/che-core-api-logger-shared/pom.xml +++ b/wsmaster/che-core-api-logger-shared/pom.xml @@ -17,7 +17,7 @@ che-master-parent org.eclipse.che.core - 7.78.0-SNAPSHOT + 7.80.0-SNAPSHOT che-core-api-logger-shared jar diff --git a/wsmaster/che-core-api-logger/pom.xml b/wsmaster/che-core-api-logger/pom.xml index eaf250d4e7..ccc8832f43 100644 --- a/wsmaster/che-core-api-logger/pom.xml +++ b/wsmaster/che-core-api-logger/pom.xml @@ -17,7 +17,7 @@ che-master-parent org.eclipse.che.core - 7.78.0-SNAPSHOT + 7.80.0-SNAPSHOT che-core-api-logger jar diff --git a/wsmaster/che-core-api-metrics/pom.xml b/wsmaster/che-core-api-metrics/pom.xml index 17077207cb..e35fe79fa2 100644 --- a/wsmaster/che-core-api-metrics/pom.xml +++ b/wsmaster/che-core-api-metrics/pom.xml @@ -17,7 +17,7 @@ che-master-parent org.eclipse.che.core - 7.78.0-SNAPSHOT + 7.80.0-SNAPSHOT che-core-api-metrics jar diff --git a/wsmaster/che-core-api-ssh-shared/pom.xml b/wsmaster/che-core-api-ssh-shared/pom.xml index d05b012363..d01f04e3fc 100644 --- a/wsmaster/che-core-api-ssh-shared/pom.xml +++ b/wsmaster/che-core-api-ssh-shared/pom.xml @@ -17,7 +17,7 @@ che-master-parent org.eclipse.che.core - 7.78.0-SNAPSHOT + 7.80.0-SNAPSHOT che-core-api-ssh-shared jar diff --git a/wsmaster/che-core-api-ssh/pom.xml b/wsmaster/che-core-api-ssh/pom.xml index e12827b5e7..8c235bf353 100644 --- a/wsmaster/che-core-api-ssh/pom.xml +++ b/wsmaster/che-core-api-ssh/pom.xml @@ -17,7 +17,7 @@ che-master-parent org.eclipse.che.core - 7.78.0-SNAPSHOT + 7.80.0-SNAPSHOT che-core-api-ssh jar diff --git a/wsmaster/che-core-api-system-shared/pom.xml b/wsmaster/che-core-api-system-shared/pom.xml index 748bea2862..602a289ac7 100644 --- a/wsmaster/che-core-api-system-shared/pom.xml +++ b/wsmaster/che-core-api-system-shared/pom.xml @@ -17,7 +17,7 @@ che-master-parent org.eclipse.che.core - 7.78.0-SNAPSHOT + 7.80.0-SNAPSHOT che-core-api-system-shared jar diff --git a/wsmaster/che-core-api-system/pom.xml b/wsmaster/che-core-api-system/pom.xml index f09dafc077..f9062f72af 100644 --- a/wsmaster/che-core-api-system/pom.xml +++ b/wsmaster/che-core-api-system/pom.xml @@ -17,7 +17,7 @@ che-master-parent org.eclipse.che.core - 7.78.0-SNAPSHOT + 7.80.0-SNAPSHOT che-core-api-system jar diff --git a/wsmaster/che-core-api-user-shared/pom.xml b/wsmaster/che-core-api-user-shared/pom.xml index f90286f76c..a047bf5db3 100644 --- a/wsmaster/che-core-api-user-shared/pom.xml +++ b/wsmaster/che-core-api-user-shared/pom.xml @@ -17,7 +17,7 @@ che-master-parent org.eclipse.che.core - 7.78.0-SNAPSHOT + 7.80.0-SNAPSHOT che-core-api-user-shared Che Core :: API :: User :: Shared diff --git a/wsmaster/che-core-api-user/pom.xml b/wsmaster/che-core-api-user/pom.xml index 84eff15e74..45dd737548 100644 --- a/wsmaster/che-core-api-user/pom.xml +++ b/wsmaster/che-core-api-user/pom.xml @@ -17,7 +17,7 @@ che-master-parent org.eclipse.che.core - 7.78.0-SNAPSHOT + 7.80.0-SNAPSHOT che-core-api-user Che Core :: API :: User diff --git a/wsmaster/che-core-api-workspace-activity/pom.xml b/wsmaster/che-core-api-workspace-activity/pom.xml index 431e54a42c..3d6b61143e 100644 --- a/wsmaster/che-core-api-workspace-activity/pom.xml +++ b/wsmaster/che-core-api-workspace-activity/pom.xml @@ -17,7 +17,7 @@ che-master-parent org.eclipse.che.core - 7.78.0-SNAPSHOT + 7.80.0-SNAPSHOT che-core-api-workspace-activity jar diff --git a/wsmaster/che-core-api-workspace-shared/pom.xml b/wsmaster/che-core-api-workspace-shared/pom.xml index d302526fb9..64f9c6c613 100644 --- a/wsmaster/che-core-api-workspace-shared/pom.xml +++ b/wsmaster/che-core-api-workspace-shared/pom.xml @@ -17,7 +17,7 @@ che-master-parent org.eclipse.che.core - 7.78.0-SNAPSHOT + 7.80.0-SNAPSHOT che-core-api-workspace-shared jar diff --git a/wsmaster/che-core-api-workspace/pom.xml b/wsmaster/che-core-api-workspace/pom.xml index f1dc59805f..e4f836c337 100644 --- a/wsmaster/che-core-api-workspace/pom.xml +++ b/wsmaster/che-core-api-workspace/pom.xml @@ -17,7 +17,7 @@ che-master-parent org.eclipse.che.core - 7.78.0-SNAPSHOT + 7.80.0-SNAPSHOT che-core-api-workspace jar diff --git a/wsmaster/che-core-sql-schema/pom.xml b/wsmaster/che-core-sql-schema/pom.xml index 345b8241c2..7e1dca4d64 100644 --- a/wsmaster/che-core-sql-schema/pom.xml +++ b/wsmaster/che-core-sql-schema/pom.xml @@ -17,7 +17,7 @@ che-master-parent org.eclipse.che.core - 7.78.0-SNAPSHOT + 7.80.0-SNAPSHOT che-core-sql-schema Che Core :: SQL :: Schema diff --git a/wsmaster/integration-tests/cascade-removal/pom.xml b/wsmaster/integration-tests/cascade-removal/pom.xml index 0014415d34..38c66cf7c2 100644 --- a/wsmaster/integration-tests/cascade-removal/pom.xml +++ b/wsmaster/integration-tests/cascade-removal/pom.xml @@ -17,7 +17,7 @@ integration-tests-parent org.eclipse.che.core - 7.78.0-SNAPSHOT + 7.80.0-SNAPSHOT cascade-removal Integration Tests :: Cascade Removal diff --git a/wsmaster/integration-tests/mysql-tck/pom.xml b/wsmaster/integration-tests/mysql-tck/pom.xml index ec8c3ff62c..2bdd8d40ab 100644 --- a/wsmaster/integration-tests/mysql-tck/pom.xml +++ b/wsmaster/integration-tests/mysql-tck/pom.xml @@ -17,7 +17,7 @@ integration-tests-parent org.eclipse.che.core - 7.78.0-SNAPSHOT + 7.80.0-SNAPSHOT mysql-tck jar diff --git a/wsmaster/integration-tests/pom.xml b/wsmaster/integration-tests/pom.xml index 4337e8ec8d..4c6cf3a26d 100644 --- a/wsmaster/integration-tests/pom.xml +++ b/wsmaster/integration-tests/pom.xml @@ -17,7 +17,7 @@ che-master-parent org.eclipse.che.core - 7.78.0-SNAPSHOT + 7.80.0-SNAPSHOT ../pom.xml integration-tests-parent diff --git a/wsmaster/integration-tests/postgresql-tck/pom.xml b/wsmaster/integration-tests/postgresql-tck/pom.xml index 9aaaebfed2..402ba484fd 100644 --- a/wsmaster/integration-tests/postgresql-tck/pom.xml +++ b/wsmaster/integration-tests/postgresql-tck/pom.xml @@ -17,7 +17,7 @@ integration-tests-parent org.eclipse.che.core - 7.78.0-SNAPSHOT + 7.80.0-SNAPSHOT postgresql-tck jar diff --git a/wsmaster/pom.xml b/wsmaster/pom.xml index 15d947bd15..f26a560460 100644 --- a/wsmaster/pom.xml +++ b/wsmaster/pom.xml @@ -17,7 +17,7 @@ che-core-parent org.eclipse.che.core - 7.78.0-SNAPSHOT + 7.80.0-SNAPSHOT ../core/pom.xml che-master-parent