diff --git a/.github/actions/build-keycloak/action.yml b/.github/actions/build-keycloak/action.yml
index a3fd22a1470c..d4707b2f8639 100644
--- a/.github/actions/build-keycloak/action.yml
+++ b/.github/actions/build-keycloak/action.yml
@@ -58,7 +58,8 @@ runs:
# By using "dependency:resolve", it will download all dependencies used in later stages for running the tests
run: |
MVN_HTTP_CONFIG="-Dhttp.keepAlive=false -Dmaven.wagon.http.pool=false -Dmaven.wagon.http.retryHandler.class=standard -Dmaven.wagon.http.retryHandler.count=3 -Dmaven.wagon.httpconnectionManager.ttlSeconds=120"
- ./mvnw install dependency:resolve -nsu -V -B -e -DskipTests -DskipExamples $MVN_HTTP_CONFIG ${{ env.MVN_PROFILES}}
+ MVN_PROFILES="-Pdistribution"
+ ./mvnw install dependency:resolve -nsu -V -B -e -DskipTests -DskipExamples -DskipTestsuite $MVN_HTTP_CONFIG $MVN_PROFILES
- id: compress-keycloak-maven-repository
name: Compress Keycloak Maven artifacts
diff --git a/.github/workflows/build-and-upload.yml b/.github/workflows/build-and-upload.yml
new file mode 100644
index 000000000000..396647f2f02f
--- /dev/null
+++ b/.github/workflows/build-and-upload.yml
@@ -0,0 +1,92 @@
+name: Build and upload
+
+on: [ push ]
+
+env:
+ DEFAULT_JDK_VERSION: 17
+ DEFAULT_JDK_DIST: temurin
+
+concurrency:
+ # Only run once for latest commit per ref and cancel other (previous) runs.
+ group: ${{ github.workflow }}-${{ github.ref }}
+ cancel-in-progress: true
+
+jobs:
+ maven-build:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v4
+
+ - uses: actions/setup-java@v4
+ with:
+ java-version: ${{ env.DEFAULT_JDK_VERSION }}
+ distribution: ${{ env.DEFAULT_JDK_DIST }}
+
+ - name: Build Keycloak
+ run: mvn clean install -Pdistribution -DskipTests -DskipExamples -DskipTestsuite
+
+ - name: Upload Keycloak artifact
+ id: store-keycloak
+ uses: actions/upload-artifact@v4
+ with:
+ name: keycloak-distribution-${{ github.sha }}
+ retention-days: 1
+ path: quarkus/dist/target/keycloak*.tar.gz
+
+ image-build:
+ runs-on: ubuntu-latest
+ needs:
+ - maven-build
+
+ env:
+ DOCKER_WORKING_DIR: ${{ github.workspace }}/quarkus/container
+
+ steps:
+ - uses: actions/checkout@v4
+
+ - name: Download Keycloak distribution
+ uses: actions/download-artifact@v4
+ with:
+ name: keycloak-distribution-${{ github.sha }}
+ path: ${{ env.DOCKER_WORKING_DIR }}
+
+ - uses: aws-actions/configure-aws-credentials@v4
+ with:
+ aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
+ aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
+ aws-region: ${{ vars.ECR_PUBLIC_REGION }}
+
+ - id: login-ecr
+ uses: aws-actions/amazon-ecr-login@v2
+ with:
+ registry-type: public
+
+ - uses: docker/setup-buildx-action@v3
+
+ - uses: docker/setup-qemu-action@v3
+
+ - name: Set variables
+ id: set-variables
+ env:
+ ECR_REGISTRY: ${{ steps.login-ecr.outputs.registry }}
+ run: |
+ export KEYCLOAK_REPOSITORY=$ECR_REGISTRY/moneymeets/keycloak
+
+ if [[ "${{ github.ref }}" == "refs/heads/moneymeets/22.0.4" ]]; then
+ export IMAGE_TAG=$KEYCLOAK_REPOSITORY:${GITHUB_REF_NAME##moneymeets/}-${GITHUB_SHA}
+ else
+ # Replace / with - because of allowed charactes in docker image tags ('feature/test-1' to 'feature-test-1')
+ export IMAGE_TAG=$KEYCLOAK_REPOSITORY:$(echo ${GITHUB_REF_NAME} | awk '{print tolower($0)}' | sed -e 's|/|-|')
+ fi
+
+ echo "image-url-with-tag=${IMAGE_TAG}" >> $GITHUB_OUTPUT
+
+ - name: Build and push
+ uses: docker/build-push-action@v5
+ env:
+ IMAGE_URL_WITH_TAG: ${{ steps.set-variables.outputs.image-url-with-tag }}
+ with:
+ context: ${{ env.DOCKER_WORKING_DIR }}
+ push: true
+ tags: ${{ env.IMAGE_URL_WITH_TAG }}
+ platforms: linux/amd64,linux/arm64
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
deleted file mode 100644
index 989984c02b7a..000000000000
--- a/.github/workflows/ci.yml
+++ /dev/null
@@ -1,732 +0,0 @@
-name: Keycloak CI
-
-on:
- push:
- branches-ignore:
- - main
- - dependabot/**
- pull_request:
- workflow_dispatch:
-
-env:
- MAVEN_ARGS: "-B -nsu -Dhttp.keepAlive=false -Dmaven.wagon.http.pool=false -Dmaven.wagon.http.retryHandler.class=standard -Dmaven.wagon.http.retryHandler.count=3 -Dmaven.wagon.httpconnectionManager.ttlSeconds=120"
- SUREFIRE_RERUN_FAILING_COUNT: 2
- SUREFIRE_RETRY: "-Dsurefire.rerunFailingTestsCount=2"
-
-concurrency:
- # Only cancel jobs for PR updates
- group: ci-${{ github.ref }}
- cancel-in-progress: true
-
-defaults:
- run:
- shell: bash
-
-jobs:
-
- conditional:
- name: Check conditional workflows and jobs
- runs-on: ubuntu-latest
- outputs:
- ci: ${{ steps.conditional.outputs.ci }}
- ci-store: ${{ steps.conditional.outputs.ci-store }}
- ci-sssd: ${{ steps.conditional.outputs.ci-sssd }}
- steps:
- - uses: actions/checkout@v3
-
- - id: conditional
- uses: ./.github/actions/conditional
- with:
- token: ${{ secrets.GITHUB_TOKEN }}
-
- build:
- name: Build
- if: needs.conditional.outputs.ci == 'true'
- runs-on: ubuntu-latest
- needs: conditional
- steps:
- - uses: actions/checkout@v3
-
- - name: Build Keycloak
- uses: ./.github/actions/build-keycloak
-
- unit-tests:
- name: Base UT
- runs-on: ubuntu-latest
- needs: build
- timeout-minutes: 30
- steps:
- - uses: actions/checkout@v3
-
- - id: unit-test-setup
- name: Unit test setup
- uses: ./.github/actions/unit-test-setup
-
- - name: Run unit tests
- run: |
- SEP=""
- PROJECTS=""
- for i in `find -name '*Test.java' -type f | egrep -v './(testsuite|quarkus|docs)/' | sed 's|/src/test/java/.*||' | sort | uniq | sed 's|./||'`; do
- PROJECTS="$PROJECTS$SEP$i"
- SEP=","
- done
-
- ./mvnw test -pl "$PROJECTS" -am
-
- - name: Upload JVM Heapdumps
- if: always()
- uses: ./.github/actions/upload-heapdumps
-
- - name: Surefire reports
- if: always()
- uses: ./.github/actions/archive-surefire-reports
- with:
- job-id: unit-tests
-
- base-integration-tests:
- name: Base IT
- needs: build
- runs-on: ubuntu-latest
- timeout-minutes: 100
- strategy:
- matrix:
- group: [1, 2, 3, 4, 5, 6]
- fail-fast: false
- steps:
- - uses: actions/checkout@v3
-
- - id: integration-test-setup
- name: Integration test setup
- uses: ./.github/actions/integration-test-setup
-
- - name: Run base tests
- run: |
- TESTS=`testsuite/integration-arquillian/tests/base/testsuites/base-suite.sh ${{ matrix.group }}`
- echo "Tests: $TESTS"
- ./mvnw test ${{ env.SUREFIRE_RETRY }} -Pauth-server-quarkus "-Dwebdriver.chrome.driver=$CHROMEWEBDRIVER/chromedriver" -Dtest=$TESTS -pl testsuite/integration-arquillian/tests/base | misc/log/trimmer.sh
-
- - name: Upload JVM Heapdumps
- if: always()
- uses: ./.github/actions/upload-heapdumps
-
- - uses: ./.github/actions/upload-flaky-tests
- name: Upload flaky tests
- env:
- GH_TOKEN: ${{ github.token }}
- with:
- job-name: Base IT
-
- - name: Surefire reports
- if: always()
- uses: ./.github/actions/archive-surefire-reports
- with:
- job-id: base-integration-tests-${{ matrix.group }}
-
- quarkus-unit-tests:
- name: Quarkus UT
- needs: build
- timeout-minutes: 15
- strategy:
- matrix:
- os: [ ubuntu-latest, windows-latest ]
- runs-on: ${{ matrix.os }}
- steps:
- - uses: actions/checkout@v3
-
- # We want to download Keycloak artifacts
- - id: integration-test-setup
- name: Integration test setup
- uses: ./.github/actions/integration-test-setup
-
- - name: Run unit tests
- run: |
- ./mvnw test -f quarkus/pom.xml -pl '!tests,!tests/junit5,!tests/integration,!dist'
-
- - name: Upload JVM Heapdumps
- if: always()
- uses: ./.github/actions/upload-heapdumps
-
- - name: Surefire reports
- if: always()
- uses: ./.github/actions/archive-surefire-reports
- with:
- job-id: quarkus-unit-tests
-
- quarkus-integration-tests:
- name: Quarkus IT
- needs: build
- timeout-minutes: 115
- strategy:
- matrix:
- os: [ubuntu-latest, windows-latest]
- server: [sanity-check-zip, zip, container, storage]
- exclude:
- - os: windows-latest
- server: zip
- - os: windows-latest
- server: container
- - os: windows-latest
- server: storage
- fail-fast: false
- runs-on: ${{ matrix.os }}
- env:
- MAVEN_OPTS: -Xmx1024m
- steps:
- - uses: actions/checkout@v3
-
- - id: unit-test-setup
- name: Unit test setup
- uses: ./.github/actions/unit-test-setup
-
- # Not sure why, but needs to re-build otherwise there's some failures starting up
- - name: Run Quarkus integration Tests
- run: |
- declare -A PARAMS
- PARAMS["sanity-check-zip"]="-Dtest=StartCommandDistTest,StartDevCommandDistTest,BuildAndStartDistTest,ImportAtStartupDistTest"
- PARAMS["zip"]=""
- PARAMS["container"]="-Dkc.quarkus.tests.dist=docker"
- PARAMS["storage"]="-Ptest-database -Dtest=PostgreSQLDistTest,MariaDBDistTest#testSuccessful,MySQLDistTest#testSuccessful,DatabaseOptionsDistTest,JPAStoreDistTest,HotRodStoreDistTest,MixedStoreDistTest,TransactionConfigurationDistTest"
-
- ./mvnw install -pl quarkus/tests/integration -am -DskipTests
- ./mvnw test -pl quarkus/tests/integration ${PARAMS["${{ matrix.server }}"]} | misc/log/trimmer.sh
-
- - name: Upload JVM Heapdumps
- if: always()
- uses: ./.github/actions/upload-heapdumps
-
- - name: Surefire reports
- if: always()
- uses: ./.github/actions/archive-surefire-reports
- with:
- job-id: quarkus-integration-tests-${{ matrix.os }}-${{ matrix.server }}
-
- jdk-integration-tests:
- name: Java Distribution IT
- needs: build
- timeout-minutes: 100
- strategy:
- matrix:
- os: [ubuntu-latest, windows-latest]
- dist: [temurin]
- version: [19]
- fail-fast: false
- runs-on: ${{ matrix.os }}
- steps:
- - uses: actions/checkout@v3
-
- - id: integration-test-setup
- name: Integration test setup
- uses: ./.github/actions/integration-test-setup
- with:
- jdk-dist: ${{ matrix.dist }}
- jdk-version: ${{ matrix.version }}
-
- - name: Prepare Quarkus distribution with current JDK
- run: ./mvnw install -e -pl testsuite/integration-arquillian/servers/auth-server/quarkus
-
- - name: Run base tests
- run: |
- TESTS=`testsuite/integration-arquillian/tests/base/testsuites/suite.sh jdk`
- echo "Tests: $TESTS"
- ./mvnw test ${{ env.SUREFIRE_RETRY }} -Pauth-server-quarkus -Dtest=$TESTS -pl testsuite/integration-arquillian/tests/base | misc/log/trimmer.sh
-
- - name: Build with JDK
- run:
- ./mvnw install -e -DskipTests -DskipExamples
-
- - name: Upload JVM Heapdumps
- if: always()
- uses: ./.github/actions/upload-heapdumps
-
- - uses: ./.github/actions/upload-flaky-tests
- name: Upload flaky tests
- env:
- GH_TOKEN: ${{ github.token }}
- with:
- job-name: Java Distribution IT
-
- - name: Surefire reports
- if: always()
- uses: ./.github/actions/archive-surefire-reports
- with:
- job-id: jdk-integration-tests-${{ matrix.os }}-${{ matrix.dist }}-${{ matrix.version }}
-
- databases-new-store:
- name: Databases New Store
- runs-on: ubuntu-latest
- needs: [conditional]
- if: needs.conditional.outputs.ci-store == 'true'
- steps:
- - id: matrix-db
- # language=bash
- run: |
- # For PRs, run only PostgreSQL, on branches and nightly runs run all databases
- if [ "${{ github.event_name }}" == "pull_request" ]; then
- echo 'db=["chm", "hot-rod", "jpa-postgres"]' >> $GITHUB_OUTPUT
- else
- echo 'db=["chm", "hot-rod", "jpa-postgres", "jpa-cockroach"]' >> $GITHUB_OUTPUT
- fi
- outputs:
- db: ${{ steps.matrix-db.outputs.db }}
-
- new-store-integration-tests:
- name: New Store IT
- needs: [conditional, build, databases-new-store]
- if: needs.conditional.outputs.ci-store == 'true'
- runs-on: ubuntu-latest
- # Currently, the run of CockroachDB (only branches and nightly runs) is the longest with 50-60 minutes
- timeout-minutes: 90
- strategy:
- matrix:
- db: ${{ fromJson(needs.databases-new-store.outputs.db) }}
- fail-fast: false
- steps:
- - uses: actions/checkout@v3
-
- - id: integration-test-setup
- name: Integration test setup
- uses: ./.github/actions/integration-test-setup
-
- - name: Run base tests
- run: |
- declare -A PARAMS
- PARAMS["chm"]="-Pmap-storage-chm -Dpageload.timeout=90000"
- PARAMS["hot-rod"]="-Pmap-storage-hot-rod -Dpageload.timeout=90000"
- PARAMS["jpa-postgres"]="-Pmap-storage-jpa-postgres -Dpageload.timeout=90000"
- PARAMS["jpa-cockroach"]="-Pmap-storage-jpa-cockroach -Dpageload.timeout=90000"
-
- TESTS=`testsuite/integration-arquillian/tests/base/testsuites/suite.sh database`
- echo "Tests: $TESTS"
- ./mvnw test ${{ env.SUREFIRE_RETRY }} -Pauth-server-quarkus ${PARAMS["${{ matrix.db }}"]} -Dtest=$TESTS -pl testsuite/integration-arquillian/tests/base | misc/log/trimmer.sh
-
- - name: Upload JVM Heapdumps
- if: always()
- uses: ./.github/actions/upload-heapdumps
-
- - uses: ./.github/actions/upload-flaky-tests
- name: Upload flaky tests
- env:
- GH_TOKEN: ${{ github.token }}
- with:
- job-name: New Store IT
-
- - name: Surefire reports
- if: always()
- uses: ./.github/actions/archive-surefire-reports
- with:
- job-id: new-store-integration-tests-${{ matrix.db }}
-
- legacy-store-integration-tests:
- name: Legacy Store IT
- needs: [build, conditional]
- if: needs.conditional.outputs.ci-store == 'true'
- runs-on: ubuntu-latest
- timeout-minutes: 75
- strategy:
- matrix:
- db: [postgres, mysql, oracle, mssql, mariadb]
- fail-fast: false
- steps:
- - uses: actions/checkout@v3
-
- - id: integration-test-setup
- name: Integration test setup
- uses: ./.github/actions/integration-test-setup
-
- - name: Run base tests
- run: |
- TESTS=`testsuite/integration-arquillian/tests/base/testsuites/suite.sh database`
- echo "Tests: $TESTS"
- ./mvnw test ${{ env.SUREFIRE_RETRY }} -Pauth-server-quarkus -Pdb-${{ matrix.db }} -Dtest=$TESTS -pl testsuite/integration-arquillian/tests/base | misc/log/trimmer.sh
-
- - name: Upload JVM Heapdumps
- if: always()
- uses: ./.github/actions/upload-heapdumps
-
- - uses: ./.github/actions/upload-flaky-tests
- name: Upload flaky tests
- env:
- GH_TOKEN: ${{ github.token }}
- with:
- job-name: Legacy Store IT
-
- - name: Surefire reports
- if: always()
- uses: ./.github/actions/archive-surefire-reports
- with:
- job-id: legacy-store-integration-tests-${{ matrix.db }}
-
- store-model-tests:
- name: Store Model Tests
- runs-on: ubuntu-latest
- needs: [build, conditional]
- if: needs.conditional.outputs.ci-store == 'true'
- timeout-minutes: 75
- steps:
- - uses: actions/checkout@v3
-
- - id: integration-test-setup
- name: Integration test setup
- uses: ./.github/actions/integration-test-setup
-
- - name: Run model tests
- run: testsuite/model/test-all-profiles.sh ${{ env.SUREFIRE_RETRY }}
-
- - name: Upload JVM Heapdumps
- if: always()
- uses: ./.github/actions/upload-heapdumps
-
- - uses: ./.github/actions/upload-flaky-tests
- name: Upload flaky tests
- env:
- GH_TOKEN: ${{ github.token }}
- with:
- job-name: Store Model Tests
-
- - name: Surefire reports
- if: always()
- uses: ./.github/actions/archive-surefire-reports
- with:
- job-id: store-model-tests
-
- clustering-integration-tests:
- name: Legacy Clustering IT
- needs: build
- runs-on: ubuntu-latest
- timeout-minutes: 35
- env:
- MAVEN_OPTS: -Xmx1024m
- steps:
- - uses: actions/checkout@v3
-
- - id: integration-test-setup
- name: Integration test setup
- uses: ./.github/actions/integration-test-setup
-
- - name: Run cluster tests
- run: |
- ./mvnw test ${{ env.SUREFIRE_RETRY }} -Pauth-server-cluster-quarkus -Dsession.cache.owners=2 -Dtest=**.cluster.** -pl testsuite/integration-arquillian/tests/base | misc/log/trimmer.sh
-
- - name: Upload JVM Heapdumps
- if: always()
- uses: ./.github/actions/upload-heapdumps
-
- - uses: ./.github/actions/upload-flaky-tests
- name: Upload flaky tests
- env:
- GH_TOKEN: ${{ github.token }}
- with:
- job-name: Legacy Clustering IT
-
- - name: Surefire reports
- if: always()
- uses: ./.github/actions/archive-surefire-reports
- with:
- job-id: clustering-integration-tests
-
- fips-unit-tests:
- name: FIPS UT
- runs-on: ubuntu-latest
- needs: build
- timeout-minutes: 20
- steps:
- - uses: actions/checkout@v3
-
- - name: Fake fips
- run: |
- cd .github/fake_fips
- make
- sudo insmod fake_fips.ko
-
- - id: unit-test-setup
- name: Unit test setup
- uses: ./.github/actions/unit-test-setup
-
- - name: Run crypto tests
- run: docker run --rm --workdir /github/workspace -v "${{ github.workspace }}":"/github/workspace" -v "$HOME/.m2":"/root/.m2" registry.access.redhat.com/ubi8/ubi:latest .github/scripts/run-fips-ut.sh
-
- - name: Upload JVM Heapdumps
- if: always()
- uses: ./.github/actions/upload-heapdumps
-
- - name: Surefire reports
- if: always()
- uses: ./.github/actions/archive-surefire-reports
- with:
- job-id: fips-unit-tests
-
- fips-integration-tests:
- name: FIPS IT
- needs: build
- runs-on: ubuntu-latest
- timeout-minutes: 45
- strategy:
- matrix:
- mode: [non-strict, strict]
- fail-fast: false
- steps:
- - uses: actions/checkout@v3
-
- - name: Fake fips
- run: |
- cd .github/fake_fips
- make
- sudo insmod fake_fips.ko
-
- - id: integration-test-setup
- name: Integration test setup
- uses: ./.github/actions/integration-test-setup
- with:
- jdk-version: 17
-
- - name: Prepare Quarkus distribution with BCFIPS
- run: ./mvnw install -e -pl testsuite/integration-arquillian/servers/auth-server/quarkus -Pauth-server-quarkus,auth-server-fips140-2
-
- - name: Run base tests
- run: docker run --rm --workdir /github/workspace -e "SUREFIRE_RERUN_FAILING_COUNT" -v "${{ github.workspace }}":"/github/workspace" -v "$HOME/.m2":"/root/.m2" registry.access.redhat.com/ubi8/ubi:latest .github/scripts/run-fips-it.sh ${{ matrix.mode }}
-
- - name: Upload JVM Heapdumps
- if: always()
- uses: ./.github/actions/upload-heapdumps
-
- - uses: ./.github/actions/upload-flaky-tests
- name: Upload flaky tests
- env:
- GH_TOKEN: ${{ github.token }}
- with:
- job-name: FIPS IT
-
- - name: Surefire reports
- if: always()
- uses: ./.github/actions/archive-surefire-reports
- with:
- job-id: fips-integration-tests-${{ matrix.mode }}
-
- account-console-integration-tests:
- name: Account Console IT
- runs-on: ubuntu-latest
- needs: build
- timeout-minutes: 75
- strategy:
- matrix:
- browser: [chrome, firefox]
- fail-fast: false
- steps:
- - uses: actions/checkout@v3
-
- - id: integration-test-setup
- name: Integration test setup
- uses: ./.github/actions/integration-test-setup
-
- - name: Run Account Console IT
- run: ./mvnw test ${{ env.SUREFIRE_RETRY }} -Pauth-server-quarkus -Dtest=**.account2.**,!SigningInTest#passwordlessWebAuthnTest,!SigningInTest#twoFactorWebAuthnTest -Dbrowser=${{ matrix.browser }} "-Dwebdriver.chrome.driver=$CHROMEWEBDRIVER/chromedriver" "-Dwebdriver.gecko.driver=$GECKOWEBDRIVER/geckodriver" -f testsuite/integration-arquillian/tests/other/base-ui/pom.xml | misc/log/trimmer.sh
-
- - name: Upload JVM Heapdumps
- if: always()
- uses: ./.github/actions/upload-heapdumps
-
- - uses: ./.github/actions/upload-flaky-tests
- name: Upload flaky tests
- env:
- GH_TOKEN: ${{ github.token }}
- with:
- job-name: Account Console IT
-
- - name: Surefire reports
- if: always()
- uses: ./.github/actions/archive-surefire-reports
- with:
- job-id: account-console-integration-tests-${{ matrix.browser }}
-
- forms-integration-tests:
- name: Forms IT
- runs-on: ubuntu-latest
- needs: build
- timeout-minutes: 75
- strategy:
- matrix:
- browser: [chrome, firefox]
- fail-fast: false
- steps:
- - uses: actions/checkout@v3
-
- - id: integration-test-setup
- name: Integration test setup
- uses: ./.github/actions/integration-test-setup
-
- - name: Run Forms IT
- run: |
- TESTS=`testsuite/integration-arquillian/tests/base/testsuites/suite.sh forms`
- echo "Tests: $TESTS"
- ./mvnw test ${{ env.SUREFIRE_RETRY }} -Pauth-server-quarkus -Dtest=$TESTS -Dbrowser=${{ matrix.browser }} "-Dwebdriver.chrome.driver=$CHROMEWEBDRIVER/chromedriver" "-Dwebdriver.gecko.driver=$GECKOWEBDRIVER/geckodriver" -f testsuite/integration-arquillian/tests/base/pom.xml | misc/log/trimmer.sh
-
- - name: Upload JVM Heapdumps
- if: always()
- uses: ./.github/actions/upload-heapdumps
-
- - uses: ./.github/actions/upload-flaky-tests
- name: Upload flaky tests
- env:
- GH_TOKEN: ${{ github.token }}
- with:
- job-name: Forms IT
-
- - name: Surefire reports
- if: always()
- uses: ./.github/actions/archive-surefire-reports
- with:
- job-id: forms-integration-tests-${{ matrix.browser }}
-
- webauthn-integration-tests:
- name: WebAuthn IT
- runs-on: ubuntu-latest
- needs: build
- timeout-minutes: 45
- strategy:
- matrix:
- browser:
- - chrome
- # - firefox disabled until https://github.com/keycloak/keycloak/issues/20777 is resolved
- fail-fast: false
- steps:
- - uses: actions/checkout@v3
-
- - id: integration-test-setup
- name: Integration test setup
- uses: ./.github/actions/integration-test-setup
-
- # Don't use Chrome for testing (just regular Chrome) until https://github.com/keycloak/keycloak/issues/22214 is resolved
- #- id: install-chrome
- # name: Install Chrome browser
- # uses: ./.github/actions/install-chrome
- # if: matrix.browser == 'chrome'
-
- - name: Run WebAuthn IT
- run: ./mvnw test ${{ env.SUREFIRE_RETRY }} -Pauth-server-quarkus -Dtest=org.keycloak.testsuite.webauthn.**.*Test -Dbrowser=${{ matrix.browser }} "-Dwebdriver.chrome.driver=$CHROMEWEBDRIVER/chromedriver" "-Dwebdriver.gecko.driver=$GECKOWEBDRIVER/geckodriver" -Pwebauthn -f testsuite/integration-arquillian/tests/other/pom.xml | misc/log/trimmer.sh
-
- - name: Upload JVM Heapdumps
- if: always()
- uses: ./.github/actions/upload-heapdumps
-
- - uses: ./.github/actions/upload-flaky-tests
- name: Upload flaky tests
- env:
- GH_TOKEN: ${{ github.token }}
- with:
- job-name: WebAuthn IT
-
- - name: Surefire reports
- if: always()
- uses: ./.github/actions/archive-surefire-reports
- with:
- job-id: webauthn-integration-tests-${{ matrix.browser }}
-
- sssd-unit-tests:
- name: SSSD
- runs-on: ubuntu-latest
- if: needs.conditional.outputs.ci-sssd == 'true'
- needs:
- - conditional
- - build
- timeout-minutes: 30
- steps:
- - name: checkout
- uses: actions/checkout@v3
-
- - id: integration-test-setup
- name: Integration test setup
- uses: ./.github/actions/integration-test-setup
-
- - id: weekly-cache-key
- name: Key for weekly rotation of cache
- shell: bash
- run: echo "key=ipa-data-`date -u "+%Y-%U"`" >> $GITHUB_OUTPUT
-
- - id: cache-maven-repository
- name: ipa-data cache
- uses: actions/cache@v3
- with:
- path: ~/ipa-data.tar
- key: ${{ steps.weekly-cache-key.outputs.key }}
-
- - name: Run tests
- run: .github/scripts/run-ipa.sh "${{ github.workspace }}"
-
- - name: Surefire reports
- if: always()
- uses: ./.github/actions/archive-surefire-reports
- with:
- job-id: sssd-unit-tests
-
- migration-tests:
- name: Migration Tests
- runs-on: ubuntu-latest
- needs: build
- timeout-minutes: 45
- strategy:
- matrix:
- old-version: [19.0.3]
- database: [postgres, mysql, oracle, mssql, mariadb]
- fail-fast: false
- steps:
- - uses: actions/checkout@v3
-
- - id: integration-test-setup
- name: Integration test setup
- uses: ./.github/actions/integration-test-setup
-
- - name: Run Migration Tests
- run: |
- ./mvnw clean install ${{ env.SUREFIRE_RETRY }} \
- -Pauth-server-quarkus -Pdb-${{ matrix.database }} -Pauth-server-migration \
- -Dtest=MigrationTest \
- -Dmigration.mode=auto \
- -Dmigrated.auth.server.version=${{ matrix.old-version }} \
- -Dmigration.import.file.name=migration-realm-${{ matrix.old-version }}.json \
- -Dauth.server.ssl.required=false \
- -Dauth.server.db.host=localhost \
- -f testsuite/integration-arquillian/pom.xml | misc/log/trimmer.sh
-
- - name: Upload JVM Heapdumps
- if: always()
- uses: ./.github/actions/upload-heapdumps
-
- - uses: ./.github/actions/upload-flaky-tests
- name: Upload flaky tests
- env:
- GH_TOKEN: ${{ github.token }}
- with:
- job-name: Migration Tests
-
- - name: Surefire reports
- if: always()
- uses: ./.github/actions/archive-surefire-reports
- with:
- job-id: migration-tests-${{ matrix.old-version }}-${{ matrix.database }}
-
- check:
- name: Status Check - Keycloak CI
- if: always()
- needs:
- - conditional
- - unit-tests
- - base-integration-tests
- - quarkus-integration-tests
- - jdk-integration-tests
- - new-store-integration-tests
- - legacy-store-integration-tests
- - store-model-tests
- - clustering-integration-tests
- - fips-unit-tests
- - fips-integration-tests
- - account-console-integration-tests
- - forms-integration-tests
- - webauthn-integration-tests
- - sssd-unit-tests
- - migration-tests
- runs-on: ubuntu-latest
- steps:
- - uses: actions/checkout@v3
- - uses: ./.github/actions/status-check
- with:
- jobs: ${{ toJSON(needs) }}
diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml
deleted file mode 100644
index 071c1d3a3ef4..000000000000
--- a/.github/workflows/codeql-analysis.yml
+++ /dev/null
@@ -1,101 +0,0 @@
-name: CodeQL
-
-on:
- push:
- branches-ignore:
- - main
- - dependabot/**
- pull_request:
- branches: [main]
- workflow_dispatch:
-
-concurrency:
- # Only cancel jobs for PR updates
- group: codeql-analysis-${{ github.ref }}
- cancel-in-progress: true
-
-defaults:
- run:
- shell: bash
-
-jobs:
-
- conditional:
- name: Check conditional workflows and jobs
- runs-on: ubuntu-latest
- outputs:
- java: ${{ steps.conditional.outputs.codeql-java }}
- themes: ${{ steps.conditional.outputs.codeql-themes }}
- steps:
- - uses: actions/checkout@v3
-
- - id: conditional
- uses: ./.github/actions/conditional
- with:
- token: ${{ secrets.GITHUB_TOKEN }}
-
- java:
- name: CodeQL Java
- needs: conditional
- runs-on: ubuntu-latest
- if: needs.conditional.outputs.java == 'true'
- outputs:
- conclusion: ${{ steps.check.outputs.conclusion }}
-
- steps:
- - uses: actions/checkout@v3
-
- - name: Initialize CodeQL
- uses: github/codeql-action/init@v2.21.5
- with:
- languages: java
-
- - name: Build Keycloak
- uses: ./.github/actions/build-keycloak
-
- - name: Perform CodeQL Analysis
- uses: github/codeql-action/analyze@v2.21.5
- with:
- wait-for-processing: true
- env:
- CODEQL_ACTION_EXTRA_OPTIONS: '{"database":{"interpret-results":["--max-paths",0]}}'
-
- themes:
- name: CodeQL Themes
- needs: conditional
- runs-on: ubuntu-latest
- if: needs.conditional.outputs.themes == 'true'
- outputs:
- conclusion: ${{ steps.check.outputs.conclusion }}
-
- steps:
- - uses: actions/checkout@v3
-
- - name: Initialize CodeQL
- uses: github/codeql-action/init@v2.21.5
- env:
- CODEQL_ACTION_EXTRA_OPTIONS: '{"database":{"finalize":["--no-run-unnecessary-builds"]}}'
- with:
- languages: javascript
- source-root: themes/src/main/
-
- - name: Perform CodeQL Analysis
- uses: github/codeql-action/analyze@v2.21.5
- with:
- wait-for-processing: true
- env:
- CODEQL_ACTION_EXTRA_OPTIONS: '{"database":{"interpret-results":["--max-paths",0]}}'
-
- check:
- name: Status Check - CodeQL
- if: always()
- needs:
- - conditional
- - java
- - themes
- runs-on: ubuntu-latest
- steps:
- - uses: actions/checkout@v3
- - uses: ./.github/actions/status-check
- with:
- jobs: ${{ toJSON(needs) }}
diff --git a/.github/workflows/documentation.yml b/.github/workflows/documentation.yml
deleted file mode 100644
index 474cef710ef5..000000000000
--- a/.github/workflows/documentation.yml
+++ /dev/null
@@ -1,112 +0,0 @@
-name: Keycloak Documentation
-
-on:
- push:
- branches-ignore:
- - main
- - dependabot/**
- pull_request:
- workflow_dispatch:
-
-concurrency:
- # Only cancel jobs for PR updates
- group: documentation-${{ github.ref }}
- cancel-in-progress: true
-
-defaults:
- run:
- shell: bash
-
-jobs:
-
- conditional:
- name: Check conditional workflows and jobs
- runs-on: ubuntu-latest
- outputs:
- documentation: ${{ steps.conditional.outputs.documentation }}
- steps:
- - uses: actions/checkout@v3
-
- - id: conditional
- uses: ./.github/actions/conditional
- with:
- token: ${{ secrets.GITHUB_TOKEN }}
-
- build:
- name: Build
- if: ${{ needs.conditional.outputs.documentation == 'true' }}
- runs-on: ubuntu-latest
- needs: conditional
- steps:
- - uses: actions/checkout@v3
-
- - id: setup-java
- name: Setup Java
- uses: ./.github/actions/java-setup
-
- - id: maven-cache
- name: Maven cache
- uses: ./.github/actions/maven-cache
-
- - id: build-documentation
- name: Build Keycloak
- shell: bash
- run: |
- MVN_HTTP_CONFIG="-Dhttp.keepAlive=false -Dmaven.wagon.http.pool=false -Dmaven.wagon.http.retryHandler.class=standard -Dmaven.wagon.http.retryHandler.count=3 -Dmaven.wagon.httpconnectionManager.ttlSeconds=120"
- ./mvnw install -DskipTests -am -pl docs/documentation/tests,docs/documentation/dist -nsu -B -e $MVN_HTTP_CONFIG -Pdocumentation
-
- - id: test-documentation
- name: Verify Keycloak documentation
- shell: bash
- run: |
- ./mvnw test -Dtest=!ExternalLinksTest -am -pl docs/documentation/tests,docs/documentation/dist -nsu -B -e -Pdocumentation
-
- - id: upload-keycloak-documentation
- name: Upload Keycloak documentation
- uses: actions/upload-artifact@v3
- with:
- name: keycloak-documentation
- path: docs/documentation/dist/target/*.zip
- retention-days: 1
-
- external-links:
- name: External links check
- if: ${{ needs.conditional.outputs.documentation == 'true' }}
- runs-on: ubuntu-latest
- needs: conditional
- steps:
- - uses: actions/checkout@v3
-
- - id: setup-java
- name: Setup Java
- uses: ./.github/actions/java-setup
-
- - id: maven-cache
- name: Maven cache
- uses: ./.github/actions/maven-cache
-
- - id: build-documentation
- name: Build Keycloak
- shell: bash
- run: |
- MVN_HTTP_CONFIG="-Dhttp.keepAlive=false -Dmaven.wagon.http.pool=false -Dmaven.wagon.http.retryHandler.class=standard -Dmaven.wagon.http.retryHandler.count=3 -Dmaven.wagon.httpconnectionManager.ttlSeconds=120"
- ./mvnw install -DskipTests -am -pl docs/documentation/tests -nsu -B -e -Pdocumentation $MVN_HTTP_CONFIG
-
- - id: test-documentation
- name: Verify Keycloak documentation
- shell: bash
- run: |
- ./mvnw test -Dtest=ExternalLinksTest -am -pl docs/documentation/tests,docs/documentation/dist -nsu -B -e -Pdocumentation
-
- check:
- name: Status Check - Keycloak Documentation
- if: always()
- needs:
- - conditional
- - build
- runs-on: ubuntu-latest
- steps:
- - uses: actions/checkout@v3
- - uses: ./.github/actions/status-check
- with:
- jobs: ${{ toJSON(needs) }}
\ No newline at end of file
diff --git a/.github/workflows/guides.yml b/.github/workflows/guides.yml
deleted file mode 100644
index 5f3ee172fd65..000000000000
--- a/.github/workflows/guides.yml
+++ /dev/null
@@ -1,59 +0,0 @@
-name: Keycloak Guides
-
-on:
- push:
- branches-ignore:
- - main
- - dependabot/**
- pull_request:
- workflow_dispatch:
-
-concurrency:
- # Only cancel jobs for PR updates
- group: guides-${{ github.ref }}
- cancel-in-progress: true
-
-defaults:
- run:
- shell: bash
-
-jobs:
-
- conditional:
- name: Check conditional workflows and jobs
- runs-on: ubuntu-latest
- outputs:
- guides: ${{ steps.conditional.outputs.guides }}
- ci: ${{ steps.conditional.outputs.ci }}
- steps:
- - uses: actions/checkout@v3
-
- - id: conditional
- uses: ./.github/actions/conditional
- with:
- token: ${{ secrets.GITHUB_TOKEN }}
-
- build:
- name: Build
- # will only build the guides if the general CI doesn't run, which will also build the guides
- if: ${{ needs.conditional.outputs.guides == 'true' && needs.conditional.outputs.ci != 'true' }}
- runs-on: ubuntu-latest
- needs: conditional
- steps:
- - uses: actions/checkout@v3
-
- - name: Build Keycloak
- uses: ./.github/actions/build-keycloak
-
- check:
- name: Status Check - Keycloak Guides
- if: always()
- needs:
- - conditional
- - build
- runs-on: ubuntu-latest
- steps:
- - uses: actions/checkout@v3
- - uses: ./.github/actions/status-check
- with:
- jobs: ${{ toJSON(needs) }}
diff --git a/.github/workflows/js-ci.yml b/.github/workflows/js-ci.yml
deleted file mode 100644
index d740e80fd8d8..000000000000
--- a/.github/workflows/js-ci.yml
+++ /dev/null
@@ -1,285 +0,0 @@
-name: Keycloak JavaScript CI
-
-on:
- push:
- branches-ignore:
- - main
- - dependabot/**
- pull_request:
- workflow_dispatch:
-
-concurrency:
- # Only cancel jobs for PR updates
- group: js-ci-${{ github.ref }}
- cancel-in-progress: true
-
-defaults:
- run:
- shell: bash
-
-jobs:
- conditional:
- name: Check conditional workflows and jobs
- runs-on: ubuntu-latest
- outputs:
- js-ci: ${{ steps.conditional.outputs.js }}
- steps:
- - uses: actions/checkout@v3
-
- - id: conditional
- uses: ./.github/actions/conditional
- with:
- token: ${{ secrets.GITHUB_TOKEN }}
-
- build-keycloak:
- name: Build Keycloak
- needs: conditional
- if: needs.conditional.outputs.js-ci == 'true'
- runs-on: ubuntu-latest
- steps:
- - uses: actions/checkout@v3
-
- - name: Setup Java
- uses: actions/setup-java@v3
- with:
- distribution: temurin
- java-version: 17
- check-latest: true
- cache: maven
-
- - name: Build Keycloak
- run: |
- mvn clean install --batch-mode --errors -DskipTests -DskipTestsuite -DskipExamples -DskipAccount2 -DskipCommon -Pdistribution
- mv ./quarkus/dist/target/keycloak-999.0.0-SNAPSHOT.tar.gz ./keycloak-999.0.0-SNAPSHOT.tar.gz
-
- - name: Upload Keycloak dist
- uses: actions/upload-artifact@v3
- with:
- name: keycloak
- path: keycloak-999.0.0-SNAPSHOT.tar.gz
-
- admin-client:
- name: Admin Client
- needs: conditional
- if: needs.conditional.outputs.js-ci == 'true'
- runs-on: ubuntu-latest
- env:
- WORKSPACE: "@keycloak/keycloak-admin-client"
- steps:
- - uses: actions/checkout@v3
-
- - uses: ./.github/actions/pnpm-setup
- with:
- working-directory: js
-
- - run: pnpm --filter=${{ env.WORKSPACE }} run lint
- working-directory: js
-
- - run: pnpm --filter=${{ env.WORKSPACE }} run build
- working-directory: js
-
- keycloak-js:
- name: Keycloak JS
- needs: conditional
- if: needs.conditional.outputs.js-ci == 'true'
- runs-on: ubuntu-latest
- env:
- WORKSPACE: keycloak-js
- steps:
- - uses: actions/checkout@v3
-
- - uses: ./.github/actions/pnpm-setup
- with:
- working-directory: js
-
- - run: pnpm --filter=${{ env.WORKSPACE }} run build
- working-directory: js
-
- keycloak-masthead:
- name: Keycloak Masthead
- needs: conditional
- if: needs.conditional.outputs.js-ci == 'true'
- runs-on: ubuntu-latest
- env:
- WORKSPACE: keycloak-masthead
- steps:
- - uses: actions/checkout@v3
-
- - uses: ./.github/actions/pnpm-setup
- with:
- working-directory: js
-
- - run: pnpm --filter=${{ env.WORKSPACE }} run lint
- working-directory: js
-
- - run: pnpm --filter=${{ env.WORKSPACE }} run build
- working-directory: js
-
- ui-shared:
- name: UI Shared
- needs: conditional
- if: needs.conditional.outputs.js-ci == 'true'
- runs-on: ubuntu-latest
- env:
- WORKSPACE: ui-shared
- steps:
- - uses: actions/checkout@v3
-
- - uses: ./.github/actions/pnpm-setup
- with:
- working-directory: js
-
- - run: pnpm --filter=${{ env.WORKSPACE }} run lint
- working-directory: js
-
- - run: pnpm --filter=${{ env.WORKSPACE }} run build
- working-directory: js
-
- account-ui:
- name: Account UI
- needs: conditional
- if: needs.conditional.outputs.js-ci == 'true'
- runs-on: ubuntu-latest
- env:
- WORKSPACE: account-ui
- steps:
- - uses: actions/checkout@v3
-
- - uses: ./.github/actions/pnpm-setup
- with:
- working-directory: js
-
- - run: pnpm --filter=${{ env.WORKSPACE }} run lint
- working-directory: js
-
- - run: pnpm --filter=${{ env.WORKSPACE }} run build
- working-directory: js
-
- admin-ui:
- name: Admin UI
- needs: conditional
- if: needs.conditional.outputs.js-ci == 'true'
- runs-on: ubuntu-latest
- env:
- WORKSPACE: admin-ui
- steps:
- - uses: actions/checkout@v3
-
- - uses: ./.github/actions/pnpm-setup
- with:
- working-directory: js
-
- - run: pnpm --filter=${{ env.WORKSPACE }} run lint
- working-directory: js
-
- - run: pnpm --filter=${{ env.WORKSPACE }} run test
- working-directory: js
-
- - run: pnpm --filter=${{ env.WORKSPACE }} run build
- working-directory: js
-
- - run: pnpm --filter=${{ env.WORKSPACE }} run cy:check-types
- working-directory: js
-
- admin-ui-e2e:
- name: Admin UI E2E
- needs:
- - conditional
- - build-keycloak
- if: needs.conditional.outputs.js-ci == 'true' && github.repository == 'keycloak/keycloak'
- runs-on: ubuntu-latest
- env:
- WORKSPACE: admin-ui
- strategy:
- matrix:
- container: [1, 2, 3, 4, 5]
- browser: [chrome, firefox]
- exclude:
- # Only test with Firefox on scheduled runs
- - browser: ${{ github.event_name != 'workflow_dispatch' && 'firefox' || '' }}
- steps:
- - uses: actions/checkout@v3
-
- - name: Install Google Chrome
- if: matrix.browser == 'chrome'
- uses: browser-actions/setup-chrome@v1
- with:
- chrome-version: stable
-
- - name: Install Firefox
- if: matrix.browser == 'firefox'
- uses: browser-actions/setup-firefox@v1
- with:
- firefox-version: latest
-
- - uses: ./.github/actions/pnpm-setup
- with:
- working-directory: js
-
- - name: Compile Admin Client
- run: pnpm --filter=@keycloak/keycloak-admin-client run build
- working-directory: js
-
- - name: Download Keycloak server
- uses: actions/download-artifact@v3
- with:
- name: keycloak
-
- - name: Setup Java
- uses: actions/setup-java@v3
- with:
- distribution: temurin
- java-version: 17
-
- - name: Start Keycloak Server
- run: |
- tar xfvz keycloak-999.0.0-SNAPSHOT.tar.gz
- keycloak-999.0.0-SNAPSHOT/bin/kc.sh start-dev --features=admin-fine-grained-authz,declarative-user-profile &> ~/server.log &
- env:
- KEYCLOAK_ADMIN: admin
- KEYCLOAK_ADMIN_PASSWORD: admin
-
- - name: Start LDAP server
- run: pnpm --filter=${{ env.WORKSPACE }} run cy:ldap-server &
- working-directory: js
-
- - name: Run Cypress
- uses: cypress-io/github-action@v6
- with:
- install: false
- record: true
- parallel: true
- group: ${{ matrix.browser }}
- browser: ${{ matrix.browser }}
- wait-on: http://localhost:8080
- working-directory: js/apps/admin-ui
- env:
- CYPRESS_BASE_URL: http://localhost:8080/admin/
- CYPRESS_KEYCLOAK_SERVER: http://localhost:8080
- CYPRESS_RECORD_KEY: b8f1d15e-eab8-4ee7-8e44-c6d7cd8fc0eb
- GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
-
- - name: Upload server logs
- uses: actions/upload-artifact@v3
- with:
- name: server-log-${{ matrix.container }}-${{ matrix.browser }}
- path: ~/server.log
-
- check:
- name: Status Check - Keycloak JavaScript CI
- if: always()
- needs:
- - conditional
- - admin-client
- - keycloak-js
- - keycloak-masthead
- - ui-shared
- - account-ui
- - admin-ui
- - admin-ui-e2e
- runs-on: ubuntu-latest
- steps:
- - uses: actions/checkout@v3
- - uses: ./.github/actions/status-check
- with:
- jobs: ${{ toJSON(needs) }}
diff --git a/.github/workflows/operator-ci.yml b/.github/workflows/operator-ci.yml
deleted file mode 100644
index 9d759be70ce0..000000000000
--- a/.github/workflows/operator-ci.yml
+++ /dev/null
@@ -1,215 +0,0 @@
-name: Keycloak Operator CI
-
-on:
- push:
- branches-ignore:
- - main
- - dependabot/**
- pull_request:
- workflow_dispatch:
-
-env:
- MINIKUBE_VERSION: v1.31.2
- KUBERNETES_VERSION: v1.24.17 # OCP 4.11
-
-defaults:
- run:
- shell: bash
-
-concurrency:
- # Only cancel jobs for PR updates
- group: operator-ci-${{ github.ref }}
- cancel-in-progress: true
-
-jobs:
-
- conditional:
- name: Check conditional workflows and jobs
- runs-on: ubuntu-latest
- outputs:
- operator: ${{ steps.conditional.outputs.operator }}
- steps:
- - uses: actions/checkout@v3
-
- - id: conditional
- uses: ./.github/actions/conditional
- with:
- token: ${{ secrets.GITHUB_TOKEN }}
-
- build:
- name: Build distribution
- if: needs.conditional.outputs.operator == 'true'
- runs-on: ubuntu-latest
- needs: conditional
- steps:
- - uses: actions/checkout@v3
-
- - name: Build Keycloak
- uses: ./.github/actions/build-keycloak
- with:
- upload-m2-repo: false
- upload-dist: true
-
- test-local:
- name: Test local
- runs-on: ubuntu-latest
- needs: [build]
- steps:
- - uses: actions/checkout@v3
-
- - name: Set version
- id: vars
- run: echo "version_local=0.0.1-${GITHUB_SHA::6}" >> $GITHUB_ENV
-
- - name: Setup Java
- uses: ./.github/actions/java-setup
-
- - name: Setup Minikube-Kubernetes
- uses: manusa/actions-setup-minikube@v2.7.2
- with:
- minikube version: ${{ env.MINIKUBE_VERSION }}
- kubernetes version: ${{ env.KUBERNETES_VERSION }}
- github token: ${{ secrets.GITHUB_TOKEN }}
- driver: docker
- start args: --addons=ingress
-
- - name: Download keycloak distribution
- id: download-keycloak-dist
- uses: actions/download-artifact@v3
- with:
- name: keycloak-dist
- path: quarkus/container
-
- - name: Build Keycloak Docker images
- run: |
- eval $(minikube -p minikube docker-env)
- (cd quarkus/container && docker build --build-arg KEYCLOAK_DIST=$(ls keycloak-*.tar.gz) . -t keycloak:${{ env.version_local }})
- (cd operator && ./scripts/build-testing-docker-images.sh ${{ env.version_local }} keycloak custom-keycloak)
-
- - name: Test operator running locally
- run: |
- mvn install -Poperator -pl :keycloak-operator -am \
- -Dquarkus.kubernetes.image-pull-policy=IfNotPresent \
- -Doperator.keycloak.image=keycloak:${{ env.version_local }} \
- -Dtest.operator.custom.image=custom-keycloak:${{ env.version_local }} \
- -Doperator.keycloak.image-pull-policy=Never \
- -Dtest.operator.kubernetes.ip=$(minikube ip)
-
- test-remote:
- name: Test remote
- runs-on: ubuntu-latest
- needs: [build]
- steps:
- - uses: actions/checkout@v3
-
- - name: Set version
- id: vars
- run: echo "version_remote=0.0.1-${GITHUB_SHA::6}" >> $GITHUB_ENV
-
- - name: Setup Java
- uses: ./.github/actions/java-setup
-
- - name: Setup Minikube-Kubernetes
- uses: manusa/actions-setup-minikube@v2.7.2
- with:
- minikube version: ${{ env.MINIKUBE_VERSION }}
- kubernetes version: ${{ env.KUBERNETES_VERSION }}
- github token: ${{ secrets.GITHUB_TOKEN }}
- driver: docker
- start args: --addons=ingress
-
- - name: Download keycloak distribution
- id: download-keycloak-dist
- uses: actions/download-artifact@v3
- with:
- name: keycloak-dist
- path: quarkus/container
-
- - name: Build Keycloak Docker images
- run: |
- eval $(minikube -p minikube docker-env)
- (cd quarkus/container && docker build --build-arg KEYCLOAK_DIST=$(ls keycloak-*.tar.gz) . -t keycloak:${{ env.version_remote }})
- (cd operator && ./scripts/build-testing-docker-images.sh ${{ env.version_remote }} keycloak custom-keycloak)
-
- - name: Test operator running in cluster
- run: |
- eval $(minikube -p minikube docker-env)
- mvn install -Poperator -pl :keycloak-operator -am \
- -Dquarkus.container-image.build=true \
- -Dquarkus.kubernetes.image-pull-policy=IfNotPresent \
- -Doperator.keycloak.image=keycloak:${{ env.version_remote }} \
- -Dquarkus.kubernetes.env.vars.operator-keycloak-image-pull-policy=Never \
- -Dtest.operator.custom.image=custom-keycloak:${{ env.version_remote }} \
- --no-transfer-progress -Dtest.operator.deployment=remote \
- -Dtest.operator.kubernetes.ip=$(minikube ip)
-
- test-olm:
- name: Test OLM installation
- runs-on: ubuntu-latest
- needs: [build]
- steps:
- - uses: actions/checkout@v3
-
- - name: Setup Java
- uses: ./.github/actions/java-setup
-
- - name: Setup Minikube-Kubernetes
- uses: manusa/actions-setup-minikube@v2.7.2
- with:
- minikube version: ${{ env.MINIKUBE_VERSION }}
- kubernetes version: ${{ env.KUBERNETES_VERSION }}
- github token: ${{ secrets.GITHUB_TOKEN }}
- driver: docker
-
- - name: Install OPM
- uses: redhat-actions/openshift-tools-installer@v1
- with:
- source: github
- opm: 1.21.0
-
- - name: Install Yq
- run: sudo snap install yq
-
- - name: Install OLM
- working-directory: operator
- run: ./scripts/install-olm.sh
-
- - name: Download keycloak distribution
- id: download-keycloak-dist
- uses: actions/download-artifact@v3
- with:
- name: keycloak-dist
- path: quarkus/container
-
- - name: Arrange OLM test installation
- working-directory: operator
- run: |
- eval $(minikube -p minikube docker-env)
- ./scripts/olm-testing.sh ${GITHUB_SHA::6}
-
- - name: Deploy an example Keycloak and wait for it to be ready
- working-directory: operator
- run: |
- kubectl apply -f src/main/resources/example-postgres.yaml
- ./scripts/check-crds-installed.sh
- kubectl apply -f src/main/resources/example-db-secret.yaml
- kubectl apply -f src/main/resources/example-tls-secret.yaml
- kubectl apply -f src/main/resources/example-keycloak.yaml
- kubectl apply -f src/main/resources/example-realm.yaml
- # Wait for the CRs to be ready
- ./scripts/check-examples-installed.sh
-
- check:
- name: Status Check - Keycloak Operator CI
- if: always()
- needs:
- - conditional
- - test-local
- - test-remote
- - test-olm
- runs-on: ubuntu-latest
- steps:
- - uses: actions/checkout@v3
- - uses: ./.github/actions/status-check
- with:
- jobs: ${{ toJSON(needs) }}
diff --git a/.github/workflows/schedule-nightly.yml b/.github/workflows/schedule-nightly.yml
deleted file mode 100644
index f40d1b5969a7..000000000000
--- a/.github/workflows/schedule-nightly.yml
+++ /dev/null
@@ -1,60 +0,0 @@
-name: Scheduled nightly workflows
-
-on:
- schedule:
- - cron: '0 0 * * *'
- workflow_dispatch:
-
-jobs:
-
- setup:
- if: github.event_name != 'schedule' || github.repository == 'keycloak/keycloak'
- runs-on: ubuntu-latest
- outputs:
- latest-release-branch: ${{ steps.latest-release.outputs.branch }}
- steps:
- - id: latest-release
- run: |
- branch="release/$(gh api repos/keycloak/keycloak/branches | jq -r '.[].name' | sort -r | awk -F'/' '/[0-9.]+$/ {print $NF; exit}')"
- echo "branch=$branch"
- echo "branch=$branch" >> "$GITHUB_OUTPUT"
- env:
- GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
-
- run-default-branch:
- name: Run default branch
- runs-on: ubuntu-latest
- needs: setup
-
- strategy:
- matrix:
- workflow:
- - ci.yml
- - documentation.yml
- - js-ci.yml
- - operator-ci.yml
- - snyk-analysis.yml
- - trivy-analysis.yml
-
- steps:
- - name: Run workflow
- run: gh workflow run -R ${{ github.repository }} ${{ matrix.workflow }}
- env:
- GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
-
- run-latest-release-branch:
- name: Run latest release branch
- needs: setup
- runs-on: ubuntu-latest
-
- strategy:
- matrix:
- workflow:
- - snyk-analysis.yml
-
- steps:
- - run: echo ${{ needs.setup.outputs.latest-release-branch }}
- - name: Run workflow
- run: gh workflow run -R ${{ github.repository }} ${{ matrix.workflow }} --ref ${{ needs.setup.outputs.latest-release-branch }}
- env:
- GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
diff --git a/.github/workflows/snyk-analysis.yml b/.github/workflows/snyk-analysis.yml
deleted file mode 100644
index 33dd9cf78963..000000000000
--- a/.github/workflows/snyk-analysis.yml
+++ /dev/null
@@ -1,47 +0,0 @@
-name: Snyk
-
-on:
- workflow_dispatch:
-
-defaults:
- run:
- shell: bash
-
-jobs:
- analysis:
- name: Analysis of Quarkus and Operator
- runs-on: ubuntu-latest
- if: github.repository == 'keycloak/keycloak'
- steps:
- - uses: actions/checkout@v3
-
- - name: Build Keycloak
- uses: ./.github/actions/build-keycloak
-
- - uses: snyk/actions/setup@master
-
- - name: Check for vulnerabilities in Quarkus
- run: snyk test --policy-path=${GITHUB_WORKSPACE}/.github/snyk/.snyk --all-projects --prune-repeated-subdependencies --exclude=tests --sarif-file-output=quarkus-report.sarif quarkus/deployment
- continue-on-error: true
- env:
- SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }}
-
- - name: Upload Quarkus scanner results to GitHub
- uses: github/codeql-action/upload-sarif@v2.21.5
- with:
- sarif_file: quarkus-report.sarif
- category: snyk-quarkus-report
-
- - name: Check for vulnerabilities in Operator
- run: |
- mvn -Poperator -pl operator -am -DskipTests clean install
- snyk test --policy-path=${GITHUB_WORKSPACE}/.github/snyk/.snyk --all-projects --prune-repeated-subdependencies --exclude=tests --sarif-file-output=operator-report.sarif operator
- continue-on-error: true
- env:
- SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }}
-
- - name: Upload Operator scanner results to GitHub
- uses: github/codeql-action/upload-sarif@v2.21.5
- with:
- sarif_file: operator-report.sarif
- category: snyk-operator-report
diff --git a/.github/workflows/trivy-analysis.yml b/.github/workflows/trivy-analysis.yml
deleted file mode 100644
index c2a74eaf847c..000000000000
--- a/.github/workflows/trivy-analysis.yml
+++ /dev/null
@@ -1,36 +0,0 @@
-name: Trivy
-
-on:
- workflow_dispatch:
-
-defaults:
- run:
- shell: bash
-
-jobs:
-
- analysis:
- name: Vulnerability scanner for nightly containers
- runs-on: ubuntu-latest
- if: github.repository == 'keycloak/keycloak'
- strategy:
- matrix:
- container: [keycloak, keycloak-operator]
- fail-fast: false
- steps:
- - name: Run Trivy vulnerability scanner
- uses: aquasecurity/trivy-action@41f05d9ecffa2ed3f1580af306000f734b733e54
- with:
- image-ref: quay.io/keycloak/${{ matrix.container}}:nightly
- format: template
- template: '@/contrib/sarif.tpl'
- output: trivy-results.sarif
- severity: MEDIUM,CRITICAL,HIGH
- ignore-unfixed: true
- security-checks: vuln
- timeout: 15m
-
- - name: Upload Trivy scan results to GitHub Security tab
- uses: github/codeql-action/upload-sarif@v2.21.5
- with:
- sarif_file: trivy-results.sarif
diff --git a/README.md b/README.md
index d6c26c652a43..0cbb23663ca9 100644
--- a/README.md
+++ b/README.md
@@ -5,6 +5,16 @@ Keycloak is an Open Source Identity and Access Management solution for modern Ap
This repository contains the source code for the Keycloak Server, Java adapters and the JavaScript adapter.
+## moneymeets Specific Fixes
+
+At moneymeets the repository keycloak-docker is used to set up and run Keycloak. Internally, it creates a docker container containing Keycloak based on the source of the latest commit in branch moneymeets/22.0.4 of this repository here.
+This fork was created so that moneymeets is able to implement fixes that are not available yet or only available in later Keycloak versions.
+Currently, the following fixes are present in the code:
+
+* MD-6399 send correct email when calling the verify email endpoint
+* MD-6730 fix email validation to be RFC compliant.
+
+
## Help and Documentation
* [Documentation](https://www.keycloak.org/documentation.html)
diff --git a/build-local.sh b/build-local.sh
new file mode 100644
index 000000000000..9f5e89dd7f95
--- /dev/null
+++ b/build-local.sh
@@ -0,0 +1,7 @@
+#!/bin/sh
+# c.f. .github/workflows/build-and-deploy.yml
+mvn clean install -Pdistribution -DskipTests -DskipExamples -DskipTestsuite
+cp quarkus/dist/target/keycloak-22.0.4.tar.gz quarkus/container/
+pushd quarkus/container
+docker build --build-arg KEYCLOAK_DIST=$(ls keycloak-*.tar.gz) . -t keycloak-local
+popd
diff --git a/pom.xml b/pom.xml
index b86af8d53137..1e17b2e2c91a 100644
--- a/pom.xml
+++ b/pom.xml
@@ -384,6 +384,11 @@
${jakarta.mail.version}
+
+ com.github.bbottema
+ emailaddress-rfc2822
+ 2.3.1
+
org.eclipse.angus
angus-mail
diff --git a/quarkus/container/Dockerfile b/quarkus/container/Dockerfile
index bdc78411e47f..4f5e6d632c8a 100644
--- a/quarkus/container/Dockerfile
+++ b/quarkus/container/Dockerfile
@@ -1,17 +1,15 @@
FROM registry.access.redhat.com/ubi9 AS ubi-micro-build
ENV KEYCLOAK_VERSION 22.0.4
-ARG KEYCLOAK_DIST=https://github.com/keycloak/keycloak/releases/download/$KEYCLOAK_VERSION/keycloak-$KEYCLOAK_VERSION.tar.gz
+ARG KEYCLOAK_DIST=keycloak-$KEYCLOAK_VERSION.tar.gz
RUN dnf install -y tar gzip
-ADD $KEYCLOAK_DIST /tmp/keycloak/
+COPY $KEYCLOAK_DIST /tmp/keycloak/
# The next step makes it uniform for local development and upstream built.
# If it is a local tar archive then it is unpacked, if from remote is just downloaded.
-RUN (cd /tmp/keycloak && \
- tar -xvf /tmp/keycloak/keycloak-*.tar.gz && \
- rm /tmp/keycloak/keycloak-*.tar.gz) || true
+RUN cd /tmp/keycloak && tar -xvf $KEYCLOAK_DIST && rm $KEYCLOAK_DIST
RUN mv /tmp/keycloak/keycloak-* /opt/keycloak && mkdir -p /opt/keycloak/data
RUN chmod -R g+rwX /opt/keycloak
diff --git a/server-spi-private/pom.xml b/server-spi-private/pom.xml
index 284992bd4bfa..c1abe86c8ef0 100755
--- a/server-spi-private/pom.xml
+++ b/server-spi-private/pom.xml
@@ -77,6 +77,11 @@
hamcrest
test
+
+ com.github.bbottema
+ emailaddress-rfc2822
+ 2.3.1
+
diff --git a/server-spi-private/src/main/java/org/keycloak/validate/validators/EmailValidator.java b/server-spi-private/src/main/java/org/keycloak/validate/validators/EmailValidator.java
index 247fdd581384..2a72725f2d0a 100644
--- a/server-spi-private/src/main/java/org/keycloak/validate/validators/EmailValidator.java
+++ b/server-spi-private/src/main/java/org/keycloak/validate/validators/EmailValidator.java
@@ -27,6 +27,8 @@
import org.keycloak.validate.ValidationError;
import org.keycloak.validate.ValidatorConfig;
+import org.hazlewood.connor.bottema.emailaddress.EmailAddressValidator;
+
/**
* Email format validation - accepts plain string and collection of strings, for basic behavior like null/blank values
* handling and collections support see {@link AbstractStringValidator}.
@@ -39,7 +41,6 @@ public class EmailValidator extends AbstractStringValidator implements Configure
public static final String MESSAGE_INVALID_EMAIL = "error-invalid-email";
-
@Override
public String getId() {
return ID;
@@ -47,11 +48,11 @@ public String getId() {
@Override
protected void doValidate(String value, String inputHint, ValidationContext context, ValidatorConfig config) {
- if (!EmailValidationUtil.isValidEmail(value)) {
+ if (!EmailAddressValidator.isValidStrict(value)) {
context.addError(new ValidationError(ID, inputHint, MESSAGE_INVALID_EMAIL, value));
}
}
-
+
@Override
public String getHelpText() {
return "Email format validator";
diff --git a/services/src/main/java/org/keycloak/services/resources/admin/UserResource.java b/services/src/main/java/org/keycloak/services/resources/admin/UserResource.java
index 4f0ee0a8f979..cfc9c60304e5 100755
--- a/services/src/main/java/org/keycloak/services/resources/admin/UserResource.java
+++ b/services/src/main/java/org/keycloak/services/resources/admin/UserResource.java
@@ -24,6 +24,7 @@
import org.jboss.resteasy.annotations.cache.NoCache;
import org.keycloak.authentication.RequiredActionProvider;
import org.keycloak.authentication.actiontoken.execactions.ExecuteActionsActionToken;
+import org.keycloak.authentication.actiontoken.verifyemail.VerifyEmailActionToken;
import org.keycloak.authentication.requiredactions.util.RequiredActionsValidator;
import org.keycloak.common.ClientConnection;
import org.keycloak.common.Profile;
@@ -153,7 +154,7 @@ public class UserResource {
protected final KeycloakSession session;
protected final HttpHeaders headers;
-
+
public UserResource(KeycloakSession session, UserModel user, AdminPermissionEvaluator auth, AdminEventBuilder adminEvent) {
this.session = session;
this.auth = auth;
@@ -163,7 +164,7 @@ public UserResource(KeycloakSession session, UserModel user, AdminPermissionEval
this.adminEvent = adminEvent.resource(ResourceType.USER);
this.headers = session.getContext().getRequestHeaders();
}
-
+
/**
* Update the user
*
@@ -960,10 +961,33 @@ public Response executeActionsEmail(@Parameter(description = "Redirect uri") @Qu
)
public Response sendVerifyEmail(
@Parameter(description = "Redirect uri") @QueryParam(OIDCLoginProtocol.REDIRECT_URI_PARAM) String redirectUri,
- @Parameter(description = "Client id") @QueryParam(OIDCLoginProtocol.CLIENT_ID_PARAM) String clientId) {
- List actions = new LinkedList<>();
- actions.add(UserModel.RequiredAction.VERIFY_EMAIL.name());
- return executeActionsEmail(redirectUri, clientId, null, actions);
+ @Parameter(description = "Client id") @QueryParam(OIDCLoginProtocol.CLIENT_ID_PARAM) String clientId,
+ @QueryParam("lifespan") Integer lifespan) {
+ auth.users().requireManage(user);
+
+ SendEmailParams result = verifySendEmailParams(redirectUri, clientId, lifespan);
+
+ int expiration = Time.currentTime() + result.lifespan;
+ VerifyEmailActionToken token = new VerifyEmailActionToken(user.getId(), expiration, null, user.getEmail(), result.clientId);
+
+ String link = LoginActionsService.actionTokenProcessor(session.getContext().getUri())
+ .queryParam("key", token.serialize(session, realm, session.getContext().getUri()))
+ .build(realm.getName()).toString();
+
+ try {
+ session
+ .getProvider(EmailTemplateProvider.class)
+ .setRealm(realm)
+ .setUser(user)
+ .sendVerifyEmail(link, TimeUnit.SECONDS.toMinutes(result.lifespan));
+ } catch (EmailException e) {
+ ServicesLogger.LOGGER.failedToSendEmail(e);
+ throw ErrorResponse.error("Failed to send verify email", Status.INTERNAL_SERVER_ERROR);
+ }
+
+ adminEvent.operation(OperationType.ACTION).resourcePath(session.getContext().getUri()).success();
+
+ return Response.noContent().build();
}
@GET
@@ -1098,4 +1122,57 @@ private Map> toValidatorMetadata(AttributeMetadata a
.filter(avm -> (Validators.validator(session, avm.getValidatorId()) instanceof ConfiguredProvider))
.collect(Collectors.toMap(AttributeValidatorMetadata::getValidatorId, AttributeValidatorMetadata::getValidatorConfig));
}
+
+ private SendEmailParams verifySendEmailParams(String redirectUri, String clientId, Integer lifespan) {
+ if (user.getEmail() == null) {
+ throw ErrorResponse.error("User email missing", Status.BAD_REQUEST);
+ }
+
+ if (!user.isEnabled()) {
+ throw ErrorResponse.error("User is disabled", Status.BAD_REQUEST);
+ }
+
+ if (redirectUri != null && clientId == null) {
+ throw ErrorResponse.error("Client id missing", Status.BAD_REQUEST);
+ }
+
+ if (clientId == null) {
+ clientId = Constants.ACCOUNT_MANAGEMENT_CLIENT_ID;
+ }
+
+ ClientModel client = realm.getClientByClientId(clientId);
+ if (client == null) {
+ logger.debugf("Client %s doesn't exist", clientId);
+ throw ErrorResponse.error("Client doesn't exist", Status.BAD_REQUEST);
+ }
+ if (!client.isEnabled()) {
+ logger.debugf("Client %s is not enabled", clientId);
+ throw ErrorResponse.error("Client is not enabled", Status.BAD_REQUEST);
+ }
+
+ if (redirectUri != null) {
+ redirectUri = RedirectUtils.verifyRedirectUri(session, redirectUri, client);
+ if (redirectUri == null) {
+ throw ErrorResponse.error("Invalid redirect uri.", Status.BAD_REQUEST);
+ }
+ }
+
+ if (lifespan == null) {
+ lifespan = realm.getActionTokenGeneratedByAdminLifespan();
+ }
+
+ return new SendEmailParams(redirectUri, clientId, lifespan);
+ }
+
+ private static class SendEmailParams {
+ final String redirectUri;
+ final String clientId;
+ final int lifespan;
+
+ public SendEmailParams(String redirectUri, String clientId, Integer lifespan) {
+ this.redirectUri = redirectUri;
+ this.clientId = clientId;
+ this.lifespan = lifespan;
+ }
+ }
}
diff --git a/services/src/main/java/org/keycloak/services/validation/Validation.java b/services/src/main/java/org/keycloak/services/validation/Validation.java
index c4d387ca353b..fa9965459a6e 100755
--- a/services/src/main/java/org/keycloak/services/validation/Validation.java
+++ b/services/src/main/java/org/keycloak/services/validation/Validation.java
@@ -17,6 +17,7 @@
package org.keycloak.services.validation;
+import org.hazlewood.connor.bottema.emailaddress.EmailAddressValidator;
import org.keycloak.models.utils.FormMessage;
import org.keycloak.userprofile.ValidationException;
import org.keycloak.utils.EmailValidationUtil;
@@ -34,6 +35,7 @@ public class Validation {
public static final String FIELD_OTP_CODE = "totp";
public static final String FIELD_OTP_LABEL = "userLabel";
+ // Actually allow same emails like angular. See ValidationTest.testEmailValidation()
private static final Pattern USERNAME_PATTERN = Pattern.compile("^[\\p{IsLatin}|\\p{IsCommon}]+$");
private static void addError(List errors, String field, String message, Object... parameters){
@@ -42,17 +44,17 @@ private static void addError(List errors, String field, String mess
/**
* Check if string is empty (null or lenght is 0)
- *
+ *
* @param s to check
* @return true if string is empty
*/
public static boolean isEmpty(String s) {
return s == null || s.length() == 0;
}
-
+
/**
* Check if string is blank (null or lenght is 0 or contains only white characters)
- *
+ *
* @param s to check
* @return true if string is blank
*/
@@ -61,7 +63,7 @@ public static boolean isBlank(String s) {
}
public static boolean isEmailValid(String email) {
- return EmailValidationUtil.isValidEmail(email);
+ return EmailAddressValidator.isValidStrict(email);
}
public static boolean isUsernameValid(String username) {