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) {