From c896756971a5bd7c8c5df7b43b7abf633270d661 Mon Sep 17 00:00:00 2001 From: stianst Date: Mon, 4 Mar 2024 12:43:55 +0100 Subject: [PATCH 001/158] Delete broken images from release notes Signed-off-by: stianst --- .../images/new-account-console.png | Bin 218826 -> 0 bytes .../images/new-welcome-screen.png | Bin 514303 -> 0 bytes .../release_notes/topics/24_0_0.adoc | 6 ------ 3 files changed, 6 deletions(-) delete mode 100644 docs/documentation/release_notes/images/new-account-console.png delete mode 100644 docs/documentation/release_notes/images/new-welcome-screen.png diff --git a/docs/documentation/release_notes/images/new-account-console.png b/docs/documentation/release_notes/images/new-account-console.png deleted file mode 100644 index 5a0beda4063c5ee54696b47fb297997ffd3ca8dc..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 218826 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 TqjCxb|F&=4^+(#~gMa@IoGygw diff --git a/docs/documentation/release_notes/images/new-welcome-screen.png b/docs/documentation/release_notes/images/new-welcome-screen.png deleted file mode 100644 index fffe4563d3a40e7b44baa2de42e3432be2db15cd..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 514303 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%0D+h0S4c zIXDV3;x5YTCHgbu`B#r{)n%hq(;u^53*Sd;jg>ck)b44ltrqhSdF&Fp^#-GVhC{a;SF)-#*SyAiL z_Tnun?6ZRlp7P*ugo0w*hqvtX+D?y}?L-WB6(Zvfud^)!uNJY87mihiz1Z*KVUz?^ zbeLnoX9j);O>do^(^;P*EP%T;5BQFIf6sZIccSn2qJEtBDwAE?qX*wAc&MEUs$rp8 zEAyT*Dl|4oZ!IUz7nFCuns@R0hwe3NIVv(nX^Y*Icl5K<@^jc<g+EL> zC!L=pG#Vhl0uE-saTn`H$cXKJvEj`Z2j$0qU-vYlcoDK9@AzcbO0SQbTfhWuFq zG&sH(6W!boSnHn(#FH7Zf5D6=LbV@FYFj3_q}>G)+m1z-S}E-11|B6NZ6Hr;&ItXa zE!O|BHJi1gm%02pJBj>sNjc@XQSRae>h_spN$1xo*Lted|cWb+r3a;P+2d$dmJ=c)8;fwjM*8VJ`?3vv+Oo z*EcGL(GKL&zFA~K5A7u)r+K^S0ru|xvhLlsA*iMw2C zvFvQltn4CtG!!RvoRdFQ(rGPltkczlusas5fi4ygu*Zzfsll8_&qoPlo*O4kG1(*(^E0(&wWxcaE)iXB6p_Psr=+Sd2 zA^ah;6^Pp46=nIt@TSTrwR!(V>eb)L3RedVGHb_>S&k-Bp<<{BQQ%l5{7X@^-S3Tg z+$R4UPhw)?u~+-RDWpS=iQtJ_7#1ql!`k~2m{@1XA#5E-o2eRNCLG^g^;k}WUe-I> zAfJUv`hf7(&GyQ(q_%rv@{_l>&t0%q|C(m^_+P#Vq&i*dHyA8e66-NeDfu21KBc0O zVPYMLM1j`=Di_dAm#M-W`=&%KS#0$UKp66{)c{V4*Y%W{-eop_W`&>g4Q(2Q0a=Sl zX#y>Ozj@qkl=mFX`a+M;fM;Zoji~HQk#%LvgScHAAQ^5fib0H4UaZoeN7s8F z`q4iR#K8XB3oywQjFct8nQhIVP_4bp@@Cmy5efP=&xYoef15cQorl?C;km*_c6C5M zWckx`G63KZ{?MGh{G)o=*#2529SwJ^t;%IQFP}6c157($9 z@)k2GTh)JsnD24FI$9ZIF;>3fi|)@7MJ0mUF1?ortwt)P^oY?J^w}8f-FvfIgoJ8BF=Ze(|cz zidF5Q>ET%PohzhME@x61OXXs8vS4-04{wf{6f zHm2HfC-~e!#UDk){r2e?e5%X#pu}(?*KS4EB5e|U_i{wflcOA@{7E#Qp|hF;!D=wq zedHTNvVYri4n=nii`fe3wrSwg753Z<0-~ACpqG!>C$oEPU&kv%d{0l{-??uhS#%$F zd(z4@SdYJjbUk;XQGI%_`93?n%W1dp%V@@!qW>l_IBd>sIY$F7c-E4-Ad zPw{W3k?3daV8IxlyOQ+KMlmSecDarZ`&^bzrn}8SLL|3DOlEVZe!Y?pZ!kKe=SqI` z)z{qF`2F9`@NAOaqE?zS_IipHnA1IGOaq&PPE?@5C&)s+K2m8mVkYuGc@+HZKSiut z(Mek6w)5`4{pdDeudT6raS)8yk@?kmq-N3V;fKCF75G4Ay}NHZ-A%uD{QMrPL0M!~ z)MZlsc1!I|8{KwG2PLuB68OC#1TOH27UN?|OG`^#KzgDbTqXR?Z_anyUQ02N}9|v_A|(2*Lzj( zEHx5hw|rP+ft2%^Uhlr0o<4Iub7?mtw|mcq>WVGe6ZehlF8`tuAyy?fT`h3z9boEW z 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
hj*Zx^HR_v6*S^QfG8QC$S!4%fYl=(!i9x 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 diff --git a/docs/documentation/release_notes/topics/24_0_0.adoc b/docs/documentation/release_notes/topics/24_0_0.adoc index 23d7f61c7246..edcb1de0ad56 100644 --- a/docs/documentation/release_notes/topics/24_0_0.adoc +++ b/docs/documentation/release_notes/topics/24_0_0.adoc @@ -96,9 +96,6 @@ endif::[] The 'welcome' page that appears at the first use of {project_name} is redesigned. It provides a better setup experience and conforms to the latest version of https://www.patternfly.org/[PatternFly]. The simplified page layout includes only a form to register the first administrative user. After completing the registration, the user is sent directly to the Admin Console. -.New welcome page with a simplified layout and registration form -image::images/new-welcome-screen.png[New welcome page with a simplified layout and registration form] - If you use a custom theme, you may need to update it to support the new welcome page. For details, see the link:{upgradingguide_link}[{upgradingguide_name}]. = New Account Console now the default @@ -107,9 +104,6 @@ We introduced version 3 of the Account Console in {project_name} 22 as a preview This new version has built-in support for the user profile feature, which allows administrators to configure which attributes are available to users in the Account Console, and lands a user directly on their personal account page after logging in. -.New Account Console with custom attributes -image::images/new-account-console.png[New Account Console with custom attributes] - If you are using or extending the customization features of this theme, you may need to perform additional migrations. For more details, see the link:{upgradingguide_link}[{upgradingguide_name}]. = Keycloak JS From 5cecc8e3054b6e0e1e40f4a938aee7fa6f2a42bc Mon Sep 17 00:00:00 2001 From: Stian Thorgersen Date: Tue, 5 Mar 2024 08:58:57 +0100 Subject: [PATCH 002/158] Added release notes for 24.0.1 (#27524) (#27526) Signed-off-by: stianst --- docs/documentation/release_notes/index.adoc | 3 +++ .../release_notes/topics/24_0_1.adoc | 21 +++++++++++++++++++ 2 files changed, 24 insertions(+) create mode 100644 docs/documentation/release_notes/topics/24_0_1.adoc diff --git a/docs/documentation/release_notes/index.adoc b/docs/documentation/release_notes/index.adoc index 5195032a73dd..0480468bfaf7 100644 --- a/docs/documentation/release_notes/index.adoc +++ b/docs/documentation/release_notes/index.adoc @@ -13,6 +13,9 @@ include::topics/templates/document-attributes.adoc[] :release_header_latest_link: {releasenotes_link_latest} include::topics/templates/release-header.adoc[] +== {project_name_full} 24.0.1 +include::topics/24_0_1.adoc[leveloffset=2] + == {project_name_full} 24.0.0 include::topics/24_0_0.adoc[leveloffset=2] diff --git a/docs/documentation/release_notes/topics/24_0_1.adoc b/docs/documentation/release_notes/topics/24_0_1.adoc new file mode 100644 index 000000000000..dd648e7eaad1 --- /dev/null +++ b/docs/documentation/release_notes/topics/24_0_1.adoc @@ -0,0 +1,21 @@ += Operator deploys nightly build instead of 24.0.0 + +Due to an issue in the release process when deploying Keycloak using the Operator it installed the `nightly` container +instead of `24.0.0`. + +As a quick fix to the issue, the `24.0.0` container was tagged with `nightly`, and the `nightly` releases was temporarily +disabled. + +If you installed or upgraded to `24.0.0` using the Operator before 5pm CET yesterday the database may have been updated +with the wrong versions. To check if you are affected connect to your database and run the following SQL command: + +``` +SELECT * from migration_model WHERE version = '999.0.0'; +``` + +If the above returns a matching row you will need to take some actions, otherwise database migrations will not run for +future releases. To resolve this run the following SQL command: + +``` +UPDATE migration_model SET version = '24.0.0' WHERE version = '999.0.0'; +``` \ No newline at end of file From bf805c3eef78dcdbf1a42d340424007ba25a3d02 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vojt=C4=9Bch=20Mare=C5=A1?= Date: Mon, 4 Mar 2024 16:12:29 +0100 Subject: [PATCH 003/158] docs(cpu and memory sizing): typo GB -> MB Closes #27504 Signed-off-by: Vojtech Mares --- .../high-availability/concepts-memory-and-cpu-sizing.adoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/guides/high-availability/concepts-memory-and-cpu-sizing.adoc b/docs/guides/high-availability/concepts-memory-and-cpu-sizing.adoc index d2f62ae2f256..f6ed19aae000 100644 --- a/docs/guides/high-availability/concepts-memory-and-cpu-sizing.adoc +++ b/docs/guides/high-availability/concepts-memory-and-cpu-sizing.adoc @@ -76,7 +76,7 @@ Limits calculated: + (1000 MB base memory plus 250 MB RAM for 50,000 active sessions) -* Memory limit: 1360 GB +* Memory limit: 1360 MB + (1250 MB expected memory usage minus 300 non-heap-usage, divided by 0.7) From dfc4274a00fc7c137de259725cf03b3454959c1e Mon Sep 17 00:00:00 2001 From: Tomas Ondrusko <67582554+tondrusk@users.noreply.github.com> Date: Tue, 5 Mar 2024 10:22:51 +0100 Subject: [PATCH 004/158] Update disabled feature status code in social login tests Closes #27366 Signed-off-by: Tomas Ondrusko (cherry picked from commit 9404b888d1f85184d95110d7019f0b5632222822) --- .../java/org/keycloak/testsuite/broker/SocialLoginTest.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/SocialLoginTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/SocialLoginTest.java index 85f8ebe4e1c0..7a155855de7a 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/SocialLoginTest.java +++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/SocialLoginTest.java @@ -643,7 +643,7 @@ protected void testTokenExchange() { Assert.assertEquals(1, users.size()); String username = users.get(0).getUsername(); - checkFeature(501, username); + checkFeature(400, username); testingClient.enableFeature(Profile.Feature.TOKEN_EXCHANGE); @@ -730,7 +730,7 @@ protected void testTokenExchange() { } finally { httpClient.close(); testingClient.disableFeature(Profile.Feature.TOKEN_EXCHANGE); - checkFeature(501, username); + checkFeature(400, username); } } } From 221daf39857f9c0fb71e56b889e62ac48e24f244 Mon Sep 17 00:00:00 2001 From: Alexander Schwartz Date: Tue, 5 Mar 2024 12:11:42 +0100 Subject: [PATCH 005/158] URL change as liquibase.org now redirects Closes #27540 Signed-off-by: Alexander Schwartz --- docs/documentation/server_development/topics/extensions.adoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/documentation/server_development/topics/extensions.adoc b/docs/documentation/server_development/topics/extensions.adoc index d636daf63427..fcb844da08a9 100644 --- a/docs/documentation/server_development/topics/extensions.adoc +++ b/docs/documentation/server_development/topics/extensions.adoc @@ -144,7 +144,7 @@ EntityManager em = session.getProvider(JpaConnectionProvider.class).getEntityMan Company myCompany = em.find(Company.class, "123"); ---- -The methods `getChangelogLocation` and `getFactoryId` are important to support automatic updating of your entities by Liquibase. https://www.liquibase.org/[Liquibase] +The methods `getChangelogLocation` and `getFactoryId` are important to support automatic updating of your entities by Liquibase. https://www.liquibase.com/community[Liquibase] is a framework for updating the database schema, which {project_name} internally uses to create the DB schema and update the DB schema among versions. You may need to use it as well and create a changelog for your entities. Note that versioning of your own Liquibase changelog is independent of {project_name} versions. In other words, when you update to a new {project_name} version, you are not forced to update your From 2d3ff421a11891f118306fa047e2e30e0126b71b Mon Sep 17 00:00:00 2001 From: Alexander Schwartz Date: Wed, 6 Mar 2024 13:11:57 +0100 Subject: [PATCH 006/158] Add multi-site active-passive support to the release notes (#27599) Closes #27573 Signed-off-by: Alexander Schwartz --- docs/documentation/release_notes/topics/23_0_0.adoc | 2 +- docs/documentation/release_notes/topics/24_0_0.adoc | 9 ++++++++- .../topics/templates/document-attributes.adoc | 2 ++ 3 files changed, 11 insertions(+), 2 deletions(-) diff --git a/docs/documentation/release_notes/topics/23_0_0.adoc b/docs/documentation/release_notes/topics/23_0_0.adoc index 1de4a9688772..c2d5a1cc8a97 100644 --- a/docs/documentation/release_notes/topics/23_0_0.adoc +++ b/docs/documentation/release_notes/topics/23_0_0.adoc @@ -80,7 +80,7 @@ Deploying {project_name} to multiple independent sites is essential for some env This release adds preview-support for active-passive deployments for {project_name}. A lot of work has gone into testing and verifying a setup which can sustain load and recover from the failure scenarios. -To get started, use the https://www.keycloak.org/guides#high-availability[high-availability guide] which also includes a comprehensive blueprint to deploy a highly available {project_name} to a cloud environment. +To get started, use the link:{highavailabilityguide_link}[{highavailabilityguide_name}] which also includes a comprehensive blueprint to deploy a highly available {project_name} to a cloud environment. = Adapters diff --git a/docs/documentation/release_notes/topics/24_0_0.adoc b/docs/documentation/release_notes/topics/24_0_0.adoc index edcb1de0ad56..8ca20b429c58 100644 --- a/docs/documentation/release_notes/topics/24_0_0.adoc +++ b/docs/documentation/release_notes/topics/24_0_0.adoc @@ -444,4 +444,11 @@ ifeval::[{project_community}==true] With sunsetting of the https://github.com/mp911de/logstash-gelf[underlying library] providing integration with GELF, Keycloak will no longer support the GELF log handler out-of-the-box. This feature will be removed in a future release. If you require an external log management, consider using file log parsing. -endif::[] \ No newline at end of file +endif::[] + += Support for multi-site active-passive deployments + +Deploying {project_name} to multiple independent sites is essential for some environments to provide high availability and a speedy recovery from failures. +This release supports active-passive deployments for {project_name}. + +To get started, use the link:{highavailabilityguide_link}[{highavailabilityguide_name}] which also includes a comprehensive blueprint to deploy a highly available {project_name} to a cloud environment. diff --git a/docs/documentation/topics/templates/document-attributes.adoc b/docs/documentation/topics/templates/document-attributes.adoc index 8fa0d6842aa8..a10366439be1 100644 --- a/docs/documentation/topics/templates/document-attributes.adoc +++ b/docs/documentation/topics/templates/document-attributes.adoc @@ -71,6 +71,8 @@ :gettingstarted_name_short: Getting Started :gettingstarted_link: https://www.keycloak.org/guides#getting-started :gettingstarted_link_latest: https://www.keycloak.org/guides#getting-started +:highavailabilityguide_name: High Availability Guide +:highavailabilityguide_link: https://www.keycloak.org/guides#high-availability :upgradingguide_name: Upgrading Guide :upgradingguide_name_short: Upgrading :upgradingguide_link: {project_doc_base_url}/upgrading/ From a473ae5717c867bfebb837f9d3303deb717dcf90 Mon Sep 17 00:00:00 2001 From: Stu Tomlinson Date: Tue, 5 Mar 2024 13:09:38 +0000 Subject: [PATCH 007/158] keycloak-model-legacy is deprecated, not removed Closes #27529 Signed-off-by: Stu Tomlinson Signed-off-by: Alexander Schwartz Co-authored-by: Alexander Schwartz --- pom.xml | 5 +++++ quarkus/runtime/pom.xml | 10 ++++++++++ 2 files changed, 15 insertions(+) diff --git a/pom.xml b/pom.xml index 45798d7c73c3..3c913366c0c5 100644 --- a/pom.xml +++ b/pom.xml @@ -1218,6 +1218,11 @@ keycloak-model-jpa ${project.version} + + org.keycloak + keycloak-model-legacy + ${project.version} + org.keycloak keycloak-model-storage diff --git a/quarkus/runtime/pom.xml b/quarkus/runtime/pom.xml index 0b5cf28e074b..24494b6e03fa 100644 --- a/quarkus/runtime/pom.xml +++ b/quarkus/runtime/pom.xml @@ -247,6 +247,16 @@ + + org.keycloak + keycloak-model-legacy + + + * + * + + + org.keycloak keycloak-model-storage-private From d4a402736e368b1946840a9b2f9a69aa5d0ecd66 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=A1clav=20Muzik=C3=A1=C5=99?= Date: Wed, 6 Mar 2024 17:46:21 +0100 Subject: [PATCH 008/158] Clarify format of keys in `additionalOptions` field in the Keycloak CR (#27435) (#27618) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Closes #27433 Signed-off-by: Václav Muzikář (cherry picked from commit 43727aa10f469b78716e1e16a4e1f29a8530ebd6) --- docs/guides/operator/advanced-configuration.adoc | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/guides/operator/advanced-configuration.adoc b/docs/guides/operator/advanced-configuration.adoc index aa090d1922a9..8aea9c296c3f 100644 --- a/docs/guides/operator/advanced-configuration.adoc +++ b/docs/guides/operator/advanced-configuration.adoc @@ -98,6 +98,8 @@ spec: - name: spi-email-template-mycustomprovider-enabled value: true # plain text value ---- +NOTE: The name format of options defined in this way is identical to the key format of options specified in the configuration file. + For details on various configuration formats, see <@links.server id="configuration"/>. === Secret References From 3fbb11727116f73fd38a5d0e0e5588ff821379f2 Mon Sep 17 00:00:00 2001 From: Theresa Henze Date: Wed, 19 Jul 2023 17:15:53 +0200 Subject: [PATCH 009/158] trigger REMOVE_TOTP event on removal of an OTP credential Closes #15403 Signed-off-by: Theresa Henze --- .../java/org/keycloak/events/Details.java | 1 + .../account/AccountCredentialResource.java | 16 +++- .../resources/account/AccountRestService.java | 2 +- .../account/AccountRestServiceTest.java | 76 ++++++++++++++++++- 4 files changed, 91 insertions(+), 4 deletions(-) diff --git a/server-spi-private/src/main/java/org/keycloak/events/Details.java b/server-spi-private/src/main/java/org/keycloak/events/Details.java index db2db25c4e47..857487b80f6a 100755 --- a/server-spi-private/src/main/java/org/keycloak/events/Details.java +++ b/server-spi-private/src/main/java/org/keycloak/events/Details.java @@ -87,6 +87,7 @@ public interface Details { String CREDENTIAL_TYPE = "credential_type"; String SELECTED_CREDENTIAL_ID = "selected_credential_id"; String AUTHENTICATION_ERROR_DETAIL = "authentication_error_detail"; + String CREDENTIAL_USER_LABEL = "credential_user_label"; String NOT_BEFORE = "not_before"; String NUM_FAILURES = "num_failures"; diff --git a/services/src/main/java/org/keycloak/services/resources/account/AccountCredentialResource.java b/services/src/main/java/org/keycloak/services/resources/account/AccountCredentialResource.java index 878687730b85..5dc87e281c41 100644 --- a/services/src/main/java/org/keycloak/services/resources/account/AccountCredentialResource.java +++ b/services/src/main/java/org/keycloak/services/resources/account/AccountCredentialResource.java @@ -11,12 +11,16 @@ import org.keycloak.credential.CredentialProviderFactory; import org.keycloak.credential.CredentialTypeMetadata; import org.keycloak.credential.CredentialTypeMetadataContext; +import org.keycloak.events.Details; +import org.keycloak.events.EventBuilder; +import org.keycloak.events.EventType; import org.keycloak.models.AccountRoles; import org.keycloak.models.AuthenticationExecutionModel; import org.keycloak.models.AuthenticationFlowModel; import org.keycloak.models.KeycloakSession; import org.keycloak.models.RealmModel; import org.keycloak.models.UserModel; +import org.keycloak.models.credential.OTPCredentialModel; import org.keycloak.models.utils.ModelToRepresentation; import org.keycloak.representations.account.CredentialMetadataRepresentation; import org.keycloak.representations.idm.CredentialRepresentation; @@ -61,11 +65,13 @@ public class AccountCredentialResource { private final UserModel user; private final RealmModel realm; private Auth auth; + private final EventBuilder event; - public AccountCredentialResource(KeycloakSession session, UserModel user, Auth auth) { + public AccountCredentialResource(KeycloakSession session, UserModel user, Auth auth, EventBuilder event) { this.session = session; this.user = user; this.auth = auth; + this.event = event; realm = session.getContext().getRealm(); } @@ -297,11 +303,17 @@ public void removeCredential(final @PathParam("credentialId") String credentialI user.credentialManager().disableCredentialType(credentialType); return; } - throw new NotFoundException("Credential not found"); } checkIfCanBeRemoved(credential.getType()); user.credentialManager().removeStoredCredentialById(credentialId); + + if (OTPCredentialModel.TYPE.equals(credential.getType())) { + event.event(EventType.REMOVE_TOTP) + .detail(Details.SELECTED_CREDENTIAL_ID, credentialId) + .detail(Details.CREDENTIAL_USER_LABEL, credential.getUserLabel()); + event.success(); + } } diff --git a/services/src/main/java/org/keycloak/services/resources/account/AccountRestService.java b/services/src/main/java/org/keycloak/services/resources/account/AccountRestService.java index eb7e3a0d5c3a..a49a57fcb746 100755 --- a/services/src/main/java/org/keycloak/services/resources/account/AccountRestService.java +++ b/services/src/main/java/org/keycloak/services/resources/account/AccountRestService.java @@ -214,7 +214,7 @@ public SessionResource sessions() { @Path("/credentials") public AccountCredentialResource credentials() { checkAccountApiEnabled(); - return new AccountCredentialResource(session, user, auth); + return new AccountCredentialResource(session, user, auth, event); } @Path("/resources") diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/account/AccountRestServiceTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/account/AccountRestServiceTest.java index 2d5c53147689..d5ba7b1ad639 100755 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/account/AccountRestServiceTest.java +++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/account/AccountRestServiceTest.java @@ -44,6 +44,7 @@ import org.keycloak.models.credential.PasswordCredentialModel; import org.keycloak.models.credential.WebAuthnCredentialModel; import org.keycloak.models.utils.DefaultAuthenticationFlows; +import org.keycloak.models.utils.ModelToRepresentation; import org.keycloak.representations.account.ClientRepresentation; import org.keycloak.representations.account.ConsentRepresentation; import org.keycloak.representations.account.ConsentScopeRepresentation; @@ -789,6 +790,79 @@ public void testCRUDCredentialOfDifferentUser() throws IOException { Assert.assertTrue(ObjectUtil.isEqualOrBothNull(otpCredential.getUserLabel(), otpCredentialLoaded.getUserLabel())); } + @Test + public void testRemoveCredentialWithNonOtpCredentialTriggeringNoEvent() throws IOException { + + List credentials = getCredentials(); + + UserResource user = ApiUtil.findUserByUsernameId(testRealm(), "test-user@localhost"); + assertEquals(1, user.credentials().size()); + + // Add non-OTP credential to the user through admin REST API + CredentialRepresentation nonOtpCredential = ModelToRepresentation.toRepresentation( + WebAuthnCredentialModel.create(WebAuthnCredentialModel.TYPE_TWOFACTOR, "foo", "foo", "foo", "foo", "foo", 2L, "foo")); + org.keycloak.representations.idm.UserRepresentation userRep = UserBuilder.edit(user.toRepresentation()) + .secret(nonOtpCredential) + .build(); + user.update(userRep); + + credentials = getCredentials(); + Assert.assertEquals(2, credentials.size()); + Assert.assertTrue(credentials.get(1).isRemoveable()); + + // Remove credential + CredentialRepresentation credential = user.credentials().stream() + .filter(credentialRep -> WebAuthnCredentialModel.TYPE_TWOFACTOR.equals(credentialRep.getType())) + .findFirst() + .get(); + Assert.assertNotNull(credential); + user.removeCredential(credential.getId()); + + events.poll(); + events.assertEmpty(); + } + + @Test + public void testRemoveCredentialWithOtpCredentialTriggeringEvent() throws IOException { + + List credentials = getCredentials(); + + UserResource user = ApiUtil.findUserByUsernameId(testRealm(), "test-user@localhost"); + assertEquals(1, user.credentials().size()); + + // Add OTP credential to the user through admin REST API + org.keycloak.representations.idm.UserRepresentation userRep = UserBuilder.edit(user.toRepresentation()) + .totpSecret("totpSecret") + .build(); + userRep.getCredentials().get(0).setUserLabel("totpCredentialUserLabel"); + user.update(userRep); + + credentials = getCredentials(); + Assert.assertEquals(2, credentials.size()); + Assert.assertTrue(credentials.get(1).isRemoveable()); + + // Remove credential + CredentialRepresentation otpCredential = user.credentials().stream() + .filter(credentialRep -> OTPCredentialModel.TYPE.equals(credentialRep.getType())) + .findFirst() + .get(); + SimpleHttp.Response response = SimpleHttp + .doDelete(getAccountUrl("credentials/" + otpCredential.getId()), httpClient) + .acceptJson() + .auth(tokenUtil.getToken()) + .asResponse(); + assertEquals(204, response.getStatus()); + + events.poll(); + events.expect(EventType.REMOVE_TOTP) + .client("account") + .user(user.toRepresentation().getId()) + .detail(Details.SELECTED_CREDENTIAL_ID, otpCredential.getId()) + .detail(Details.CREDENTIAL_USER_LABEL, "totpCredentialUserLabel") + .assertEvent(); + events.assertEmpty(); + } + // Send REST request to get all credential containers and credentials of current user private List getCredentials() throws IOException { return SimpleHttp.doGet(getAccountUrl("credentials"), httpClient) @@ -1688,7 +1762,7 @@ public void testCustomAccountResourceTheme() throws Exception { testRealm().update(realmRep); } } - + @EnableFeature(Profile.Feature.UPDATE_EMAIL) public void testEmailWhenUpdateEmailEnabled() throws Exception { reconnectAdminClient(); From 33d79c87d2c937237de4c27ac6f7fd3b30923600 Mon Sep 17 00:00:00 2001 From: Jon Koops Date: Thu, 7 Mar 2024 11:01:09 +0100 Subject: [PATCH 010/158] added better default value (#27594) (#27650) fixes: #27548 Signed-off-by: Erik Jan de Wit (cherry picked from commit c6210b773f971bfe2f6016f3554b2f5ff4aac3e2) Co-authored-by: Erik Jan de Wit --- js/apps/admin-ui/src/authentication/form/CreateFlow.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/apps/admin-ui/src/authentication/form/CreateFlow.tsx b/js/apps/admin-ui/src/authentication/form/CreateFlow.tsx index 798d142ca11e..3c632f7650eb 100644 --- a/js/apps/admin-ui/src/authentication/form/CreateFlow.tsx +++ b/js/apps/admin-ui/src/authentication/form/CreateFlow.tsx @@ -69,7 +69,7 @@ export default function CreateFlow() { label={t("flowType")} labelIcon={t("topLevelFlowTypeHelp")} aria-label={t("selectFlowType")} - controller={{ defaultValue: "" }} + controller={{ defaultValue: TYPES[0] }} options={TYPES.map((type) => ({ key: type, value: t(`top-level-flow-type.${type}`), From 10fe36e3e8449d24a67761bb4173c5c8e8368368 Mon Sep 17 00:00:00 2001 From: Jon Koops Date: Thu, 7 Mar 2024 11:02:22 +0100 Subject: [PATCH 011/158] fixed the search attributes when federation is enabled (#27550) (#27651) fixes: #23701 Signed-off-by: Erik Jan de Wit (cherry picked from commit 4feb776d3d6ae15236d38444bfce087df1c94e88) Co-authored-by: Erik Jan de Wit --- .../src/components/users/UserDataTable.tsx | 5 ++++- .../users/UserDataTableToolbarItems.tsx | 15 ++++++++++++--- 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/js/apps/admin-ui/src/components/users/UserDataTable.tsx b/js/apps/admin-ui/src/components/users/UserDataTable.tsx index 98c92b6e247c..1db1d31baa42 100644 --- a/js/apps/admin-ui/src/components/users/UserDataTable.tsx +++ b/js/apps/admin-ui/src/components/users/UserDataTable.tsx @@ -104,6 +104,7 @@ export function UserDataTable() { const [realm, setRealm] = useState(); const [selectedRows, setSelectedRows] = useState([]); const [searchType, setSearchType] = useState("default"); + const [searchDropdownOpen, setSearchDropdownOpen] = useState(false); const [activeFilters, setActiveFilters] = useState([]); const [profile, setProfile] = useState({}); const [query, setQuery] = useState(""); @@ -153,7 +154,7 @@ export function UserDataTable() { params.search = searchParam; } - if (!listUsers && !searchParam) { + if (!listUsers && !(params.search || params.q)) { return []; } @@ -277,6 +278,8 @@ export function UserDataTable() { const toolbar = () => { return ( void; realm: RealmRepresentation; hasSelectedRows: boolean; toggleDeleteDialog: () => void; @@ -40,6 +42,8 @@ type UserDataTableToolbarItemsProps = { }; export function UserDataTableToolbarItems({ + searchDropdownOpen, + setSearchDropdownOpen, realm, hasSelectedRows, toggleDeleteDialog, @@ -59,7 +63,6 @@ export function UserDataTableToolbarItems({ }: UserDataTableToolbarItemsProps) { const { t } = useTranslation(); const [kebabOpen, setKebabOpen] = useState(false); - const [searchDropdownOpen, setSearchDropdownOpen] = useState(false); const { hasAccess } = useAccess(); @@ -130,13 +133,19 @@ export function UserDataTableToolbarItems({ setActiveFilters={setActiveFilters} profile={profile} createAttributeSearchChips={createAttributeSearchChips} - searchUserWithAttributes={searchUserWithAttributes} + searchUserWithAttributes={() => { + searchUserWithAttributes(); + setSearchDropdownOpen(false); + }} /> - )} - , - ]} - /> - - + {container.userCredentialMetadatas.map((meta) => ( + + + + {container.removeable ? ( + { + try { + await deleteCredentials( + context, + meta.credential, + ); + addAlert( + t("successRemovedMessage", { + userLabel: label(meta.credential), + }), + ); + refresh(); + } catch (error) { + addError( + t("errorRemovedMessage", { + userLabel: label(meta.credential), + error, + }).toString(), + ); + } + }} + > + {t("stopUsingCred", { + name: label(meta.credential), + })} + + ) : ( + + )} + , + ]} + /> + + + ))} + + ))} - ))} diff --git a/js/apps/account-ui/src/components/datalist/EmptyRow.tsx b/js/apps/account-ui/src/components/datalist/EmptyRow.tsx index 2a4a73d1d516..550767ae967e 100644 --- a/js/apps/account-ui/src/components/datalist/EmptyRow.tsx +++ b/js/apps/account-ui/src/components/datalist/EmptyRow.tsx @@ -9,12 +9,16 @@ type EmptyRowProps = { message: string; }; -export const EmptyRow = ({ message }: EmptyRowProps) => { +export const EmptyRow = ({ message, ...props }: EmptyRowProps) => { return ( {message}]} + dataListCells={[ + + {message} + , + ]} /> diff --git a/js/apps/account-ui/test/account-security/signing-in.spec.ts b/js/apps/account-ui/test/account-security/signing-in.spec.ts index 1a6af4c9edc2..81ce20adf301 100644 --- a/js/apps/account-ui/test/account-security/signing-in.spec.ts +++ b/js/apps/account-ui/test/account-security/signing-in.spec.ts @@ -21,26 +21,22 @@ test.describe("Signing in", () => { page.getByTestId("account-security/signing-in").click(); await expect( - page - .getByTestId("basic-authentication/credential-list") - .getByRole("listitem"), + page.getByTestId("password/credential-list").getByRole("listitem"), ).toHaveCount(1); await expect( - page - .getByTestId("basic-authentication/credential-list") - .getByRole("listitem"), + page.getByTestId("password/credential-list").getByRole("listitem"), ).toContainText("My password"); - await expect(page.getByTestId("basic-authentication/create")).toBeHidden(); + await expect(page.getByTestId("password/create")).toBeHidden(); await expect( - page.getByTestId("two-factor/credential-list").getByRole("listitem"), + page.getByTestId("otp/credential-list").getByRole("listitem"), ).toHaveCount(1); await expect( - page.getByTestId("two-factor/credential-list").getByRole("listitem"), + page.getByTestId("otp/credential-list").getByRole("listitem"), ).toContainText("not set up"); - await expect(page.getByTestId("two-factor/create")).toBeVisible(); + await expect(page.getByTestId("otp/create")).toBeVisible(); - await page.getByTestId("two-factor/create").click(); + await page.getByTestId("otp/create").click(); await expect(page.locator("#kc-page-title")).toContainText( "Mobile Authenticator Setup", ); @@ -65,26 +61,22 @@ test.describe("Signing in 2", () => { page.getByTestId("account-security/signing-in").click(); await expect( - page - .getByTestId("basic-authentication/credential-list") - .getByRole("listitem"), + page.getByTestId("password/credential-list").getByRole("listitem"), ).toHaveCount(1); await expect( - page - .getByTestId("basic-authentication/credential-list") - .getByRole("listitem"), + page.getByTestId("password/credential-list").getByRole("listitem"), ).toContainText("not set up"); - await expect(page.getByTestId("basic-authentication/create")).toBeVisible(); + await expect(page.getByTestId("password/create")).toBeVisible(); await expect( - page.getByTestId("two-factor/credential-list").getByRole("listitem"), + page.getByTestId("otp/credential-list").getByRole("listitem"), ).toHaveCount(1); await expect( - page.getByTestId("two-factor/credential-list").getByRole("listitem"), + page.getByTestId("otp/credential-list").getByRole("listitem"), ).toContainText("not set up"); - await expect(page.getByTestId("two-factor/create")).toBeVisible(); + await expect(page.getByTestId("otp/create")).toBeVisible(); - await page.getByTestId("basic-authentication/create").click(); + await page.getByTestId("password/create").click(); await expect(page.locator("#kc-page-title")).toContainText( "Update password", ); diff --git a/js/libs/ui-shared/src/continue-cancel/ContinueCancelModal.tsx b/js/libs/ui-shared/src/continue-cancel/ContinueCancelModal.tsx index 5ff5c4f8ecf2..1ac3790dc0a5 100644 --- a/js/libs/ui-shared/src/continue-cancel/ContinueCancelModal.tsx +++ b/js/libs/ui-shared/src/continue-cancel/ContinueCancelModal.tsx @@ -7,6 +7,7 @@ export type ContinueCancelModalProps = Omit & { cancelLabel: string; buttonTitle: string | ReactNode; buttonVariant?: ButtonProps["variant"]; + buttonTestRole?: string; isDisabled?: boolean; onContinue: () => void; component?: React.ElementType | React.ComponentType; @@ -20,6 +21,7 @@ export const ContinueCancelModal = ({ buttonTitle, isDisabled, buttonVariant, + buttonTestRole, onContinue, component = Button, children, @@ -34,6 +36,7 @@ export const ContinueCancelModal = ({ variant={buttonVariant} onClick={() => setOpen(true)} isDisabled={isDisabled} + data-testrole={buttonTestRole} > {buttonTitle} From 2b329a88a94e714a49937eab4cadbd9852a87eef Mon Sep 17 00:00:00 2001 From: Erik Jan de Wit Date: Tue, 12 Mar 2024 12:35:07 +0100 Subject: [PATCH 052/158] fixed merge conflict fixes: #27745 Signed-off-by: Erik Jan de Wit --- themes/src/main/resources/theme/keycloak.v2/login/register.ftl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/themes/src/main/resources/theme/keycloak.v2/login/register.ftl b/themes/src/main/resources/theme/keycloak.v2/login/register.ftl index dd27958a5963..38e864654111 100755 --- a/themes/src/main/resources/theme/keycloak.v2/login/register.ftl +++ b/themes/src/main/resources/theme/keycloak.v2/login/register.ftl @@ -1,4 +1,4 @@ -<#import "pf-5-template.ftl" as layout> +<#import "template.ftl" as layout> <#import "user-profile-commons.ftl" as userProfileCommons> <#import "register-commons.ftl" as registerCommons> <@layout.registrationLayout displayMessage=messagesPerField.exists('global') displayRequiredFields=true; section> From 3f69958a67a7ab5017d2d43006dd2bffde8f2972 Mon Sep 17 00:00:00 2001 From: Erik Jan de Wit Date: Wed, 20 Mar 2024 12:20:26 +0100 Subject: [PATCH 053/158] add beerify to account-ui (#28082) fixes: #27966 Signed-off-by: Erik Jan de Wit --- js/apps/account-ui/src/personal-info/PersonalInfo.tsx | 6 +++++- js/libs/ui-shared/src/main.ts | 1 + js/libs/ui-shared/src/user-profile/utils.ts | 3 +++ 3 files changed, 9 insertions(+), 1 deletion(-) diff --git a/js/apps/account-ui/src/personal-info/PersonalInfo.tsx b/js/apps/account-ui/src/personal-info/PersonalInfo.tsx index 6044d33c8e01..b1fae4f73c7c 100644 --- a/js/apps/account-ui/src/personal-info/PersonalInfo.tsx +++ b/js/apps/account-ui/src/personal-info/PersonalInfo.tsx @@ -13,6 +13,7 @@ import { ErrorOption, useForm } from "react-hook-form"; import { useTranslation } from "react-i18next"; import { UserProfileFields, + beerify, debeerify, setUserProfileServerError, useAlerts, @@ -39,7 +40,7 @@ export const PersonalInfo = () => { useState(); const [supportedLocales, setSupportedLocales] = useState([]); const form = useForm({ mode: "onChange" }); - const { handleSubmit, reset, setError } = form; + const { handleSubmit, reset, setValue, setError } = form; const { addAlert, addError } = useAlerts(); usePromise( @@ -52,6 +53,9 @@ export const PersonalInfo = () => { setUserProfileMetadata(personalInfo.userProfileMetadata); setSupportedLocales(supportedLocales); reset(personalInfo); + Object.entries(personalInfo.attributes || {}).forEach(([k, v]) => + setValue(`attributes[${beerify(k)}]`, v), + ); }, ); diff --git a/js/libs/ui-shared/src/main.ts b/js/libs/ui-shared/src/main.ts index 89025a5a8c51..e4bb5d2bcbd5 100644 --- a/js/libs/ui-shared/src/main.ts +++ b/js/libs/ui-shared/src/main.ts @@ -20,6 +20,7 @@ export { setUserProfileServerError, isUserProfileError, label, + beerify, debeerify, } from "./user-profile/utils"; export type { UserFormFields } from "./user-profile/utils"; diff --git a/js/libs/ui-shared/src/user-profile/utils.ts b/js/libs/ui-shared/src/user-profile/utils.ts index 2f1ae4911755..07183bcc2a4b 100644 --- a/js/libs/ui-shared/src/user-profile/utils.ts +++ b/js/libs/ui-shared/src/user-profile/utils.ts @@ -49,6 +49,9 @@ export const fieldName = (name?: string) => "🍺", )}` as FieldPath; +export const beerify = (name: T) => + name.replaceAll(".", "🍺"); + export const debeerify = (name: T) => name.replaceAll("🍺", "."); From 3fdb396ac917abd566ce8bf5d26e79f01627b31c Mon Sep 17 00:00:00 2001 From: Jon Koops Date: Wed, 20 Mar 2024 14:01:45 +0100 Subject: [PATCH 054/158] Attributes without a group should appear first (#28091) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixes #27981 Signed-off-by: René Zeidler (cherry picked from commit 83a3500ccf26f5920c976fab13e0f00a3bf56ea6) Co-authored-by: René Zeidler --- .../model/AbstractUserProfileBean.java | 2 +- ...ctionUpdateProfileWithUserProfileTest.java | 18 ++++++++--------- .../broker/KcOidcFirstBrokerLoginTest.java | 18 ++++++++--------- .../forms/RegisterWithUserProfileTest.java | 20 +++++++++---------- .../testsuite/forms/VerifyProfileTest.java | 18 ++++++++--------- 5 files changed, 38 insertions(+), 38 deletions(-) diff --git a/services/src/main/java/org/keycloak/forms/login/freemarker/model/AbstractUserProfileBean.java b/services/src/main/java/org/keycloak/forms/login/freemarker/model/AbstractUserProfileBean.java index b055e198cd14..fdab733435da 100644 --- a/services/src/main/java/org/keycloak/forms/login/freemarker/model/AbstractUserProfileBean.java +++ b/services/src/main/java/org/keycloak/forms/login/freemarker/model/AbstractUserProfileBean.java @@ -42,7 +42,7 @@ public abstract class AbstractUserProfileBean { return a1.compareTo(a2); } - return Comparator.nullsLast(AttributeGroup::compareTo).compare(g1, g2); + return Comparator.nullsFirst(AttributeGroup::compareTo).compare(g1, g2); }; protected final MultivaluedMap formData; diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/actions/RequiredActionUpdateProfileWithUserProfileTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/actions/RequiredActionUpdateProfileWithUserProfileTest.java index 2364a65dc133..7ba2f0a3494d 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/actions/RequiredActionUpdateProfileWithUserProfileTest.java +++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/actions/RequiredActionUpdateProfileWithUserProfileTest.java @@ -174,45 +174,45 @@ public void testAttributeGrouping() { updateProfilePage.assertCurrent(); String htmlFormId="kc-update-profile-form"; - //assert fields and groups location in form, attributes without a group are the last + //assert fields and groups location in form, attributes without a group appear first Assert.assertTrue( driver.findElement( - By.cssSelector("form#"+htmlFormId+" > div:nth-child(1) > div:nth-child(1) > label#header-company") + By.cssSelector("form#"+htmlFormId+" > div:nth-child(1) > div:nth-child(2) > input#lastName") ).isDisplayed() ); Assert.assertTrue( driver.findElement( - By.cssSelector("form#"+htmlFormId+" > div:nth-child(1) > div:nth-child(2) > label#description-company") + By.cssSelector("form#"+htmlFormId+" > div:nth-child(2) > div:nth-child(2) > input#username") ).isDisplayed() ); Assert.assertTrue( driver.findElement( - By.cssSelector("form#"+htmlFormId+" > div:nth-child(2) > div:nth-child(2) > input#department") + By.cssSelector("form#"+htmlFormId+" > div:nth-child(3) > div:nth-child(2) > input#firstName") ).isDisplayed() ); Assert.assertTrue( driver.findElement( - By.cssSelector("form#"+htmlFormId+" > div:nth-child(3) > div:nth-child(1) > label#header-contact") + By.cssSelector("form#"+htmlFormId+" > div:nth-child(4) > div:nth-child(1) > label#header-company") ).isDisplayed() ); Assert.assertTrue( driver.findElement( - By.cssSelector("form#"+htmlFormId+" > div:nth-child(4) > div:nth-child(2) > input#email") + By.cssSelector("form#"+htmlFormId+" > div:nth-child(4) > div:nth-child(2) > label#description-company") ).isDisplayed() ); Assert.assertTrue( driver.findElement( - By.cssSelector("form#"+htmlFormId+" > div:nth-child(5) > div:nth-child(2) > input#lastName") + By.cssSelector("form#"+htmlFormId+" > div:nth-child(5) > div:nth-child(2) > input#department") ).isDisplayed() ); Assert.assertTrue( driver.findElement( - By.cssSelector("form#"+htmlFormId+" > div:nth-child(6) > div:nth-child(2) > input#username") + By.cssSelector("form#"+htmlFormId+" > div:nth-child(6) > div:nth-child(1) > label#header-contact") ).isDisplayed() ); Assert.assertTrue( driver.findElement( - By.cssSelector("form#"+htmlFormId+" > div:nth-child(7) > div:nth-child(2) > input#firstName") + By.cssSelector("form#"+htmlFormId+" > div:nth-child(7) > div:nth-child(2) > input#email") ).isDisplayed() ); } diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/KcOidcFirstBrokerLoginTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/KcOidcFirstBrokerLoginTest.java index b265b0b2f564..f666eee26ce4 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/KcOidcFirstBrokerLoginTest.java +++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/KcOidcFirstBrokerLoginTest.java @@ -407,45 +407,45 @@ public void testAttributeGrouping() { //assert fields location in form String htmlFormId = "kc-idp-review-profile-form"; - //assert fields and groups location in form, attributes without a group are the last + //assert fields and groups location in form, attributes without a group appear first org.junit.Assert.assertTrue( driver.findElement( - By.cssSelector("form#"+htmlFormId+" > div:nth-child(1) > div:nth-child(1) > label#header-company") + By.cssSelector("form#"+htmlFormId+" > div:nth-child(1) > div:nth-child(2) > input#lastName") ).isDisplayed() ); org.junit.Assert.assertTrue( driver.findElement( - By.cssSelector("form#"+htmlFormId+" > div:nth-child(1) > div:nth-child(2) > label#description-company") + By.cssSelector("form#"+htmlFormId+" > div:nth-child(2) > div:nth-child(2) > input#username") ).isDisplayed() ); org.junit.Assert.assertTrue( driver.findElement( - By.cssSelector("form#"+htmlFormId+" > div:nth-child(2) > div:nth-child(2) > input#department") + By.cssSelector("form#"+htmlFormId+" > div:nth-child(3) > div:nth-child(2) > input#firstName") ).isDisplayed() ); org.junit.Assert.assertTrue( driver.findElement( - By.cssSelector("form#"+htmlFormId+" > div:nth-child(3) > div:nth-child(1) > label#header-contact") + By.cssSelector("form#"+htmlFormId+" > div:nth-child(4) > div:nth-child(1) > label#header-company") ).isDisplayed() ); org.junit.Assert.assertTrue( driver.findElement( - By.cssSelector("form#"+htmlFormId+" > div:nth-child(4) > div:nth-child(2) > input#email") + By.cssSelector("form#"+htmlFormId+" > div:nth-child(4) > div:nth-child(2) > label#description-company") ).isDisplayed() ); org.junit.Assert.assertTrue( driver.findElement( - By.cssSelector("form#"+htmlFormId+" > div:nth-child(5) > div:nth-child(2) > input#lastName") + By.cssSelector("form#"+htmlFormId+" > div:nth-child(5) > div:nth-child(2) > input#department") ).isDisplayed() ); org.junit.Assert.assertTrue( driver.findElement( - By.cssSelector("form#"+htmlFormId+" > div:nth-child(6) > div:nth-child(2) > input#username") + By.cssSelector("form#"+htmlFormId+" > div:nth-child(6) > div:nth-child(1) > label#header-contact") ).isDisplayed() ); org.junit.Assert.assertTrue( driver.findElement( - By.cssSelector("form#"+htmlFormId+" > div:nth-child(7) > div:nth-child(2) > input#firstName") + By.cssSelector("form#"+htmlFormId+" > div:nth-child(7) > div:nth-child(2) > input#email") ).isDisplayed() ); } diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/forms/RegisterWithUserProfileTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/forms/RegisterWithUserProfileTest.java index d0c4a84954ea..8d8ccd88579d 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/forms/RegisterWithUserProfileTest.java +++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/forms/RegisterWithUserProfileTest.java @@ -442,46 +442,46 @@ public void testAttributeGrouping() { registerPage.assertCurrent(); String htmlFormId="kc-register-form"; - //assert fields and groups location in form, attributes without a group are the last + //assert fields and groups location in form, attributes without a group appear first Assert.assertTrue( driver.findElement( - By.cssSelector("form#"+htmlFormId+" > div:nth-child(1) > div:nth-child(1) > label#header-company") + By.cssSelector("form#"+htmlFormId+" > div:nth-child(1) > div:nth-child(2) > input#lastName") ).isDisplayed() ); Assert.assertTrue( driver.findElement( - By.cssSelector("form#"+htmlFormId+" > div:nth-child(1) > div:nth-child(2) > label#description-company") + By.cssSelector("form#"+htmlFormId+" > div:nth-child(2) > div:nth-child(2) > input#username") ).isDisplayed() ); + // password and password confirmation fields appear after the username field, in positions 3 and 4 Assert.assertTrue( driver.findElement( - By.cssSelector("form#"+htmlFormId+" > div:nth-child(2) > div:nth-child(2) > input#department") + By.cssSelector("form#"+htmlFormId+" > div:nth-child(5) > div:nth-child(2) > input#firstName") ).isDisplayed() ); Assert.assertTrue( driver.findElement( - By.cssSelector("form#"+htmlFormId+" > div:nth-child(3) > div:nth-child(1) > label#header-contact") + By.cssSelector("form#"+htmlFormId+" > div:nth-child(6) > div:nth-child(1) > label#header-company") ).isDisplayed() ); Assert.assertTrue( driver.findElement( - By.cssSelector("form#"+htmlFormId+" > div:nth-child(4) > div:nth-child(2) > input#email") + By.cssSelector("form#"+htmlFormId+" > div:nth-child(6) > div:nth-child(2) > label#description-company") ).isDisplayed() ); Assert.assertTrue( driver.findElement( - By.cssSelector("form#"+htmlFormId+" > div:nth-child(5) > div:nth-child(2) > input#lastName") + By.cssSelector("form#"+htmlFormId+" > div:nth-child(7) > div:nth-child(2) > input#department") ).isDisplayed() ); Assert.assertTrue( driver.findElement( - By.cssSelector("form#"+htmlFormId+" > div:nth-child(6) > div:nth-child(2) > input#username") + By.cssSelector("form#"+htmlFormId+" > div:nth-child(8) > div:nth-child(1) > label#header-contact") ).isDisplayed() ); - // firstname order is after username, so it will render after password and password confirmation fields Assert.assertTrue( driver.findElement( - By.cssSelector("form#"+htmlFormId+" > div:nth-child(9) > div:nth-child(2) > input#firstName") + By.cssSelector("form#"+htmlFormId+" > div:nth-child(9) > div:nth-child(2) > input#email") ).isDisplayed() ); } diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/forms/VerifyProfileTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/forms/VerifyProfileTest.java index e99342b9a202..5c60803e2655 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/forms/VerifyProfileTest.java +++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/forms/VerifyProfileTest.java @@ -220,45 +220,45 @@ public void testAttributeGrouping() { verifyProfilePage.assertCurrent(); String htmlFormId="kc-update-profile-form"; - //assert fields and groups location in form, attributes without a group are the last + //assert fields and groups location in form, attributes without a group appear first Assert.assertTrue( driver.findElement( - By.cssSelector("form#"+htmlFormId+" > div:nth-child(1) > div:nth-child(1) > label#header-company") + By.cssSelector("form#"+htmlFormId+" > div:nth-child(1) > div:nth-child(2) > input#lastName") ).isDisplayed() ); Assert.assertTrue( driver.findElement( - By.cssSelector("form#"+htmlFormId+" > div:nth-child(1) > div:nth-child(2) > label#description-company") + By.cssSelector("form#"+htmlFormId+" > div:nth-child(2) > div:nth-child(2) > input#username") ).isDisplayed() ); Assert.assertTrue( driver.findElement( - By.cssSelector("form#"+htmlFormId+" > div:nth-child(2) > div:nth-child(2) > input#department") + By.cssSelector("form#"+htmlFormId+" > div:nth-child(3) > div:nth-child(2) > input#firstName") ).isDisplayed() ); Assert.assertTrue( driver.findElement( - By.cssSelector("form#"+htmlFormId+" > div:nth-child(3) > div:nth-child(1) > label#header-contact") + By.cssSelector("form#"+htmlFormId+" > div:nth-child(4) > div:nth-child(1) > label#header-company") ).isDisplayed() ); Assert.assertTrue( driver.findElement( - By.cssSelector("form#"+htmlFormId+" > div:nth-child(4) > div:nth-child(2) > input#email") + By.cssSelector("form#"+htmlFormId+" > div:nth-child(4) > div:nth-child(2) > label#description-company") ).isDisplayed() ); Assert.assertTrue( driver.findElement( - By.cssSelector("form#"+htmlFormId+" > div:nth-child(5) > div:nth-child(2) > input#lastName") + By.cssSelector("form#"+htmlFormId+" > div:nth-child(5) > div:nth-child(2) > input#department") ).isDisplayed() ); Assert.assertTrue( driver.findElement( - By.cssSelector("form#"+htmlFormId+" > div:nth-child(6) > div:nth-child(2) > input#username") + By.cssSelector("form#"+htmlFormId+" > div:nth-child(6) > div:nth-child(1) > label#header-contact") ).isDisplayed() ); Assert.assertTrue( driver.findElement( - By.cssSelector("form#"+htmlFormId+" > div:nth-child(7) > div:nth-child(2) > input#firstName") + By.cssSelector("form#"+htmlFormId+" > div:nth-child(7) > div:nth-child(2) > input#email") ).isDisplayed() ); } From 641b2d418075f20af40fc7fbff90b2b17bc65135 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Barto=C5=A1?= Date: Wed, 20 Mar 2024 15:45:54 +0100 Subject: [PATCH 055/158] Multi datasource configuration does not work (#28051) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixes #27894 Signed-off-by: Martin Bartoš Co-authored-by: Steven Hawkins --- .../keycloak/config/database/Database.java | 4 +- .../configuration/IgnoredArtifacts.java | 37 +++++++++++++------ .../test/IgnoredArtifactsTest.java | 33 ++++++++++++++++- .../META-INF/services/quarkus.properties | 6 ++- 4 files changed, 63 insertions(+), 17 deletions(-) diff --git a/quarkus/config-api/src/main/java/org/keycloak/config/database/Database.java b/quarkus/config-api/src/main/java/org/keycloak/config/database/Database.java index e2a55fbe29f7..1b3631910694 100644 --- a/quarkus/config-api/src/main/java/org/keycloak/config/database/Database.java +++ b/quarkus/config-api/src/main/java/org/keycloak/config/database/Database.java @@ -52,9 +52,9 @@ public static boolean isLiquibaseDatabaseSupported(String databaseType, String d return false; } - public static Optional getVendorByDbKind(String dbKind) { + public static Optional getVendor(String vendor) { return Arrays.stream(Vendor.values()) - .filter(v -> v.isOfKind(dbKind)) + .filter(v -> v.isOfKind(vendor) || asList(v.aliases).contains(vendor)) .findAny(); } diff --git a/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/configuration/IgnoredArtifacts.java b/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/configuration/IgnoredArtifacts.java index aab257e3d011..e1b31f8f1fcc 100644 --- a/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/configuration/IgnoredArtifacts.java +++ b/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/configuration/IgnoredArtifacts.java @@ -120,18 +120,31 @@ private static Set fips() { .collect(Collectors.toUnmodifiableSet()); private static Set jdbcDrivers() { - final Database.Vendor vendor = Configuration.getOptionalValue("quarkus.datasource.db-kind") - .flatMap(Database::getVendorByDbKind) - .orElse(Database.Vendor.H2); - - final Set jdbcArtifacts = switch (vendor) { - case H2 -> JDBC_H2; - case MYSQL -> JDBC_MYSQL; - case MARIADB -> JDBC_MARIADB; - case POSTGRES -> JDBC_POSTGRES; - case MSSQL -> JDBC_MSSQL; - case ORACLE -> JDBC_ORACLE; - }; + final Set vendorsOfAllDatasources = new HashSet<>(); + + Configuration.getConfig().getPropertyNames().forEach(p -> { + if (p.startsWith("quarkus.datasource.") && p.endsWith(".db-kind")) { + Configuration.getOptionalValue(p) + .flatMap(Database::getVendor) + .ifPresent(vendorsOfAllDatasources::add); + } + }); + + if (vendorsOfAllDatasources.isEmpty()) { + vendorsOfAllDatasources.add(Database.Vendor.H2); + } + + final Set jdbcArtifacts = vendorsOfAllDatasources.stream() + .map(vendor -> switch (vendor) { + case H2 -> JDBC_H2; + case MYSQL -> JDBC_MYSQL; + case MARIADB -> JDBC_MARIADB; + case POSTGRES -> JDBC_POSTGRES; + case MSSQL -> JDBC_MSSQL; + case ORACLE -> JDBC_ORACLE; + }) + .flatMap(Collection::stream) + .collect(Collectors.toSet()); final Set allJdbcDrivers = new HashSet<>(JDBC_DRIVERS); allJdbcDrivers.removeAll(jdbcArtifacts); diff --git a/quarkus/runtime/src/test/java/org/keycloak/quarkus/runtime/configuration/test/IgnoredArtifactsTest.java b/quarkus/runtime/src/test/java/org/keycloak/quarkus/runtime/configuration/test/IgnoredArtifactsTest.java index 072349fd80e7..7b2e5b25a6e0 100644 --- a/quarkus/runtime/src/test/java/org/keycloak/quarkus/runtime/configuration/test/IgnoredArtifactsTest.java +++ b/quarkus/runtime/src/test/java/org/keycloak/quarkus/runtime/configuration/test/IgnoredArtifactsTest.java @@ -24,12 +24,16 @@ import org.keycloak.config.DatabaseOptions; import org.keycloak.config.HealthOptions; import org.keycloak.config.MetricsOptions; +import org.keycloak.quarkus.runtime.configuration.Configuration; import org.keycloak.quarkus.runtime.configuration.IgnoredArtifacts; import org.keycloak.quarkus.runtime.configuration.MicroProfileConfigProvider; +import java.util.Collection; import java.util.HashSet; import java.util.Properties; import java.util.Set; +import java.util.stream.Collectors; +import java.util.stream.Stream; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.CoreMatchers.not; @@ -94,16 +98,41 @@ public void jdbcPostgres() { assertJdbc("postgres", JDBC_POSTGRES); } + // default ignored JDBC artifacts specified in quarkus.properties + private static final Set IGNORED_JDBC_FROM_PROPS = Stream.of(JDBC_MARIADB, JDBC_POSTGRES) + .flatMap(Collection::stream) + .collect(Collectors.toSet()); + + @Test + public void multipleDatasources() { + var defaultDS = Configuration.getOptionalValue("quarkus.datasource.db-kind"); + assertThat(defaultDS.isPresent(), is(true)); + assertThat(defaultDS.get(), is("h2")); + + var dogStoreDS = Configuration.getOptionalValue("quarkus.datasource.dog-store.db-kind"); + assertThat(dogStoreDS.isPresent(), is(true)); + assertThat(dogStoreDS.get(), is("mariadb")); + + var catStoreDS = Configuration.getOptionalValue("quarkus.datasource.cat-store.db-kind"); + assertThat(catStoreDS.isPresent(), is(true)); + assertThat(catStoreDS.get(), is("postgresql")); + + assertJdbc("h2", JDBC_H2); + } + private void assertJdbc(String vendor, Set notIgnored) { + var notIgnoredWithDefaults = new HashSet<>(notIgnored); + notIgnoredWithDefaults.addAll(IGNORED_JDBC_FROM_PROPS); + System.setProperty(MicroProfileConfigProvider.NS_KEYCLOAK_PREFIX + DatabaseOptions.DB.getKey(), vendor); try { final var resultArtifacts = IgnoredArtifacts.getDefaultIgnoredArtifacts(); assertThat(String.format("Ignored artifacts does not comply with the specified artifacts for '%s' JDBC driver", vendor), resultArtifacts, - not(CoreMatchers.hasItems(notIgnored.toArray(new String[0])))); + not(CoreMatchers.hasItems(notIgnoredWithDefaults.toArray(new String[0])))); final var includedArtifacts = new HashSet<>(IgnoredArtifacts.JDBC_DRIVERS); - includedArtifacts.removeAll(notIgnored); + includedArtifacts.removeAll(notIgnoredWithDefaults); assertThat("Ignored artifacts does not contain items for the other JDBC drivers", resultArtifacts, CoreMatchers.hasItems(includedArtifacts.toArray(new String[0]))); diff --git a/quarkus/runtime/src/test/resources/META-INF/services/quarkus.properties b/quarkus/runtime/src/test/resources/META-INF/services/quarkus.properties index 880d86225c2b..95d0635596bb 100644 --- a/quarkus/runtime/src/test/resources/META-INF/services/quarkus.properties +++ b/quarkus/runtime/src/test/resources/META-INF/services/quarkus.properties @@ -8,4 +8,8 @@ quarkus.log.category."org.infinispan.transaction.lookup.JBossStandaloneJTAManage # For test nested properties quarkus.datasource.foo = jdbc:h2:file:${kc.home.dir:${kc.db.url.path:~}}/data/keycloakdb -quarkus.datasource.bar = foo-${kc.prop3:${kc.prop4:${kc.prop5:def}-suffix}} \ No newline at end of file +quarkus.datasource.bar = foo-${kc.prop3:${kc.prop4:${kc.prop5:def}-suffix}} + +# test multiple datasources db-kind +quarkus.datasource.dog-store.db-kind=mariadb +quarkus.datasource.cat-store.db-kind=postgresql \ No newline at end of file From 04d76b20ed49071bbb74f97974a1ece7f46f6c94 Mon Sep 17 00:00:00 2001 From: Alexander Schwartz Date: Wed, 20 Mar 2024 18:16:16 +0100 Subject: [PATCH 056/158] Upgrading to Quarkus 3.8.3 (#28086) Closes #28084 Signed-off-by: Alexander Schwartz --- pom.xml | 4 ++-- testsuite/integration-arquillian/pom.xml | 7 +++++++ 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 6ecdad753415..0379b0805f1d 100644 --- a/pom.xml +++ b/pom.xml @@ -45,8 +45,8 @@ jboss-snapshots-repository https://s01.oss.sonatype.org/content/repositories/snapshots/ - 3.8.2 - 3.8.2 + 3.8.3 + 3.8.3 ${timestamp} diff --git a/testsuite/integration-arquillian/pom.xml b/testsuite/integration-arquillian/pom.xml index 3ec43f7bb981..f65553f6b695 100644 --- a/testsuite/integration-arquillian/pom.xml +++ b/testsuite/integration-arquillian/pom.xml @@ -85,6 +85,13 @@ + + org.keycloak + keycloak-parent + ${project.version} + pom + import + From c453cdd535ae3c4f2dfe1b7daa0ee5a7d6f0ef5c Mon Sep 17 00:00:00 2001 From: Pedro Igor Date: Wed, 20 Mar 2024 12:21:49 -0300 Subject: [PATCH 057/158] Do not grant scopes not granted for resources owned the resource server itself Closes #25057 Signed-off-by: Pedro Igor --- .../AuthorizationTokenService.java | 8 ++- .../testsuite/authz/EntitlementAPITest.java | 61 +++++++++++++++++++ 2 files changed, 68 insertions(+), 1 deletion(-) diff --git a/services/src/main/java/org/keycloak/authorization/authorization/AuthorizationTokenService.java b/services/src/main/java/org/keycloak/authorization/authorization/AuthorizationTokenService.java index 96ecf9e19fb4..baa8ce87420b 100644 --- a/services/src/main/java/org/keycloak/authorization/authorization/AuthorizationTokenService.java +++ b/services/src/main/java/org/keycloak/authorization/authorization/AuthorizationTokenService.java @@ -32,6 +32,7 @@ import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; import java.util.function.BiFunction; +import java.util.function.Predicate; import java.util.stream.Collectors; import jakarta.ws.rs.HttpMethod; @@ -656,7 +657,12 @@ private void resolveResourcePermission(KeycloakAuthorizationRequest request, ResourcePermission resourcePermission = addPermission(request, resourceServer, authorization, permissionsToEvaluate, limit, requestedScopesModel, grantedResource); - + if (resourcePermission != null) { + Collection permissionScopes = resourcePermission.getScopes(); + if (permissionScopes != null) { + permissionScopes.retainAll(scopes); + } + } // the permission is explicitly granted by the owner, mark this permission as granted so that we don't run the evaluation engine on it resourcePermission.setGranted(true); } diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/authz/EntitlementAPITest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/authz/EntitlementAPITest.java index f03e213fa869..cdd36b7064db 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/authz/EntitlementAPITest.java +++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/authz/EntitlementAPITest.java @@ -35,7 +35,9 @@ import java.util.List; import java.util.Map; import java.util.Set; +import java.util.function.Predicate; import java.util.function.Supplier; +import java.util.stream.Collectors; import org.apache.http.client.HttpClient; import org.apache.http.impl.client.HttpClients; @@ -2415,6 +2417,65 @@ public void testPermissionOrder() throws Exception { .getScopes().contains("entity:read"))); } + @Test + public void testSameResultRegardlessOPermissionParameterValue() throws Exception { + ClientResource client = getClient(getRealm(), RESOURCE_SERVER_TEST); + AuthorizationResource authorization = client.authorization(); + ResourceRepresentation resource = new ResourceRepresentation(); + + resource.setName(KeycloakModelUtils.generateId()); + resource.addScope("scope1", "scope2"); + resource.setOwnerManagedAccess(true); + + try (Response response = authorization.resources().create(resource)) { + resource = response.readEntity(ResourceRepresentation.class); + } + + UserPolicyRepresentation policy = new UserPolicyRepresentation(); + + policy.setName(KeycloakModelUtils.generateId()); + policy.addUser("marta"); + + authorization.policies().user().create(policy).close(); + + ScopePermissionRepresentation representation = new ScopePermissionRepresentation(); + + representation.setName(KeycloakModelUtils.generateId()); + representation.addScope("scope1"); + representation.addPolicy(policy.getName()); + + authorization.permissions().scope().create(representation).close(); + + AuthzClient authzClient = getAuthzClient(AUTHZ_CLIENT_CONFIG); + PermissionTicketRepresentation ticket = new PermissionTicketRepresentation(); + + ticket.setResource(resource.getId()); + ticket.setRequesterName("marta"); + ticket.setGranted(true); + ticket.setScopeName("scope1"); + + authzClient.protection().permission().create(ticket); + + AuthorizationRequest request = new AuthorizationRequest(); + request.addPermission(resource.getId()); + AuthorizationResponse response = authzClient.authorization("marta", "password").authorize(request); + AccessToken rpt = toAccessToken(response.getToken()); + ResourceRepresentation finalResource = resource; + List permissions = rpt.getAuthorization().getPermissions().stream().filter(permission -> permission.getResourceId().equals(finalResource.getId())).collect(Collectors.toList()); + assertEquals(1, permissions.size()); + assertEquals(1, permissions.get(0).getScopes().size()); + assertEquals("scope1", permissions.get(0).getScopes().iterator().next()); + + request = new AuthorizationRequest(); + request.addPermission(resource.getName()); + response = authzClient.authorization("marta", "password").authorize(request); + rpt = toAccessToken(response.getToken()); + permissions = rpt.getAuthorization().getPermissions().stream().filter(permission -> permission.getResourceId().equals(finalResource.getId())).collect(Collectors.toList()); + assertEquals(1, permissions.size()); + assertEquals(1, permissions.get(0).getScopes().size()); + assertEquals("scope1", permissions.get(0).getScopes().iterator().next()); + } + private void testRptRequestWithResourceName(String configFile) { Metadata metadata = new Metadata(); From 80991e681427678624b7acedcc0d0172f48f3493 Mon Sep 17 00:00:00 2001 From: Steven Hawkins Date: Thu, 21 Mar 2024 09:20:07 -0400 Subject: [PATCH 058/158] fix: allow the formbodyhandler to run tasks in the calling thread (#27642) (#27999) closes: #25687 Signed-off-by: Steve Hawkins (cherry picked from commit ffd42bfdfc6643fd16d157f32b08a103a9f85f95) --- .../resteasy/KeycloakHandlerChainCustomizer.java | 14 ++------------ 1 file changed, 2 insertions(+), 12 deletions(-) diff --git a/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/integration/resteasy/KeycloakHandlerChainCustomizer.java b/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/integration/resteasy/KeycloakHandlerChainCustomizer.java index 54f8d14aa5b8..32dbc7ed0663 100644 --- a/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/integration/resteasy/KeycloakHandlerChainCustomizer.java +++ b/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/integration/resteasy/KeycloakHandlerChainCustomizer.java @@ -22,11 +22,8 @@ import static jakarta.ws.rs.HttpMethod.PUT; import java.util.ArrayList; -import java.util.Collections; import java.util.List; import java.util.Set; -import java.util.concurrent.Executor; -import java.util.function.Supplier; import org.jboss.resteasy.reactive.common.model.ResourceClass; import org.jboss.resteasy.reactive.server.handlers.FormBodyHandler; import org.jboss.resteasy.reactive.server.model.HandlerChainCustomizer; @@ -37,14 +34,7 @@ public final class KeycloakHandlerChainCustomizer implements HandlerChainCustomi private final CreateSessionHandler TRANSACTIONAL_SESSION_HANDLER = new CreateSessionHandler(); - private final FormBodyHandler formBodyHandler = new FormBodyHandler(true, new Supplier() { - @Override - public Executor get() { - // we always run in blocking mode and never run in an event loop thread - // we don't need to provide an executor to dispatch to a worker thread to parse the body - return null; - } - }, Set.of()); + private final FormBodyHandler formBodyHandler = new FormBodyHandler(true, () -> Runnable::run, Set.of()); @Override public List handlers(Phase phase, ResourceClass resourceClass, @@ -53,7 +43,7 @@ public List handlers(Phase phase, ResourceClass resourceClass switch (phase) { case BEFORE_METHOD_INVOKE: - if (!resourceMethod.isFormParamRequired() && + if (!resourceMethod.isFormParamRequired() && (PATCH.equalsIgnoreCase(resourceMethod.getHttpMethod()) || POST.equalsIgnoreCase(resourceMethod.getHttpMethod()) || PUT.equalsIgnoreCase(resourceMethod.getHttpMethod()))) { From e1349f42467f90a3e2cf5092d63cf4e2159e2394 Mon Sep 17 00:00:00 2001 From: Steven Hawkins Date: Thu, 21 Mar 2024 11:46:00 -0400 Subject: [PATCH 059/158] task: ensuring that keycloaksessions are closed (#27682) (#28000) closes: #27681 Signed-off-by: Steve Hawkins (cherry picked from commit a3be85b2cd7c0ddf904b5a7748c695cd804f9601) --- .../resteasy/CreateSessionHandler.java | 10 +++++++++- .../org/keycloak/it/cli/dist/FipsDistTest.java | 6 ++---- .../it/utils/RawKeycloakDistribution.java | 15 +++++---------- 3 files changed, 16 insertions(+), 15 deletions(-) diff --git a/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/integration/resteasy/CreateSessionHandler.java b/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/integration/resteasy/CreateSessionHandler.java index c3bbea9651d8..816637bcf5ca 100644 --- a/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/integration/resteasy/CreateSessionHandler.java +++ b/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/integration/resteasy/CreateSessionHandler.java @@ -22,6 +22,7 @@ import jakarta.ws.rs.container.CompletionCallback; import org.jboss.resteasy.reactive.server.core.ResteasyReactiveRequestContext; import org.jboss.resteasy.reactive.server.spi.ServerRestHandler; +import org.keycloak.common.util.Resteasy; import org.keycloak.models.KeycloakSession; import org.keycloak.quarkus.runtime.transaction.TransactionalSessionHandler; @@ -39,13 +40,20 @@ public void handle(ResteasyReactiveRequestContext requestContext) { if (currentSession == null) { // this handler might be invoked multiple times when resolving sub-resources // make sure the session is created once - routingContext.put(KeycloakSession.class.getName(), create()); + KeycloakSession session = create(); + routingContext.put(KeycloakSession.class.getName(), session); context.registerCompletionCallback(this); + Resteasy.pushContext(KeycloakSession.class, session); } } @Override public void onComplete(Throwable throwable) { + try { + close(Resteasy.getContextData(KeycloakSession.class)); + } catch (Exception e) { + + } clearContextData(); } } diff --git a/quarkus/tests/integration/src/test/java/org/keycloak/it/cli/dist/FipsDistTest.java b/quarkus/tests/integration/src/test/java/org/keycloak/it/cli/dist/FipsDistTest.java index 68c4d06cb2e7..2faf8e184a93 100644 --- a/quarkus/tests/integration/src/test/java/org/keycloak/it/cli/dist/FipsDistTest.java +++ b/quarkus/tests/integration/src/test/java/org/keycloak/it/cli/dist/FipsDistTest.java @@ -78,8 +78,7 @@ void testUnsupportedHttpsJksKeyStoreInStrictMode(KeycloakDistribution dist) { dist.copyOrReplaceFileFromClasspath("/server.keystore", Path.of("conf", "server.keystore")); CLIResult cliResult = dist.run("start", "--fips-mode=strict"); dist.assertStopped(); - // after https://issues.redhat.com/browse/JBTM-3830 reenable this check - //cliResult.assertMessage("ERROR: java.lang.IllegalArgumentException: malformed sequence"); + cliResult.assertMessage("ERROR: java.lang.IllegalArgumentException: malformed sequence"); }); } @@ -127,8 +126,7 @@ void testUnsupportedHttpsPkcs12KeyStoreInStrictMode(KeycloakDistribution dist) { dist.copyOrReplaceFileFromClasspath("/server.keystore.pkcs12", Path.of("conf", "server.keystore")); CLIResult cliResult = dist.run("start", "--fips-mode=strict", "--https-key-store-password=passwordpassword"); dist.assertStopped(); - // after https://issues.redhat.com/browse/JBTM-3830 reenable this check - //cliResult.assertMessage("ERROR: java.lang.IllegalArgumentException: malformed sequence"); + cliResult.assertMessage("ERROR: java.lang.IllegalArgumentException: malformed sequence"); }); } diff --git a/quarkus/tests/junit5/src/main/java/org/keycloak/it/utils/RawKeycloakDistribution.java b/quarkus/tests/junit5/src/main/java/org/keycloak/it/utils/RawKeycloakDistribution.java index 5ed1160a9bc9..d8333a0ec41a 100644 --- a/quarkus/tests/junit5/src/main/java/org/keycloak/it/utils/RawKeycloakDistribution.java +++ b/quarkus/tests/junit5/src/main/java/org/keycloak/it/utils/RawKeycloakDistribution.java @@ -79,10 +79,6 @@ public final class RawKeycloakDistribution implements KeycloakDistribution { - // TODO: reconsider the hardcoded timeout once https://issues.redhat.com/browse/JBTM-3830 is pulled into Keycloak - // ensures that the total wait time (two minutes for readiness + 200 seconds) is longer than the transaction timeout of 5 minutes - private static final int LONG_SHUTDOWN_WAIT = 200; - private static final int DEFAULT_SHUTDOWN_TIMEOUT_SECONDS = 10; private static final Logger LOG = Logger.getLogger(RawKeycloakDistribution.class); @@ -166,7 +162,7 @@ public void stop() { destroyDescendantsOnWindows(keycloak, false); keycloak.destroy(); - keycloak.waitFor(LONG_SHUTDOWN_WAIT, TimeUnit.SECONDS); + keycloak.waitFor(DEFAULT_SHUTDOWN_TIMEOUT_SECONDS, TimeUnit.SECONDS); exitCode = keycloak.exitValue(); } catch (Exception cause) { destroyDescendantsOnWindows(keycloak, true); @@ -266,7 +262,7 @@ public String[] getCliArgs(List arguments) { public void assertStopped() { try { if (keycloak != null) { - keycloak.onExit().get(LONG_SHUTDOWN_WAIT, TimeUnit.SECONDS); + keycloak.onExit().get(DEFAULT_SHUTDOWN_TIMEOUT_SECONDS, TimeUnit.SECONDS); } } catch (InterruptedException e) { Thread.currentThread().interrupt(); @@ -276,7 +272,7 @@ public void assertStopped() { } catch (TimeoutException e) { LOG.warn("Process did not exit as expected, will attempt a thread dump"); threadDump(); - LOG.warn("TODO: this should be a hard error / re-diagnosed after https://issues.redhat.com/browse/JBTM-3830 is pulled into Keycloak"); + throw new RuntimeException(e); } } @@ -297,9 +293,8 @@ private void waitForReadiness(String scheme, int port) throws MalformedURLExcept while (true) { if (System.currentTimeMillis() - startTime > getStartTimeout()) { threadDump(); - LOG.warn("Timeout [" + getStartTimeout() + "] while waiting for Quarkus server", ex); - LOG.warn("TODO: this should be a hard error / re-diagnosed after https://issues.redhat.com/browse/JBTM-3830 is pulled into Keycloak"); - return; + throw new IllegalStateException( + "Timeout [" + getStartTimeout() + "] while waiting for Quarkus server", ex); } if (!keycloak.isAlive()) { From f7bcaaa6877bc577295e83954131af67c0585fdc Mon Sep 17 00:00:00 2001 From: Martin Kanis Date: Fri, 8 Mar 2024 13:51:45 +0100 Subject: [PATCH 060/158] Invalidating offline token is not working from client sessions tab Closes #27275 Signed-off-by: Martin Kanis (cherry picked from commit 4154d27941e4cc7ccb0452e6f43978897b5dc3d3) --- .../admin/client/resource/RealmResource.java | 3 +- .../cypress/e2e/sessions_test.spec.ts | 4 +-- .../pages/admin-ui/components/TablePage.ts | 18 ---------- .../admin-ui/src/sessions/SessionsTable.tsx | 33 +++++++++++++++++-- .../src/resources/realms.ts | 3 +- .../managers/AuthenticationManager.java | 3 +- .../resources/admin/RealmAdminResource.java | 15 ++++++--- .../testsuite/admin/PermissionsTest.java | 2 +- .../testsuite/admin/realm/RealmTest.java | 4 +-- .../crossdc/SessionExpirationCrossDCTest.java | 2 +- .../oauth/UserInfoEndpointCorsTest.java | 2 +- 11 files changed, 54 insertions(+), 35 deletions(-) diff --git a/integration/admin-client-jee/src/main/java/org/keycloak/admin/client/resource/RealmResource.java b/integration/admin-client-jee/src/main/java/org/keycloak/admin/client/resource/RealmResource.java index 40293fb70b4f..f7cc6699d27b 100644 --- a/integration/admin-client-jee/src/main/java/org/keycloak/admin/client/resource/RealmResource.java +++ b/integration/admin-client-jee/src/main/java/org/keycloak/admin/client/resource/RealmResource.java @@ -17,6 +17,7 @@ package org.keycloak.admin.client.resource; +import jakarta.ws.rs.DefaultValue; import org.keycloak.representations.adapters.action.GlobalRequestResult; import org.keycloak.representations.idm.AdminEventRepresentation; import org.keycloak.representations.idm.ClientRepresentation; @@ -268,7 +269,7 @@ Response testLDAPConnection(@FormParam("action") String action, @FormParam("conn @Path("sessions/{session}") @DELETE - void deleteSession(@PathParam("session") String sessionId); + void deleteSession(@PathParam("session") String sessionId, @DefaultValue("false") @QueryParam("isOffline") boolean offline); @Path("components") ComponentsResource components(); diff --git a/js/apps/admin-ui/cypress/e2e/sessions_test.spec.ts b/js/apps/admin-ui/cypress/e2e/sessions_test.spec.ts index a3efd75994e0..113523f2f24f 100644 --- a/js/apps/admin-ui/cypress/e2e/sessions_test.spec.ts +++ b/js/apps/admin-ui/cypress/e2e/sessions_test.spec.ts @@ -96,11 +96,11 @@ describe("Sessions test", () => { sidebarPage.waitForPageLoad(); // Now check that offline session exists (online one has been logged off above) - // and that it is not possible to sign it out + // and that it is possible to revoke it commonPage .tableUtils() .checkRowItemExists(username) - .assertRowItemActionDoesNotExist(username, "Sign out"); + .selectRowItemAction(username, "Revoke"); }); }); diff --git a/js/apps/admin-ui/cypress/support/pages/admin-ui/components/TablePage.ts b/js/apps/admin-ui/cypress/support/pages/admin-ui/components/TablePage.ts index fcae54db787c..9b42844ff915 100644 --- a/js/apps/admin-ui/cypress/support/pages/admin-ui/components/TablePage.ts +++ b/js/apps/admin-ui/cypress/support/pages/admin-ui/components/TablePage.ts @@ -53,24 +53,6 @@ export default class TablePage extends CommonElements { return this; } - assertRowItemActionDoesNotExist(itemName: string, actionItemName: string) { - cy.get( - (this.#tableInModal ? ".pf-c-modal-box.pf-m-md " : "") + - this.#tableRowItem, - ) - .contains(itemName) - .parentsUntil("tbody") - .then(($tbody) => { - if ($tbody.find(".pf-c-dropdown__toggle").length > 0) { - $tbody.find(".pf-c-dropdown__toggle").click(); - cy.get(this.dropdownMenuItem) - .contains(actionItemName) - .should("not.exist"); - } - }); - return this; - } - #getRowItemAction(itemName: string, actionItemName: string) { return cy .get( diff --git a/js/apps/admin-ui/src/sessions/SessionsTable.tsx b/js/apps/admin-ui/src/sessions/SessionsTable.tsx index 3eb6f594b6fc..b5328ba4b06b 100644 --- a/js/apps/admin-ui/src/sessions/SessionsTable.tsx +++ b/js/apps/admin-ui/src/sessions/SessionsTable.tsx @@ -146,13 +146,32 @@ export default function SessionsTable({ }, }); + async function onClickRevoke( + event: MouseEvent, + rowIndex: number, + rowData: IRowData, + ) { + const session = rowData.data as UserSessionRepresentation; + await adminClient.realms.deleteSession({ + realm, + session: session.id!, + isOffline: true, + }); + + refresh(); + } + async function onClickSignOut( event: MouseEvent, rowIndex: number, rowData: IRowData, ) { const session = rowData.data as UserSessionRepresentation; - await adminClient.realms.deleteSession({ realm, session: session.id! }); + await adminClient.realms.deleteSession({ + realm, + session: session.id!, + isOffline: false, + }); if (session.userId === whoAmI.getUserId()) { await keycloak.logout({ redirectUri: "" }); @@ -185,8 +204,16 @@ export default function SessionsTable({ } columns={columns} actionResolver={(rowData: IRowData) => { - if (rowData.data.type === "OFFLINE") { - return []; + if ( + rowData.data.type === "Offline" || + rowData.data.type === "OFFLINE" + ) { + return [ + { + title: t("revoke"), + onClick: onClickRevoke, + } as Action, + ]; } return [ { diff --git a/js/libs/keycloak-admin-client/src/resources/realms.ts b/js/libs/keycloak-admin-client/src/resources/realms.ts index d760cfbf8dc3..49b10dc46e0b 100644 --- a/js/libs/keycloak-admin-client/src/resources/realms.ts +++ b/js/libs/keycloak-admin-client/src/resources/realms.ts @@ -311,12 +311,13 @@ export class Realms extends Resource { }); public deleteSession = this.makeRequest< - { realm: string; session: string }, + { realm: string; session: string; isOffline: boolean }, void >({ method: "DELETE", path: "/{realm}/sessions/{session}", urlParamKeys: ["realm", "session"], + queryParamKeys: ["isOffline"], }); public pushRevocation = this.makeRequest< diff --git a/services/src/main/java/org/keycloak/services/managers/AuthenticationManager.java b/services/src/main/java/org/keycloak/services/managers/AuthenticationManager.java index 3bc4784c6314..179ec9378670 100755 --- a/services/src/main/java/org/keycloak/services/managers/AuthenticationManager.java +++ b/services/src/main/java/org/keycloak/services/managers/AuthenticationManager.java @@ -251,7 +251,8 @@ public static BackchannelLogoutResponse backchannelLogout(KeycloakSession sessio UserSessionModel userSession, UriInfo uriInfo, ClientConnection connection, HttpHeaders headers, boolean logoutBroker) { - return backchannelLogout(session, realm, userSession, uriInfo, connection, headers, logoutBroker, false); + + return backchannelLogout(session, realm, userSession, uriInfo, connection, headers, logoutBroker, userSession == null ? false : userSession.isOffline()); } /** diff --git a/services/src/main/java/org/keycloak/services/resources/admin/RealmAdminResource.java b/services/src/main/java/org/keycloak/services/resources/admin/RealmAdminResource.java index 2abdfa896d9b..5cc70a3ecf42 100644 --- a/services/src/main/java/org/keycloak/services/resources/admin/RealmAdminResource.java +++ b/services/src/main/java/org/keycloak/services/resources/admin/RealmAdminResource.java @@ -30,6 +30,8 @@ import java.util.stream.Collectors; import java.util.stream.Stream; +import jakarta.enterprise.inject.Default; +import jakarta.ws.rs.DefaultValue; import org.eclipse.microprofile.openapi.annotations.Operation; import org.eclipse.microprofile.openapi.annotations.extensions.Extension; import org.eclipse.microprofile.openapi.annotations.parameters.Parameter; @@ -625,14 +627,19 @@ public GlobalRequestResult logoutAll() { @DELETE @Tag(name = KeycloakOpenAPI.Admin.Tags.REALMS_ADMIN) @Operation( summary = "Remove a specific user session.", description = "Any client that has an admin url will also be told to invalidate this particular session.") - public void deleteSession(@PathParam("session") String sessionId) { + public void deleteSession(@PathParam("session") String sessionId, @DefaultValue("false") @QueryParam("isOffline") boolean offline) { auth.users().requireManage(); - UserSessionModel userSession = session.sessions().getUserSession(realm, sessionId); - if (userSession == null) throw new NotFoundException("Sesssion not found"); + UserSessionModel userSession = offline ? session.sessions().getOfflineUserSession(realm, sessionId) : session.sessions().getUserSession(realm, sessionId); + if (userSession == null) { + throw new NotFoundException("Sesssion not found"); + } + AuthenticationManager.backchannelLogout(session, realm, userSession, session.getContext().getUri(), connection, headers, true); - adminEvent.operation(OperationType.DELETE).resource(ResourceType.USER_SESSION).resourcePath(session.getContext().getUri()).success(); + Map eventRep = new HashMap<>(); + eventRep.put("offline", offline); + adminEvent.operation(OperationType.DELETE).resource(ResourceType.USER_SESSION).resourcePath(session.getContext().getUri()).representation(eventRep).success(); } /** diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/PermissionsTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/PermissionsTest.java index 905a1841cc1b..3830e8537898 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/PermissionsTest.java +++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/PermissionsTest.java @@ -336,7 +336,7 @@ public void invoke(RealmResource realm) { }, Resource.REALM, true); invoke(new Invocation() { public void invoke(RealmResource realm) { - realm.deleteSession("nosuch"); + realm.deleteSession("nosuch", false); } }, Resource.USER, true); invoke(new Invocation() { diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/realm/RealmTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/realm/RealmTest.java index e39653fce975..df3ada02f9b8 100755 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/realm/RealmTest.java +++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/realm/RealmTest.java @@ -947,10 +947,10 @@ public void deleteSession() { EventRepresentation event = events.poll(); assertNotNull(event); - realm.deleteSession(event.getSessionId()); + realm.deleteSession(event.getSessionId(), false); assertAdminEvents.assertEvent(realmId, OperationType.DELETE, AdminEventPaths.deleteSessionPath(event.getSessionId()), ResourceType.USER_SESSION); try { - realm.deleteSession(event.getSessionId()); + realm.deleteSession(event.getSessionId(), false); fail("Expected 404"); } catch (NotFoundException e) { // Expected diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/crossdc/SessionExpirationCrossDCTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/crossdc/SessionExpirationCrossDCTest.java index e9f8e29d49af..316f2079239a 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/crossdc/SessionExpirationCrossDCTest.java +++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/crossdc/SessionExpirationCrossDCTest.java @@ -425,7 +425,7 @@ public void testLogoutUser( // Logout single session of user first UserResource user = ApiUtil.findUserByUsernameId(getAdminClient().realm(REALM_NAME), "login-test"); UserSessionRepresentation userSession = user.getUserSessions().get(0); - getAdminClient().realm(REALM_NAME).deleteSession(userSession.getId()); + getAdminClient().realm(REALM_NAME).deleteSession(userSession.getId(), false); // Just one session expired. assertStatisticsExpected("After logout single session", InfinispanConnectionProvider.USER_SESSION_CACHE_NAME, InfinispanConnectionProvider.CLIENT_SESSION_CACHE_NAME, diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/oauth/UserInfoEndpointCorsTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/oauth/UserInfoEndpointCorsTest.java index 9ff1a5cfa7ca..13a593b16a4f 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/oauth/UserInfoEndpointCorsTest.java +++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/oauth/UserInfoEndpointCorsTest.java @@ -123,7 +123,7 @@ public void userInfoCorsInvalidSession() throws Exception { // remove the session in keycloak AccessToken accessToken = oauth.verifyToken(accessTokenResponse.getAccessToken()); - adminClient.realm("test").deleteSession(accessToken.getSessionState()); + adminClient.realm("test").deleteSession(accessToken.getSessionState(), false); try (ResteasyClient resteasyClient = AdminClientUtil.createResteasyClient()) { WebTarget userInfoTarget = UserInfoClientUtil.getUserInfoWebTarget(resteasyClient); From ca1c1eb3cf0bc057d79f0370bb833b808ed22458 Mon Sep 17 00:00:00 2001 From: Giuseppe Graziano Date: Wed, 20 Mar 2024 12:19:42 +0100 Subject: [PATCH 061/158] Always include offline_access scope when refreshing with offline token Closes #27878 Signed-off-by: Giuseppe Graziano (cherry picked from commit 939420cea1dd98e779b991c5420a8158e4db6a13) --- .../keycloak/protocol/oidc/TokenManager.java | 2 +- .../testsuite/oauth/OfflineTokenTest.java | 50 +++++++++++++------ 2 files changed, 37 insertions(+), 15 deletions(-) diff --git a/services/src/main/java/org/keycloak/protocol/oidc/TokenManager.java b/services/src/main/java/org/keycloak/protocol/oidc/TokenManager.java index ef436cdea3ee..41288454e581 100755 --- a/services/src/main/java/org/keycloak/protocol/oidc/TokenManager.java +++ b/services/src/main/java/org/keycloak/protocol/oidc/TokenManager.java @@ -412,7 +412,7 @@ public AccessTokenResponseBuilder refreshAccessToken(KeycloakSession session, Ur //if scope parameter is not null, remove every scope that is not part of scope parameter if (scopeParameter != null && ! scopeParameter.isEmpty()) { Set scopeParamScopes = Arrays.stream(scopeParameter.split(" ")).collect(Collectors.toSet()); - oldTokenScope = Arrays.stream(oldTokenScope.split(" ")).filter(sc -> scopeParamScopes.contains(sc)) + oldTokenScope = Arrays.stream(oldTokenScope.split(" ")).filter(sc -> scopeParamScopes.contains(sc) || sc.equals(OAuth2Constants.OFFLINE_ACCESS)) .collect(Collectors.joining(" ")); } diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/oauth/OfflineTokenTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/oauth/OfflineTokenTest.java index 17b260192e64..b50603c2746d 100755 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/oauth/OfflineTokenTest.java +++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/oauth/OfflineTokenTest.java @@ -260,7 +260,7 @@ public void offlineTokenBrowserFlow() throws Exception { assertTrue(tokenResponse.getScope().contains(OAuth2Constants.OFFLINE_ACCESS)); - String newRefreshTokenString = testRefreshWithOfflineToken(token, offlineToken, offlineTokenString, sessionId, userId, false); + String newRefreshTokenString = testRefreshWithOfflineToken(token, offlineToken, offlineTokenString, sessionId, userId); // Change offset to very big value to ensure offline session expires setTimeOffset(3000000); @@ -281,7 +281,7 @@ public void offlineTokenBrowserFlow() throws Exception { } private String testRefreshWithOfflineToken(AccessToken oldToken, RefreshToken offlineToken, String offlineTokenString, - final String sessionId, String userId, boolean scopeParameterExist) { + final String sessionId, String userId) { // Change offset to big value to ensure userSession expired setTimeOffset(99999); assertFalse(oldToken.isActive()); @@ -306,7 +306,7 @@ private String testRefreshWithOfflineToken(AccessToken oldToken, RefreshToken of Assert.assertNotEquals(oldToken.getId(), refreshedToken.getId()); // scope parameter contains "offline_access" if not filter via scope parameter - assertTrue(scopeParameterExist ? !refreshedToken.getScope().contains(OAuth2Constants.OFFLINE_ACCESS) : refreshedToken.getScope().contains(OAuth2Constants.OFFLINE_ACCESS)); + assertTrue(refreshedToken.getScope().contains(OAuth2Constants.OFFLINE_ACCESS)); // Assert refresh token scope parameter contains "offline_access" assertTrue(newRefreshTokenFull.getScope().contains(OAuth2Constants.OFFLINE_ACCESS)); Assert.assertEquals(TokenUtil.TOKEN_TYPE_OFFLINE, newRefreshTokenFull.getType()); @@ -358,10 +358,10 @@ public void offlineTokenDirectGrantFlow() throws Exception { Assert.assertEquals(TokenUtil.TOKEN_TYPE_OFFLINE, offlineToken.getType()); Assert.assertEquals(0, offlineToken.getExpiration()); - testRefreshWithOfflineToken(token, offlineToken, offlineTokenString, token.getSessionState(), userId, false); + testRefreshWithOfflineToken(token, offlineToken, offlineTokenString, token.getSessionState(), userId); // Assert same token can be refreshed again - testRefreshWithOfflineToken(token, offlineToken, offlineTokenString, token.getSessionState(), userId, false); + testRefreshWithOfflineToken(token, offlineToken, offlineTokenString, token.getSessionState(), userId); } @Test @@ -393,7 +393,7 @@ public void offlineTokenDirectGrantFlowWithRefreshTokensRevoked() throws Excepti Assert.assertEquals(TokenUtil.TOKEN_TYPE_OFFLINE, offlineToken.getType()); Assert.assertEquals(0, offlineToken.getExpiration()); - String offlineTokenString2 = testRefreshWithOfflineToken(token, offlineToken, offlineTokenString, token.getSessionState(), userId, false); + String offlineTokenString2 = testRefreshWithOfflineToken(token, offlineToken, offlineTokenString, token.getSessionState(), userId); RefreshToken offlineToken2 = oauth.parseRefreshToken(offlineTokenString2); // Assert second refresh with same refresh token will fail @@ -442,7 +442,7 @@ public void offlineTokenServiceAccountFlow() throws Exception { Assert.assertEquals(TokenUtil.TOKEN_TYPE_OFFLINE, offlineToken.getType()); Assert.assertEquals(0, offlineToken.getExpiration()); - testRefreshWithOfflineToken(token, offlineToken, offlineTokenString, token.getSessionState(), serviceAccountUserId, false); + testRefreshWithOfflineToken(token, offlineToken, offlineTokenString, token.getSessionState(), serviceAccountUserId); // Now retrieve another offline token and verify that previous offline token is still valid tokenResponse = oauth.doClientCredentialsGrantAccessTokenRequest("secret1"); @@ -462,8 +462,8 @@ public void offlineTokenServiceAccountFlow() throws Exception { .assertEvent(); // Refresh with both offline tokens is fine - testRefreshWithOfflineToken(token, offlineToken, offlineTokenString, token.getSessionState(), serviceAccountUserId, false); - testRefreshWithOfflineToken(token2, offlineToken2, offlineTokenString2, token2.getSessionState(), serviceAccountUserId, false); + testRefreshWithOfflineToken(token, offlineToken, offlineTokenString, token.getSessionState(), serviceAccountUserId); + testRefreshWithOfflineToken(token2, offlineToken2, offlineTokenString2, token2.getSessionState(), serviceAccountUserId); } @Test @@ -978,7 +978,7 @@ private void offlineTokenRequest(String expectedRefreshAlg, String expectedAcces Assert.assertEquals(TokenUtil.TOKEN_TYPE_OFFLINE, offlineToken.getType()); Assert.assertEquals(0, offlineToken.getExpiration()); - testRefreshWithOfflineToken(token, offlineToken, offlineTokenString, token.getSessionState(), serviceAccountUserId, false); + testRefreshWithOfflineToken(token, offlineToken, offlineTokenString, token.getSessionState(), serviceAccountUserId); // Now retrieve another offline token and decode that previous offline token is still valid tokenResponse = oauth.doClientCredentialsGrantAccessTokenRequest("secret1"); @@ -998,8 +998,8 @@ private void offlineTokenRequest(String expectedRefreshAlg, String expectedAcces .assertEvent(); // Refresh with both offline tokens is fine - testRefreshWithOfflineToken(token, offlineToken, offlineTokenString, token.getSessionState(), serviceAccountUserId, false); - testRefreshWithOfflineToken(token2, offlineToken2, offlineTokenString2, token2.getSessionState(), serviceAccountUserId, false); + testRefreshWithOfflineToken(token, offlineToken, offlineTokenString, token.getSessionState(), serviceAccountUserId); + testRefreshWithOfflineToken(token2, offlineToken2, offlineTokenString2, token2.getSessionState(), serviceAccountUserId); } @@ -1050,9 +1050,9 @@ private void offlineTokenRequestWithScopeParameter(String expectedRefreshAlg, St Assert.assertEquals(TokenUtil.TOKEN_TYPE_OFFLINE, offlineToken.getType()); Assert.assertEquals(0, offlineToken.getExpiration()); - //refresh token without sending offline_access scope => access token without it and same refresh token + //refresh token without sending offline_access scope oauth.scope("phone"); - testRefreshWithOfflineToken(token, offlineToken, offlineTokenString, token.getSessionState(), serviceAccountUserId, true); + testRefreshWithOfflineToken(token, offlineToken, offlineTokenString, token.getSessionState(), serviceAccountUserId); } @Test @@ -1289,4 +1289,26 @@ public void testClientOfflineSessionIdleTimeout() throws Exception { client.update(clientRepresentation); } } + + @Test + public void offlineTokenRefreshWithoutOfflineAccessScope() { + ClientManager.realm(adminClient.realm("test")).clientId("offline-client").fullScopeAllowed(false); + + try { + oauth.scope("openid " + OAuth2Constants.OFFLINE_ACCESS); + oauth.clientId("offline-client"); + oauth.redirectUri(offlineClientAppUri); + oauth.doLogin("test-user@localhost", "password"); + String code = oauth.getCurrentQuery().get(OAuth2Constants.CODE); + OAuthClient.AccessTokenResponse response = oauth.doAccessTokenRequest(code, "secret1"); + + oauth.scope("openid"); + response = oauth.doRefreshTokenRequest(response.getRefreshToken(), "secret1"); + assertEquals(200, response.getStatusCode()); + } + finally { + ClientManager.realm(adminClient.realm("test")).clientId("offline-client").fullScopeAllowed(true); + } + } + } From 2d534c324b6ffd02ef8e8ae0ed858e25d7d11a20 Mon Sep 17 00:00:00 2001 From: Giuseppe Graziano Date: Wed, 20 Mar 2024 17:01:07 +0100 Subject: [PATCH 062/158] Avoid using wait() to wait for the redirect Closes #22644 Signed-off-by: Giuseppe Graziano (cherry picked from commit b24d446911bd3d717066fec555bb52f1ea0ddc81) --- .../java/org/keycloak/testsuite/forms/BrowserFlowTest.java | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/forms/BrowserFlowTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/forms/BrowserFlowTest.java index 59dbe39550ab..38ce45b54a74 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/forms/BrowserFlowTest.java +++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/forms/BrowserFlowTest.java @@ -60,6 +60,7 @@ import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.containsString; import static org.keycloak.testsuite.admin.AbstractAdminTest.loadJson; +import static org.keycloak.testsuite.broker.BrokerTestTools.waitForPage; import static org.keycloak.testsuite.broker.SocialLoginTest.Provider.GITHUB; import static org.keycloak.testsuite.broker.SocialLoginTest.Provider.GITLAB; import static org.keycloak.testsuite.broker.SocialLoginTest.Provider.GOOGLE; @@ -565,10 +566,8 @@ public void testAlternativeNonInteractiveExecutorInSubflow() { WebElement aHref = driver.findElement(By.tagName("a")); driver.get(aHref.getAttribute("href")); // Waiting for account redirection from app page - driver.wait(1000); + waitForPage(driver, "Account Management", true); assertThat(driver.getTitle(), containsString("Account Management")); - } catch (Throwable t) { - t.printStackTrace(); } finally { revertFlows("browser - alternative non-interactive executor"); } From 6f686df099cbacd0b5f4ff9b375cbf9aee12f734 Mon Sep 17 00:00:00 2001 From: synth3 <19573241+synth3@users.noreply.github.com> Date: Fri, 15 Mar 2024 19:29:58 +0100 Subject: [PATCH 063/158] Remove custom Hibernate dialect detection Closes #27954 Signed-off-by: synth3 <19573241+synth3@users.noreply.github.com> (cherry picked from commit 99478887a47796ecd65ae7347c9ffd44fe7f6755) --- .../DefaultJpaConnectionProviderFactory.java | 46 ++----------------- 1 file changed, 3 insertions(+), 43 deletions(-) diff --git a/model/jpa/src/main/java/org/keycloak/connections/jpa/DefaultJpaConnectionProviderFactory.java b/model/jpa/src/main/java/org/keycloak/connections/jpa/DefaultJpaConnectionProviderFactory.java index ba17fb9f0482..c138acd4a935 100755 --- a/model/jpa/src/main/java/org/keycloak/connections/jpa/DefaultJpaConnectionProviderFactory.java +++ b/model/jpa/src/main/java/org/keycloak/connections/jpa/DefaultJpaConnectionProviderFactory.java @@ -210,8 +210,9 @@ private void lazyInit(KeycloakSession session) { try { prepareOperationalInfo(connection); - String driverDialect = detectDialect(connection); - if (driverDialect != null) { + String driverDialect = config.get("driverDialect"); + // use configured dialect, else rely on Hibernate detection + if (driverDialect != null && !driverDialect.isBlank()) { properties.put("hibernate.dialect", driverDialect); } @@ -298,47 +299,6 @@ protected void prepareOperationalInfo(Connection connection) { } } - - protected String detectDialect(Connection connection) { - String driverDialect = config.get("driverDialect"); - if (driverDialect != null && driverDialect.length() > 0) { - return driverDialect; - } else { - try { - String dbProductName = connection.getMetaData().getDatabaseProductName(); - String dbProductVersion = connection.getMetaData().getDatabaseProductVersion(); - - // For MSSQL2014, we may need to fix the autodetected dialect by hibernate - if (dbProductName.equals("Microsoft SQL Server")) { - String topVersionStr = dbProductVersion.split("\\.")[0]; - boolean shouldSet2012Dialect = true; - try { - int topVersion = Integer.parseInt(topVersionStr); - if (topVersion < 12) { - shouldSet2012Dialect = false; - } - } catch (NumberFormatException nfe) { - } - if (shouldSet2012Dialect) { - String sql2012Dialect = "org.hibernate.dialect.SQLServer2012Dialect"; - logger.debugf("Manually override hibernate dialect to %s", sql2012Dialect); - return sql2012Dialect; - } - } - - // For Oracle19c, we may need to set dialect explicitly to workaround https://hibernate.atlassian.net/browse/HHH-13184 - if (dbProductName.equals("Oracle") && connection.getMetaData().getDatabaseMajorVersion() > 12) { - logger.debugf("Manually specify dialect for Oracle to org.hibernate.dialect.Oracle12cDialect"); - return "org.hibernate.dialect.Oracle12cDialect"; - } - } catch (SQLException e) { - logger.warnf("Unable to detect hibernate dialect due database exception : %s", e.getMessage()); - } - - return null; - } - } - protected void startGlobalStats(KeycloakSession session, int globalStatsIntervalSecs) { logger.debugf("Started Hibernate statistics with the interval %s seconds", globalStatsIntervalSecs); TimerProvider timer = session.getProvider(TimerProvider.class); From 2454565083f265f9dc8794cf0489b1c1fd13b7e3 Mon Sep 17 00:00:00 2001 From: Steven Hawkins Date: Thu, 21 Mar 2024 17:34:28 -0400 Subject: [PATCH 064/158] fix: making the truststore name field optional (#28013) (#28148) closes: #28012 Signed-off-by: Steve Hawkins (cherry picked from commit 05056330dc755d232836a050fa826d23bc6d2208) --- .../operator/crds/v2alpha1/deployment/spec/Truststore.java | 3 ++- .../testsuite/integration/KeycloakTruststoresTests.java | 4 ++-- .../src/test/resources/test-serialization-keycloak-cr.yml | 4 ++++ 3 files changed, 8 insertions(+), 3 deletions(-) diff --git a/operator/src/main/java/org/keycloak/operator/crds/v2alpha1/deployment/spec/Truststore.java b/operator/src/main/java/org/keycloak/operator/crds/v2alpha1/deployment/spec/Truststore.java index 80a9cbbfb9be..761189351d61 100644 --- a/operator/src/main/java/org/keycloak/operator/crds/v2alpha1/deployment/spec/Truststore.java +++ b/operator/src/main/java/org/keycloak/operator/crds/v2alpha1/deployment/spec/Truststore.java @@ -21,12 +21,13 @@ import io.sundr.builder.annotations.Buildable; import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonPropertyDescription; @JsonInclude(JsonInclude.Include.NON_NULL) @Buildable(editableEnabled = false, builderPackage = "io.fabric8.kubernetes.api.builder") public class Truststore { - @Required + @JsonPropertyDescription("Not used. To be removed in later versions.") private String name; @Required private TruststoreSource secret; diff --git a/operator/src/test/java/org/keycloak/operator/testsuite/integration/KeycloakTruststoresTests.java b/operator/src/test/java/org/keycloak/operator/testsuite/integration/KeycloakTruststoresTests.java index 61982aa3741d..890e939f0967 100644 --- a/operator/src/test/java/org/keycloak/operator/testsuite/integration/KeycloakTruststoresTests.java +++ b/operator/src/test/java/org/keycloak/operator/testsuite/integration/KeycloakTruststoresTests.java @@ -39,7 +39,7 @@ public class KeycloakTruststoresTests extends BaseOperatorTest { public void testTruststoreMissing() { var kc = getTestKeycloakDeployment(true); var deploymentName = kc.getMetadata().getName(); - kc.getSpec().getTruststores().put("xyz", new TruststoreBuilder().withName("xyz").withNewSecret().withName("xyz").endSecret().build()); + kc.getSpec().getTruststores().put("xyz", new TruststoreBuilder().withNewSecret().withName("xyz").endSecret().build()); deployKeycloak(k8sclient, kc, false); Resource stsResource = k8sclient.resources(StatefulSet.class).withName(deploymentName); @@ -58,7 +58,7 @@ public void testTrustroreExists() { var deploymentName = kc.getMetadata().getName(); K8sUtils.set(k8sclient, getResourceFromFile("example-truststore-secret.yaml", Secret.class)); - kc.getSpec().getTruststores().put("example", new TruststoreBuilder().withName("example").withNewSecret().withName("example-truststore-secret").endSecret().build()); + kc.getSpec().getTruststores().put("example", new TruststoreBuilder().withNewSecret().withName("example-truststore-secret").endSecret().build()); deployKeycloak(k8sclient, kc, true); Resource stsResource = k8sclient.resources(StatefulSet.class).withName(deploymentName); diff --git a/operator/src/test/resources/test-serialization-keycloak-cr.yml b/operator/src/test/resources/test-serialization-keycloak-cr.yml index cad8830982f6..f6e5e486290e 100644 --- a/operator/src/test/resources/test-serialization-keycloak-cr.yml +++ b/operator/src/test/resources/test-serialization-keycloak-cr.yml @@ -65,6 +65,10 @@ spec: memory: "1500M" proxy: headers: forwarded + truststores: + x: + secret: + name: my-secret unsupported: podTemplate: metadata: From 895da882cff886c31f33fb23e1fba14fa138a0b9 Mon Sep 17 00:00:00 2001 From: Steven Hawkins Date: Thu, 21 Mar 2024 17:34:44 -0400 Subject: [PATCH 065/158] doc: add a note about lack of other JAX-RS support (#28048) (#28149) closes: #27057 Signed-off-by: Steve Hawkins (cherry picked from commit cbe185fbabbd844c651de7211a8466deb7792f5e) --- docs/documentation/server_development/topics/extensions.adoc | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/documentation/server_development/topics/extensions.adoc b/docs/documentation/server_development/topics/extensions.adoc index fcb844da08a9..f8d5180fac1c 100644 --- a/docs/documentation/server_development/topics/extensions.adoc +++ b/docs/documentation/server_development/topics/extensions.adoc @@ -34,6 +34,8 @@ or <<_extensions_jpa,Extending the datamodel with custom JPA entities>>. For details on how to package and deploy a custom provider, refer to the <<_providers,Service Provider Interfaces>> chapter. +NOTE: While it is possible to install other JAX-RS components via the providers extension mechanism, such as filters and interceptors, these are not officially supported. + [[_extensions_spi]] === Add your own custom SPI From 3e4e51577a9c5764c1340d073ad0080cb7006ec9 Mon Sep 17 00:00:00 2001 From: andymunro <48995441+andymunro@users.noreply.github.com> Date: Fri, 22 Mar 2024 03:18:58 -0400 Subject: [PATCH 066/158] Edits to Operator Guide (#28151) Closes #28009 Signed-off-by: AndyMunro Signed-off-by: Alexander Schwartz Co-authored-by: Alexander Schwartz (cherry picked from commit 8602b4f9cfe1f4356c19183ca4c0e2054b6a96cf) --- docs/guides/attributes.adoc | 1 + docs/guides/operator/advanced-configuration.adoc | 3 +-- docs/guides/operator/basic-deployment.adoc | 4 +++- docs/guides/operator/realm-import.adoc | 2 +- 4 files changed, 6 insertions(+), 4 deletions(-) diff --git a/docs/guides/attributes.adoc b/docs/guides/attributes.adoc index b3291dbbf530..ff8e4155fabc 100644 --- a/docs/guides/attributes.adoc +++ b/docs/guides/attributes.adoc @@ -7,3 +7,4 @@ :infinispan-operator-docs: https://infinispan.org/docs/infinispan-operator/main/operator.html :infinispan-xsite-docs: https://infinispan.org/docs/stable/titles/xsite/xsite.html :containerlabel: latest +:apidocs_adminrest_link: https://www.keycloak.org/docs-api/{version}/rest-api/ diff --git a/docs/guides/operator/advanced-configuration.adoc b/docs/guides/operator/advanced-configuration.adoc index 8aea9c296c3f..1ad0091fe48e 100644 --- a/docs/guides/operator/advanced-configuration.adoc +++ b/docs/guides/operator/advanced-configuration.adoc @@ -203,7 +203,6 @@ spec: Moreover, the {project_name} container manages the heap size more effectively by providing relative values for the heap size. It is achieved by providing certain JVM options. -For more details, check the -https://www.keycloak.org/server/containers[Running Keycloak in a container]. +For more details, see <@links.server id="containers" />. diff --git a/docs/guides/operator/basic-deployment.adoc b/docs/guides/operator/basic-deployment.adoc index 7a846826b787..0b62ea552e1f 100644 --- a/docs/guides/operator/basic-deployment.adoc +++ b/docs/guides/operator/basic-deployment.adoc @@ -52,11 +52,13 @@ spec: spec: containers: - name: postgresql-db - image: postgres:latest + image: postgres:15 volumeMounts: - mountPath: /data name: cache-volume env: + - name: POSTGRES_USER + value: testuser - name: POSTGRES_PASSWORD value: testpassword - name: PGDATA diff --git a/docs/guides/operator/realm-import.adoc b/docs/guides/operator/realm-import.adoc index f21a1693d716..5ce4bcee2f9e 100644 --- a/docs/guides/operator/realm-import.adoc +++ b/docs/guides/operator/realm-import.adoc @@ -36,7 +36,7 @@ spec: ---- This CR should be created in the same namespace as the Keycloak Deployment CR, defined in the field `keycloakCRName`. -The `realm` field accepts a full https://www.keycloak.org/docs-api/{version}/rest-api/index.html#RealmRepresentation[RealmRepresentation]. +The `realm` field accepts a full {apidocs_adminrest_link}/index.html#RealmRepresentation[RealmRepresentation]. The recommended way to obtain a `RealmRepresentation` is by leveraging the export functionality <@links.server id="importExport"/>. From 749602c5988efaf210391b16c7b5205839fdc690 Mon Sep 17 00:00:00 2001 From: rmartinc Date: Fri, 22 Mar 2024 10:28:26 +0100 Subject: [PATCH 067/158] Upgrading note to warn truststore changes affect webauthn registration Closes #28113 Signed-off-by: rmartinc (cherry picked from commit d4da0c816c87a62e522cc92d9c89b3f2e52f309d) --- .../upgrading/topics/changes/changes-24_0_0.adoc | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/docs/documentation/upgrading/topics/changes/changes-24_0_0.adoc b/docs/documentation/upgrading/topics/changes/changes-24_0_0.adoc index a46bd8b71093..5eb502c191c3 100644 --- a/docs/documentation/upgrading/topics/changes/changes-24_0_0.adoc +++ b/docs/documentation/upgrading/topics/changes/changes-24_0_0.adoc @@ -183,6 +183,10 @@ The `+spi-truststore-file-*+` options and the truststore related options `+https The `tls-hostname-verifier` property should be used instead of the `spi-truststore-file-hostname-verification-policy` property. +A collateral effect of the changes is that now the truststore provider is always configured with some certificates (at least the default java trusted certificates are present). This new behavior can affect other parts of {project_name}. + +For example, *webauthn* registration can fail if *attestation conveyance* was configured to *Direct* validation. Previously, if the truststore provider was not configured the incoming certificate was not validated. But now this validation is always performed. The registration fails with `invalid cert path` error as the certificate chain sent by the dongle is not trusted by {project_name}. The Certificate Authorities of the authenticator need to be present in the truststore provider to correctly perform the attestation. + = Deprecated `--proxy` option The `--proxy` option has been deprecated and will be removed in a future release. The following table explains how the deprecated option maps to supported options. From 343852b0ef17a86706ff6863c9e834438f87f7b3 Mon Sep 17 00:00:00 2001 From: Erik Jan de Wit Date: Fri, 22 Mar 2024 11:43:54 +0100 Subject: [PATCH 068/158] added "on" label to checkbox (#28121) Signed-off-by: Erik Jan de Wit --- .../src/user-profile/OptionsComponent.tsx | 14 +++++++++++--- .../ui-shared/src/user-profile/SelectComponent.tsx | 7 +++++-- .../src/user-profile/UserProfileFields.tsx | 2 ++ 3 files changed, 18 insertions(+), 5 deletions(-) diff --git a/js/libs/ui-shared/src/user-profile/OptionsComponent.tsx b/js/libs/ui-shared/src/user-profile/OptionsComponent.tsx index f919fdd41166..98f03cf16538 100644 --- a/js/libs/ui-shared/src/user-profile/OptionsComponent.tsx +++ b/js/libs/ui-shared/src/user-profile/OptionsComponent.tsx @@ -1,8 +1,12 @@ import { Checkbox, Radio } from "@patternfly/react-core"; import { Controller } from "react-hook-form"; -import { Options, UserProfileFieldProps } from "./UserProfileFields"; +import { + OptionLabel, + Options, + UserProfileFieldProps, +} from "./UserProfileFields"; import { UserProfileGroup } from "./UserProfileGroup"; -import { fieldName, isRequiredAttribute } from "./utils"; +import { fieldName, isRequiredAttribute, unWrap } from "./utils"; export const OptionComponent = (props: UserProfileFieldProps) => { const { form, inputType, attribute } = props; @@ -12,6 +16,10 @@ export const OptionComponent = (props: UserProfileFieldProps) => { const options = (attribute.validators?.options as Options | undefined)?.options || []; + const optionLabel = attribute.annotations?.[ + "inputOptionLabels" + ] as OptionLabel; + return ( { key={option} id={option} data-testid={option} - label={option} + label={props.t(unWrap(optionLabel?.on || option))} value={option} isChecked={field.value.includes(option)} onChange={() => { diff --git a/js/libs/ui-shared/src/user-profile/SelectComponent.tsx b/js/libs/ui-shared/src/user-profile/SelectComponent.tsx index 6a2222a6c430..edbecb2c2465 100644 --- a/js/libs/ui-shared/src/user-profile/SelectComponent.tsx +++ b/js/libs/ui-shared/src/user-profile/SelectComponent.tsx @@ -1,7 +1,11 @@ import { Select, SelectOption } from "@patternfly/react-core"; import { useState } from "react"; import { Controller, ControllerRenderProps } from "react-hook-form"; -import { Options, UserProfileFieldProps } from "./UserProfileFields"; +import { + OptionLabel, + Options, + UserProfileFieldProps, +} from "./UserProfileFields"; import { UserProfileGroup } from "./UserProfileGroup"; import { UserFormFields, @@ -10,7 +14,6 @@ import { unWrap, } from "./utils"; -type OptionLabel = Record | undefined; export const SelectComponent = (props: UserProfileFieldProps) => { const { t, form, inputType, attribute } = props; const [open, setOpen] = useState(false); diff --git a/js/libs/ui-shared/src/user-profile/UserProfileFields.tsx b/js/libs/ui-shared/src/user-profile/UserProfileFields.tsx index 38f06a5a6e5b..9aef0c72f910 100644 --- a/js/libs/ui-shared/src/user-profile/UserProfileFields.tsx +++ b/js/libs/ui-shared/src/user-profile/UserProfileFields.tsx @@ -54,6 +54,8 @@ export type UserProfileFieldProps = { renderer?: (attribute: UserProfileAttributeMetadata) => ReactNode; }; +export type OptionLabel = Record | undefined; + export const FIELDS: { [type in InputType]: (props: UserProfileFieldProps) => JSX.Element; } = { From e603366da7f6e6460caea25b0b4437e25b9bfde4 Mon Sep 17 00:00:00 2001 From: Steven Hawkins Date: Fri, 22 Mar 2024 07:36:10 -0400 Subject: [PATCH 069/158] doc: add keycloak cr truststores (#28015) (#28168) closes: #27892 Signed-off-by: Steve Hawkins (cherry picked from commit 6cc66109d513458935357decad93b706158ae3d2) --- .../operator/advanced-configuration.adoc | 37 +++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/docs/guides/operator/advanced-configuration.adoc b/docs/guides/operator/advanced-configuration.adoc index 1ad0091fe48e..206c23458fb9 100644 --- a/docs/guides/operator/advanced-configuration.adoc +++ b/docs/guides/operator/advanced-configuration.adoc @@ -205,4 +205,41 @@ It is achieved by providing certain JVM options. For more details, see <@links.server id="containers" />. +=== Truststores + +If you need to provide trusted certificates, the Keycloak CR provides a top level feature for configuring the server's truststore as discussed in <@links.server id="keycloak-truststore"/>. + +Use the truststores stanza of the Keycloak spec to specify Secrets containing PEM encoded files, or PKCS12 files with extension `.p12` or `.pfx`, for example: + +[source,yaml] +---- +apiVersion: k8s.keycloak.org/v2alpha1 +kind: Keycloak +metadata: + name: example-kc +spec: + ... + truststores: + my-truststore: + secret: + name: my-secret +---- + +Where the contents of my-secret could be a PEM file, for example: + +[source,yaml] +------ +apiVersion: v1 +kind: Secret +metadata: + name: my-secret +stringData: + cert.pem: | + -----BEGIN CERTIFICATE----- + ... +------ + +When running on a Kubernetes or OpenShift environment well-known locations of trusted certificates are included automatically. +This includes /var/run/secrets/kubernetes.io/serviceaccount/ca.crt and the /var/run/secrets/kubernetes.io/serviceaccount/service-ca.crt when present. + From d2a864ed29ddda23532348426d94add2a34fce03 Mon Sep 17 00:00:00 2001 From: rmartinc Date: Wed, 20 Mar 2024 22:15:46 +0100 Subject: [PATCH 070/158] ORA-01450 error for index IDX_CLIENT_ATT_BY_NAME_VALUE in oracle when MAX_STRING_SIZE is EXTENDED Closes #27967 Signed-off-by: rmartinc (cherry picked from commit 220564c7ba9584d6681e6d34bc073a175c44cd50) --- .../keycloak/models/jpa/JpaRealmProvider.java | 13 +++++++------ .../META-INF/jpa-changelog-24.0.0.xml | 5 +---- .../META-INF/jpa-changelog-24.0.2.xml | 19 +++++++++++++++++++ 3 files changed, 27 insertions(+), 10 deletions(-) diff --git a/model/jpa/src/main/java/org/keycloak/models/jpa/JpaRealmProvider.java b/model/jpa/src/main/java/org/keycloak/models/jpa/JpaRealmProvider.java index 0dd32b2a10e6..c1df1e2a9ad4 100644 --- a/model/jpa/src/main/java/org/keycloak/models/jpa/JpaRealmProvider.java +++ b/model/jpa/src/main/java/org/keycloak/models/jpa/JpaRealmProvider.java @@ -865,12 +865,13 @@ public Stream searchClientsByAttributes(RealmModel realm, Map + 9:bd2bd0fc7768cf0845ac96a8786fa735 - @@ -81,9 +81,6 @@ - - - diff --git a/model/jpa/src/main/resources/META-INF/jpa-changelog-24.0.2.xml b/model/jpa/src/main/resources/META-INF/jpa-changelog-24.0.2.xml index 9e98c27f021e..1b13a52ef45e 100644 --- a/model/jpa/src/main/resources/META-INF/jpa-changelog-24.0.2.xml +++ b/model/jpa/src/main/resources/META-INF/jpa-changelog-24.0.2.xml @@ -21,4 +21,23 @@ + + + + + + + + + + + + + + + + + + + From c3c3b2cbe098332b054353fd0656d7eeff355585 Mon Sep 17 00:00:00 2001 From: Lukas Hanusovsky <61745358+lhanusov@users.noreply.github.com> Date: Fri, 22 Mar 2024 13:25:31 +0100 Subject: [PATCH 071/158] surefire reports new release 24 Closes #28167 Signed-off-by: Lukas Hanusovsky (cherry picked from commit 31293d36e857b6bb10e5a797bb0154411011aa09) --- .github/actions/archive-surefire-reports/action.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/actions/archive-surefire-reports/action.yml b/.github/actions/archive-surefire-reports/action.yml index a9104e66bcef..5862825479a6 100644 --- a/.github/actions/archive-surefire-reports/action.yml +++ b/.github/actions/archive-surefire-reports/action.yml @@ -7,7 +7,7 @@ inputs: release-branches: description: 'List of all related release branches (in JSON format)' required: false - default: '["refs/heads/release/22.0"]' + default: '["refs/heads/release/22.0","refs/heads/release/24.0"]' keep-days: description: 'For how many days to store the particular artifact.' required: false From 9d9817e15a07195f16f554b7f60ee3a918369e26 Mon Sep 17 00:00:00 2001 From: Jon Koops Date: Sat, 23 Mar 2024 08:23:14 +0100 Subject: [PATCH 072/158] Limit requests sent through session status iframe (#132) Closes #116 Signed-off-by: Jon Koops --- .../protocol/oidc/endpoints/login-status-iframe.html | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/services/src/main/resources/org/keycloak/protocol/oidc/endpoints/login-status-iframe.html b/services/src/main/resources/org/keycloak/protocol/oidc/endpoints/login-status-iframe.html index ee640cffa0fa..73ac4bc95355 100755 --- a/services/src/main/resources/org/keycloak/protocol/oidc/endpoints/login-status-iframe.html +++ b/services/src/main/resources/org/keycloak/protocol/oidc/endpoints/login-status-iframe.html @@ -28,6 +28,7 @@ } let init; + let preventAdditionalRequests = false; async function checkState(clientId, origin, sessionState) { // Check if the browser has granted us access to 3rd-party storage (such as cookies). @@ -41,6 +42,13 @@ // If not initialized, verify this client is allowed access with a call to the server. if (!init) { + // Prevent additional requests to the server to avoid potential DoS attacks. + if (preventAdditionalRequests) { + return "error"; + } else { + preventAdditionalRequests = true; + } + const url = new URL(`${location.origin}${location.pathname}/init`); url.searchParams.set("client_id", clientId); From 4ffb69ecefce155f297d3bb9f6ecc8fa8600d308 Mon Sep 17 00:00:00 2001 From: Ricardo Martin Date: Sat, 23 Mar 2024 15:09:31 +0100 Subject: [PATCH 073/158] Perform exact string match if redirect URI contains userinfo, encoded slashes or parent access (#131) Closes keycloak/keycloak-private#113 Closes keycloak/keycloak-private#134 Signed-off-by: rmartinc Co-authored-by: Stian Thorgersen --- .../java/org/keycloak/common/util/Encode.java | 81 ++++++++++++++++ .../common/util/KeycloakUriBuilder.java | 40 +++++++- .../common/util/KeycloakUriBuilderTest.java | 12 +++ .../clients/oidc/con-basic-settings.adoc | 8 +- .../topics/changes/changes-24_0_3.adoc | 7 ++ .../upgrading/topics/changes/changes.adoc | 4 + .../protocol/oidc/utils/RedirectUtils.java | 97 +++++-------------- .../oidc/utils/RedirectUtilsTest.java | 41 +++++--- .../testsuite/oauth/OAuthRedirectUriTest.java | 12 +-- .../account/src/app/util/RedirectUri.ts | 4 +- 10 files changed, 212 insertions(+), 94 deletions(-) create mode 100644 docs/documentation/upgrading/topics/changes/changes-24_0_3.adoc diff --git a/common/src/main/java/org/keycloak/common/util/Encode.java b/common/src/main/java/org/keycloak/common/util/Encode.java index b17d61be5bf5..682d29fedaab 100755 --- a/common/src/main/java/org/keycloak/common/util/Encode.java +++ b/common/src/main/java/org/keycloak/common/util/Encode.java @@ -46,6 +46,7 @@ public class Encode private static final String[] matrixParameterEncoding = new String[128]; private static final String[] queryNameValueEncoding = new String[128]; private static final String[] queryStringEncoding = new String[128]; + private static final String[] userInfoStringEncoding = new String[128]; static { @@ -158,6 +159,44 @@ public class Encode } queryStringEncoding[i] = URLEncoder.encode(String.valueOf((char) i)); } + + /* + * userinfo = *( unreserved / pct-encoded / sub-delims / ":" ) + * unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~" + * pct-encoded = "%" HEXDIG HEXDIG + * sub-delims = "!" / "$" / "&" / "'" / "(" / ")" + * / "*" / "+" / "," / ";" / "=" + */ + for (int i = 0; i < 128; i++) + { + if (i >= 'a' && i <= 'z') continue; + if (i >= 'A' && i <= 'Z') continue; + if (i >= '0' && i <= '9') continue; + switch ((char) i) + { + case '-': + case '.': + case '_': + case '~': + case '!': + case '$': + case '&': + case '\'': + case '(': + case ')': + case '*': + case '+': + case ',': + case ';': + case '=': + case ':': + continue; + case ' ': + userInfoStringEncoding[i] = "%20"; + continue; + } + userInfoStringEncoding[i] = URLEncoder.encode(String.valueOf((char) i)); + } } /** @@ -177,6 +216,24 @@ public static String encodeQueryStringNotTemplateParameters(String value) { return encodeNonCodes(encodeFromArray(value, queryStringEncoding, false)); } + /** + * Keep encoded values "%..." and template parameters intact. + * @param value The user-info value to encode + * @return The user-info encoded + */ + public static String encodeUserInfo(String value) { + return encodeValue(value, userInfoStringEncoding); + } + + /** + * Keep encoded values "%..." but not the template parameters. + * @param value The user-info to encode + * @return The user-info encoded + */ + public static String encodeUserInfoNotTemplateParameters(String value) { + return encodeNonCodes(encodeFromArray(value, userInfoStringEncoding, false)); + } + /** * Keep encoded values "%...", matrix parameters, template parameters, and '/' characters intact. */ @@ -424,6 +481,30 @@ public static String encodeQueryParamSaveEncodings(String segment) return result; } + /** + * Encodes everything in user-info + * + * @param nameOrValue + * @return + */ + public static String encodeUserInfoAsIs(String nameOrValue) + { + return encodeFromArray(nameOrValue, userInfoStringEncoding, true); + } + + /** + * Keep any valid encodings from string i.e. keep "%2D" but don't keep "%p" + * + * @param segment + * @return + */ + public static String encodeUserInfoSaveEncodings(String segment) + { + String result = encodeFromArray(segment, userInfoStringEncoding, false); + result = encodeNonCodes(result); + return result; + } + public static String encodeFragmentAsIs(String nameOrValue) { return encodeFromArray(nameOrValue, queryNameValueEncoding, true); diff --git a/common/src/main/java/org/keycloak/common/util/KeycloakUriBuilder.java b/common/src/main/java/org/keycloak/common/util/KeycloakUriBuilder.java index 659c1d8c418c..5b27f988796c 100755 --- a/common/src/main/java/org/keycloak/common/util/KeycloakUriBuilder.java +++ b/common/src/main/java/org/keycloak/common/util/KeycloakUriBuilder.java @@ -150,7 +150,7 @@ protected KeycloakUriBuilder parseHierarchicalUri(String uri, Matcher match, boo if (at > -1) { String user = host.substring(0, at); host = host.substring(at + 1); - this.userInfo = user; + replaceUserInfo(user, template); } Matcher hostPortMatch = hostPortPattern.matcher(host); if (hostPortMatch.matches()) { @@ -459,7 +459,7 @@ private String buildString(Map paramMap, boolean fromEncodedMap, bool } else if (userInfo != null || host != null || port != -1) { buffer.append("//"); if (userInfo != null) - replaceParameter(paramMap, fromEncodedMap, isTemplate, userInfo, buffer, encodeSlash).append("@"); + replaceUserInfoParameter(paramMap, fromEncodedMap, isTemplate, userInfo, buffer).append("@"); if (host != null) { if ("".equals(host)) throw new RuntimeException("empty host name"); replaceParameter(paramMap, fromEncodedMap, isTemplate, host, buffer, encodeSlash); @@ -571,6 +571,33 @@ protected StringBuffer replaceQueryStringParameter(Map paramMap, bool return buffer; } + protected StringBuffer replaceUserInfoParameter(Map paramMap, boolean fromEncodedMap, boolean isTemplate, String string, StringBuffer buffer) { + Matcher matcher = createUriParamMatcher(string); + while (matcher.find()) { + String param = matcher.group(1); + Object valObj = paramMap.get(param); + if (valObj == null && !isTemplate) { + throw new IllegalArgumentException("NULL value for template parameter: " + param); + } else if (valObj == null && isTemplate) { + matcher.appendReplacement(buffer, matcher.group()); + continue; + } + String value = valObj.toString(); + if (value != null) { + if (!fromEncodedMap) { + value = Encode.encodeUserInfoAsIs(value); + } else { + value = Encode.encodeUserInfoSaveEncodings(value); + } + matcher.appendReplacement(buffer, value); + } else { + throw new IllegalArgumentException("path param " + param + " has not been provided by the parameter map"); + } + } + matcher.appendTail(buffer); + return buffer; + } + /** * Return a unique order list of path params * @@ -742,6 +769,15 @@ public KeycloakUriBuilder replacePath(String path, boolean template) { return this; } + public KeycloakUriBuilder replaceUserInfo(String userInfo, boolean template) { + if (userInfo == null) { + this.userInfo = null; + return this; + } + this.userInfo = template? Encode.encodeUserInfo(userInfo) : Encode.encodeUserInfoNotTemplateParameters(userInfo); + return this; + } + public URI build(Object[] values, boolean encodeSlashInPath) throws IllegalArgumentException { if (values == null) throw new IllegalArgumentException("values param is null"); return buildFromValues(encodeSlashInPath, false, values); diff --git a/common/src/test/java/org/keycloak/common/util/KeycloakUriBuilderTest.java b/common/src/test/java/org/keycloak/common/util/KeycloakUriBuilderTest.java index 5e418d73e5de..950b3db7c0ab 100644 --- a/common/src/test/java/org/keycloak/common/util/KeycloakUriBuilderTest.java +++ b/common/src/test/java/org/keycloak/common/util/KeycloakUriBuilderTest.java @@ -80,4 +80,16 @@ public void testTemplateAndNotTemplate() { Assert.assertEquals("https://localhost:8443/%7Bpath%7D?key=%7Bquery%7D#%7Bfragment%7D", KeycloakUriBuilder.fromUri( "https://localhost:8443/{path}?key={query}#{fragment}", false).buildAsString()); } + + @Test + public void testUserInfo() { + Assert.assertEquals("https://user-info@localhost:8443/path?key=query#fragment", KeycloakUriBuilder.fromUri( + "https://{userinfo}@localhost:8443/{path}?key={query}#{fragment}").buildAsString("user-info", "path", "query", "fragment")); + Assert.assertEquals("https://user%20info%40%2F@localhost:8443/path?key=query#fragment", KeycloakUriBuilder.fromUri( + "https://{userinfo}@localhost:8443/{path}?key={query}#{fragment}").buildAsString("user info@/", "path", "query", "fragment")); + Assert.assertEquals("https://user-info%E2%82%AC@localhost:8443", KeycloakUriBuilder.fromUri( + "https://user-info%E2%82%AC@localhost:8443", false).buildAsString()); + Assert.assertEquals("https://user-info%E2%82%AC@localhost:8443", KeycloakUriBuilder.fromUri( + "https://user-info€@localhost:8443", false).buildAsString()); + } } diff --git a/docs/documentation/server_admin/topics/clients/oidc/con-basic-settings.adoc b/docs/documentation/server_admin/topics/clients/oidc/con-basic-settings.adoc index 6732d45c602b..4a35b8c290f6 100644 --- a/docs/documentation/server_admin/topics/clients/oidc/con-basic-settings.adoc +++ b/docs/documentation/server_admin/topics/clients/oidc/con-basic-settings.adoc @@ -24,9 +24,13 @@ the name, set up a replacement string value. For example, a string value such as *Home URL*:: Provides the default URL for when the auth server needs to redirect or link back to the client. -*Valid Redirect URIs*:: Required field. Enter a URL pattern and click *+* to add and *-* to remove existing URLs and click *Save*. You can use wildcards at the end of the URL pattern. For example $$http://host.com/*$$ +*Valid Redirect URIs*:: Required field. Enter a URL pattern and click *+* to add and *-* to remove existing URLs and click *Save*. Exact (case sensitive) string matching is used to compare valid redirect URIs. + -Exclusive redirect URL patterns are typically more secure. See xref:unspecific-redirect-uris_{context}[Unspecific Redirect URIs] for more information. +You can use wildcards at the end of the URL pattern. For example `$$http://host.com/path/*$$`. To avoid security issues, if the passed redirect URI contains the *userinfo* part or its *path* manages access to parent directory (`/../`) no wildcard comparison is performed but the standard and secure exact string matching. ++ +The full wildcard `$$*$$` valid redirect URI can also be configured to allow any *http* or *https* redirect URI. Please do not use it in production environments. ++ +Exclusive redirect URI patterns are typically more secure. See xref:unspecific-redirect-uris_{context}[Unspecific Redirect URIs] for more information. Web Origins:: Enter a URL pattern and click + to add and - to remove existing URLs. Click Save. + diff --git a/docs/documentation/upgrading/topics/changes/changes-24_0_3.adoc b/docs/documentation/upgrading/topics/changes/changes-24_0_3.adoc new file mode 100644 index 000000000000..27265c6c4cef --- /dev/null +++ b/docs/documentation/upgrading/topics/changes/changes-24_0_3.adoc @@ -0,0 +1,7 @@ += Changes in redirect URI verification when using wildcards + +Because of security concerns, the redirect URI verification now performs a exact string matching (no wildcard involved) if the passed redirect uri contains a `userinfo` part or its `path` accesses parent directory (`/../`). + +The full wildcard `*` can still be used as a valid redirect in development for http(s) URIs with those characteristics. In production environments a exact valid redirect URI without wildcard needs to be configured for any URI of that type. + +Please note that wildcard valid redirect URIs are not recommended for production and not covered by the OAuth 2.0 specification. diff --git a/docs/documentation/upgrading/topics/changes/changes.adoc b/docs/documentation/upgrading/topics/changes/changes.adoc index 78f0f0817d8a..62f7cbd487de 100644 --- a/docs/documentation/upgrading/topics/changes/changes.adoc +++ b/docs/documentation/upgrading/topics/changes/changes.adoc @@ -1,6 +1,10 @@ [[migration-changes]] == Migration Changes +=== Migrating to 24.0.3 + +include::changes-24_0_3.adoc[leveloffset=3] + === Migrating to 24.0.2 include::changes-24_0_2.adoc[leveloffset=3] diff --git a/services/src/main/java/org/keycloak/protocol/oidc/utils/RedirectUtils.java b/services/src/main/java/org/keycloak/protocol/oidc/utils/RedirectUtils.java index e171d8da8951..de8fd84e0a56 100644 --- a/services/src/main/java/org/keycloak/protocol/oidc/utils/RedirectUtils.java +++ b/services/src/main/java/org/keycloak/protocol/oidc/utils/RedirectUtils.java @@ -18,8 +18,6 @@ package org.keycloak.protocol.oidc.utils; import org.jboss.logging.Logger; -import org.keycloak.common.util.Encode; -import org.keycloak.common.util.KeycloakUriBuilder; import org.keycloak.common.util.UriUtils; import org.keycloak.models.ClientModel; import org.keycloak.models.Constants; @@ -34,6 +32,7 @@ import java.util.Collection; import java.util.Set; import java.util.TreeSet; +import java.util.regex.Pattern; import java.util.stream.Collectors; /** @@ -111,16 +110,13 @@ public static String verifyRedirectUri(KeycloakSession session, String rootUrl, return null; } - // Make the validations against fully decoded and normalized redirect-url. This also allows wildcards (case when client configured "Valid redirect-urls" contain wildcards) - String decodedRedirectUri = decodeRedirectUri(redirectUri); - URI decodedRedirect = toUri(decodedRedirectUri); - decodedRedirectUri = getNormalizedRedirectUri(decodedRedirect); - if (decodedRedirectUri == null) return null; + // check if the passed URI allows wildcards + boolean allowWildcards = areWildcardsAllowed(originalRedirect); - String r = decodedRedirectUri; + String r = redirectUri; Set resolveValidRedirects = resolveValidRedirects(session, rootUrl, validRedirects); - String valid = matchesRedirects(resolveValidRedirects, r, true); + String valid = matchesRedirects(resolveValidRedirects, r, allowWildcards); if (valid == null && (r.startsWith(Constants.INSTALLED_APP_URL) || r.startsWith(Constants.INSTALLED_APP_LOOPBACK)) && r.indexOf(':', Constants.INSTALLED_APP_URL.length()) >= 0) { int i = r.indexOf(':', Constants.INSTALLED_APP_URL.length()); @@ -135,15 +131,7 @@ public static String verifyRedirectUri(KeycloakSession session, String rootUrl, r = sb.toString(); - valid = matchesRedirects(resolveValidRedirects, r, true); - } - - // Return the original redirectUri, which can be partially encoded - for example http://localhost:8280/foo/bar%20bar%2092%2F72/3 . Just make sure it is normalized - redirectUri = getNormalizedRedirectUri(originalRedirect); - - // We try to check validity also for original (encoded) redirectUrl, but just in case it exactly matches some "Valid Redirect URL" specified for client (not wildcards allowed) - if (valid == null) { - valid = matchesRedirects(resolveValidRedirects, redirectUri, false); + valid = matchesRedirects(resolveValidRedirects, r, allowWildcards); } if (valid != null && !originalRedirect.isAbsolute()) { @@ -154,7 +142,7 @@ public static String verifyRedirectUri(KeycloakSession session, String rootUrl, redirectUri = relativeToAbsoluteURI(session, rootUrl, redirectUri); } - String scheme = decodedRedirect.getScheme(); + String scheme = originalRedirect.getScheme(); if (valid != null && scheme != null) { // check the scheme is valid, it should be http(s) or explicitly allowed by the validation if (!valid.startsWith(scheme + ":") && !"http".equalsIgnoreCase(scheme) && !"https".equalsIgnoreCase(scheme)) { @@ -179,51 +167,22 @@ private static URI toUri(String redirectUri) { try { uri = URI.create(redirectUri); } catch (IllegalArgumentException cause) { - logger.debug("Invalid redirect uri", cause); + logger.debugf(cause, "Invalid redirect uri %s", redirectUri); } catch (Exception cause) { - logger.debug("Unexpected error when parsing redirect uri", cause); + logger.debugf(cause, "Unexpected error when parsing redirect uri %s", redirectUri); } } return uri; } - private static String getNormalizedRedirectUri(URI uri) { - String redirectUri = null; - if (uri != null) { - redirectUri = uri.normalize().toString(); - } - return redirectUri; - } - - // Decode redirectUri. Only path is decoded as other elements can be encoded in the original URL or cannot be encoded at all. - // URL can be decoded multiple times (in case it was encoded multiple times, or some of it's parts were encoded multiple times) - private static String decodeRedirectUri(String redirectUri) { - if (redirectUri == null) return null; - int MAX_DECODING_COUNT = 5; // Max count of attempts for decoding URL (in case it was encoded multiple times) + // any access to parent folder /../ is unsafe with or without encoding + private final static Pattern UNSAFE_PATH_PATTERN = Pattern.compile( + "(/|%2[fF]|%5[cC]|\\\\)(%2[eE]|\\.){2}(/|%2[fF]|%5[cC]|\\\\)|(/|%2[fF]|%5[cC]|\\\\)(%2[eE]|\\.){2}$"); - try { - KeycloakUriBuilder uriBuilder = KeycloakUriBuilder.fromUri(redirectUri, false).preserveDefaultPort(); - if (uriBuilder.getPath() == null) { - return redirectUri; - } - String encodedPath = uriBuilder.getPath(); - String decodedPath; - - for (int i = 0; i < MAX_DECODING_COUNT; i++) { - decodedPath = Encode.decode(encodedPath); - if (decodedPath.equals(encodedPath)) { - // URL path is decoded. We can return it in the original redirect URI - return uriBuilder.replacePath(decodedPath, false).buildAsString(); - } else { - // Next attempt - encodedPath = decodedPath; - } - } - } catch (IllegalArgumentException iae) { - logger.debugf("Illegal redirect URI used: %s, Details: %s", redirectUri, iae.getMessage()); - } - logger.debugf("Was not able to decode redirect URI: %s", redirectUri); - return null; + private static boolean areWildcardsAllowed(URI redirectUri) { + // wildcars are only allowed if no user-info and no unsafe pattern in path + return redirectUri.getRawUserInfo() == null + && (redirectUri.getRawPath() == null || !UNSAFE_PATH_PATTERN.matcher(redirectUri.getRawPath()).find()); } private static String relativeToAbsoluteURI(KeycloakSession session, String rootUrl, String relative) { @@ -240,24 +199,20 @@ private static String relativeToAbsoluteURI(KeycloakSession session, String root return sb.toString(); } - // removes the queryString, fragment and userInfo from the redirect - // to avoid comparing this when wildcards are used - private static String stripOffRedirectForWildcard(String redirect) { - return KeycloakUriBuilder.fromUri(redirect, false) - .preserveDefaultPort() - .userInfo(null) - .replaceQuery(null) - .fragment(null) - .buildAsString(); - } - // return the String that matched the redirect or null if not matched private static String matchesRedirects(Set validRedirects, String redirect, boolean allowWildcards) { logger.tracef("matchesRedirects: redirect URL to check: %s, allow wildcards: %b, Configured valid redirect URLs: %s", redirect, allowWildcards, validRedirects); for (String validRedirect : validRedirects) { - if (validRedirect.endsWith("*") && !validRedirect.contains("?") && allowWildcards) { - // strip off the userInfo, query or fragment components - we don't check them when wildcards are effective - String r = stripOffRedirectForWildcard(redirect); + if ("*".equals(validRedirect)) { + // the valid redirect * is a full wildcard for http(s) even if the redirect URI does not allow wildcards + return validRedirect; + } else if (validRedirect.endsWith("*") && !validRedirect.contains("?") && allowWildcards) { + // strip off the query or fragment components - we don't check them when wildcards are effective + int idx = redirect.indexOf('?'); + if (idx == -1) { + idx = redirect.indexOf('#'); + } + String r = idx == -1 ? redirect : redirect.substring(0, idx); // strip off * int length = validRedirect.length() - 1; validRedirect = validRedirect.substring(0, length); diff --git a/services/src/test/java/org/keycloak/protocol/oidc/utils/RedirectUtilsTest.java b/services/src/test/java/org/keycloak/protocol/oidc/utils/RedirectUtilsTest.java index 814d29920690..fcc501934d3c 100644 --- a/services/src/test/java/org/keycloak/protocol/oidc/utils/RedirectUtilsTest.java +++ b/services/src/test/java/org/keycloak/protocol/oidc/utils/RedirectUtilsTest.java @@ -87,6 +87,8 @@ public void testverifyRedirectUriMixedSchemes() { Assert.assertEquals("custom1:/parent/child", RedirectUtils.verifyRedirectUri(session, null, "custom1:/parent/child", set, false)); Assert.assertEquals("custom2:/something", RedirectUtils.verifyRedirectUri(session, null, "custom2:/something", set, false)); Assert.assertEquals("https://keycloak.org/test", RedirectUtils.verifyRedirectUri(session, null, "https://keycloak.org/test", set, false)); + Assert.assertEquals("https://keycloak.org/", RedirectUtils.verifyRedirectUri(session, null, "https://keycloak.org/", set, false)); + Assert.assertEquals("https://keycloak.org", RedirectUtils.verifyRedirectUri(session, null, "https://keycloak.org", set, false)); Assert.assertNull(RedirectUtils.verifyRedirectUri(session, null, "custom1:/test", set, false)); Assert.assertNull(RedirectUtils.verifyRedirectUri(session, null, "custom1:/test1/test", set, false)); @@ -172,6 +174,8 @@ public void testRelativeRedirectUri() { Assert.assertEquals("https://keycloak.org/path", RedirectUtils.verifyRedirectUri(session, "https://keycloak.org", "/path", set, false)); Assert.assertEquals("https://keycloak.org/path", RedirectUtils.verifyRedirectUri(session, "https://keycloak.org", "path", set, false)); + Assert.assertEquals("https://keycloak.org/test/../other", RedirectUtils.verifyRedirectUri(session, null, "https://keycloak.org/test/../other", set, false)); + Assert.assertEquals("http://keycloak.org/test%2Fother", RedirectUtils.verifyRedirectUri(session, null, "http://keycloak.org/test%2Fother", set, false)); } @Test @@ -184,8 +188,6 @@ public void testUserInfo() { Assert.assertEquals("https://keycloak.org/index.html", RedirectUtils.verifyRedirectUri(session, null, "https://keycloak.org/index.html", set, false)); Assert.assertEquals("https://test.com/index.html", RedirectUtils.verifyRedirectUri(session, null, "https://test.com/index.html", set, false)); - Assert.assertEquals("https://something@keycloak.org/path", RedirectUtils.verifyRedirectUri(session, null, "https://something@keycloak.org/path", set, false)); - Assert.assertEquals("https://some%20thing@test.com/path", RedirectUtils.verifyRedirectUri(session, null, "https://some%20thing@test.com/path", set, false)); Assert.assertEquals("https://something@keycloak.com/exact", RedirectUtils.verifyRedirectUri(session, null, "https://something@keycloak.com/exact", set, false)); Assert.assertNull(RedirectUtils.verifyRedirectUri(session, null, "https://something@other.com/", set, false)); @@ -193,12 +195,15 @@ public void testUserInfo() { Assert.assertNull(RedirectUtils.verifyRedirectUri(session, null, "https://keycloak.org%2F@other.com", set, false)); Assert.assertNull(RedirectUtils.verifyRedirectUri(session, null, "https://test@other.com", set, false)); Assert.assertNull(RedirectUtils.verifyRedirectUri(session, null, "https://test.com@other.com", set, false)); + Assert.assertNull(RedirectUtils.verifyRedirectUri(session, null, "https://something@keycloak.org/path", set, false)); + Assert.assertNull(RedirectUtils.verifyRedirectUri(session, null, "https://some%20thing@test.com/path", set, false)); } @Test public void testEncodedRedirectUri() { Set set = Stream.of( - "https://keycloak.org/test/*" + "https://keycloak.org/test/*", + "https://keycloak.org/exact/%5C%2F/.." ).collect(Collectors.toSet()); Assert.assertEquals("https://keycloak.org/test/index.html", RedirectUtils.verifyRedirectUri(session, null, "https://keycloak.org/test/index.html", set, false)); @@ -206,16 +211,30 @@ public void testEncodedRedirectUri() { Assert.assertEquals("https://keycloak.org/test?encodeTest=a%3Cb#encode2=a%3Cb", RedirectUtils.verifyRedirectUri(session, null, "https://keycloak.org/test?encodeTest=a%3Cb#encode2=a%3Cb", set, false)); Assert.assertEquals("https://keycloak.org/test/#encode2=a%3Cb", RedirectUtils.verifyRedirectUri(session, null, "https://keycloak.org/test/#encode2=a%3Cb", set, false)); - Assert.assertNull(RedirectUtils.verifyRedirectUri(session, null, "https://keycloak.org/test/../", set, false)); // direct - Assert.assertNull(RedirectUtils.verifyRedirectUri(session, null, "https://keycloak.org/test/%2E%2E/", set, false)); // encoded - Assert.assertNull(RedirectUtils.verifyRedirectUri(session, null, "https://keycloak.org/test%2F%2E%2E%2F", set, false)); // encoded - Assert.assertNull(RedirectUtils.verifyRedirectUri(session, null, "https://keycloak.org/test/%252E%252E/", set, false)); // double-encoded - Assert.assertNull(RedirectUtils.verifyRedirectUri(session, null, "https://keycloak.org/test/%252E%252E/?some_query_param=some_value", set, false)); // double-encoded - Assert.assertNull(RedirectUtils.verifyRedirectUri(session, null, "https://keycloak.org/test/%252E%252E/#encodeTest=a%3Cb", set, false)); // double-encoded - Assert.assertNull(RedirectUtils.verifyRedirectUri(session, null, "https://keycloak.org/test/%25252E%25252E/", set, false)); // triple-encoded - Assert.assertNull(RedirectUtils.verifyRedirectUri(session, null, "https://keycloak.org/test/%2525252525252E%2525252525252E/", set, false)); // seventh-encoded + Assert.assertNull(RedirectUtils.verifyRedirectUri(session, null, "https://keycloak.org/test/../", set, false)); + Assert.assertNull(RedirectUtils.verifyRedirectUri(session, null, "https://keycloak.org/test\\..\\", set, false)); + Assert.assertNull(RedirectUtils.verifyRedirectUri(session, null, "https://keycloak.org/test/%2E%2E/", set, false)); + Assert.assertNull(RedirectUtils.verifyRedirectUri(session, null, "https://keycloak.org/test/%2e%2e/", set, false)); + Assert.assertNull(RedirectUtils.verifyRedirectUri(session, null, "https://keycloak.org/test/%2E./", set, false)); + Assert.assertNull(RedirectUtils.verifyRedirectUri(session, null, "https://keycloak.org/test/%2E.", set, false)); + Assert.assertNull(RedirectUtils.verifyRedirectUri(session, null, "https://keycloak.org/test\\%2E.", set, false)); + Assert.assertNull(RedirectUtils.verifyRedirectUri(session, null, "https://keycloak.org/test%2f%2E%2e%2F", set, false)); + Assert.assertNull(RedirectUtils.verifyRedirectUri(session, null, "https://keycloak.org/test%5C%2E.%5c", set, false)); + Assert.assertNull(RedirectUtils.verifyRedirectUri(session, null, "https://keycloak.org/test%5C..", set, false)); + Assert.assertNull(RedirectUtils.verifyRedirectUri(session, null, "https://keycloak.org/test/%2F%2E%2E%2Fdocumentation", set, false)); + Assert.assertEquals("https://keycloak.org/test/.../", RedirectUtils.verifyRedirectUri(session, null, "https://keycloak.org/test/.../", set, false)); + Assert.assertEquals("https://keycloak.org/test/%2E../", RedirectUtils.verifyRedirectUri(session, null, "https://keycloak.org/test/%2E../", set, false)); // encoded + Assert.assertEquals("https://keycloak.org/test/some%2Fthing/", RedirectUtils.verifyRedirectUri(session, null, "https://keycloak.org/test/some%2Fthing/", set, false)); // encoded + Assert.assertEquals("https://keycloak.org/test/./", RedirectUtils.verifyRedirectUri(session, null, "https://keycloak.org/test/./", set, false)); + Assert.assertEquals("https://keycloak.org/test/%252E%252E/", RedirectUtils.verifyRedirectUri(session, null, "https://keycloak.org/test/%252E%252E/", set, false)); // double-encoded + Assert.assertEquals("https://keycloak.org/test/%252E%252E/#encodeTest=a%3Cb", RedirectUtils.verifyRedirectUri(session, null, "https://keycloak.org/test/%252E%252E/#encodeTest=a%3Cb", set, false)); // double-encoded + Assert.assertEquals("https://keycloak.org/test/%25252E%25252E/", RedirectUtils.verifyRedirectUri(session, null, "https://keycloak.org/test/%25252E%25252E/", set, false)); // triple-encoded + Assert.assertEquals("https://keycloak.org/exact/%5C%2F/..", RedirectUtils.verifyRedirectUri(session, null, "https://keycloak.org/exact/%5C%2F/..", set, false)); Assert.assertNull(RedirectUtils.verifyRedirectUri(session, null, "https://keycloak%2Eorg/test/", set, false)); Assert.assertNull(RedirectUtils.verifyRedirectUri(session, null, "https://keycloak.org%2Ftest%2F%40sample.com", set, false)); + + Assert.assertNull(RedirectUtils.verifyRedirectUri(session, null, "https://keycloak.org/test%2Fanother/../any/path/", set, false)); + Assert.assertNull(RedirectUtils.verifyRedirectUri(session, null, "https://keycloak.org/test%2Fanother/%2E%2E/any/path/", set, false)); } } diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/oauth/OAuthRedirectUriTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/oauth/OAuthRedirectUriTest.java index 842b7b5f2f7a..313ffce5f1ff 100755 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/oauth/OAuthRedirectUriTest.java +++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/oauth/OAuthRedirectUriTest.java @@ -351,12 +351,12 @@ public void testWildcard() throws IOException { checkRedirectUri("http://example.com/foo/../", false); checkRedirectUri("http://example.com/foo/%2E%2E/", false); // url-encoded "http://example.com/foobar/../" checkRedirectUri("http://example.com/foo%2F%2E%2E%2F", false); // url-encoded "http://example.com/foobar/../" - checkRedirectUri("http://example.com/foo/%252E%252E/", false); // double-encoded "http://example.com/foobar/../" - checkRedirectUri("http://example.com/foo/%252E%252E/?some_query_param=some_value", false); // double-encoded "http://example.com/foobar/../?some_query_param=some_value" - checkRedirectUri("http://example.com/foo/%252E%252E/?encodeTest=a%3Cb", false); // double-encoded "http://example.com/foobar/../?encodeTest=a { redirectUri += "?referrer=" + referrer + "&referrer_uri=" + referrerUri.replace('#', '_hash_'); } - return encodeURIComponent(redirectUri) + encodeURIComponent("/#" + currentLocation); -} \ No newline at end of file + return encodeURIComponent(redirectUri) + encodeURIComponent("#" + currentLocation); +} From df1cc0a4d93de7ca9d1fdd9dbe7069339f291c96 Mon Sep 17 00:00:00 2001 From: Ricardo Martin Date: Sat, 23 Mar 2024 15:12:07 +0100 Subject: [PATCH 074/158] Validate Saml URLs inside DefaultClientValidationProvider (#135) Closes keycloak/keycloak-private#62 Signed-off-by: rmartinc --- .../DefaultClientValidationProvider.java | 61 ++++++++++++++++++- .../keycloak/testsuite/admin/ClientTest.java | 52 ++++++++++++++++ .../admin/client/InstallationTest.java | 32 +++++----- .../client/ClientRegistrationTest.java | 47 +++++++++++++- 4 files changed, 174 insertions(+), 18 deletions(-) diff --git a/services/src/main/java/org/keycloak/validation/DefaultClientValidationProvider.java b/services/src/main/java/org/keycloak/validation/DefaultClientValidationProvider.java index b2202e2ed781..954da121e7b1 100644 --- a/services/src/main/java/org/keycloak/validation/DefaultClientValidationProvider.java +++ b/services/src/main/java/org/keycloak/validation/DefaultClientValidationProvider.java @@ -27,6 +27,7 @@ import org.keycloak.protocol.oidc.utils.PairwiseSubMapperUtils; import org.keycloak.protocol.oidc.utils.PairwiseSubMapperValidator; import org.keycloak.protocol.oidc.utils.SubjectType; +import org.keycloak.protocol.saml.SamlProtocol; import org.keycloak.representations.idm.ProtocolMapperRepresentation; import org.keycloak.representations.oidc.OIDCClientRepresentation; import org.keycloak.services.util.ResolveRelative; @@ -76,7 +77,52 @@ private enum FieldMessages { TOS_URI(ClientModel.TOS_URI, "Terms of service URL is not a valid URL", "tosURLInvalid", null, null, - "Terms of service URL uses an illegal scheme", "tosURLIllegalSchemeError"); + "Terms of service URL uses an illegal scheme", "tosURLIllegalSchemeError"), + + ADMIN_URL("masterSamlProcessingUrl", + "Master SAML Processing URL is not a valid URL", "adminUrlURLInvalid", + null, null, + "Master SAML Processing URL uses an illegal scheme", "adminUrlURLIllegalSchemeError"), + + SAML_ASSERTION_CONSUMER_URL_POST_URI(SamlProtocol.SAML_ASSERTION_CONSUMER_URL_POST_ATTRIBUTE, + "Assertion Consumer Service POST Binding URL is not a valid URL", "samlAssertionConsumerUrlPostURLInvalid", + null, null, + "Assertion Consumer Service POST Binding URL uses an illegal scheme", "samlAssertionConsumerUrlPostURLIllegalSchemeError"), + + SAML_ASSERTION_CONSUMER_URL_REDIRECT_URI(SamlProtocol.SAML_ASSERTION_CONSUMER_URL_REDIRECT_ATTRIBUTE, + "Assertion Consumer Service Redirect Binding URL is not a valid URL", "samlAssertionConsumerUrlRedirectURLInvalid", + null, null, + "Assertion Consumer Service Redirect Binding URL uses an illegal scheme", "samlAssertionConsumerUrlRedirectURLIllegalSchemeError"), + + SAML_ASSERTION_CONSUMER_URL_ARTIFACT_URI(SamlProtocol.SAML_ASSERTION_CONSUMER_URL_ARTIFACT_ATTRIBUTE, + "Artifact Binding URL is not a valid URL", "samlAssertionConsumerUrlArtifactURLInvalid", + null, null, + "Artifact Binding URL uses an illegal scheme", "samlAssertionConsumerUrlArtifactURLIllegalSchemeError"), + + SAML_SINGLE_LOGOUT_SERVICE_URL_POST_URI(SamlProtocol.SAML_SINGLE_LOGOUT_SERVICE_URL_POST_ATTRIBUTE, + "Logout Service POST Binding URL is not a valid URL", "samlLogoutServiceUrlPostURLInvalid", + null, null, + "Logout Service POST Binding URL uses an illegal scheme", "samlLogoutServiceUrlPostURLIllegalSchemeError"), + + SAML_SINGLE_LOGOUT_SERVICE_URL_ARTIFACT_URI(SamlProtocol.SAML_SINGLE_LOGOUT_SERVICE_URL_ARTIFACT_ATTRIBUTE, + "Logout Service ARTIFACT Binding URL is not a valid URL", "samlLogoutServiceUrlArtifactURLInvalid", + null, null, + "Logout Service ARTIFACT Binding URL uses an illegal scheme", "samlLogoutServiceUrlArtifactURLIllegalSchemeError"), + + SAML_SINGLE_LOGOUT_SERVICE_URL_REDIRECT_URI(SamlProtocol.SAML_SINGLE_LOGOUT_SERVICE_URL_REDIRECT_ATTRIBUTE, + "Logout Service Redirect Binding URL is not a valid URL", "samlLogoutServiceUrlRedirectURLInvalid", + null, null, + "Logout Service Redirect Binding URL uses an illegal scheme", "samlLogoutServiceUrlRedirectURLIllegalSchemeError"), + + SAML_SINGLE_LOGOUT_SERVICE_URL_SOAP_URI(SamlProtocol.SAML_SINGLE_LOGOUT_SERVICE_URL_SOAP_ATTRIBUTE, + "Logout Service SOAP Binding URL is not a valid URL", "samlLogoutServiceUrlSoapURLInvalid", + null, null, + "Logout Service SOAP Binding URL uses an illegal scheme", "samlAssertionConsumerUrlPostURLIllegalSchemeError"), + + SAML_ARTIFACT_RESOLUTION_SERVICE_URL_URI(SamlProtocol.SAML_ARTIFACT_RESOLUTION_SERVICE_URL_ATTRIBUTE, + "Artifact Resolution Service is not a valid URL", "samlAssertionConsumerUrlPostURLInvalid", + null, null, + "Artifact Resolution Service uses an illegal scheme", "samlAssertionConsumerUrlPostURLIllegalSchemeError"); private String fieldId; @@ -174,6 +220,19 @@ private void validateUrls(ValidationContext context) { checkUriLogo(FieldMessages.LOGO_URI, client.getAttribute(ClientModel.LOGO_URI), context); checkUri(FieldMessages.POLICY_URI, client.getAttribute(ClientModel.POLICY_URI), context, true, false); checkUri(FieldMessages.TOS_URI, client.getAttribute(ClientModel.TOS_URI), context, true, false); + + // extra validation URLs for SAML clients + if (SamlProtocol.LOGIN_PROTOCOL.equals(client.getProtocol())) { + checkUri(FieldMessages.ADMIN_URL, client.getManagementUrl(), context, true, false); + checkUri(FieldMessages.SAML_ASSERTION_CONSUMER_URL_POST_URI, client.getAttribute(SamlProtocol.SAML_ASSERTION_CONSUMER_URL_POST_ATTRIBUTE), context, true, false); + checkUri(FieldMessages.SAML_ASSERTION_CONSUMER_URL_REDIRECT_URI, client.getAttribute(SamlProtocol.SAML_ASSERTION_CONSUMER_URL_REDIRECT_ATTRIBUTE), context, true, false); + checkUri(FieldMessages.SAML_ASSERTION_CONSUMER_URL_ARTIFACT_URI, client.getAttribute(SamlProtocol.SAML_ASSERTION_CONSUMER_URL_ARTIFACT_ATTRIBUTE), context, true, false); + checkUri(FieldMessages.SAML_SINGLE_LOGOUT_SERVICE_URL_POST_URI, client.getAttribute(SamlProtocol.SAML_SINGLE_LOGOUT_SERVICE_URL_POST_ATTRIBUTE), context, true, false); + checkUri(FieldMessages.SAML_SINGLE_LOGOUT_SERVICE_URL_ARTIFACT_URI, client.getAttribute(SamlProtocol.SAML_SINGLE_LOGOUT_SERVICE_URL_ARTIFACT_ATTRIBUTE), context, true, false); + checkUri(FieldMessages.SAML_SINGLE_LOGOUT_SERVICE_URL_REDIRECT_URI, client.getAttribute(SamlProtocol.SAML_SINGLE_LOGOUT_SERVICE_URL_REDIRECT_ATTRIBUTE), context, true, false); + checkUri(FieldMessages.SAML_SINGLE_LOGOUT_SERVICE_URL_SOAP_URI, client.getAttribute(SamlProtocol.SAML_SINGLE_LOGOUT_SERVICE_URL_SOAP_ATTRIBUTE), context, true, false); + checkUri(FieldMessages.SAML_ARTIFACT_RESOLUTION_SERVICE_URL_URI, client.getAttribute(SamlProtocol.SAML_ARTIFACT_RESOLUTION_SERVICE_URL_ATTRIBUTE), context, true, false); + } } private void checkUri(FieldMessages field, String url, ValidationContext context, boolean checkValidUrl, boolean checkFragment) { diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/ClientTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/ClientTest.java index 40fdd085908a..e9967f641cb4 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/ClientTest.java +++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/ClientTest.java @@ -51,6 +51,7 @@ import org.keycloak.protocol.oidc.OIDCConfigAttributes; import org.keycloak.protocol.oidc.OIDCLoginProtocol; import org.keycloak.protocol.oidc.OIDCLoginProtocolFactory; +import org.keycloak.protocol.saml.SamlProtocol; import org.keycloak.representations.adapters.action.GlobalRequestResult; import org.keycloak.representations.adapters.action.PushNotBeforeAction; import org.keycloak.representations.adapters.action.TestAvailabilityAction; @@ -72,6 +73,7 @@ import java.io.IOException; import java.util.Arrays; import java.util.Collections; +import java.util.HashMap; import java.util.HashSet; import java.util.LinkedList; import java.util.List; @@ -95,11 +97,18 @@ public void getClients() { } private ClientRepresentation createClient() { + return createClient(null); + } + + private ClientRepresentation createClient(String protocol) { ClientRepresentation rep = new ClientRepresentation(); rep.setClientId("my-app"); rep.setDescription("my-app description"); rep.setEnabled(true); rep.setPublicClient(true); + if (protocol != null) { + rep.setProtocol(protocol); + } Response response = realm.clients().create(rep); response.close(); String id = ApiUtil.getCreatedId(response); @@ -185,6 +194,49 @@ public void testFragmentProhibitedClientValidation() { ); } + @Test + public void testSamlSpecificUrls() { + testSamlSpecificUrls(true, "javascript:alert('TEST')", "data:text/html;base64,PHNjcmlwdD5jb25maXJtKGRvY3VtZW50LmRvbWFpbik7PC9zY3JpcHQ+"); + testSamlSpecificUrls(false, "javascript:alert('TEST')", "data:text/html;base64,PHNjcmlwdD5jb25maXJtKGRvY3VtZW50LmRvbWFpbik7PC9zY3JpcHQ+"); + } + + private void testSamlSpecificUrls(boolean create, String... testUrls) { + ClientRepresentation rep; + if (create) { + rep = new ClientRepresentation(); + rep.setClientId("my-app2"); + rep.setEnabled(true); + rep.setProtocol(SamlProtocol.LOGIN_PROTOCOL); + } + else { + rep = createClient(SamlProtocol.LOGIN_PROTOCOL); + } + rep.setAttributes(new HashMap<>()); + + Map attrs = Map.of( + SamlProtocol.SAML_ASSERTION_CONSUMER_URL_POST_ATTRIBUTE, "Assertion Consumer Service POST Binding URL", + SamlProtocol.SAML_ASSERTION_CONSUMER_URL_REDIRECT_ATTRIBUTE, "Assertion Consumer Service Redirect Binding URL", + SamlProtocol.SAML_ASSERTION_CONSUMER_URL_ARTIFACT_ATTRIBUTE, "Artifact Binding URL", + SamlProtocol.SAML_SINGLE_LOGOUT_SERVICE_URL_POST_ATTRIBUTE, "Logout Service POST Binding URL", + SamlProtocol.SAML_SINGLE_LOGOUT_SERVICE_URL_ARTIFACT_ATTRIBUTE, "Logout Service ARTIFACT Binding URL", + SamlProtocol.SAML_SINGLE_LOGOUT_SERVICE_URL_REDIRECT_ATTRIBUTE, "Logout Service Redirect Binding URL", + SamlProtocol.SAML_SINGLE_LOGOUT_SERVICE_URL_SOAP_ATTRIBUTE, "Logout Service SOAP Binding URL", + SamlProtocol.SAML_ARTIFACT_RESOLUTION_SERVICE_URL_ATTRIBUTE, "Artifact Resolution Service"); + + for (String testUrl : testUrls) { + // admin url + rep.setAdminUrl(testUrl); + createOrUpdateClientExpectingValidationErrors(rep, create, "Master SAML Processing URL uses an illegal scheme"); + rep.setAdminUrl(null); + // attributes + for (Map.Entry entry : attrs.entrySet()) { + rep.getAttributes().put(entry.getKey(), testUrl); + createOrUpdateClientExpectingValidationErrors(rep, create, entry.getValue() + " uses an illegal scheme"); + rep.getAttributes().remove(entry.getKey()); + } + } + } + private void testClientUriValidation(String expectedRootUrlError, String expectedBaseUrlError, String expectedBackchannelLogoutUrlError, String expectedRedirectUrisError, String... testUrls) { testClientUriValidation(false, expectedRootUrlError, expectedBaseUrlError, expectedBackchannelLogoutUrlError, expectedRedirectUrisError, testUrls); testClientUriValidation(true, expectedRootUrlError, expectedBaseUrlError, expectedBackchannelLogoutUrlError, expectedRedirectUrisError, testUrls); diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/client/InstallationTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/client/InstallationTest.java index ace6e2869d39..c5d6fdf4ec9f 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/client/InstallationTest.java +++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/client/InstallationTest.java @@ -229,30 +229,30 @@ public void testSamlMetadataSpDescriptorPost() throws Exception { attrNamesAndValues.clear(); //fallback to adminUrl - updater.setAdminUrl("admin-url").update(); + updater.setAdminUrl("https://admin-url").update(); assertAdminEvents.assertEvent(getRealmId(), OperationType.UPDATE, AdminEventPaths.clientResourcePath(samlClientId), ResourceType.CLIENT); doc = getDocumentFromXmlString(updater.getResource().getInstallationProvider(SamlSPDescriptorClientInstallation.SAML_CLIENT_INSTALATION_SP_DESCRIPTOR)); attrNamesAndValues.put("Binding", JBossSAMLURIConstants.SAML_HTTP_POST_BINDING.get()); - attrNamesAndValues.put("Location", "admin-url"); + attrNamesAndValues.put("Location", "https://admin-url"); assertElements(doc, METADATA_NSURI.get(), "SingleLogoutService", attrNamesAndValues); assertElements(doc, METADATA_NSURI.get(), "AssertionConsumerService", attrNamesAndValues); attrNamesAndValues.clear(); //fine grained - updater.setAttribute(SamlProtocol.SAML_ASSERTION_CONSUMER_URL_POST_ATTRIBUTE, "saml-assertion-post-url") - .setAttribute(SamlProtocol.SAML_SINGLE_LOGOUT_SERVICE_URL_POST_ATTRIBUTE, "saml-logout-post-url") - .setAttribute(SamlProtocol.SAML_ASSERTION_CONSUMER_URL_REDIRECT_ATTRIBUTE, "saml-assertion-redirect-url") - .setAttribute(SamlProtocol.SAML_SINGLE_LOGOUT_SERVICE_URL_REDIRECT_ATTRIBUTE, "saml-logout-redirect-url") + updater.setAttribute(SamlProtocol.SAML_ASSERTION_CONSUMER_URL_POST_ATTRIBUTE, "https://saml-assertion-post-url") + .setAttribute(SamlProtocol.SAML_SINGLE_LOGOUT_SERVICE_URL_POST_ATTRIBUTE, "https://saml-logout-post-url") + .setAttribute(SamlProtocol.SAML_ASSERTION_CONSUMER_URL_REDIRECT_ATTRIBUTE, "https://saml-assertion-redirect-url") + .setAttribute(SamlProtocol.SAML_SINGLE_LOGOUT_SERVICE_URL_REDIRECT_ATTRIBUTE, "https://saml-logout-redirect-url") .update(); assertAdminEvents.assertEvent(getRealmId(), OperationType.UPDATE, AdminEventPaths.clientResourcePath(samlClientId), ResourceType.CLIENT); doc = getDocumentFromXmlString(updater.getResource().getInstallationProvider(SamlSPDescriptorClientInstallation.SAML_CLIENT_INSTALATION_SP_DESCRIPTOR)); attrNamesAndValues.put("Binding", JBossSAMLURIConstants.SAML_HTTP_POST_BINDING.get()); - attrNamesAndValues.put("Location", "saml-logout-post-url"); + attrNamesAndValues.put("Location", "https://saml-logout-post-url"); assertElements(doc, METADATA_NSURI.get(), "SingleLogoutService", attrNamesAndValues); attrNamesAndValues.clear(); attrNamesAndValues.put("Binding", JBossSAMLURIConstants.SAML_HTTP_POST_BINDING.get()); - attrNamesAndValues.put("Location", "saml-assertion-post-url"); + attrNamesAndValues.put("Location", "https://saml-assertion-post-url"); assertElements(doc, METADATA_NSURI.get(), "AssertionConsumerService", attrNamesAndValues); } assertAdminEvents.assertEvent(getRealmId(), OperationType.UPDATE, AdminEventPaths.clientResourcePath(samlClientId), ResourceType.CLIENT); @@ -277,29 +277,29 @@ public void testSamlMetadataSpDescriptorRedirect() throws Exception { attrNamesAndValues.clear(); //fallback to adminUrl - updater.setAdminUrl("admin-url").update(); + updater.setAdminUrl("https://admin-url").update(); assertAdminEvents.assertEvent(getRealmId(), OperationType.UPDATE, AdminEventPaths.clientResourcePath(samlClientId), ResourceType.CLIENT); doc = getDocumentFromXmlString(updater.getResource().getInstallationProvider(SamlSPDescriptorClientInstallation.SAML_CLIENT_INSTALATION_SP_DESCRIPTOR)); attrNamesAndValues.put("Binding", JBossSAMLURIConstants.SAML_HTTP_REDIRECT_BINDING.get()); - attrNamesAndValues.put("Location", "admin-url"); + attrNamesAndValues.put("Location", "https://admin-url"); assertElements(doc, METADATA_NSURI.get(), "SingleLogoutService", attrNamesAndValues); assertElements(doc, METADATA_NSURI.get(), "AssertionConsumerService", attrNamesAndValues); attrNamesAndValues.clear(); //fine grained - updater.setAttribute(SamlProtocol.SAML_ASSERTION_CONSUMER_URL_POST_ATTRIBUTE, "saml-assertion-post-url") - .setAttribute(SamlProtocol.SAML_SINGLE_LOGOUT_SERVICE_URL_POST_ATTRIBUTE, "saml-logout-post-url") - .setAttribute(SamlProtocol.SAML_ASSERTION_CONSUMER_URL_REDIRECT_ATTRIBUTE, "saml-assertion-redirect-url") - .setAttribute(SamlProtocol.SAML_SINGLE_LOGOUT_SERVICE_URL_REDIRECT_ATTRIBUTE, "saml-logout-redirect-url") + updater.setAttribute(SamlProtocol.SAML_ASSERTION_CONSUMER_URL_POST_ATTRIBUTE, "https://saml-assertion-post-url") + .setAttribute(SamlProtocol.SAML_SINGLE_LOGOUT_SERVICE_URL_POST_ATTRIBUTE, "https://saml-logout-post-url") + .setAttribute(SamlProtocol.SAML_ASSERTION_CONSUMER_URL_REDIRECT_ATTRIBUTE, "https://saml-assertion-redirect-url") + .setAttribute(SamlProtocol.SAML_SINGLE_LOGOUT_SERVICE_URL_REDIRECT_ATTRIBUTE, "https://saml-logout-redirect-url") .update(); assertAdminEvents.assertEvent(getRealmId(), OperationType.UPDATE, AdminEventPaths.clientResourcePath(samlClientId), ResourceType.CLIENT); doc = getDocumentFromXmlString(updater.getResource().getInstallationProvider(SamlSPDescriptorClientInstallation.SAML_CLIENT_INSTALATION_SP_DESCRIPTOR)); attrNamesAndValues.put("Binding", JBossSAMLURIConstants.SAML_HTTP_REDIRECT_BINDING.get()); - attrNamesAndValues.put("Location", "saml-logout-redirect-url"); + attrNamesAndValues.put("Location", "https://saml-logout-redirect-url"); assertElements(doc, METADATA_NSURI.get(), "SingleLogoutService", attrNamesAndValues); attrNamesAndValues.clear(); attrNamesAndValues.put("Binding", JBossSAMLURIConstants.SAML_HTTP_REDIRECT_BINDING.get()); - attrNamesAndValues.put("Location", "saml-assertion-redirect-url"); + attrNamesAndValues.put("Location", "https://saml-assertion-redirect-url"); assertElements(doc, METADATA_NSURI.get(), "AssertionConsumerService", attrNamesAndValues); } assertAdminEvents.assertEvent(getRealmId(), OperationType.UPDATE, AdminEventPaths.clientResourcePath(samlClientId), ResourceType.CLIENT); diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/client/ClientRegistrationTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/client/ClientRegistrationTest.java index bff7d220d66c..1f8436dee77c 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/client/ClientRegistrationTest.java +++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/client/ClientRegistrationTest.java @@ -36,6 +36,8 @@ import org.keycloak.models.Constants; import org.keycloak.models.utils.KeycloakModelUtils; import org.keycloak.protocol.oidc.OIDCAdvancedConfigWrapper; +import org.keycloak.protocol.oidc.OIDCLoginProtocol; +import org.keycloak.protocol.saml.SamlProtocol; import org.keycloak.representations.idm.ClientRepresentation; import org.keycloak.representations.idm.OAuth2ErrorRepresentation; import org.keycloak.representations.idm.ProtocolMapperRepresentation; @@ -52,7 +54,9 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; +import java.util.HashMap; import java.util.List; +import java.util.Map; import java.util.Objects; import java.util.HashSet; import java.util.Set; @@ -70,7 +74,6 @@ import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; -import org.keycloak.protocol.oidc.OIDCLoginProtocol; import static org.keycloak.services.clientregistration.ErrorCodes.INVALID_CLIENT_METADATA; import static org.keycloak.services.clientregistration.ErrorCodes.INVALID_REDIRECT_URI; import static org.keycloak.utils.MediaType.APPLICATION_JSON; @@ -302,6 +305,48 @@ public void testFragmentProhibitedClientValidation() { ); } + @Test + public void testSamlSpecificUrls() throws ClientRegistrationException { + testSamlSpecificUrls(true, "javascript:alert('TEST')", "data:text/html;base64,PHNjcmlwdD5jb25maXJtKGRvY3VtZW50LmRvbWFpbik7PC9zY3JpcHQ+"); + testSamlSpecificUrls(false, "javascript:alert('TEST')", "data:text/html;base64,PHNjcmlwdD5jb25maXJtKGRvY3VtZW50LmRvbWFpbik7PC9zY3JpcHQ+"); + } + + private void testSamlSpecificUrls(boolean register, String... testUrls) throws ClientRegistrationException { + ClientRepresentation rep = buildClient(); + rep.setProtocol(SamlProtocol.LOGIN_PROTOCOL); + if (register) { + authCreateClients(); + } else { + authManageClients(); + registerClient(rep); + rep = reg.get(CLIENT_ID); + } + rep.setAttributes(new HashMap<>()); + + Map attrs = Map.of( + SamlProtocol.SAML_ASSERTION_CONSUMER_URL_POST_ATTRIBUTE, "Assertion Consumer Service POST Binding URL", + SamlProtocol.SAML_ASSERTION_CONSUMER_URL_REDIRECT_ATTRIBUTE, "Assertion Consumer Service Redirect Binding URL", + SamlProtocol.SAML_ASSERTION_CONSUMER_URL_ARTIFACT_ATTRIBUTE, "Artifact Binding URL", + SamlProtocol.SAML_SINGLE_LOGOUT_SERVICE_URL_POST_ATTRIBUTE, "Logout Service POST Binding URL", + SamlProtocol.SAML_SINGLE_LOGOUT_SERVICE_URL_ARTIFACT_ATTRIBUTE, "Logout Service ARTIFACT Binding URL", + SamlProtocol.SAML_SINGLE_LOGOUT_SERVICE_URL_REDIRECT_ATTRIBUTE, "Logout Service Redirect Binding URL", + SamlProtocol.SAML_SINGLE_LOGOUT_SERVICE_URL_SOAP_ATTRIBUTE, "Logout Service SOAP Binding URL", + SamlProtocol.SAML_ARTIFACT_RESOLUTION_SERVICE_URL_ATTRIBUTE, "Artifact Resolution Service"); + + for (String testUrl : testUrls) { + // admin url + rep.setAdminUrl(testUrl); + registerOrUpdateClientExpectingValidationErrors(rep, register, false, "Master SAML Processing URL uses an illegal scheme"); + rep.setAdminUrl(null); + // attributes + for (Map.Entry entry : attrs.entrySet()) { + rep.getAttributes().put(entry.getKey(), testUrl); + registerOrUpdateClientExpectingValidationErrors(rep, register, false, entry.getValue() + " uses an illegal scheme"); + rep.getAttributes().remove(entry.getKey()); + } + } + } + @Test public void testUpdateAuthorizationSettings() throws ClientRegistrationException { authManageClients(); From aebd051cf063a775ceba4e20d7c493538594f1ed Mon Sep 17 00:00:00 2001 From: Giuseppe Graziano Date: Sat, 23 Mar 2024 15:14:16 +0100 Subject: [PATCH 075/158] Avoid the same userSessionId after re-authentication (#136) Closes #69 Signed-off-by: Giuseppe Graziano --- .../AuthenticationProcessor.java | 19 ++++++ .../AuthenticationSessionManager.java | 25 +++++--- .../resources/LoginActionsService.java | 3 +- .../testsuite/forms/ReAuthenticationTest.java | 64 +++++++++++++++++++ 4 files changed, 100 insertions(+), 11 deletions(-) diff --git a/services/src/main/java/org/keycloak/authentication/AuthenticationProcessor.java b/services/src/main/java/org/keycloak/authentication/AuthenticationProcessor.java index 9640d926b54f..21233f75c514 100755 --- a/services/src/main/java/org/keycloak/authentication/AuthenticationProcessor.java +++ b/services/src/main/java/org/keycloak/authentication/AuthenticationProcessor.java @@ -42,6 +42,7 @@ import org.keycloak.models.utils.FormMessage; import org.keycloak.protocol.LoginProtocol; import org.keycloak.protocol.LoginProtocol.Error; +import org.keycloak.protocol.RestartLoginCookie; import org.keycloak.protocol.oidc.TokenManager; import org.keycloak.services.ErrorPage; import org.keycloak.services.ErrorPageException; @@ -50,12 +51,14 @@ import org.keycloak.services.managers.BruteForceProtector; import org.keycloak.services.managers.ClientSessionCode; import org.keycloak.services.managers.UserSessionManager; +import org.keycloak.services.managers.AuthenticationSessionManager; import org.keycloak.services.messages.Messages; import org.keycloak.services.resources.LoginActionsService; import org.keycloak.services.util.CacheControlUtil; import org.keycloak.services.util.AuthenticationFlowURLHelper; import org.keycloak.sessions.AuthenticationSessionModel; import org.keycloak.sessions.CommonClientSessionModel; +import org.keycloak.sessions.RootAuthenticationSessionModel; import org.keycloak.util.JsonSerialization; import jakarta.ws.rs.core.MultivaluedHashMap; @@ -937,6 +940,22 @@ public static void resetFlow(AuthenticationSessionModel authSession, String flow authSession.setAuthNote(CURRENT_FLOW_PATH, flowPath); } + // Recreate new root auth session and new auth session from the given auth session. + public static AuthenticationSessionModel recreate(KeycloakSession session, AuthenticationSessionModel authSession) { + AuthenticationSessionManager authenticationSessionManager = new AuthenticationSessionManager(session); + RootAuthenticationSessionModel rootAuthenticationSession = authenticationSessionManager.createAuthenticationSession(authSession.getRealm(), true); + AuthenticationSessionModel newAuthSession = rootAuthenticationSession.createAuthenticationSession(authSession.getClient()); + newAuthSession.setRedirectUri(authSession.getRedirectUri()); + newAuthSession.setProtocol(authSession.getProtocol()); + + for (Map.Entry clientNote : authSession.getClientNotes().entrySet()) { + newAuthSession.setClientNote(clientNote.getKey(), clientNote.getValue()); + } + + authenticationSessionManager.removeAuthenticationSession(authSession.getRealm(), authSession, true); + RestartLoginCookie.setRestartCookie(session, authSession); + return newAuthSession; + } // Clone new authentication session from the given authSession. New authenticationSession will have same parent (rootSession) and will use same client public static AuthenticationSessionModel clone(KeycloakSession session, AuthenticationSessionModel authSession) { diff --git a/services/src/main/java/org/keycloak/services/managers/AuthenticationSessionManager.java b/services/src/main/java/org/keycloak/services/managers/AuthenticationSessionManager.java index 241501028add..befbef09de86 100644 --- a/services/src/main/java/org/keycloak/services/managers/AuthenticationSessionManager.java +++ b/services/src/main/java/org/keycloak/services/managers/AuthenticationSessionManager.java @@ -213,20 +213,25 @@ public boolean removeTabIdInAuthenticationSession(RealmModel realm, Authenticati public void updateAuthenticationSessionAfterSuccessfulAuthentication(RealmModel realm, AuthenticationSessionModel authSession) { boolean removedRootAuthSession = removeTabIdInAuthenticationSession(realm, authSession); if (!removedRootAuthSession) { - RootAuthenticationSessionModel rootAuthSession = authSession.getParentSession(); + if(realm.getSsoSessionIdleTimeout() < SessionExpiration.getAuthSessionLifespan(realm) && realm.getSsoSessionMaxLifespan() < SessionExpiration.getAuthSessionLifespan(realm)) { + removeAuthenticationSession(realm, authSession, true); + } + else { + RootAuthenticationSessionModel rootAuthSession = authSession.getParentSession(); - // 1 minute by default. Same timeout, which is used for client to complete "authorization code" flow - // Very short timeout should be OK as when this cookie is set, other existing browser tabs are supposed to be refreshed immediately by JS script authChecker.js - // and login user automatically. No need to have authenticationSession and cookie living any longer - int authSessionExpiresIn = realm.getAccessCodeLifespan(); + // 1 minute by default. Same timeout, which is used for client to complete "authorization code" flow + // Very short timeout should be OK as when this cookie is set, other existing browser tabs are supposed to be refreshed immediately by JS script authChecker.js + // and login user automatically. No need to have authenticationSession and cookie living any longer + int authSessionExpiresIn = realm.getAccessCodeLifespan(); - // Set timestamp to the past to make sure that authSession is scheduled for expiration in "authSessionExpiresIn" seconds - int authSessionExpirationTime = Time.currentTime() - SessionExpiration.getAuthSessionLifespan(realm) + authSessionExpiresIn; - rootAuthSession.setTimestamp(authSessionExpirationTime); + // Set timestamp to the past to make sure that authSession is scheduled for expiration in "authSessionExpiresIn" seconds + int authSessionExpirationTime = Time.currentTime() - SessionExpiration.getAuthSessionLifespan(realm) + authSessionExpiresIn; + rootAuthSession.setTimestamp(authSessionExpirationTime); - log.tracef("Removed authentication session of root session '%s' with tabId '%s'. But there are remaining tabs in the root session. Root authentication session will expire in %d seconds", rootAuthSession.getId(), authSession.getTabId(), authSessionExpiresIn); + log.tracef("Removed authentication session of root session '%s' with tabId '%s'. But there are remaining tabs in the root session. Root authentication session will expire in %d seconds", rootAuthSession.getId(), authSession.getTabId(), authSessionExpiresIn); - AuthenticationStateCookie.generateAndSetCookie(session, rootAuthSession, authSessionExpiresIn); + AuthenticationStateCookie.generateAndSetCookie(session, rootAuthSession, authSessionExpiresIn); + } } } diff --git a/services/src/main/java/org/keycloak/services/resources/LoginActionsService.java b/services/src/main/java/org/keycloak/services/resources/LoginActionsService.java index bf177aa208d5..725a1fcbc9e9 100755 --- a/services/src/main/java/org/keycloak/services/resources/LoginActionsService.java +++ b/services/src/main/java/org/keycloak/services/resources/LoginActionsService.java @@ -243,12 +243,13 @@ public Response restartSession(@QueryParam(AUTH_SESSION_ID) String authSessionId if (userSession != null) { logger.debugf("Logout of user session %s when restarting flow during re-authentication", userSession.getId()); AuthenticationManager.backchannelLogout(session, userSession, false); + authSession = AuthenticationProcessor.recreate(session, authSession); } } AuthenticationProcessor.resetFlow(authSession, flowPath); - URI redirectUri = getLastExecutionUrl(flowPath, null, authSession.getClient().getClientId(), tabId); + URI redirectUri = getLastExecutionUrl(flowPath, null, authSession.getClient().getClientId(), authSession.getTabId()); logger.debugf("Flow restart requested. Redirecting to %s", redirectUri); return Response.status(Response.Status.FOUND).location(redirectUri).build(); } diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/forms/ReAuthenticationTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/forms/ReAuthenticationTest.java index 24207a2d7157..9b1a36c8538a 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/forms/ReAuthenticationTest.java +++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/forms/ReAuthenticationTest.java @@ -28,10 +28,13 @@ import org.jboss.arquillian.graphene.page.Page; import org.jboss.arquillian.test.api.ArquillianResource; import org.junit.Test; +import org.keycloak.OAuth2Constants; import org.keycloak.admin.client.resource.UserResource; import org.keycloak.authentication.authenticators.browser.PasswordFormFactory; import org.keycloak.authentication.authenticators.browser.UsernameFormFactory; import org.keycloak.models.AuthenticationExecutionModel; +import org.keycloak.protocol.oidc.OIDCLoginProtocol; +import org.keycloak.representations.AccessToken; import org.keycloak.representations.idm.FederatedIdentityRepresentation; import org.keycloak.representations.idm.IdentityProviderRepresentation; import org.keycloak.representations.idm.RealmRepresentation; @@ -302,6 +305,67 @@ public void identityFirstFormReauthenticationWithGithubLink() { BrowserFlowTest.revertFlows(testRealm(), "browser - identity first"); } + @Test + public void restartLoginWithNewRootAuthSession() { + loginPage.open(); + loginPage.login("test-user@localhost", "password"); + String code = oauth.getCurrentQuery().get(OAuth2Constants.CODE); + OAuthClient.AccessTokenResponse response1 = oauth.doAccessTokenRequest(code, "password"); + + oauth.prompt(OIDCLoginProtocol.PROMPT_VALUE_LOGIN); + loginPage.open(); + loginPage.clickResetLogin(); + loginPage.login("john-doh@localhost", "password"); + + code = oauth.getCurrentQuery().get(OAuth2Constants.CODE); + OAuthClient.AccessTokenResponse response2 = oauth.doAccessTokenRequest(code, "password"); + + + AccessToken accessToken1 = oauth.verifyToken(response1.getAccessToken()); + AccessToken accessToken2 = oauth.verifyToken(response2.getAccessToken()); + + Assert.assertNotEquals(accessToken1.getSubject(), accessToken2.getSubject()); + Assert.assertNotEquals(accessToken1.getSessionId(), accessToken2.getSessionId()); + } + + @Test + public void loginAfterExpiredUserSession() { + RealmRepresentation rep = testRealm().toRepresentation(); + Integer originalSsoSessionIdleTimeout = rep.getSsoSessionIdleTimeout(); + Integer originalSsoSessionMaxLifespan = rep.getSsoSessionMaxLifespan(); + + rep.setSsoSessionIdleTimeout(10); + rep.setSsoSessionMaxLifespan(10); + realmsResouce().realm(rep.getRealm()).update(rep); + + loginPage.open(); + driver.navigate().refresh(); + loginPage.login("test-user@localhost", "password"); + + String code = oauth.getCurrentQuery().get(OAuth2Constants.CODE); + OAuthClient.AccessTokenResponse response1 = oauth.doAccessTokenRequest(code, "password"); + + //set time offset after user session expiration (10s) but before accessCodeLifespanLogin (1800s) and accessCodeLifespan (60s) + setTimeOffset(20); + + loginPage.open(); + loginPage.login("john-doh@localhost", "password"); + + code = oauth.getCurrentQuery().get(OAuth2Constants.CODE); + OAuthClient.AccessTokenResponse response2 = oauth.doAccessTokenRequest(code, "password"); + + AccessToken accessToken1 = oauth.verifyToken(response1.getAccessToken()); + AccessToken accessToken2 = oauth.verifyToken(response2.getAccessToken()); + + Assert.assertNotEquals(accessToken1.getSubject(), accessToken2.getSubject()); + Assert.assertNotEquals(accessToken1.getSessionId(), accessToken2.getSessionId()); + + setTimeOffset(0); + rep.setSsoSessionIdleTimeout(originalSsoSessionIdleTimeout); + rep.setSsoSessionMaxLifespan(originalSsoSessionMaxLifespan); + realmsResouce().realm(rep.getRealm()).update(rep); + } + private void setupIdentityFirstFlow() { String newFlowAlias = "browser - identity first"; testingClient.server("test").run(session -> FlowUtil.inCurrentRealm(session).copyBrowserFlow(newFlowAlias)); From 9d9b57879c8f193b7693153780b591cd6002bd1b Mon Sep 17 00:00:00 2001 From: Ricardo Martin Date: Sat, 23 Mar 2024 15:15:14 +0100 Subject: [PATCH 076/158] Better management of domains in TrustedHostClientRegistrationPolicy (#139) Closes keycloak/keycloak-private#63 Signed-off-by: rmartinc --- .../TrustedHostClientRegistrationPolicy.java | 59 +++++---- .../{policies => policy}/impl/HostsTest.java | 2 +- ...ustedHostClientRegistrationPolicyTest.java | 125 ++++++++++++++++++ 3 files changed, 160 insertions(+), 26 deletions(-) rename services/src/test/java/org/keycloak/services/clientregistration/{policies => policy}/impl/HostsTest.java (97%) create mode 100644 services/src/test/java/org/keycloak/services/clientregistration/policy/impl/TrustedHostClientRegistrationPolicyTest.java diff --git a/services/src/main/java/org/keycloak/services/clientregistration/policy/impl/TrustedHostClientRegistrationPolicy.java b/services/src/main/java/org/keycloak/services/clientregistration/policy/impl/TrustedHostClientRegistrationPolicy.java index 6d79c701b4b9..65d26fbc661e 100644 --- a/services/src/main/java/org/keycloak/services/clientregistration/policy/impl/TrustedHostClientRegistrationPolicy.java +++ b/services/src/main/java/org/keycloak/services/clientregistration/policy/impl/TrustedHostClientRegistrationPolicy.java @@ -23,7 +23,7 @@ import java.net.URISyntaxException; import java.net.URL; import java.net.UnknownHostException; -import java.util.LinkedList; +import java.util.Arrays; import java.util.List; import java.util.Set; import java.util.stream.Collectors; @@ -97,6 +97,10 @@ protected void verifyHost() throws ClientRegistrationPolicyException { String hostAddress = session.getContext().getConnection().getRemoteAddr(); + verifyHost(hostAddress); + } + + protected void verifyHost(String hostAddress) throws ClientRegistrationPolicyException { logger.debugf("Verifying remote host : %s", hostAddress); List trustedHosts = getTrustedHosts(); @@ -130,20 +134,9 @@ protected List getTrustedHosts() { protected List getTrustedDomains() { - List trustedHostsConfig = componentModel.getConfig().getList(TrustedHostClientRegistrationPolicyFactory.TRUSTED_HOSTS); - List domains = new LinkedList<>(); - - for (String hostname : trustedHostsConfig) { - if (hostname.startsWith("*.")) { - hostname = hostname.substring(2); - domains.add(hostname); - } - } - - return domains; + return componentModel.getConfig().getList(TrustedHostClientRegistrationPolicyFactory.TRUSTED_HOSTS); } - protected String verifyHostInTrustedHosts(String hostAddress, List trustedHosts) { for (String confHostName : trustedHosts) { try { @@ -162,23 +155,39 @@ protected String verifyHostInTrustedHosts(String hostAddress, List trust return null; } + private boolean checkTrustedDomain(String hostname, String trustedDomain) { + if (trustedDomain.startsWith("*.")) { + String domain = trustedDomain.substring(2); + return hostname.equals(domain) || hostname.endsWith("." + domain); + } + return hostname.equals(trustedDomain); + } protected String verifyHostInTrustedDomains(String hostAddress, List trustedDomains) { - if (!trustedDomains.isEmpty()) { - try { - String hostname = InetAddress.getByName(hostAddress).getHostName(); + try { + InetAddress address = InetAddress.getByName(hostAddress); + String hostname = address.getHostName(); + + logger.debugf("Trying verify request from address '%s' of host '%s' by domains", hostAddress, hostname); - logger.debugf("Trying verify request from address '%s' of host '%s' by domains", hostAddress, hostname); + if (hostname.equals(address.getHostAddress())) { + logger.debugf("The hostAddress '%s' was not resolved to a hostname", hostAddress); + return null; + } + + if (Arrays.stream(InetAddress.getAllByName(hostname)).filter(a -> address.equals(a)).findAny().isEmpty()) { + logger.debugf("The hostAddress '%s' is not among the direct lookups returned resolving '%s'", hostAddress, hostname); + return null; + } - for (String confDomain : trustedDomains) { - if (hostname.endsWith(confDomain)) { - logger.debugf("Successfully verified host '%s' by trusted domain '%s'", hostname, confDomain); - return hostname; - } + for (String confDomain : trustedDomains) { + if (checkTrustedDomain(hostname, confDomain)) { + logger.debugf("Successfully verified host '%s' by trusted domain '%s'", hostname, confDomain); + return hostname; } - } catch (UnknownHostException uhe) { - logger.debugf(uhe, "Request of address '%s' came from unknown host. Skip verification by domains", hostAddress); } + } catch (UnknownHostException uhe) { + logger.debugf(uhe, "Request of address '%s' came from unknown host. Skip verification by domains", hostAddress); } return null; @@ -260,7 +269,7 @@ private boolean checkHostTrusted(String host, List trustedHosts, List policy.verifyHost("10.0.0.1")); + policy.checkURLTrusted("https://localhost", policy.getTrustedHosts(), policy.getTrustedDomains()); + Assert.assertThrows(ClientRegistrationPolicyException.class, () -> policy.checkURLTrusted("https://otherhost", + policy.getTrustedHosts(), policy.getTrustedDomains())); + } + + @Test + public void testLocalhostDomain() { + TrustedHostClientRegistrationPolicyFactory factory = new TrustedHostClientRegistrationPolicyFactory(); + ComponentModel model = createComponentModel("*.localhost"); + TrustedHostClientRegistrationPolicy policy = (TrustedHostClientRegistrationPolicy) factory.create(session, model); + + policy.verifyHost("127.0.0.1"); + Assert.assertThrows(ClientRegistrationPolicyException.class, () -> policy.verifyHost("10.0.0.1")); + policy.checkURLTrusted("https://localhost", policy.getTrustedHosts(), policy.getTrustedDomains()); + policy.checkURLTrusted("https://other.localhost", policy.getTrustedHosts(), policy.getTrustedDomains()); + Assert.assertThrows(ClientRegistrationPolicyException.class, () -> policy.checkURLTrusted("https://otherlocalhost", + policy.getTrustedHosts(), policy.getTrustedDomains())); + } + + @Test + public void testLocalhostIP() { + TrustedHostClientRegistrationPolicyFactory factory = new TrustedHostClientRegistrationPolicyFactory(); + ComponentModel model = createComponentModel("127.0.0.1"); + TrustedHostClientRegistrationPolicy policy = (TrustedHostClientRegistrationPolicy) factory.create(session, model); + + policy.verifyHost("127.0.0.1"); + Assert.assertThrows(ClientRegistrationPolicyException.class, () -> policy.verifyHost("10.0.0.1")); + policy.checkURLTrusted("https://127.0.0.1", policy.getTrustedHosts(), policy.getTrustedDomains()); + Assert.assertThrows(ClientRegistrationPolicyException.class, () -> policy.checkURLTrusted("https://localhost", + policy.getTrustedHosts(), policy.getTrustedDomains())); + } + + @Test + public void testGoogleCrawlBot() { + // https://developers.google.com/search/blog/2006/09/how-to-verify-googlebot + TrustedHostClientRegistrationPolicyFactory factory = new TrustedHostClientRegistrationPolicyFactory(); + ComponentModel model = createComponentModel("*.googlebot.com"); + TrustedHostClientRegistrationPolicy policy = (TrustedHostClientRegistrationPolicy) factory.create(session, model); + + policy.verifyHost("66.249.66.1"); + policy.checkURLTrusted("https://www.googlebot.com", policy.getTrustedHosts(), policy.getTrustedDomains()); + policy.checkURLTrusted("https://googlebot.com", policy.getTrustedHosts(), policy.getTrustedDomains()); + Assert.assertThrows(ClientRegistrationPolicyException.class, () -> policy.checkURLTrusted("https://www.othergooglebot.com", + policy.getTrustedHosts(), policy.getTrustedDomains())); + } + + @Test + public void testGithubDomain() throws UnknownHostException { + TrustedHostClientRegistrationPolicyFactory factory = new TrustedHostClientRegistrationPolicyFactory(); + ComponentModel model = createComponentModel("*.github.com"); + TrustedHostClientRegistrationPolicy policy = (TrustedHostClientRegistrationPolicy) factory.create(session, model); + + policy.verifyHost(InetAddress.getByName("www.github.com").getHostAddress()); + policy.verifyHost(InetAddress.getByName("github.com").getHostAddress()); + policy.checkURLTrusted("https://www.github.com", policy.getTrustedHosts(), policy.getTrustedDomains()); + policy.checkURLTrusted("https://github.com", policy.getTrustedHosts(), policy.getTrustedDomains()); + Assert.assertThrows(ClientRegistrationPolicyException.class, () -> policy.checkURLTrusted("https://othergithub.com", + policy.getTrustedHosts(), policy.getTrustedDomains())); + } + + private ComponentModel createComponentModel(String... hosts) { + ComponentModel model = new ComponentModel(); + model.put(TrustedHostClientRegistrationPolicyFactory.HOST_SENDING_REGISTRATION_REQUEST_MUST_MATCH, "true"); + model.put(TrustedHostClientRegistrationPolicyFactory.CLIENT_URIS_MUST_MATCH, "true"); + model.getConfig().addAll(TrustedHostClientRegistrationPolicyFactory.TRUSTED_HOSTS, hosts); + return model; + } +} From 77254a28e9e53dbc434fbeb475a8fd549b25648f Mon Sep 17 00:00:00 2001 From: Marek Posolda Date: Sat, 23 Mar 2024 15:16:21 +0100 Subject: [PATCH 077/158] Secondary factor bypass in step-up authentication (#143) closes #34 Signed-off-by: mposolda --- .../SecretQuestionRequiredAction.java | 7 + .../src/account-security/SigningIn.tsx | 61 ++-- .../migration/migrators/MigrateTo24_0_3.java | 56 ++++ .../datastore/DefaultMigrationManager.java | 4 +- .../AbstractAuthenticationFlowContext.java | 6 + .../AuthenticationFlowCallback.java | 6 +- .../authentication/CredentialAction.java | 37 +++ .../authentication/CredentialRegistrator.java | 5 +- .../RequiredActionProvider.java | 6 +- .../java/org/keycloak/events/Details.java | 1 + .../main/java/org/keycloak/events/Errors.java | 4 + .../java/org/keycloak/models/Constants.java | 2 + .../models/utils/DefaultRequiredActions.java | 14 + .../AuthenticationProcessor.java | 5 + .../authentication/AuthenticatorUtil.java | 38 +++ .../DefaultAuthenticationFlow.java | 2 +- .../browser/CookieAuthenticator.java | 9 +- .../ConditionalLoaAuthenticator.java | 16 +- .../authenticators/util/AcrStore.java | 82 +++++- .../authenticators/util/LoAUtil.java | 85 +++++- .../DeleteCredentialAction.java | 194 +++++++++++++ .../RecoveryAuthnCodesAction.java | 9 +- .../requiredactions/UpdateTotp.java | 6 + .../requiredactions/WebAuthnRegister.java | 6 + .../util/CredentialDeleteHelper.java | 111 ++++++++ .../keycloak/protocol/oidc/TokenManager.java | 2 +- .../oidc/endpoints/AuthorizationEndpoint.java | 12 +- .../protocol/oidc/utils/AcrUtils.java | 4 +- .../keycloak/services/messages/Messages.java | 1 + .../account/AccountCredentialResource.java | 70 +++-- ...cloak.authentication.RequiredActionFactory | 1 + .../CustomAuthenticationFlowCallback.java | 3 +- .../testsuite/account/AccountRestClient.java | 172 +++++++++++ .../testsuite/pages/DeleteCredentialPage.java | 59 ++++ .../account/AccountRestServiceTest.java | 2 +- ...ppInitiatedActionDeleteCredentialTest.java | 267 ++++++++++++++++++ .../authentication/RequiredActionsTest.java | 1 + .../forms/LevelOfAssuranceFlowTest.java | 258 ++++++++++++++++- .../migration/AbstractMigrationTest.java | 14 + .../page/utils/SigningInPageUtils.java | 17 +- .../testsuite/ui/account2/SigningInTest.java | 6 +- .../account/AbstractWebAuthnAccountTest.java | 6 +- .../theme/base/login/delete-credential.ftl | 15 + .../login/messages/messages_en.properties | 4 + .../content/signingin-page/SigningInPage.tsx | 22 +- 45 files changed, 1570 insertions(+), 138 deletions(-) create mode 100644 model/storage-private/src/main/java/org/keycloak/migration/migrators/MigrateTo24_0_3.java create mode 100644 server-spi-private/src/main/java/org/keycloak/authentication/CredentialAction.java create mode 100644 services/src/main/java/org/keycloak/authentication/requiredactions/DeleteCredentialAction.java create mode 100644 services/src/main/java/org/keycloak/authentication/requiredactions/util/CredentialDeleteHelper.java create mode 100644 testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/account/AccountRestClient.java create mode 100644 testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/pages/DeleteCredentialPage.java create mode 100644 testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/actions/AppInitiatedActionDeleteCredentialTest.java create mode 100644 themes/src/main/resources/theme/base/login/delete-credential.ftl diff --git a/examples/providers/authenticator/src/main/java/org/keycloak/examples/authenticator/SecretQuestionRequiredAction.java b/examples/providers/authenticator/src/main/java/org/keycloak/examples/authenticator/SecretQuestionRequiredAction.java index 1463ed629cdc..0323f7154e51 100755 --- a/examples/providers/authenticator/src/main/java/org/keycloak/examples/authenticator/SecretQuestionRequiredAction.java +++ b/examples/providers/authenticator/src/main/java/org/keycloak/examples/authenticator/SecretQuestionRequiredAction.java @@ -24,6 +24,8 @@ import org.keycloak.examples.authenticator.credential.SecretQuestionCredentialModel; import jakarta.ws.rs.core.Response; +import org.keycloak.models.KeycloakSession; +import org.keycloak.sessions.AuthenticationSessionModel; /** * @author Bill Burke @@ -37,6 +39,11 @@ public void evaluateTriggers(RequiredActionContext context) { } + @Override + public String getCredentialType(KeycloakSession session, AuthenticationSessionModel authenticationSession) { + return SecretQuestionCredentialModel.TYPE; + } + @Override public void requiredActionChallenge(RequiredActionContext context) { Response challenge = context.form().createForm("secret-question-config.ftl"); diff --git a/js/apps/account-ui/src/account-security/SigningIn.tsx b/js/apps/account-ui/src/account-security/SigningIn.tsx index b49e75ddec36..3b065d65b82b 100644 --- a/js/apps/account-ui/src/account-security/SigningIn.tsx +++ b/js/apps/account-ui/src/account-security/SigningIn.tsx @@ -17,12 +17,10 @@ import { } from "@patternfly/react-core"; import { CSSProperties, useState } from "react"; import { Trans, useTranslation } from "react-i18next"; -import { ContinueCancelModal, useAlerts } from "ui-shared"; -import { deleteCredentials, getCredentials } from "../api/methods"; +import { getCredentials } from "../api/methods"; import { CredentialContainer, CredentialMetadataRepresentation, - CredentialRepresentation, } from "../api/representations"; import { EmptyRow } from "../components/datalist/EmptyRow"; import { Page } from "../components/page/Page"; @@ -68,16 +66,15 @@ const MobileLink = ({ title, onClick, testid }: MobileLinkProps) => { export const SigningIn = () => { const { t } = useTranslation(); const context = useEnvironment(); - const { addAlert, addError } = useAlerts(); const { login } = context.keycloak; const [credentials, setCredentials] = useState(); - const [key, setKey] = useState(1); - const refresh = () => setKey(key + 1); - usePromise((signal) => getCredentials({ signal, context }), setCredentials, [ - key, - ]); + usePromise( + (signal) => getCredentials({ signal, context }), + setCredentials, + [], + ); const credentialRowCells = ( credMetadata: CredentialMetadataRepresentation, @@ -111,9 +108,6 @@ export const SigningIn = () => { return items; }; - const label = (credential: CredentialRepresentation) => - credential.userLabel || t(credential.type as TFuncKey); - if (!credentials) { return ; } @@ -201,41 +195,18 @@ export const SigningIn = () => { aria-labelledby={`cred-${meta.credential.id}`} > {container.removeable ? ( - { - try { - await deleteCredentials( - context, - meta.credential, - ); - addAlert( - t("successRemovedMessage", { - userLabel: label(meta.credential), - }), - ); - refresh(); - } catch (error) { - addError( - t("errorRemovedMessage", { - userLabel: label(meta.credential), - error, - }).toString(), - ); - } + ) : ( ); } From e3edf768678572d0302d83f6a4d477ddf97ddabb Mon Sep 17 00:00:00 2001 From: Pedro Igor Date: Sat, 23 Mar 2024 11:17:52 -0300 Subject: [PATCH 078/158] Restrict the token types that can be verified when not using the user info endpoint (#146) Closes #47 Signed-off-by: Pedro Igor Conflicts: core/src/main/java/org/keycloak/util/TokenUtil.java testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/oauth/ClientTokenExchangeTest.java --- .../java/org/keycloak/util/TokenUtil.java | 4 ++ .../broker/oidc/OIDCIdentityProvider.java | 13 +++- .../rest/TestApplicationResourceProvider.java | 17 +++-- ...estApplicationResourceProviderFactory.java | 2 +- .../resources/TestApplicationResource.java | 5 ++ .../broker/KcOidcBrokerTokenExchangeTest.java | 66 +++++++++++++++++++ .../oauth/ClientTokenExchangeTest.java | 34 ++++++++++ 7 files changed, 133 insertions(+), 8 deletions(-) diff --git a/core/src/main/java/org/keycloak/util/TokenUtil.java b/core/src/main/java/org/keycloak/util/TokenUtil.java index 41432d68e7ae..92d7d132734a 100644 --- a/core/src/main/java/org/keycloak/util/TokenUtil.java +++ b/core/src/main/java/org/keycloak/util/TokenUtil.java @@ -42,6 +42,10 @@ public class TokenUtil { public static final String TOKEN_TYPE_DPOP = "DPoP"; + // JWT Access Token types from https://datatracker.ietf.org/doc/html/rfc9068#section-2.1 + public static final String TOKEN_TYPE_JWT_ACCESS_TOKEN = "at+jwt"; + public static final String TOKEN_TYPE_JWT_ACCESS_TOKEN_PREFIXED = "application/" + TOKEN_TYPE_JWT_ACCESS_TOKEN; + public static final String TOKEN_TYPE_KEYCLOAK_ID = "Serialized-ID"; public static final String TOKEN_TYPE_ID = "ID"; diff --git a/services/src/main/java/org/keycloak/broker/oidc/OIDCIdentityProvider.java b/services/src/main/java/org/keycloak/broker/oidc/OIDCIdentityProvider.java index 952040f4dfdb..5f550ad456a6 100755 --- a/services/src/main/java/org/keycloak/broker/oidc/OIDCIdentityProvider.java +++ b/services/src/main/java/org/keycloak/broker/oidc/OIDCIdentityProvider.java @@ -64,6 +64,7 @@ import org.keycloak.services.resources.RealmsResource; import org.keycloak.sessions.AuthenticationSessionModel; import org.keycloak.util.JsonSerialization; +import org.keycloak.util.TokenUtil; import org.keycloak.vault.VaultStringSecret; import jakarta.ws.rs.GET; @@ -79,6 +80,7 @@ import java.io.IOException; import java.nio.charset.StandardCharsets; import java.util.ArrayList; +import java.util.Arrays; import java.util.List; import java.util.Objects; @@ -97,6 +99,7 @@ public class OIDCIdentityProvider extends AbstractOAuth2IdentityProvider SUPPORTED_TOKEN_TYPES = Arrays.asList(TokenUtil.TOKEN_TYPE_ID, TokenUtil.TOKEN_TYPE_BEARER, TokenUtil.TOKEN_TYPE_JWT_ACCESS_TOKEN, TokenUtil.TOKEN_TYPE_JWT_ACCESS_TOKEN_PREFIXED); public OIDCIdentityProvider(KeycloakSession session, OIDCIdentityProviderConfig config) { super(session, config); @@ -849,7 +852,7 @@ final protected BrokeredIdentityContext validateJwt(EventBuilder event, String s throw new ErrorResponseException(Errors.INVALID_CONFIG, "Invalid server config", Response.Status.BAD_REQUEST); } - JsonWebToken parsedToken = null; + JsonWebToken parsedToken; try { parsedToken = validateToken(subjectToken, true); } catch (IdentityBrokerException e) { @@ -860,7 +863,9 @@ final protected BrokeredIdentityContext validateJwt(EventBuilder event, String s } try { - + if (!isTokenTypeSupported(parsedToken)) { + throw new ErrorResponseException(OAuthErrorException.INVALID_TOKEN, "token type not supported", Response.Status.BAD_REQUEST); + } boolean idTokenType = OAuth2Constants.ID_TOKEN_TYPE.equals(subjectTokenType); BrokeredIdentityContext context = extractIdentity(null, idTokenType ? null : subjectToken, parsedToken); if (context == null) { @@ -886,6 +891,10 @@ final protected BrokeredIdentityContext validateJwt(EventBuilder event, String s } + protected static boolean isTokenTypeSupported(JsonWebToken parsedToken) { + return SUPPORTED_TOKEN_TYPES.contains(parsedToken.getType()); + } + @Override protected BrokeredIdentityContext exchangeExternalImpl(EventBuilder event, MultivaluedMap params) { if (!supportsExternalExchange()) return null; diff --git a/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/java/org/keycloak/testsuite/rest/TestApplicationResourceProvider.java b/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/java/org/keycloak/testsuite/rest/TestApplicationResourceProvider.java index 5fc050ca3641..c52b047c08db 100644 --- a/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/java/org/keycloak/testsuite/rest/TestApplicationResourceProvider.java +++ b/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/java/org/keycloak/testsuite/rest/TestApplicationResourceProvider.java @@ -59,7 +59,7 @@ public class TestApplicationResourceProvider implements RealmResourceProvider { private final BlockingQueue adminLogoutActions; private final BlockingQueue frontChannelLogoutTokens; - private final BlockingQueue backChannelLogoutTokens; + private final BlockingQueue backChannelLogoutTokens; private final BlockingQueue adminPushNotBeforeActions; private final BlockingQueue adminTestAvailabilityAction; private final TestApplicationResourceProviderFactory.OIDCClientData oidcClientData; @@ -71,7 +71,7 @@ public class TestApplicationResourceProvider implements RealmResourceProvider { private final HttpRequest request; public TestApplicationResourceProvider(KeycloakSession session, BlockingQueue adminLogoutActions, - BlockingQueue backChannelLogoutTokens, + BlockingQueue backChannelLogoutTokens, BlockingQueue frontChannelLogoutTokens, BlockingQueue adminPushNotBeforeActions, BlockingQueue adminTestAvailabilityAction, @@ -102,8 +102,8 @@ public void adminLogout(String data) throws JWSInputException { @POST @Consumes(MediaType.APPLICATION_FORM_URLENCODED) @Path("/admin/backchannelLogout") - public void backchannelLogout() throws JWSInputException { - backChannelLogoutTokens.add(new JWSInput(request.getDecodedFormParameters().getFirst(OAuth2Constants.LOGOUT_TOKEN)).readJsonContent(LogoutToken.class)); + public void backchannelLogout() { + backChannelLogoutTokens.add(request.getDecodedFormParameters().getFirst(OAuth2Constants.LOGOUT_TOKEN)); } @GET @@ -139,7 +139,14 @@ public LogoutAction getAdminLogoutAction() throws InterruptedException { @GET @Produces(MediaType.APPLICATION_JSON) @Path("/poll-backchannel-logout") - public LogoutToken getBackChannelLogoutAction() throws InterruptedException { + public LogoutToken getBackChannelLogoutAction() throws InterruptedException, JWSInputException { + return new JWSInput(backChannelLogoutTokens.poll(20, TimeUnit.SECONDS)).readJsonContent(LogoutToken.class); + } + + @GET + @Produces(MediaType.APPLICATION_JSON) + @Path("/poll-backchannel-raw-logout") + public String getBackChanneRawlLogoutAction() throws InterruptedException { return backChannelLogoutTokens.poll(20, TimeUnit.SECONDS); } diff --git a/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/java/org/keycloak/testsuite/rest/TestApplicationResourceProviderFactory.java b/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/java/org/keycloak/testsuite/rest/TestApplicationResourceProviderFactory.java index e15772ad90cd..a1a8db9c0858 100644 --- a/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/java/org/keycloak/testsuite/rest/TestApplicationResourceProviderFactory.java +++ b/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/java/org/keycloak/testsuite/rest/TestApplicationResourceProviderFactory.java @@ -45,7 +45,7 @@ public class TestApplicationResourceProviderFactory implements RealmResourceProviderFactory { private BlockingQueue adminLogoutActions = new LinkedBlockingDeque<>(); - private BlockingQueue backChannelLogoutTokens = new LinkedBlockingDeque<>(); + private BlockingQueue backChannelLogoutTokens = new LinkedBlockingDeque<>(); private BlockingQueue frontChannelLogoutTokens = new LinkedBlockingDeque<>(); private BlockingQueue pushNotBeforeActions = new LinkedBlockingDeque<>(); private BlockingQueue testAvailabilityActions = new LinkedBlockingDeque<>(); diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/client/resources/TestApplicationResource.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/client/resources/TestApplicationResource.java index c0d4d3a59484..698741083eb0 100644 --- a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/client/resources/TestApplicationResource.java +++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/client/resources/TestApplicationResource.java @@ -45,6 +45,11 @@ public interface TestApplicationResource { @Path("/poll-backchannel-logout") LogoutToken getBackChannelLogoutToken(); + @GET + @Produces(MediaType.APPLICATION_JSON) + @Path("/poll-backchannel-raw-logout") + String getBackChannelRawLogoutToken(); + @GET @Produces(MediaType.APPLICATION_JSON) @Path("/poll-frontchannel-logout") diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/KcOidcBrokerTokenExchangeTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/KcOidcBrokerTokenExchangeTest.java index 6899c587ce95..c7f78de3b270 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/KcOidcBrokerTokenExchangeTest.java +++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/KcOidcBrokerTokenExchangeTest.java @@ -23,12 +23,15 @@ import static org.keycloak.testsuite.broker.BrokerTestConstants.IDP_OIDC_ALIAS; import static org.keycloak.testsuite.util.ProtocolMapperUtil.createHardcodedClaim; +import java.util.concurrent.TimeUnit; + import jakarta.ws.rs.client.Client; import jakarta.ws.rs.client.Entity; import jakarta.ws.rs.client.WebTarget; import jakarta.ws.rs.core.Form; import jakarta.ws.rs.core.HttpHeaders; import jakarta.ws.rs.core.Response; +import jakarta.ws.rs.core.Response.Status; import org.junit.Test; import org.keycloak.OAuth2Constants; import org.keycloak.admin.client.resource.ClientResource; @@ -45,13 +48,16 @@ import org.keycloak.models.IdentityProviderModel; import org.keycloak.models.KeycloakSession; import org.keycloak.models.RealmModel; +import org.keycloak.protocol.oidc.OIDCConfigAttributes; import org.keycloak.protocol.oidc.OIDCLoginProtocol; import org.keycloak.representations.idm.ClientRepresentation; import org.keycloak.representations.idm.IdentityProviderMapperRepresentation; +import org.keycloak.representations.idm.IdentityProviderRepresentation; import org.keycloak.representations.idm.UserRepresentation; import org.keycloak.representations.idm.authorization.ClientPolicyRepresentation; import org.keycloak.services.resources.admin.permissions.AdminPermissionManagement; import org.keycloak.services.resources.admin.permissions.AdminPermissions; +import org.keycloak.testsuite.Assert; import org.keycloak.testsuite.arquillian.annotation.EnableFeature; import org.keycloak.testsuite.arquillian.annotation.EnableFeatures; import org.keycloak.testsuite.util.AdminClientUtil; @@ -127,6 +133,61 @@ public void testExternalInternalTokenExchange() throws Exception { } } + @Test + public void testSupportedTokenTypesWhenValidatingSubjectToken() throws Exception { + testingClient.server().run(KcOidcBrokerTokenExchangeTest::setupRealm); + RealmResource providerRealm = realmsResouce().realm(bc.providerRealmName()); + ClientsResource clients = providerRealm.clients(); + ClientRepresentation brokerApp = clients.findByClientId("brokerapp").get(0); + brokerApp.setDirectAccessGrantsEnabled(true); + ClientResource brokerAppResource = providerRealm.clients().get(brokerApp.getId()); + brokerAppResource.update(brokerApp); + RealmResource consumerRealm = realmsResouce().realm(bc.consumerRealmName()); + IdentityProviderResource identityProviderResource = consumerRealm.identityProviders().get(bc.getIDPAlias()); + IdentityProviderRepresentation idpRep = identityProviderResource.toRepresentation(); + idpRep.getConfig().put("disableUserInfo", "true"); + identityProviderResource.update(idpRep); + getCleanup().addCleanup(() -> { + idpRep.getConfig().put("disableUserInfo", "false"); + identityProviderResource.update(idpRep); + }); + + OAuthClient.AccessTokenResponse tokenResponse = oauth.doGrantAccessTokenRequest(bc.providerRealmName(), bc.getUserLogin(), bc.getUserPassword(), null, brokerApp.getClientId(), brokerApp.getSecret()); + assertThat(tokenResponse.getIdToken(), notNullValue()); + String idTokenString = tokenResponse.getIdToken(); + oauth.realm(bc.providerRealmName()); + String logoutUrl = oauth.getLogoutUrl().idTokenHint(idTokenString) + .postLogoutRedirectUri(oauth.APP_AUTH_ROOT).build(); + driver.navigate().to(logoutUrl); + String logoutToken = testingClient.testApp().getBackChannelRawLogoutToken(); + Assert.assertNotNull(logoutToken); + + Client httpClient = AdminClientUtil.createResteasyClient(); + try { + WebTarget exchangeUrl = httpClient.target(OAuthClient.AUTH_SERVER_ROOT) + .path("/realms") + .path(bc.consumerRealmName()) + .path("protocol/openid-connect/token"); + // test user info validation. + try (Response response = exchangeUrl.request() + .header(HttpHeaders.AUTHORIZATION, BasicAuthHelper.createHeader( + "test-app", "secret")) + .post(Entity.form( + new Form() + .param(OAuth2Constants.GRANT_TYPE, OAuth2Constants.TOKEN_EXCHANGE_GRANT_TYPE) + .param(OAuth2Constants.SUBJECT_TOKEN, logoutToken) + .param(OAuth2Constants.SUBJECT_TOKEN_TYPE, OAuth2Constants.JWT_TOKEN_TYPE) + .param(OAuth2Constants.SUBJECT_ISSUER, bc.getIDPAlias()) + .param(OAuth2Constants.SCOPE, OAuth2Constants.SCOPE_OPENID) + + ))) { + assertThat(response.getStatus(), equalTo(Status.BAD_REQUEST.getStatusCode())); + } + } finally { + httpClient.close(); + } + } + private static void setupRealm(KeycloakSession session) { RealmModel realm = session.realms().getRealmByName(BrokerTestConstants.REALM_CONS_NAME); IdentityProviderModel idp = realm.getIdentityProviderByAlias(IDP_OIDC_ALIAS); @@ -149,5 +210,10 @@ private static void setupRealm(KeycloakSession session) { ResourceServer server = management.realmResourceServer(); Policy clientPolicy = management.authz().getStoreFactory().getPolicyStore().create(server, clientRep); management.idps().exchangeToPermission(idp).addAssociatedPolicy(clientPolicy); + + realm = session.realms().getRealmByName(BrokerTestConstants.REALM_PROV_NAME); + client = realm.getClientByClientId("brokerapp"); + client.addRedirectUri(OAuthClient.APP_ROOT + "/auth"); + client.setAttribute(OIDCConfigAttributes.BACKCHANNEL_LOGOUT_URL, OAuthClient.APP_ROOT + "/admin/backchannelLogout"); } } diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/oauth/ClientTokenExchangeTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/oauth/ClientTokenExchangeTest.java index 5e8a75debfed..3620e884177b 100755 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/oauth/ClientTokenExchangeTest.java +++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/oauth/ClientTokenExchangeTest.java @@ -17,11 +17,13 @@ package org.keycloak.testsuite.oauth; +import jakarta.ws.rs.core.Response.Status; import org.junit.Rule; import org.junit.Test; import org.keycloak.OAuth2Constants; import org.keycloak.TokenVerifier; import org.keycloak.admin.client.resource.ClientResource; +import org.keycloak.admin.client.resource.ClientsResource; import org.keycloak.authorization.model.Policy; import org.keycloak.authorization.model.ResourceServer; import org.keycloak.common.Profile; @@ -65,6 +67,7 @@ import java.util.Arrays; import java.util.List; import java.util.Map; +import java.util.concurrent.TimeUnit; import static org.hamcrest.Matchers.instanceOf; import static org.junit.Assert.assertEquals; @@ -179,6 +182,7 @@ public static void setupRealm(KeycloakSession session) { directLegal.setSecret("secret"); directLegal.setProtocol(OIDCLoginProtocol.LOGIN_PROTOCOL); directLegal.setFullScopeAllowed(false); + directLegal.addRedirectUri(OAuthClient.APP_ROOT + "/auth"); ClientModel directPublic = realm.addClient("direct-public"); directPublic.setClientId("direct-public"); @@ -1017,6 +1021,36 @@ public void testExchangeWithDynamicScopesEnabled() throws Exception { testExchange(); } + @Test + public void testSupportedTokenTypesWhenValidatingSubjectToken() throws Exception { + testingClient.server().run(ClientTokenExchangeTest::setupRealm); + oauth.realm(TEST); + oauth.clientId("direct-legal"); + oauth.scope(OAuth2Constants.SCOPE_OPENID); + ClientsResource clients = adminClient.realm(oauth.getRealm()).clients(); + ClientRepresentation rep = clients.findByClientId(oauth.getClientId()).get(0); + rep.getAttributes().put(OIDCConfigAttributes.BACKCHANNEL_LOGOUT_URL, oauth.APP_ROOT + "/admin/backchannelLogout"); + getCleanup().addCleanup(() -> { + rep.getAttributes().put(OIDCConfigAttributes.BACKCHANNEL_LOGOUT_URL, ""); + clients.get(rep.getId()).update(rep); + }); + clients.get(rep.getId()).update(rep); + String logoutToken; + oauth.clientSessionState("client-session"); + oauth.doLogin("user", "password"); + String code = oauth.getCurrentQuery().get(OAuth2Constants.CODE); + OAuthClient.AccessTokenResponse tokenResponse = oauth.doAccessTokenRequest(code, "secret"); + String idTokenString = tokenResponse.getIdToken(); + String logoutUrl = oauth.getLogoutUrl().idTokenHint(idTokenString) + .postLogoutRedirectUri(oauth.APP_AUTH_ROOT).build(); + driver.navigate().to(logoutUrl); + logoutToken = testingClient.testApp().getBackChannelRawLogoutToken(); + Assert.assertNotNull(logoutToken); + OAuthClient.AccessTokenResponse response = oauth.doTokenExchange(TEST, logoutToken, "target", "direct-legal", "secret"); + assertEquals(Status.BAD_REQUEST.getStatusCode(), response.getStatusCode()); + + } + private static void addDirectExchanger(KeycloakSession session) { RealmModel realm = session.realms().getRealmByName(TEST); RoleModel exampleRole = realm.addRole("example"); From d7947bb3360d3d30846040a0f8ee5167c2892a92 Mon Sep 17 00:00:00 2001 From: Pedro Igor Date: Tue, 2 Apr 2024 04:14:43 -0300 Subject: [PATCH 079/158] Resolve the user federation link as null when decorating the user profile metadata in the LDAP provider (#147) Closes #28100 Signed-off-by: Pedro Igor --- .../broker/IdpReviewProfileAuthenticator.java | 5 + .../broker/KcOidcBrokerLdapTest.java | 118 ++++++++++++++++++ 2 files changed, 123 insertions(+) create mode 100644 testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/KcOidcBrokerLdapTest.java diff --git a/services/src/main/java/org/keycloak/authentication/authenticators/broker/IdpReviewProfileAuthenticator.java b/services/src/main/java/org/keycloak/authentication/authenticators/broker/IdpReviewProfileAuthenticator.java index 510f3b02d013..469ce861e988 100755 --- a/services/src/main/java/org/keycloak/authentication/authenticators/broker/IdpReviewProfileAuthenticator.java +++ b/services/src/main/java/org/keycloak/authentication/authenticators/broker/IdpReviewProfileAuthenticator.java @@ -188,6 +188,11 @@ public void setUsername(String username) { public String getServiceAccountClientLink() { return null; } + + @Override + public String getFederationLink() { + return null; + } }; UserProfileProvider profileProvider = context.getSession().getProvider(UserProfileProvider.class); diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/KcOidcBrokerLdapTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/KcOidcBrokerLdapTest.java new file mode 100644 index 000000000000..df5c073433c2 --- /dev/null +++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/KcOidcBrokerLdapTest.java @@ -0,0 +1,118 @@ +/* + * Copyright 2023 Red Hat, Inc. and/or its affiliates + * and other contributors as indicated by the @author tags. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.keycloak.testsuite.broker; + +import java.util.Map; +import java.util.concurrent.TimeUnit; + +import jakarta.ws.rs.core.Response; +import org.junit.Assert; +import org.junit.Before; +import org.junit.ClassRule; +import org.junit.Test; +import org.keycloak.common.util.MultivaluedHashMap; +import org.keycloak.federation.kerberos.CommonKerberosConfig; +import org.keycloak.models.LDAPConstants; +import org.keycloak.models.utils.ModelToRepresentation; +import org.keycloak.representations.idm.ComponentRepresentation; +import org.keycloak.storage.UserStorageProvider.EditMode; +import org.keycloak.storage.UserStorageProviderModel; +import org.keycloak.storage.ldap.LDAPStorageProviderFactory; +import org.keycloak.storage.ldap.kerberos.LDAPProviderKerberosConfig; +import org.keycloak.testsuite.KerberosEmbeddedServer; +import org.keycloak.testsuite.admin.ApiUtil; +import org.keycloak.testsuite.federation.kerberos.KeycloakSPNegoSchemeFactory; +import org.keycloak.testsuite.util.KerberosRule; + +public final class KcOidcBrokerLdapTest extends AbstractInitializedBaseBrokerTest { + + private static final String PROVIDER_CONFIG_LOCATION = "classpath:kerberos/kerberos-ldap-connection.properties"; + + private KeycloakSPNegoSchemeFactory spnegoSchemeFactory; + + @Override + protected BrokerConfiguration getBrokerConfiguration() { + return KcOidcBrokerConfiguration.INSTANCE; + } + + @ClassRule + public static KerberosRule kerberosRule = new KerberosRule(PROVIDER_CONFIG_LOCATION, KerberosEmbeddedServer.DEFAULT_KERBEROS_REALM); + + @Before + public void onBefore() { + getKerberosRule().setKrb5ConfPath(testingClient.testing()); + spnegoSchemeFactory = new KeycloakSPNegoSchemeFactory(getKerberosConfig()); + oauth.clientId("kerberos-app"); + ComponentRepresentation rep = getUserStorageConfiguration(); + Response resp = adminClient.realm(bc.consumerRealmName()).components().add(rep); + getCleanup().addComponentId(ApiUtil.getCreatedId(resp)); + resp.close(); + } + + @Test + public void testUpdateProfileOnFirstLogin() { + driver.manage().timeouts().pageLoadTimeout(1, TimeUnit.DAYS); + updateExecutions(AbstractBrokerTest::enableUpdateProfileOnFirstLogin); + oauth.clientId("broker-app"); + loginPage.open(bc.consumerRealmName()); + logInWithBroker(bc); + updateAccountInformationPage.updateAccountInformation(bc.getUserLogin(), bc.getUserEmail(), "f", "l"); + Assert.assertFalse(errorPage.isCurrent()); + } + + private ComponentRepresentation getUserStorageConfiguration(String providerName, String providerId) { + Map kerberosConfig = getKerberosRule().getConfig(); + kerberosConfig.put(LDAPConstants.SYNC_REGISTRATIONS, "false"); + kerberosConfig.put(LDAPConstants.EDIT_MODE, EditMode.UNSYNCED.name()); + kerberosConfig.put(UserStorageProviderModel.IMPORT_ENABLED, "true"); + MultivaluedHashMap config = toComponentConfig(kerberosConfig); + + UserStorageProviderModel model = new UserStorageProviderModel(); + model.setLastSync(0); + model.setChangedSyncPeriod(-1); + model.setFullSyncPeriod(-1); + model.setName(providerName); + model.setPriority(0); + model.setProviderId(providerId); + model.setConfig(config); + + return ModelToRepresentation.toRepresentationWithoutConfig(model); + } + + private static MultivaluedHashMap toComponentConfig(Map ldapConfig) { + MultivaluedHashMap config = new MultivaluedHashMap<>(); + for (Map.Entry entry : ldapConfig.entrySet()) { + config.add(entry.getKey(), entry.getValue()); + + } + return config; + } + + private KerberosRule getKerberosRule() { + return kerberosRule; + } + + + private CommonKerberosConfig getKerberosConfig() { + return new LDAPProviderKerberosConfig(getUserStorageConfiguration()); + } + + private ComponentRepresentation getUserStorageConfiguration() { + return getUserStorageConfiguration("kerberos-ldap", LDAPStorageProviderFactory.PROVIDER_NAME); + } +} From b64102e76f56039117985a1d9eff0b17f1b659b0 Mon Sep 17 00:00:00 2001 From: Stefan Guilhen Date: Wed, 3 Apr 2024 06:53:13 -0300 Subject: [PATCH 080/158] Align isEnabled in MSAD mappers to how other properties are processed in UserAttributeLDAPStorageMapper (#148) - user model is updated by onImport with the enabled/disabled status of the LDAP user - a config option always.read.enabled.value.from.ldap was introduced, in synch to what we have in UserAttributeLDAPStorageMapper - isEnabled checks the flag to decide if it should always retrieve the value from LDAP, or return the local value. - setEnabled first updates the LDAP tx, and then calls the delegate to avoid issue #24201 Closes #26695 Closed #24201 Signed-off-by: Stefan Guilhen (cherry picked from commit 2ca59d414171d932d5dca634328eb046676de140) --- .../ldap/LDAPStorageProviderFactory.java | 4 +- .../MSADUserAccountControlStorageMapper.java | 26 ++-- ...serAccountControlStorageMapperFactory.java | 20 ++-- ...SADLDSUserAccountControlStorageMapper.java | 31 +++-- ...serAccountControlStorageMapperFactory.java | 24 +++- .../federation/ldap/LDAPMSADMapperTest.java | 111 +++++++++++++++++- 6 files changed, 175 insertions(+), 41 deletions(-) diff --git a/federation/ldap/src/main/java/org/keycloak/storage/ldap/LDAPStorageProviderFactory.java b/federation/ldap/src/main/java/org/keycloak/storage/ldap/LDAPStorageProviderFactory.java index 6bb287d4546a..63c345f2fddc 100755 --- a/federation/ldap/src/main/java/org/keycloak/storage/ldap/LDAPStorageProviderFactory.java +++ b/federation/ldap/src/main/java/org/keycloak/storage/ldap/LDAPStorageProviderFactory.java @@ -60,6 +60,7 @@ import org.keycloak.storage.ldap.mappers.LDAPStorageMapper; import org.keycloak.storage.ldap.mappers.UserAttributeLDAPStorageMapper; import org.keycloak.storage.ldap.mappers.UserAttributeLDAPStorageMapperFactory; +import org.keycloak.storage.ldap.mappers.msad.MSADUserAccountControlStorageMapper; import org.keycloak.storage.ldap.mappers.msad.MSADUserAccountControlStorageMapperFactory; import org.keycloak.storage.user.ImportSynchronization; import org.keycloak.storage.user.SynchronizationResult; @@ -434,7 +435,8 @@ public void onCreate(KeycloakSession session, RealmModel realm, ComponentModel m // MSAD specific mapper for account state propagation if (activeDirectory) { - mapperModel = KeycloakModelUtils.createComponentModel("MSAD account controls", model.getId(), MSADUserAccountControlStorageMapperFactory.PROVIDER_ID,LDAPStorageMapper.class.getName()); + mapperModel = KeycloakModelUtils.createComponentModel("MSAD account controls", model.getId(), MSADUserAccountControlStorageMapperFactory.PROVIDER_ID,LDAPStorageMapper.class.getName(), + MSADUserAccountControlStorageMapper.ALWAYS_READ_ENABLED_VALUE_FROM_LDAP, alwaysReadValueFromLDAP); realm.addComponentModel(mapperModel); } diff --git a/federation/ldap/src/main/java/org/keycloak/storage/ldap/mappers/msad/MSADUserAccountControlStorageMapper.java b/federation/ldap/src/main/java/org/keycloak/storage/ldap/mappers/msad/MSADUserAccountControlStorageMapper.java index 4d5b1773a6ab..2e01c3b17712 100644 --- a/federation/ldap/src/main/java/org/keycloak/storage/ldap/mappers/msad/MSADUserAccountControlStorageMapper.java +++ b/federation/ldap/src/main/java/org/keycloak/storage/ldap/mappers/msad/MSADUserAccountControlStorageMapper.java @@ -49,6 +49,7 @@ public class MSADUserAccountControlStorageMapper extends AbstractLDAPStorageMapper implements PasswordUpdateCallback { public static final String LDAP_PASSWORD_POLICY_HINTS_ENABLED = "ldap.password.policy.hints.enabled"; + public static final String ALWAYS_READ_ENABLED_VALUE_FROM_LDAP = "always.read.enabled.value.from.ldap"; private static final Logger logger = Logger.getLogger(MSADUserAccountControlStorageMapper.class); @@ -112,7 +113,7 @@ public void passwordUpdateFailed(UserModel user, LDAPObject ldapUser, UserCreden @Override public UserModel proxy(LDAPObject ldapUser, UserModel delegate, RealmModel realm) { - return new MSADUserModelDelegate(delegate, ldapUser); + return new MSADUserModelDelegate(delegate, ldapUser, parseBooleanParameter(mapperModel, ALWAYS_READ_ENABLED_VALUE_FROM_LDAP)); } @Override @@ -122,7 +123,8 @@ public void onRegisterUserToLDAP(LDAPObject ldapUser, UserModel localUser, Realm @Override public void onImportUserFromLDAP(LDAPObject ldapUser, UserModel user, RealmModel realm, boolean isCreate) { - + // check if user is enabled in MSAD or not. + user.setEnabled(!getUserAccountControl(ldapUser).has(UserAccountControl.ACCOUNTDISABLE)); } @Override @@ -228,30 +230,24 @@ private String getRealmName() { public class MSADUserModelDelegate extends TxAwareLDAPUserModelDelegate { private final LDAPObject ldapUser; + private final boolean isAlwaysReadEnabledFromLdap; - public MSADUserModelDelegate(UserModel delegate, LDAPObject ldapUser) { + public MSADUserModelDelegate(UserModel delegate, LDAPObject ldapUser, boolean isAlwaysReadEnabledFromLdap) { super(delegate, ldapProvider, ldapUser); this.ldapUser = ldapUser; + this.isAlwaysReadEnabledFromLdap = isAlwaysReadEnabledFromLdap; } @Override public boolean isEnabled() { - boolean kcEnabled = super.isEnabled(); - - if (getPwdLastSet() > 0) { - // Merge KC and MSAD - return kcEnabled && !getUserAccountControl(ldapUser).has(UserAccountControl.ACCOUNTDISABLE); - } else { - // If new MSAD user is created and pwdLastSet is still 0, MSAD account is in disabled state. So read just from Keycloak DB. User is not able to login via MSAD anyway - return kcEnabled; + if (isAlwaysReadEnabledFromLdap) { + return !getUserAccountControl(ldapUser).has(UserAccountControl.ACCOUNTDISABLE); } + return super.isEnabled(); } @Override public void setEnabled(boolean enabled) { - // Always update DB - super.setEnabled(enabled); - if (ldapProvider.getEditMode() == UserStorageProvider.EditMode.WRITABLE && getPwdLastSet() > 0) { MSADUserAccountControlStorageMapper.logger.debugf("Going to propagate enabled=%s for ldapUser '%s' to MSAD", enabled, ldapUser.getDn().toString()); @@ -266,6 +262,8 @@ public void setEnabled(boolean enabled) { updateUserAccountControl(false, ldapUser, control); } + // Always update DB + super.setEnabled(enabled); } @Override diff --git a/federation/ldap/src/main/java/org/keycloak/storage/ldap/mappers/msad/MSADUserAccountControlStorageMapperFactory.java b/federation/ldap/src/main/java/org/keycloak/storage/ldap/mappers/msad/MSADUserAccountControlStorageMapperFactory.java index 81566f776509..a4785d864883 100644 --- a/federation/ldap/src/main/java/org/keycloak/storage/ldap/mappers/msad/MSADUserAccountControlStorageMapperFactory.java +++ b/federation/ldap/src/main/java/org/keycloak/storage/ldap/mappers/msad/MSADUserAccountControlStorageMapperFactory.java @@ -22,12 +22,10 @@ import org.keycloak.models.RealmModel; import org.keycloak.provider.ProviderConfigProperty; import org.keycloak.provider.ProviderConfigurationBuilder; -import org.keycloak.storage.UserStorageProvider; -import org.keycloak.storage.ldap.LDAPConfig; +import org.keycloak.storage.UserStorageProviderModel; import org.keycloak.storage.ldap.LDAPStorageProvider; import org.keycloak.storage.ldap.mappers.AbstractLDAPStorageMapper; import org.keycloak.storage.ldap.mappers.AbstractLDAPStorageMapperFactory; -import org.keycloak.storage.ldap.mappers.FullNameLDAPStorageMapper; import java.util.ArrayList; import java.util.List; @@ -44,17 +42,25 @@ public class MSADUserAccountControlStorageMapperFactory extends AbstractLDAPStor configProperties = getConfigProps(null); } - private static List getConfigProps(ComponentModel parent) { - return ProviderConfigurationBuilder.create() + private static List getConfigProps(ComponentModel parentModel) { + UserStorageProviderModel parent = parentModel != null ? new UserStorageProviderModel(parentModel) : new UserStorageProviderModel(); + + ProviderConfigurationBuilder config = ProviderConfigurationBuilder.create() .property().name(MSADUserAccountControlStorageMapper.LDAP_PASSWORD_POLICY_HINTS_ENABLED) .label("Password Policy Hints Enabled") .helpText("Applicable just for writable MSAD. If on, then updating password of MSAD user will use LDAP_SERVER_POLICY_HINTS_OID " + "extension, which means that advanced MSAD password policies like 'password history' or 'minimal password age' will be applied. This extension works just for MSAD 2008 R2 or newer.") .type(ProviderConfigProperty.BOOLEAN_TYPE) .defaultValue("false") - .add() - .build(); + .add(); + if (parent.isImportEnabled()) { + config + .property().name(MSADUserAccountControlStorageMapper.ALWAYS_READ_ENABLED_VALUE_FROM_LDAP).label("Always Read Enabled Value From LDAP") + .helpText("If on, the user enabled/disabled state will always be read from MSAD by checking the proper userAccountControl") + .type(ProviderConfigProperty.BOOLEAN_TYPE).defaultValue("false").add(); + } + return config.build(); } @Override diff --git a/federation/ldap/src/main/java/org/keycloak/storage/ldap/mappers/msadlds/MSADLDSUserAccountControlStorageMapper.java b/federation/ldap/src/main/java/org/keycloak/storage/ldap/mappers/msadlds/MSADLDSUserAccountControlStorageMapper.java index 3bae11aacdad..092297e24d6f 100644 --- a/federation/ldap/src/main/java/org/keycloak/storage/ldap/mappers/msadlds/MSADLDSUserAccountControlStorageMapper.java +++ b/federation/ldap/src/main/java/org/keycloak/storage/ldap/mappers/msadlds/MSADLDSUserAccountControlStorageMapper.java @@ -32,6 +32,7 @@ import org.keycloak.storage.ldap.mappers.AbstractLDAPStorageMapper; import org.keycloak.storage.ldap.mappers.LDAPOperationDecorator; import org.keycloak.storage.ldap.mappers.PasswordUpdateCallback; +import org.keycloak.storage.ldap.mappers.msad.UserAccountControl; import javax.naming.AuthenticationException; import java.util.Objects; @@ -39,6 +40,8 @@ import java.util.regex.Pattern; import java.util.stream.Stream; +import static org.keycloak.storage.ldap.mappers.msad.MSADUserAccountControlStorageMapper.ALWAYS_READ_ENABLED_VALUE_FROM_LDAP; + /** * Mapper specific to MSAD LDS. It's able to read the msDS-UserAccountDisabled, msDS-UserPasswordExpired and pwdLastSet attributes and set actions in Keycloak based on that. * It's also able to handle exception code from LDAP user authentication (See http://www-01.ibm.com/support/docview.wss?uid=swg21290631 ) @@ -105,7 +108,7 @@ public void passwordUpdateFailed(UserModel user, LDAPObject ldapUser, UserCreden @Override public UserModel proxy(LDAPObject ldapUser, UserModel delegate, RealmModel realm) { - return new MSADUserModelDelegate(delegate, ldapUser); + return new MSADUserModelDelegate(delegate, ldapUser, parseBooleanParameter(mapperModel, ALWAYS_READ_ENABLED_VALUE_FROM_LDAP)); } @Override @@ -115,7 +118,8 @@ public void onRegisterUserToLDAP(LDAPObject ldapUser, UserModel localUser, Realm @Override public void onImportUserFromLDAP(LDAPObject ldapUser, UserModel user, RealmModel realm, boolean isCreate) { - + // check if user is enabled in MSAD or not. + user.setEnabled(!Boolean.parseBoolean(ldapUser.getAttributeAsString(LDAPConstants.MSDS_USER_ACCOUNT_DISABLED))); } @Override @@ -174,32 +178,24 @@ protected ModelException processFailedPasswordUpdateException(ModelException e) public class MSADUserModelDelegate extends UserModelDelegate { private final LDAPObject ldapUser; + private final boolean isAlwaysReadEnabledFromLdap; - public MSADUserModelDelegate(UserModel delegate, LDAPObject ldapUser) { + public MSADUserModelDelegate(UserModel delegate, LDAPObject ldapUser, boolean isAlwaysReadEnabledFromLdap) { super(delegate); this.ldapUser = ldapUser; + this.isAlwaysReadEnabledFromLdap = isAlwaysReadEnabledFromLdap; } @Override public boolean isEnabled() { - boolean kcEnabled = super.isEnabled(); - - // getPwdLastSet() == -1 when is set but not commit in AD LDS (-1 set pwdLastSet time to now) - if (getPwdLastSet() > 0 - || getPwdLastSet() == -1) { - // Merge KC and MSAD LDS - return kcEnabled && !Boolean.parseBoolean(ldapUser.getAttributeAsString(LDAPConstants.MSDS_USER_ACCOUNT_DISABLED)); - } else { - // If new MSAD LDS user is created and pwdLastSet is still 0, MSAD account is in disabled state. So read just from Keycloak DB. User is not able to login via MSAD anyway - return kcEnabled; + if (isAlwaysReadEnabledFromLdap) { + return !Boolean.parseBoolean(ldapUser.getAttributeAsString(LDAPConstants.MSDS_USER_ACCOUNT_DISABLED)); } + return super.isEnabled(); } @Override public void setEnabled(boolean enabled) { - // Always update DB - super.setEnabled(enabled); - if (ldapProvider.getEditMode() == UserStorageProvider.EditMode.WRITABLE && getPwdLastSet() > 0) { if (enabled) { logger.debugf("Removing msDS-UserAccountDisabled of user '%s'", ldapUser.getDn().toString()); @@ -209,9 +205,10 @@ public void setEnabled(boolean enabled) { logger.debugf("Setting msDS-UserAccountDisabled of user '%s' to value 'TRUE'", ldapUser.getDn().toString()); ldapUser.setSingleAttribute(LDAPConstants.MSDS_USER_ACCOUNT_DISABLED, "TRUE"); } - ldapProvider.getLdapIdentityStore().update(ldapUser); } + // Always update DB + super.setEnabled(enabled); } @Override diff --git a/federation/ldap/src/main/java/org/keycloak/storage/ldap/mappers/msadlds/MSADLDSUserAccountControlStorageMapperFactory.java b/federation/ldap/src/main/java/org/keycloak/storage/ldap/mappers/msadlds/MSADLDSUserAccountControlStorageMapperFactory.java index 5f5d12e96543..189c9c47f0f8 100644 --- a/federation/ldap/src/main/java/org/keycloak/storage/ldap/mappers/msadlds/MSADLDSUserAccountControlStorageMapperFactory.java +++ b/federation/ldap/src/main/java/org/keycloak/storage/ldap/mappers/msadlds/MSADLDSUserAccountControlStorageMapperFactory.java @@ -21,9 +21,12 @@ import org.keycloak.models.LDAPConstants; import org.keycloak.models.RealmModel; import org.keycloak.provider.ProviderConfigProperty; +import org.keycloak.provider.ProviderConfigurationBuilder; +import org.keycloak.storage.UserStorageProviderModel; import org.keycloak.storage.ldap.LDAPStorageProvider; import org.keycloak.storage.ldap.mappers.AbstractLDAPStorageMapper; import org.keycloak.storage.ldap.mappers.AbstractLDAPStorageMapperFactory; +import org.keycloak.storage.ldap.mappers.msad.MSADUserAccountControlStorageMapper; import java.util.ArrayList; import java.util.List; @@ -35,9 +38,23 @@ public class MSADLDSUserAccountControlStorageMapperFactory extends AbstractLDAPStorageMapperFactory { public static final String PROVIDER_ID = LDAPConstants.MSADLDS_USER_ACCOUNT_CONTROL_MAPPER; - protected static final List configProperties = new ArrayList<>(); + protected static final List configProperties; static { + configProperties = getConfigProps(null); + } + + private static List getConfigProps(ComponentModel parentModel) { + UserStorageProviderModel parent = parentModel != null ? new UserStorageProviderModel(parentModel) : new UserStorageProviderModel(); + if (parent.isImportEnabled()) { + ProviderConfigurationBuilder config = ProviderConfigurationBuilder.create() + .property().name(MSADUserAccountControlStorageMapper.ALWAYS_READ_ENABLED_VALUE_FROM_LDAP).label("Always Read Enabled Value From LDAP") + .helpText("If on, the user enabled/disabled state will always be read from MSAD LDS by checking the msDS-UserAccountDisabled attribute") + .type(ProviderConfigProperty.BOOLEAN_TYPE).defaultValue("false").add(); + + return config.build(); + } + return new ArrayList<>(); } @Override @@ -51,6 +68,11 @@ public List getConfigProperties() { return configProperties; } + @Override + public List getConfigProperties(RealmModel realm, ComponentModel parent) { + return getConfigProps(parent); + } + @Override public String getId() { return PROVIDER_ID; diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/federation/ldap/LDAPMSADMapperTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/federation/ldap/LDAPMSADMapperTest.java index 764c97df3d9a..5cb0e35f1557 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/federation/ldap/LDAPMSADMapperTest.java +++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/federation/ldap/LDAPMSADMapperTest.java @@ -28,13 +28,18 @@ import org.junit.Test; import org.junit.runners.MethodSorters; import org.keycloak.admin.client.resource.UserResource; +import org.keycloak.component.ComponentModel; import org.keycloak.models.LDAPConstants; import org.keycloak.models.RealmModel; import org.keycloak.models.UserModel; import org.keycloak.representations.idm.UserRepresentation; import org.keycloak.storage.UserStorageProvider; +import org.keycloak.storage.UserStorageProviderModel; import org.keycloak.storage.ldap.LDAPStorageProvider; import org.keycloak.storage.ldap.idm.model.LDAPObject; +import org.keycloak.storage.ldap.mappers.LDAPStorageMapper; +import org.keycloak.storage.ldap.mappers.msad.MSADUserAccountControlStorageMapper; +import org.keycloak.storage.ldap.mappers.msad.MSADUserAccountControlStorageMapperFactory; import org.keycloak.storage.ldap.mappers.msad.UserAccountControl; import org.keycloak.testsuite.admin.ApiUtil; import org.keycloak.testsuite.pages.AppPage; @@ -262,7 +267,7 @@ public void test05UpdatePasswordUnsyncedMode() { johnRep.setRequiredActions(Collections.singletonList(UserModel.RequiredAction.UPDATE_PASSWORD.name())); john.update(johnRep); - // Check in LDAP, that johnkeycloak has pwdLastSet set attribute set in MSAD to bigger value than 0. Previous update of requiredAction did not updated LDAP + // Check in LDAP, that johnkeycloak has pwdLastSet set attribute set in MSAD to bigger value than 0. Previous update of requiredAction did not update LDAP long pwdLastSetFromLDAP = getPwdLastSetOfJohn(); assertThat(pwdLastSetFromLDAP, Matchers.greaterThan(0L)); @@ -384,6 +389,15 @@ public void test08DisabledUserUnsyncedMode() { ctx.getLdapModel().getConfig().putSingle(LDAPConstants.EDIT_MODE, UserStorageProvider.EditMode.UNSYNCED.toString()); appRealm.updateComponent(ctx.getLdapModel()); + + // change MSAD mapper config "ALWAYS_READ_ENABLED_VALUE_FROM_LDAP" to false, so that local db has priority. + ComponentModel msadMapperComponent = appRealm.getComponentsStream(ctx.getLdapModel().getId(), LDAPStorageMapper.class.getName()) + .filter(c -> MSADUserAccountControlStorageMapperFactory.PROVIDER_ID.equals(c.getProviderId())) + .findFirst().orElse(null); + if (msadMapperComponent != null) { + msadMapperComponent.getConfig().putSingle(MSADUserAccountControlStorageMapper.ALWAYS_READ_ENABLED_VALUE_FROM_LDAP, "false"); + appRealm.updateComponent(msadMapperComponent); + } }); // Disable user johnkeycloak through Keycloak admin API. Due UNSYNCED mode, this should update Keycloak DB, but not MSAD @@ -401,6 +415,7 @@ public void test08DisabledUserUnsyncedMode() { Assert.assertEquals("Account is disabled, contact your administrator.", loginPage.getError()); // Enable johnkeycloak in admin REST API + johnRep = john.toRepresentation(); johnRep.setEnabled(true); john.update(johnRep); @@ -419,9 +434,103 @@ public void test08DisabledUserUnsyncedMode() { ctx.getLdapModel().getConfig().putSingle(LDAPConstants.EDIT_MODE, UserStorageProvider.EditMode.WRITABLE.toString()); appRealm.updateComponent(ctx.getLdapModel()); + + // reset MSAD mapper config "ALWAYS_READ_ENABLED_VALUE_FROM_LDAP" to true. + ComponentModel msadMapperComponent = appRealm.getComponentsStream(ctx.getLdapModel().getId(), LDAPStorageMapper.class.getName()) + .filter(c -> MSADUserAccountControlStorageMapperFactory.PROVIDER_ID.equals(c.getProviderId())) + .findFirst().orElse(null); + if (msadMapperComponent != null) { + msadMapperComponent.getConfig().putSingle(MSADUserAccountControlStorageMapper.ALWAYS_READ_ENABLED_VALUE_FROM_LDAP, "true"); + appRealm.updateComponent(msadMapperComponent); + } + + }); + } + + @Test + public void test09DisableUserImportDisabled() { + testingClient.server().run(session -> { + // set import enabled to false - in this case only attributes known to LDAP (via one of the mappers) are written + LDAPTestContext ctx = LDAPTestContext.init(session); + RealmModel appRealm = ctx.getRealm(); + ctx.getLdapModel().getConfig().putSingle(UserStorageProviderModel.IMPORT_ENABLED, "false"); + appRealm.updateComponent(ctx.getLdapModel()); + }); + + // check user is enabled both locally and on MSAD. + UserResource john = ApiUtil.findUserByUsernameId(adminClient.realm("test"), "johnkeycloak"); + UserRepresentation johnRep = john.toRepresentation(); + Assert.assertTrue(johnRep.isEnabled()); + Assert.assertTrue(isJohnEnabledInMSAD()); + + // disable user johnkeycloak - it should disable both locally and on MSAD. + johnRep.setEnabled(false); + john.update(johnRep); + + // Login as johnkeycloak and see the user is disabled. + loginPage.open(); + loginPage.login("johnkeycloak", "Password1"); + Assert.assertEquals("Account is disabled, contact your administrator.", loginPage.getError()); + + // check user is disabled in all places. + johnRep = john.toRepresentation(); + Assert.assertFalse(johnRep.isEnabled()); + Assert.assertFalse(isJohnEnabledInMSAD()); + + // restore john to enabled state. + johnRep.setEnabled(true); + john.update(johnRep); + + // Login again. User should be enabled. + loginPage.open(); + loginPage.login("johnkeycloak", "Password1"); + Assert.assertEquals(AppPage.RequestType.AUTH_RESPONSE, appPage.getRequestType()); + + testingClient.server().run(session -> { + // restore import enabled mode in the storage provider. + LDAPTestContext ctx = LDAPTestContext.init(session); + RealmModel appRealm = ctx.getRealm(); + ctx.getLdapModel().getConfig().putSingle(UserStorageProviderModel.IMPORT_ENABLED, "true"); + appRealm.updateComponent(ctx.getLdapModel()); }); } + @Test + public void test10DisabledUserSwitchedToEnabledOnMSAD() { + // disable user johnkeycloak via REST API - should be disabled in MSAD as well. + UserResource john = ApiUtil.findUserByUsernameId(adminClient.realm("test"), "johnkeycloak"); + UserRepresentation johnRep = john.toRepresentation(); + johnRep.setEnabled(false); + john.update(johnRep); + + Assert.assertFalse(isJohnEnabledInMSAD()); + + // Login as johnkeycloak and see the user is disabled. + loginPage.open(); + loginPage.login("johnkeycloak", "Password1"); + Assert.assertEquals("Account is disabled, contact your administrator.", loginPage.getError()); + + // enable user johnkeycloak in MSAD only + testingClient.server().run(session -> { + LDAPTestContext ctx = LDAPTestContext.init(session); + RealmModel appRealm = ctx.getRealm(); + + LDAPObject ldapJohn = ctx.getLdapProvider().loadLDAPUserByUsername(appRealm, "johnkeycloak"); + String userAccountControlStr = ldapJohn.getAttributeAsString(LDAPConstants.USER_ACCOUNT_CONTROL); + UserAccountControl control = new UserAccountControl(Long.parseLong(userAccountControlStr)); + control.remove(UserAccountControl.ACCOUNTDISABLE); + ldapJohn.setSingleAttribute(LDAPConstants.USER_ACCOUNT_CONTROL, String.valueOf(control.getValue())); + ctx.getLdapProvider().getLdapIdentityStore().update(ldapJohn); + }); + + Assert.assertTrue(isJohnEnabledInMSAD()); + + // Login again. User should be enabled. + loginPage.open(); + loginPage.login("johnkeycloak", "Password1"); + Assert.assertEquals(AppPage.RequestType.AUTH_RESPONSE, appPage.getRequestType()); + } + private long getPwdLastSetOfJohn() { String pwdLastSett = testingClient.server().fetchString(session -> { LDAPTestContext ctx = LDAPTestContext.init(session); From 8afc60ad33613a2ae93aa3316d22d74faba492bd Mon Sep 17 00:00:00 2001 From: Stian Thorgersen Date: Tue, 9 Apr 2024 08:09:54 +0200 Subject: [PATCH 081/158] Ignore all links to GitHub when checking external links in docs due to rate limiting issues (#151) Closes #28330 Signed-off-by: stianst --- .../tests/src/test/resources/ignored-links | 13 ++----------- 1 file changed, 2 insertions(+), 11 deletions(-) diff --git a/docs/documentation/tests/src/test/resources/ignored-links b/docs/documentation/tests/src/test/resources/ignored-links index fc5442b8739f..a0d459af1db4 100644 --- a/docs/documentation/tests/src/test/resources/ignored-links +++ b/docs/documentation/tests/src/test/resources/ignored-links @@ -13,7 +13,7 @@ http://host:port* https://host:port* http://broker-keycloak:8180* https://expressjs.com/ -https://github.com/keycloak/keycloak/tree/* +https://github.com/* http://node11:8080* http://node11:8080/auth/ http://node12:8080* @@ -21,10 +21,7 @@ http://node21:8080* http://node22:8080* http://web.example.com* https://keycloak.example.com* -https://github.com/keycloak/keycloak-documentation/blob/master/* https://openshift.example.com:8443/console -https://github.com/keycloak/keycloak-quickstarts.git -https://github.com/go-chi/chi#router-design https://accounts.google.com/o/oauth2/revoke https://keycloak.example.com/auth/realms/REALM_NAME/protocol/openid-connect/logout http://127.0.0.1:3000/oauth/callback @@ -38,10 +35,4 @@ https://developer.paypal.com/developer/applications https://account.live.com/developers/applications/create https://developer.twitter.com/apps/ https://kubernetes.io/docs/tutorials/stateful-application/basic-stateful-set/#rolling-update -https://github.com/keycloak/keycloak/blob/main/docs/tests.md#kerberos-server -https://github.com/apache/felix-dev/tree/master/http#using-the-osgi-http-whiteboard -https://github.com/keycloak/keycloak/blob/025778fe9c745316f80b53fe3052aeb314e868ef/js/apps/admin-ui/public/locales/en/dashboard.json#L3 -https://github.com/keycloak/keycloak/issues/new?* -https://stackapps.com/apps/oauth/register -https://github.com/keycloak/keycloak-quickstarts/tree/release/24.0/extension/extend-account-console -https://github.com/keycloak/keycloak/releases/download/DEV/keycloak-DEV.zip \ No newline at end of file +https://stackapps.com/apps/oauth/register \ No newline at end of file From eb0f7924312c62bc2c65df2d1bc859ce2147b0d0 Mon Sep 17 00:00:00 2001 From: Pedro Igor Date: Tue, 9 Apr 2024 03:12:02 -0300 Subject: [PATCH 082/158] Make sure attribute metadata from user storage providers are added only for the provider associated with a federated user (#150) Closes #28248 Signed-off-by: Pedro Igor Conflicts: docs/documentation/upgrading/topics/changes/changes-24_0_3.adoc --- .../topics/changes/changes-24_0_3.adoc | 17 +++++ .../kerberos/KerberosFederationProvider.java | 17 ++--- .../storage/ldap/LDAPStorageProvider.java | 60 ++++++---------- .../sssd/SSSDFederationProvider.java | 28 +++----- .../cache/infinispan/UserCacheSession.java | 6 +- .../keycloak/storage/UserStorageManager.java | 19 +++-- .../userprofile/DefaultAttributes.java | 33 ++++++++- .../keycloak/userprofile/UserProfileUtil.java | 32 ++++++--- .../userprofile/UserProfileDecorator.java | 17 +++-- .../DeclarativeUserProfileProvider.java | 17 ----- .../testsuite/util/LDAPTestUtils.java | 8 +++ .../ldap/LDAPAccountRestApiTest.java | 5 ++ .../federation/ldap/LDAPTestContext.java | 6 +- .../federation/ldap/LDAPUserProfileTest.java | 71 +++++++++++++++++-- .../base/src/test/resources/ldap/users.ldif | 5 ++ .../testsuite/sssd/SSSDUserProfileTest.java | 3 +- 16 files changed, 226 insertions(+), 118 deletions(-) diff --git a/docs/documentation/upgrading/topics/changes/changes-24_0_3.adoc b/docs/documentation/upgrading/topics/changes/changes-24_0_3.adoc index 27265c6c4cef..f61284a3d189 100644 --- a/docs/documentation/upgrading/topics/changes/changes-24_0_3.adoc +++ b/docs/documentation/upgrading/topics/changes/changes-24_0_3.adoc @@ -5,3 +5,20 @@ Because of security concerns, the redirect URI verification now performs a exact The full wildcard `*` can still be used as a valid redirect in development for http(s) URIs with those characteristics. In production environments a exact valid redirect URI without wildcard needs to be configured for any URI of that type. Please note that wildcard valid redirect URIs are not recommended for production and not covered by the OAuth 2.0 specification. + +ifeval::[{project_community}==true] += Changes to the `org.keycloak.userprofile.UserProfileDecorator` interface + +To properly support multiple user storage providers within a realm, the `org.keycloak.userprofile.UserProfileDecorator` +interface has changed. + +The `decorateUserProfile` method is no longer invoked when parsing the user profile configuration for the first time (and caching it), +but everytime a user is being managed through the user profile provider. As a result, the method changed its contract to: + +```java +List decorateUserProfile(String providerId, UserProfileMetadata metadata) +``` + +Differently than the previous contract and behavior, this method is only invoked for the user storage provider from where the user +was loaded from. +endif::[] \ No newline at end of file diff --git a/federation/kerberos/src/main/java/org/keycloak/federation/kerberos/KerberosFederationProvider.java b/federation/kerberos/src/main/java/org/keycloak/federation/kerberos/KerberosFederationProvider.java index 137449a0794c..362945f69e04 100755 --- a/federation/kerberos/src/main/java/org/keycloak/federation/kerberos/KerberosFederationProvider.java +++ b/federation/kerberos/src/main/java/org/keycloak/federation/kerberos/KerberosFederationProvider.java @@ -42,16 +42,16 @@ import org.keycloak.storage.UserStorageProviderModel; import org.keycloak.storage.user.ImportedUserValidation; import org.keycloak.storage.user.UserLookupProvider; -import org.keycloak.userprofile.AttributeContext; import org.keycloak.userprofile.AttributeGroupMetadata; import org.keycloak.userprofile.AttributeMetadata; import org.keycloak.userprofile.UserProfileDecorator; import org.keycloak.userprofile.UserProfileMetadata; import org.keycloak.userprofile.UserProfileUtil; +import java.util.Collections; import java.util.HashMap; +import java.util.List; import java.util.Map; -import java.util.function.Predicate; import java.util.stream.Stream; import javax.security.auth.login.LoginException; @@ -302,22 +302,13 @@ public String toString() { } @Override - public void decorateUserProfile(RealmModel realm, UserProfileMetadata metadata) { - Predicate kerberosUsersSelector = (attributeContext -> { - UserModel user = attributeContext.getUser(); - if (user == null) { - return false; - } - - return model.getId().equals(user.getFederationLink()); - }); - + public List decorateUserProfile(String providerId, UserProfileMetadata metadata) { int guiOrder = (int) metadata.getAttributes().stream() .map(AttributeMetadata::getName) .distinct() .count(); AttributeGroupMetadata metadataGroup = UserProfileUtil.lookupUserMetadataGroup(session); - UserProfileUtil.addMetadataAttributeToUserProfile(KerberosConstants.KERBEROS_PRINCIPAL, metadata, metadataGroup, kerberosUsersSelector, guiOrder++, model.getName()); + return Collections.singletonList(UserProfileUtil.createAttributeMetadata(KerberosConstants.KERBEROS_PRINCIPAL, metadata, metadataGroup, guiOrder++, model.getName())); } } diff --git a/federation/ldap/src/main/java/org/keycloak/storage/ldap/LDAPStorageProvider.java b/federation/ldap/src/main/java/org/keycloak/storage/ldap/LDAPStorageProvider.java index fbb5da27d1e0..dea8cab938f0 100755 --- a/federation/ldap/src/main/java/org/keycloak/storage/ldap/LDAPStorageProvider.java +++ b/federation/ldap/src/main/java/org/keycloak/storage/ldap/LDAPStorageProvider.java @@ -23,7 +23,6 @@ import java.util.Collections; import java.util.HashMap; import java.util.HashSet; -import java.util.LinkedHashSet; import java.util.LinkedList; import java.util.List; import java.util.Map; @@ -1104,47 +1103,27 @@ public String toString() { } @Override - public void decorateUserProfile(RealmModel realm, UserProfileMetadata metadata) { - Predicate ldapUsersSelector = (attributeContext -> { - UserModel user = attributeContext.getUser(); - if (user == null) { - return false; - } - - if (model.isImportEnabled()) { - return getModel().getId().equals(user.getFederationLink()); - } else { - return getModel().getId().equals(new StorageId(user.getId()).getProviderId()); - } - }); - - Predicate onlyAdminCondition = context -> metadata.getContext().isAdminContext(); - + public List decorateUserProfile(String providerId, UserProfileMetadata metadata) { int guiOrder = (int) metadata.getAttributes().stream() .map(AttributeMetadata::getName) .distinct() .count(); - + RealmModel realm = session.getContext().getRealm(); // 1 - get configured attributes from LDAP mappers and add them to the user profile (if they not already present) - Set attributes = new LinkedHashSet<>(); - realm.getComponentsStream(model.getId(), LDAPStorageMapper.class.getName()) + List attributes = realm.getComponentsStream(model.getId(), LDAPStorageMapper.class.getName()) .sorted(ldapMappersComparator.sortAsc()) - .forEachOrdered(mapperModel -> { + .flatMap(mapperModel -> { LDAPStorageMapper ldapMapper = mapperManager.getMapper(mapperModel); - attributes.addAll(ldapMapper.getUserAttributes()); - }); + return ldapMapper.getUserAttributes().stream(); + }).toList(); + + List metadatas = new ArrayList<>(); + for (String attrName : attributes) { - // In case that attributes from LDAP mappers are explicitly defined on user profile, we can prefer defined configuration - if (!metadata.getAttribute(attrName).isEmpty()) { - logger.debugf("Ignore adding attribute '%s' to user profile by LDAP provider '%s' as attribute is already defined on user profile.", attrName, getModel().getName()); - } else { - logger.debugf("Adding attribute '%s' to user profile by LDAP provider '%s' for user profile context '%s'.", attrName, getModel().getName(), metadata.getContext().toString()); - // Writable and readable only by administrators by default. Applied only for LDAP users - AttributeMetadata attributeMetadata = metadata.addAttribute(attrName, guiOrder++, Collections.emptyList()) - .addWriteCondition(onlyAdminCondition) - .addReadCondition(onlyAdminCondition) - .setRequired(AttributeMetadata.ALWAYS_FALSE); - attributeMetadata.setSelector(ldapUsersSelector); + AttributeMetadata attributeMetadata = UserProfileUtil.createAttributeMetadata(attrName, metadata, guiOrder++, getModel().getName()); + + if (attributeMetadata != null) { + metadatas.add(attributeMetadata); } } @@ -1157,17 +1136,20 @@ public void decorateUserProfile(RealmModel realm, UserProfileMetadata metadata) AttributeGroupMetadata metadataGroup = UserProfileUtil.lookupUserMetadataGroup(session); for (String attrName : metadataAttributes) { - boolean attributeAdded = UserProfileUtil.addMetadataAttributeToUserProfile(attrName, metadata, metadataGroup, ldapUsersSelector, guiOrder++, getModel().getName()); - if (!attributeAdded) { + AttributeMetadata attributeAdded = UserProfileUtil.createAttributeMetadata(attrName, metadata, metadataGroup, guiOrder++, getModel().getName()); + if (attributeAdded == null) { guiOrder--; + } else { + metadatas.add(attributeAdded); } } // 3 - make all attributes read-only for LDAP users in case that LDAP itself is read-only if (getEditMode() == EditMode.READ_ONLY) { - for (AttributeMetadata attrMetadata : metadata.getAttributes()) { - attrMetadata.addWriteCondition(ldapUsersSelector.negate()); - } + Stream.concat(metadata.getAttributes().stream(), metadatas.stream()) + .forEach(attrMetadata -> attrMetadata.addWriteCondition(AttributeMetadata.ALWAYS_FALSE)); } + + return metadatas; } } diff --git a/federation/sssd/src/main/java/org/keycloak/federation/sssd/SSSDFederationProvider.java b/federation/sssd/src/main/java/org/keycloak/federation/sssd/SSSDFederationProvider.java index e56176b96aa9..cdc0bb48b2ad 100755 --- a/federation/sssd/src/main/java/org/keycloak/federation/sssd/SSSDFederationProvider.java +++ b/federation/sssd/src/main/java/org/keycloak/federation/sssd/SSSDFederationProvider.java @@ -33,16 +33,15 @@ import org.keycloak.storage.UserStorageProviderModel; import org.keycloak.storage.user.ImportedUserValidation; import org.keycloak.storage.user.UserLookupProvider; -import org.keycloak.userprofile.AttributeContext; import org.keycloak.userprofile.AttributeMetadata; import org.keycloak.userprofile.UserProfileDecorator; import org.keycloak.userprofile.UserProfileMetadata; +import org.keycloak.userprofile.UserProfileUtil; -import java.util.Collections; +import java.util.ArrayList; import java.util.HashSet; import java.util.List; import java.util.Set; -import java.util.function.Predicate; import java.util.stream.Stream; /** @@ -221,38 +220,29 @@ public Stream getDisableableCredentialTypesStream(RealmModel realm, User } @Override - public void decorateUserProfile(RealmModel realm, UserProfileMetadata metadata) { - // selector by sssd - Predicate sssdUsersSelector = attributeContext -> - attributeContext.getUser() != null && model.getId().equals(attributeContext.getUser().getFederationLink()); - - // condition to view only by admin - Predicate onlyAdminCondition = context -> metadata.getContext().isAdminContext(); - + public List decorateUserProfile(String providerId, UserProfileMetadata metadata) { // guiOrder if new attributes are needed int guiOrder = (int) metadata.getAttributes().stream() .map(AttributeMetadata::getName) .distinct() .count(); + List metadatas = new ArrayList<>(); + // firstName, lastName, username and email should be read-only for (String attrName : List.of(UserModel.FIRST_NAME, UserModel.LAST_NAME, UserModel.EMAIL, UserModel.USERNAME)) { List attrMetadatas = metadata.getAttribute(attrName); if (attrMetadatas.isEmpty()) { logger.debugf("Adding user profile attribute '%s' for sssd provider and context '%s'.", attrName, metadata.getContext()); - AttributeMetadata sssdAttrMetadata = metadata.addAttribute(attrName, guiOrder++, Collections.emptyList()) - .addWriteCondition(AttributeMetadata.ALWAYS_FALSE) - .addReadCondition(onlyAdminCondition) - .setRequired(AttributeMetadata.ALWAYS_FALSE); - sssdAttrMetadata.setSelector(sssdUsersSelector); + metadatas.add(UserProfileUtil.createAttributeMetadata(attrName, metadata, null, guiOrder++, model.getName())); } else { for (AttributeMetadata attrMetadata : attrMetadatas) { logger.debugf("Cloning attribute '%s' as read-only for sssd provider and context '%s'.", attrName, metadata.getContext()); - AttributeMetadata sssdAttrMetadata = metadata.addAttribute(attrMetadata.clone()) - .addWriteCondition(AttributeMetadata.ALWAYS_FALSE); - sssdAttrMetadata.setSelector(sssdUsersSelector); + metadatas.add(attrMetadata.clone().addWriteCondition(AttributeMetadata.ALWAYS_FALSE)); } } } + + return metadatas; } } diff --git a/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/UserCacheSession.java b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/UserCacheSession.java index 91df0c7de9b8..4ad8ed3e4382 100755 --- a/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/UserCacheSession.java +++ b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/UserCacheSession.java @@ -63,6 +63,7 @@ import org.keycloak.storage.UserStorageProvider; import org.keycloak.storage.UserStorageProviderModel; import org.keycloak.storage.client.ClientStorageProvider; +import org.keycloak.userprofile.AttributeMetadata; import org.keycloak.userprofile.UserProfileDecorator; import org.keycloak.userprofile.UserProfileMetadata; @@ -950,9 +951,10 @@ public void onCreate(KeycloakSession session, RealmModel realm, ComponentModel m } @Override - public void decorateUserProfile(RealmModel realm, UserProfileMetadata metadata) { + public List decorateUserProfile(String providerId, UserProfileMetadata metadata) { if (getDelegate() instanceof UserProfileDecorator) { - ((UserProfileDecorator) getDelegate()).decorateUserProfile(realm, metadata); + return ((UserProfileDecorator) getDelegate()).decorateUserProfile(providerId, metadata); } + return List.of(); } } diff --git a/model/storage-private/src/main/java/org/keycloak/storage/UserStorageManager.java b/model/storage-private/src/main/java/org/keycloak/storage/UserStorageManager.java index c6f21fcaa23b..e3dcb4ebdc2b 100755 --- a/model/storage-private/src/main/java/org/keycloak/storage/UserStorageManager.java +++ b/model/storage-private/src/main/java/org/keycloak/storage/UserStorageManager.java @@ -21,6 +21,8 @@ import static org.keycloak.utils.StreamsUtil.distinctByKey; import static org.keycloak.utils.StreamsUtil.paginatedStream; +import java.util.Collections; +import java.util.List; import java.util.Map; import java.util.Objects; import java.util.Optional; @@ -71,6 +73,7 @@ import org.keycloak.storage.user.UserQueryMethodsProvider; import org.keycloak.storage.user.UserQueryProvider; import org.keycloak.storage.user.UserRegistrationProvider; +import org.keycloak.userprofile.AttributeMetadata; import org.keycloak.userprofile.UserProfileDecorator; import org.keycloak.userprofile.UserProfileMetadata; @@ -857,10 +860,18 @@ public void onCache(RealmModel realm, CachedUserModel user, UserModel delegate) } @Override - public void decorateUserProfile(RealmModel realm, UserProfileMetadata metadata) { - for (UserProfileDecorator decorator : getEnabledStorageProviders(session.getContext().getRealm(), UserProfileDecorator.class) - .collect(Collectors.toList())) { - decorator.decorateUserProfile(realm, metadata); + public List decorateUserProfile(String providerId, UserProfileMetadata metadata) { + RealmModel realm = session.getContext().getRealm(); + UserStorageProviderModel providerModel = getStorageProviderModel(realm, providerId); + + if (providerModel != null) { + UserProfileDecorator decorator = getStorageProviderInstance(providerModel, UserProfileDecorator.class); + + if (decorator != null) { + return decorator.decorateUserProfile(providerId, metadata); + } } + + return Collections.emptyList(); } } diff --git a/server-spi-private/src/main/java/org/keycloak/userprofile/DefaultAttributes.java b/server-spi-private/src/main/java/org/keycloak/userprofile/DefaultAttributes.java index 321411e34261..f9cb740cdac6 100644 --- a/server-spi-private/src/main/java/org/keycloak/userprofile/DefaultAttributes.java +++ b/server-spi-private/src/main/java/org/keycloak/userprofile/DefaultAttributes.java @@ -30,6 +30,7 @@ import java.util.Optional; import java.util.Set; import java.util.function.Consumer; +import java.util.function.Function; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -39,9 +40,11 @@ import org.keycloak.models.KeycloakSession; import org.keycloak.models.RealmModel; import org.keycloak.models.UserModel; +import org.keycloak.models.UserProvider; import org.keycloak.models.utils.KeycloakModelUtils; import org.keycloak.representations.userprofile.config.UPConfig; import org.keycloak.representations.userprofile.config.UPConfig.UnmanagedAttributePolicy; +import org.keycloak.storage.StorageId; import org.keycloak.utils.StringUtil; import org.keycloak.validate.ValidationContext; import org.keycloak.validate.ValidationError; @@ -84,7 +87,7 @@ public DefaultAttributes(UserProfileContext context, Map attributes, this.context = context; this.user = user; this.session = session; - this.metadataByAttribute = configureMetadata(profileMetadata.getAttributes()); + this.metadataByAttribute = configureMetadata(profileMetadata.getAttributes(), profileMetadata); this.upConfig = session.getProvider(UserProfileProvider.class).getConfiguration(); putAll(Collections.unmodifiableMap(normalizeAttributes(attributes))); } @@ -324,7 +327,7 @@ protected AttributeContext createAttributeContext(AttributeMetadata metadata) { return createAttributeContext(createAttribute(metadata.getName()), metadata); } - private Map configureMetadata(List attributes) { + private Map configureMetadata(List attributes, UserProfileMetadata profileMetadata) { Map metadatas = new HashMap<>(); for (AttributeMetadata metadata : attributes) { @@ -334,9 +337,35 @@ private Map configureMetadata(List } } + metadatas.putAll(getUserStorageProviderMetadata(profileMetadata)); + return metadatas; } + private Map getUserStorageProviderMetadata(UserProfileMetadata profileMetadata) { + if (user == null || (StorageId.isLocalStorage(user.getId()) && user.getFederationLink() == null)) { + // new user or not a user from a storage provider other than local + return Collections.emptyMap(); + } + + String providerId = user.getFederationLink(); + + if (providerId == null) { + providerId = StorageId.providerId(user.getId()); + } + + UserProvider userProvider = session.users(); + + if (userProvider instanceof UserProfileDecorator) { + // query the user provider from the source user storage provider for additional attribute metadata + UserProfileDecorator decorator = (UserProfileDecorator) userProvider; + return decorator.decorateUserProfile(providerId, profileMetadata).stream() + .collect(Collectors.toMap(AttributeMetadata::getName, Function.identity())); + } + + return Collections.emptyMap(); + } + private SimpleImmutableEntry> createAttribute(String name) { return new SimpleImmutableEntry>(name, null) { @Override diff --git a/server-spi-private/src/main/java/org/keycloak/userprofile/UserProfileUtil.java b/server-spi-private/src/main/java/org/keycloak/userprofile/UserProfileUtil.java index 0b716608d044..6c28ddd784e4 100644 --- a/server-spi-private/src/main/java/org/keycloak/userprofile/UserProfileUtil.java +++ b/server-spi-private/src/main/java/org/keycloak/userprofile/UserProfileUtil.java @@ -47,6 +47,8 @@ public class UserProfileUtil { public static final String USER_METADATA_GROUP = "user-metadata"; + public static final Predicate ONLY_ADMIN_CONDITION = context -> context.getContext().isAdminContext(); + /** * Find the metadata group "user-metadata" * @@ -69,30 +71,38 @@ public static AttributeGroupMetadata lookupUserMetadataGroup(KeycloakSession ses * @param attrName attribute name * @param metadata user-profile metadata where attribute would be added * @param metadataGroup metadata group in user-profile - * @param userFederationUsersSelector used to recognize if user belongs to this user-storage provider or not * @param guiOrder guiOrder to where to put the attribute * @param storageProviderName storageProviderName (just for logging purposes) - * @return true if attribute was added. False otherwise + * @return the attribute metadata if attribute was created. False otherwise */ - public static boolean addMetadataAttributeToUserProfile(String attrName, UserProfileMetadata metadata, AttributeGroupMetadata metadataGroup, Predicate userFederationUsersSelector, int guiOrder, String storageProviderName) { - // In case that attributes like LDAP_ID, KERBEROS_PRINCIPAL are explicitly defined on user profile, we can prefer defined configuration + public static AttributeMetadata createAttributeMetadata(String attrName, UserProfileMetadata metadata, AttributeGroupMetadata metadataGroup, int guiOrder, String storageProviderName) { + return createAttributeMetadata(attrName, metadata, metadataGroup, ONLY_ADMIN_CONDITION, AttributeMetadata.ALWAYS_FALSE, guiOrder, storageProviderName); + } + + public static AttributeMetadata createAttributeMetadata(String attrName, UserProfileMetadata metadata, int guiOrder, String storageProviderName) { + return createAttributeMetadata(attrName, metadata, null, ONLY_ADMIN_CONDITION, ONLY_ADMIN_CONDITION, guiOrder, storageProviderName); + } + + private static AttributeMetadata createAttributeMetadata(String attrName, UserProfileMetadata metadata, AttributeGroupMetadata metadataGroup, Predicate readCondition, Predicate writeCondition, int guiOrder, String storageProviderName) { if (!metadata.getAttribute(attrName).isEmpty()) { logger.tracef("Ignore adding metadata attribute '%s' to user profile by user storage provider '%s' as attribute is already defined on user profile.", attrName, storageProviderName); - return false; } else { logger.tracef("Adding metadata attribute '%s' to user profile by user storage provider '%s' for user profile context '%s'.", attrName, storageProviderName, metadata.getContext().toString()); - Predicate onlyAdminCondition = context -> metadata.getContext().isAdminContext(); - AttributeMetadata attributeMetadata = metadata.addAttribute(attrName, guiOrder, Collections.emptyList()) - .addWriteCondition(AttributeMetadata.ALWAYS_FALSE) // Not writable for anyone - .addReadCondition(onlyAdminCondition) // Read-only for administrators + + AttributeMetadata attributeMetadata = new AttributeMetadata(attrName, guiOrder) + .setValidators(Collections.emptyList()) + .addWriteCondition(writeCondition) + .addReadCondition(readCondition) .setRequired(AttributeMetadata.ALWAYS_FALSE); if (metadataGroup != null) { attributeMetadata.setAttributeGroupMetadata(metadataGroup); } - attributeMetadata.setSelector(userFederationUsersSelector); - return true; + + return attributeMetadata; } + + return null; } /** diff --git a/server-spi/src/main/java/org/keycloak/userprofile/UserProfileDecorator.java b/server-spi/src/main/java/org/keycloak/userprofile/UserProfileDecorator.java index acf7b5508f38..0675d769b7d7 100644 --- a/server-spi/src/main/java/org/keycloak/userprofile/UserProfileDecorator.java +++ b/server-spi/src/main/java/org/keycloak/userprofile/UserProfileDecorator.java @@ -19,18 +19,27 @@ package org.keycloak.userprofile; +import java.util.List; + import org.keycloak.models.RealmModel; /** + *

This interface allows user storage providers to customize the user profile configuration and its attributes for realm + * on a per-user storage provider basis. + * * @author Pedro Igor */ public interface UserProfileDecorator { /** - * Decorates user profile with additional metadata. For instance, metadata attributes, which are available just for your user-storage - * provider can be added there, so they are available just for the users coming from your provider + *

Decorates user profile with additional metadata. For instance, metadata attributes, which are available just for your user-storage + * provider can be added there, so they are available just for the users coming from your provider. + * + *

This method is invoked every time a user is being managed through a user profile provider. * - * @param metadata to decorate + * @param providerId the id of the user storage provider to which the user is associated with + * @param metadata the current {@link UserProfileMetadata} for the current realm + * @return a list of attribute metadata.The {@link AttributeMetadata} returned from this method overrides any other metadata already set in {@code metadata} for a given attribute. */ - void decorateUserProfile(RealmModel realm, UserProfileMetadata metadata); + List decorateUserProfile(String providerId, UserProfileMetadata metadata); } diff --git a/services/src/main/java/org/keycloak/userprofile/DeclarativeUserProfileProvider.java b/services/src/main/java/org/keycloak/userprofile/DeclarativeUserProfileProvider.java index 78e45124383e..65503a9fb687 100644 --- a/services/src/main/java/org/keycloak/userprofile/DeclarativeUserProfileProvider.java +++ b/services/src/main/java/org/keycloak/userprofile/DeclarativeUserProfileProvider.java @@ -176,13 +176,9 @@ public UserModel apply(Attributes attributes) { protected UserProfileMetadata configureUserProfile(UserProfileMetadata metadata, KeycloakSession session) { UserProfileContext context = metadata.getContext(); UserProfileMetadata decoratedMetadata = metadata.clone(); - RealmModel realm = session.getContext().getRealm(); - ComponentModel component = getComponentModel().orElse(null); if (component == null) { - // makes sure user providers can override metadata for any attribute - decorateUserProfileMetadataWithUserStorage(realm, decoratedMetadata); return decoratedMetadata; } @@ -411,23 +407,10 @@ public boolean test(AttributeContext context) { } } - if (session != null) { - // makes sure user providers can override metadata for any attribute - decorateUserProfileMetadataWithUserStorage(session.getContext().getRealm(), decoratedMetadata); - } - return decoratedMetadata; } - private void decorateUserProfileMetadataWithUserStorage(RealmModel realm, UserProfileMetadata userProfileMetadata) { - // makes sure user providers can override metadata for any attribute - UserProvider users = session.users(); - if (users instanceof UserProfileDecorator) { - ((UserProfileDecorator) users).decorateUserProfile(realm, userProfileMetadata); - } - } - private Map asHashMap(List groups) { return groups.stream().collect(Collectors.toMap(g -> g.getName(), g -> g)); } diff --git a/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/java/org/keycloak/testsuite/util/LDAPTestUtils.java b/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/java/org/keycloak/testsuite/util/LDAPTestUtils.java index b1adb22bcbf0..e9a62b15841c 100644 --- a/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/java/org/keycloak/testsuite/util/LDAPTestUtils.java +++ b/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/java/org/keycloak/testsuite/util/LDAPTestUtils.java @@ -187,6 +187,14 @@ public static ComponentModel getLdapProviderModel(RealmModel realm) { .orElse(null); } + public static ComponentModel getLdapProviderModel(RealmModel realm, String providerName) { + return realm.getComponentsStream(realm.getId(), UserStorageProvider.class.getName()) + .filter(component -> Objects.equals(component.getProviderId(), LDAPStorageProviderFactory.PROVIDER_NAME)) + .filter(component -> providerName == null || component.getName().equals(providerName)) + .findFirst() + .orElse(null); + } + public static LDAPStorageProvider getLdapProvider(KeycloakSession keycloakSession, ComponentModel ldapFedModel) { return (LDAPStorageProvider)keycloakSession.getProvider(UserStorageProvider.class, ldapFedModel); } diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/federation/ldap/LDAPAccountRestApiTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/federation/ldap/LDAPAccountRestApiTest.java index e66f0b408319..8f89449c2ab0 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/federation/ldap/LDAPAccountRestApiTest.java +++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/federation/ldap/LDAPAccountRestApiTest.java @@ -123,6 +123,11 @@ public void testUpdateProfile() throws IOException { List origLdapEntryDn = adminRestUserRep.getAttributes().get(LDAPConstants.LDAP_ENTRY_DN); Assert.assertNotNull(origLdapId.get(0)); Assert.assertNotNull(origLdapEntryDn.get(0)); + adminRestUserRep = testRealm().users().get(adminRestUserRep.getId()).toRepresentation(); + origLdapId = adminRestUserRep.getAttributes().get(LDAPConstants.LDAP_ID); + origLdapEntryDn = adminRestUserRep.getAttributes().get(LDAPConstants.LDAP_ENTRY_DN); + Assert.assertNotNull(origLdapId.get(0)); + Assert.assertNotNull(origLdapEntryDn.get(0)); // Trying to add KERBEROS_PRINCIPAL (Adding attribute, which was not yet present). Request does not fail, but attribute is not updated user.setFirstName("JohnUpdated"); diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/federation/ldap/LDAPTestContext.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/federation/ldap/LDAPTestContext.java index e724296e2d8d..a7f263fe2c65 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/federation/ldap/LDAPTestContext.java +++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/federation/ldap/LDAPTestContext.java @@ -34,8 +34,12 @@ public class LDAPTestContext { private final LDAPStorageProvider ldapProvider; public static LDAPTestContext init(KeycloakSession session) { + return init(session, null); + } + + public static LDAPTestContext init(KeycloakSession session, String providerName) { RealmModel testRealm = session.realms().getRealmByName(AbstractLDAPTest.TEST_REALM_NAME); - ComponentModel ldapCompModel = LDAPTestUtils.getLdapProviderModel(testRealm); + ComponentModel ldapCompModel = LDAPTestUtils.getLdapProviderModel(testRealm, providerName); UserStorageProviderModel ldapModel = new UserStorageProviderModel(ldapCompModel); LDAPStorageProvider ldapProvider = LDAPTestUtils.getLdapProvider(session, ldapModel); return new LDAPTestContext(testRealm, ldapModel, ldapProvider); diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/federation/ldap/LDAPUserProfileTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/federation/ldap/LDAPUserProfileTest.java index b99d2c23298b..dd0ecf15d87a 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/federation/ldap/LDAPUserProfileTest.java +++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/federation/ldap/LDAPUserProfileTest.java @@ -29,6 +29,8 @@ import org.junit.Test; import org.junit.runners.MethodSorters; import org.keycloak.admin.client.resource.UserResource; +import org.keycloak.component.ComponentModel; +import org.keycloak.component.PrioritizedComponentModel; import org.keycloak.models.LDAPConstants; import org.keycloak.models.RealmModel; import org.keycloak.models.UserModel; @@ -38,6 +40,8 @@ import org.keycloak.representations.userprofile.config.UPAttributePermissions; import org.keycloak.representations.userprofile.config.UPConfig; import org.keycloak.storage.UserStorageProvider; +import org.keycloak.storage.UserStorageProviderModel; +import org.keycloak.storage.ldap.LDAPStorageProvider; import org.keycloak.storage.ldap.idm.model.LDAPObject; import org.keycloak.testsuite.admin.ApiUtil; import org.keycloak.testsuite.pages.LoginUpdateProfilePage; @@ -68,7 +72,7 @@ protected LDAPRule getLDAPRule() { @Override protected void afterImportTestRealm() { testingClient.server().run(session -> { - LDAPTestContext ctx = LDAPTestContext.init(session); + LDAPTestContext ctx = LDAPTestContext.init(session, "test-ldap"); RealmModel appRealm = ctx.getRealm(); UserModel user = LDAPTestUtils.addLocalUser(session, appRealm, "marykeycloak", "mary@test.com", "Password1"); @@ -218,7 +222,20 @@ public void testUserProfileWithReadOnlyLdapLocalUser() { @Test public void testUserProfileWithoutImport() { setLDAPImportDisabled(); + UPConfig origConfig = testRealm().users().userProfile().getConfiguration(); try { + UPConfig config = testRealm().users().userProfile().getConfiguration(); + // Set postal code + UPAttribute postalCode = new UPAttribute(); + postalCode.setName("postal_code"); + postalCode.setDisplayName("Postal Code"); + + UPAttributePermissions permissions = new UPAttributePermissions(); + permissions.setView(Set.of(UPConfigUtils.ROLE_USER, UPConfigUtils.ROLE_ADMIN)); + permissions.setEdit(Set.of(UPConfigUtils.ROLE_USER, UPConfigUtils.ROLE_ADMIN)); + postalCode.setPermissions(permissions); + config.getAttributes().add(postalCode); + testRealm().users().userProfile().update(config); // Test local user is writable and has only attributes defined explicitly in user-profile // Test user profile of user johnkeycloak in admin API UserResource johnResource = ApiUtil.findUserByUsernameId(testRealm(), "johnkeycloak2"); @@ -229,12 +246,56 @@ public void testUserProfileWithoutImport() { assertProfileAttributes(john, USER_METADATA_GROUP, true, LDAPConstants.LDAP_ID, LDAPConstants.LDAP_ENTRY_DN); } finally { setLDAPImportEnabled(); + testRealm().users().userProfile().update(origConfig); + } + } + + @Test + public void testMultipleLDAPProviders() { + testingClient.server().run(session -> { + RealmModel testRealm = session.realms().getRealmByName(AbstractLDAPTest.TEST_REALM_NAME); + ComponentModel ldapCompModel = LDAPTestUtils.getLdapProviderModel(testRealm); + UserStorageProviderModel ldapModel = new UserStorageProviderModel(ldapCompModel); + ldapModel.setId(null); + ldapModel.setParentId(null); + ldapModel.setName("other-ldap"); + ldapModel.put(LDAPConstants.USERS_DN, ldapModel.getConfig().getFirst(LDAPConstants.USERS_DN).replace("People", "OtherPeople")); + ldapCompModel.put(PrioritizedComponentModel.PRIORITY, "100"); + testRealm.addComponentModel(ldapModel); + LDAPStorageProvider ldapProvider = LDAPTestUtils.getLdapProvider(session, ldapModel); + LDAPObject john = LDAPTestUtils.addLDAPUser(ldapProvider, testRealm, "anotherjohn", "AnotherJohn", "AnotherDoe", "anotherjohn@email.org", null, "1234"); + LDAPTestUtils.updateLDAPPassword(ldapProvider, john, "Password1"); + }); + + // the provider for this user does not have postal_code mapper + UserResource userResource = ApiUtil.findUserByUsernameId(testRealm(), "anotherjohn"); + UserRepresentation userRep = userResource.toRepresentation(true); + Assert.assertNull(userRep.getAttributes().get("postal_code")); + + // the provider for this user does have postal_code mapper + userResource = ApiUtil.findUserByUsernameId(testRealm(), "johnkeycloak"); + userRep = userResource.toRepresentation(true); + Assert.assertNotNull(userRep.getAttributes().get("postal_code")); + + setLDAPReadOnly(); + try { + // the second provider is not readonly + userResource = ApiUtil.findUserByUsernameId(testRealm(), "anotherjohn"); + userRep = userResource.toRepresentation(true); + assertProfileAttributes(userRep, null, false, "username", "email", "firstName", "lastName"); + + // the original provider is readonly + userResource = ApiUtil.findUserByUsernameId(testRealm(), "johnkeycloak"); + userRep = userResource.toRepresentation(true); + assertProfileAttributes(userRep, null, true, "username", "email", "firstName", "lastName", "postal_code"); + } finally { + setLDAPWritable(); } } private void setLDAPReadOnly() { testingClient.server().run(session -> { - LDAPTestContext ctx = LDAPTestContext.init(session); + LDAPTestContext ctx = LDAPTestContext.init(session, "test-ldap"); RealmModel appRealm = ctx.getRealm(); ctx.getLdapModel().getConfig().putSingle(LDAPConstants.EDIT_MODE, UserStorageProvider.EditMode.READ_ONLY.toString()); @@ -244,7 +305,7 @@ private void setLDAPReadOnly() { private void setLDAPWritable() { testingClient.server().run(session -> { - LDAPTestContext ctx = LDAPTestContext.init(session); + LDAPTestContext ctx = LDAPTestContext.init(session, "test-ldap"); RealmModel appRealm = ctx.getRealm(); ctx.getLdapModel().getConfig().putSingle(LDAPConstants.EDIT_MODE, UserStorageProvider.EditMode.WRITABLE.toString()); @@ -254,7 +315,7 @@ private void setLDAPWritable() { private void setLDAPImportDisabled() { testingClient.server().run(session -> { - LDAPTestContext ctx = LDAPTestContext.init(session); + LDAPTestContext ctx = LDAPTestContext.init(session, "test-ldap"); RealmModel appRealm = ctx.getRealm(); ctx.getLdapModel().getConfig().putSingle(IMPORT_ENABLED, "false"); @@ -264,7 +325,7 @@ private void setLDAPImportDisabled() { private void setLDAPImportEnabled() { testingClient.server().run(session -> { - LDAPTestContext ctx = LDAPTestContext.init(session); + LDAPTestContext ctx = LDAPTestContext.init(session, "test-ldap"); RealmModel appRealm = ctx.getRealm(); ctx.getLdapModel().getConfig().putSingle(IMPORT_ENABLED, "true"); diff --git a/testsuite/integration-arquillian/tests/base/src/test/resources/ldap/users.ldif b/testsuite/integration-arquillian/tests/base/src/test/resources/ldap/users.ldif index 4df8b5e61a64..388686add060 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/resources/ldap/users.ldif +++ b/testsuite/integration-arquillian/tests/base/src/test/resources/ldap/users.ldif @@ -23,3 +23,8 @@ dn: ou=Groups,dc=keycloak,dc=org objectclass: top objectclass: organizationalUnit ou: Groups + +dn: ou=OtherPeople,dc=keycloak,dc=org +objectclass: top +objectclass: organizationalUnit +ou: People diff --git a/testsuite/integration-arquillian/tests/other/sssd/src/test/java/org/keycloak/testsuite/sssd/SSSDUserProfileTest.java b/testsuite/integration-arquillian/tests/other/sssd/src/test/java/org/keycloak/testsuite/sssd/SSSDUserProfileTest.java index 9d9ea49274e1..81965f78e2a4 100644 --- a/testsuite/integration-arquillian/tests/other/sssd/src/test/java/org/keycloak/testsuite/sssd/SSSDUserProfileTest.java +++ b/testsuite/integration-arquillian/tests/other/sssd/src/test/java/org/keycloak/testsuite/sssd/SSSDUserProfileTest.java @@ -184,7 +184,8 @@ public void test04MixedSSSDUserProfile() throws Exception { String sssdId = getSssdProviderId(); UserResource userResource = ApiUtil.findUserByUsernameId(testRealm(), username); UserRepresentation user = userResource.toRepresentation(true); - assertUser(user, username, getEmail(username), getFirstName(username), getLastName(username), sssdId); + // first and last names are removed from the UP config (unmanaged) and are not available from the representation + assertUser(user, username, getEmail(username), null, null, sssdId); assertProfileAttributes(user, null, true, UserModel.USERNAME, UserModel.EMAIL, UserModel.FIRST_NAME, UserModel.LAST_NAME); assertProfileAttributes(user, null, false, "postal_code"); From 3ea3b26730b1c8c3799ef7f4f62c50485cd03bdf Mon Sep 17 00:00:00 2001 From: Jon Koops Date: Tue, 9 Apr 2024 11:23:19 +0200 Subject: [PATCH 083/158] Allow `false` to be set for `pkceMethod` option (#28347) (#152) Closes #28335 Signed-off-by: Jon Koops --- js/libs/keycloak-js/src/keycloak.js | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/js/libs/keycloak-js/src/keycloak.js b/js/libs/keycloak-js/src/keycloak.js index 35ff2d24de30..333c703164db 100755 --- a/js/libs/keycloak-js/src/keycloak.js +++ b/js/libs/keycloak-js/src/keycloak.js @@ -132,10 +132,11 @@ function Keycloak (config) { kc.silentCheckSsoFallback = true; } - if (initOptions.pkceMethod) { - if (initOptions.pkceMethod !== "S256") { - throw new TypeError(`Invalid value for 'pkceMethod', expected 'S256' but got '${initOptions.pkceMethod}'.`); + if (typeof initOptions.pkceMethod !== "undefined") { + if (initOptions.pkceMethod !== "S256" && initOptions.pkceMethod !== false) { + throw new TypeError(`Invalid value for pkceMethod', expected 'S256' or false but got ${initOptions.pkceMethod}.`); } + kc.pkceMethod = initOptions.pkceMethod; } else { kc.pkceMethod = "S256"; From e20efaaf9de4687b2b3faaa712cb2eb0efc6471f Mon Sep 17 00:00:00 2001 From: Steven Hawkins Date: Mon, 15 Apr 2024 05:02:14 -0400 Subject: [PATCH 084/158] fix: adds a test and permissions for cache configmap (#153) closes: #28638 Signed-off-by: Steve Hawkins --- operator/src/main/kubernetes/kubernetes.yml | 8 ++ .../integration/BaseOperatorTest.java | 3 + .../testsuite/integration/CacheTest.java | 124 ++++++++++++++++++ .../operator/testsuite/utils/K8sUtils.java | 2 +- operator/src/test/resources/cache-ispn.xml | 86 ++++++++++++ 5 files changed, 222 insertions(+), 1 deletion(-) create mode 100644 operator/src/test/java/org/keycloak/operator/testsuite/integration/CacheTest.java create mode 100644 operator/src/test/resources/cache-ispn.xml diff --git a/operator/src/main/kubernetes/kubernetes.yml b/operator/src/main/kubernetes/kubernetes.yml index 45c60438c585..888c2068dfb8 100644 --- a/operator/src/main/kubernetes/kubernetes.yml +++ b/operator/src/main/kubernetes/kubernetes.yml @@ -15,6 +15,14 @@ rules: - delete - patch - update + - apiGroups: + - "" + resources: + - configmaps + verbs: + - get + - list + - watch - apiGroups: - "" resources: diff --git a/operator/src/test/java/org/keycloak/operator/testsuite/integration/BaseOperatorTest.java b/operator/src/test/java/org/keycloak/operator/testsuite/integration/BaseOperatorTest.java index a49e53f557f9..3d4159ad4b78 100644 --- a/operator/src/test/java/org/keycloak/operator/testsuite/integration/BaseOperatorTest.java +++ b/operator/src/test/java/org/keycloak/operator/testsuite/integration/BaseOperatorTest.java @@ -26,6 +26,7 @@ import io.fabric8.kubernetes.api.model.apps.StatefulSet; import io.fabric8.kubernetes.api.model.events.v1.Event; import io.fabric8.kubernetes.api.model.rbac.ClusterRoleBinding; +import io.fabric8.kubernetes.api.model.rbac.RoleBinding; import io.fabric8.kubernetes.client.Config; import io.fabric8.kubernetes.client.ConfigBuilder; import io.fabric8.kubernetes.client.KubernetesClient; @@ -166,6 +167,8 @@ private static void createRBACresourcesAndOperatorDeployment() throws FileNotFou K8sUtils.set(k8sclient, new FileInputStream(TARGET_KUBERNETES_GENERATED_YML_FOLDER + deploymentTarget + ".yml"), obj -> { if (obj instanceof ClusterRoleBinding) { ((ClusterRoleBinding)obj).getSubjects().forEach(s -> s.setNamespace(namespace)); + } else if (obj instanceof RoleBinding && "keycloak-operator-view".equals(((RoleBinding)obj).getMetadata().getName())) { + return null; // exclude this role since it's not present in olm } return obj; }); diff --git a/operator/src/test/java/org/keycloak/operator/testsuite/integration/CacheTest.java b/operator/src/test/java/org/keycloak/operator/testsuite/integration/CacheTest.java new file mode 100644 index 000000000000..497146a6720b --- /dev/null +++ b/operator/src/test/java/org/keycloak/operator/testsuite/integration/CacheTest.java @@ -0,0 +1,124 @@ +/* + * Copyright 2022 Red Hat, Inc. and/or its affiliates + * and other contributors as indicated by the @author tags. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.keycloak.operator.testsuite.integration; + +import io.fabric8.kubernetes.api.model.ConfigMapBuilder; +import io.fabric8.kubernetes.api.model.apps.StatefulSet; +import io.fabric8.kubernetes.client.dsl.Resource; +import io.fabric8.kubernetes.client.utils.Serialization; +import io.quarkus.logging.Log; +import io.quarkus.test.junit.QuarkusTest; + +import org.apache.commons.io.IOUtils; +import org.awaitility.Awaitility; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.Test; +import org.keycloak.operator.crds.v2alpha1.deployment.Keycloak; +import org.keycloak.operator.crds.v2alpha1.deployment.KeycloakStatusCondition; +import org.keycloak.operator.crds.v2alpha1.deployment.spec.CacheSpecBuilder; +import org.keycloak.operator.testsuite.utils.CRAssert; +import org.keycloak.operator.testsuite.utils.K8sUtils; + +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.util.concurrent.TimeUnit; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.keycloak.operator.testsuite.utils.K8sUtils.deployKeycloak; + +@QuarkusTest +public class CacheTest extends BaseOperatorTest { + + private static final String CONFIGMAP_NAME = "my-config"; + + @AfterEach + public void cleanupConfigMap() { + k8sclient.configMaps().withName(CONFIGMAP_NAME).delete(); + } + + @Test + public void testCreateCacheConfigMapFileAfterDeployment() { + var kc = getTestKeycloakDeployment(false); + var deploymentName = kc.getMetadata().getName(); + kc.getSpec().setCacheSpec(new CacheSpecBuilder().withNewConfigMapFile("file", CONFIGMAP_NAME, false).build()); + + deployKeycloak(k8sclient, kc, false); + + // Check Operator has deployed Keycloak and the statefulset exists, this allows + // for the watched configmap to be picked up + Log.info("Checking Operator has deployed Keycloak deployment"); + Resource stsResource = k8sclient.resources(StatefulSet.class).withName(deploymentName); + Resource keycloakResource = k8sclient.resources(Keycloak.class).withName(deploymentName); + // expect no errors and not ready, which means we'll keep reconciling + Awaitility.await().ignoreExceptions().atMost(2, TimeUnit.MINUTES).during(10, TimeUnit.SECONDS) + .untilAsserted(() -> { + StatefulSet deployment = stsResource.get(); + assertThat(deployment).isNotNull(); + Keycloak keycloak = keycloakResource.get(); + CRAssert.assertKeycloakStatusCondition(keycloak, KeycloakStatusCondition.HAS_ERRORS, false); + CRAssert.assertKeycloakStatusCondition(keycloak, KeycloakStatusCondition.READY, false); + var pod = k8sclient.pods().withLabelSelector(deployment.getSpec().getSelector()).list().getItems() + .get(0); + assertThat(pod.getStatus().getPhase()).isEqualTo("Pending"); + }); + + // should allow the deployment to proceed, but it won't become ready + Log.info("Checking Operator has picked up a bad configmap"); + createCacheConfigMap(false); + + Awaitility.await().ignoreExceptions().atMost(3, TimeUnit.MINUTES).untilAsserted(() -> { + StatefulSet deployment = stsResource.get(); + assertThat(deployment).isNotNull(); + // check the pod directly - it takes longer for us to update our Keycloak status + // with an error + var pod = k8sclient.pods().withLabelSelector(deployment.getSpec().getSelector()).list().getItems().get(0); + assertThat(Serialization.asYaml(pod.getStatus().getContainerStatuses().get(0))).contains("terminated"); + }); + + // should become fully ready + Log.info("Checking Operator has picked up a valid configmap"); + createCacheConfigMap(true); + + K8sUtils.waitForKeycloakToBeReady(k8sclient, kc); + } + + @Test + public void testCacheConfigMapFile() { + var kc = getTestKeycloakDeployment(true); + kc.getSpec().setCacheSpec(new CacheSpecBuilder().withNewConfigMapFile("file", CONFIGMAP_NAME, false).build()); + + createCacheConfigMap(true); + + // should immediately seem ready because probes are disabled + deployKeycloak(k8sclient, kc, true); + } + + private void createCacheConfigMap(boolean valid) { + try { + K8sUtils.set(k8sclient, + new ConfigMapBuilder().withNewMetadata().withName(CONFIGMAP_NAME).endMetadata() + .addToData("file", + valid ? IOUtils.resourceToString("/cache-ispn.xml", StandardCharsets.UTF_8) + : "this isn't right") + .build()); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + +} diff --git a/operator/src/test/java/org/keycloak/operator/testsuite/utils/K8sUtils.java b/operator/src/test/java/org/keycloak/operator/testsuite/utils/K8sUtils.java index 8bb1561f1543..256ed3df09ff 100644 --- a/operator/src/test/java/org/keycloak/operator/testsuite/utils/K8sUtils.java +++ b/operator/src/test/java/org/keycloak/operator/testsuite/utils/K8sUtils.java @@ -68,7 +68,7 @@ public static List set(KubernetesClient client, InputStream stream) } public static List set(KubernetesClient client, InputStream stream, Function modifier) { - return client.load(stream).items().stream().map(modifier).map(i -> set(client, i)).collect(Collectors.toList()); + return client.load(stream).items().stream().map(modifier).filter(Objects::nonNull).map(i -> set(client, i)).collect(Collectors.toList()); } public static T set(KubernetesClient client, T hasMetadata) { diff --git a/operator/src/test/resources/cache-ispn.xml b/operator/src/test/resources/cache-ispn.xml new file mode 100644 index 000000000000..72cf71785ea5 --- /dev/null +++ b/operator/src/test/resources/cache-ispn.xml @@ -0,0 +1,86 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + From 2b78c83b00cdf40be7b9efcd89442179a844aeee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Vacek?= <86605314+vaceksimon@users.noreply.github.com> Date: Mon, 15 Apr 2024 17:28:16 +0200 Subject: [PATCH 085/158] Workflow failure: Fuse adapter tests Closes: #27021 Signed-off-by: Simon Vacek --- testsuite/integration-arquillian/pom.xml | 8 +++ .../resources/product-portal-keycloak.json | 10 +++ .../servers/app-server/karaf/pom.xml | 1 + .../fuse/camel-fuse7-undertow/pom.xml | 1 - .../test-apps/fuse/customer-app-fuse/pom.xml | 4 +- .../fuse/cxf-jaxrs-fuse7-undertow/pom.xml | 6 ++ .../example/rs/CxfCustomerService.java | 6 +- .../test-apps/fuse/demorealm.json | 45 +++++++++--- .../test-apps/fuse/external-config/pom.xml | 4 +- .../features/src/main/resources/features.xml | 2 +- .../test-apps/fuse/pom.xml | 2 +- .../example/ProductPortalServlet.java | 2 +- .../OSGI-INF/blueprint/blueprint.xml | 17 ++++- .../src/main/resources/keycloak.json | 9 +++ .../adapter/example/fuse/FuseAdapterTest.java | 71 ++++++++++--------- .../testsuite/utils/fuse/FuseUtils.java | 2 +- 16 files changed, 134 insertions(+), 56 deletions(-) create mode 100644 testsuite/integration-arquillian/servers/app-server/karaf/fuse7x/src/main/resources/product-portal-keycloak.json create mode 100644 testsuite/integration-arquillian/test-apps/fuse/product-app-fuse7-undertow/src/main/resources/keycloak.json diff --git a/testsuite/integration-arquillian/pom.xml b/testsuite/integration-arquillian/pom.xml index f65553f6b695..b8e4b1024784 100644 --- a/testsuite/integration-arquillian/pom.xml +++ b/testsuite/integration-arquillian/pom.xml @@ -236,6 +236,14 @@ com.google.guava guava + + org.slf4j + slf4j-api + + + org.slf4j + slf4j-log4j12 + diff --git a/testsuite/integration-arquillian/servers/app-server/karaf/fuse7x/src/main/resources/product-portal-keycloak.json b/testsuite/integration-arquillian/servers/app-server/karaf/fuse7x/src/main/resources/product-portal-keycloak.json new file mode 100644 index 000000000000..0a28d8c5c4d2 --- /dev/null +++ b/testsuite/integration-arquillian/servers/app-server/karaf/fuse7x/src/main/resources/product-portal-keycloak.json @@ -0,0 +1,10 @@ + +{ + "realm": "demo", + "resource": "product-portal", + "auth-server-url": "http://localhost:8080/auth", + "ssl-required" : "external", + "credentials": { + "secret": "password" + } +} \ No newline at end of file diff --git a/testsuite/integration-arquillian/servers/app-server/karaf/pom.xml b/testsuite/integration-arquillian/servers/app-server/karaf/pom.xml index fc156da61e25..c36e3c40911c 100644 --- a/testsuite/integration-arquillian/servers/app-server/karaf/pom.xml +++ b/testsuite/integration-arquillian/servers/app-server/karaf/pom.xml @@ -119,6 +119,7 @@ keycloak-direct-access.json keycloak-hawtio-client.json keycloak-hawtio.json + product-portal-keycloak.json diff --git a/testsuite/integration-arquillian/test-apps/fuse/camel-fuse7-undertow/pom.xml b/testsuite/integration-arquillian/test-apps/fuse/camel-fuse7-undertow/pom.xml index 201c50a85380..debf59b9f149 100755 --- a/testsuite/integration-arquillian/test-apps/fuse/camel-fuse7-undertow/pom.xml +++ b/testsuite/integration-arquillian/test-apps/fuse/camel-fuse7-undertow/pom.xml @@ -38,7 +38,6 @@ javax.servlet;version="[3.1,5)", javax.servlet.http;version="[3.1,5)", javax.net.ssl, - org.apache.camel.*, io.undertow.*;version="[1.4,3)", org.apache.camel;version="[2.13,3)", org.keycloak.*;version="${fuse.adapter.version}", diff --git a/testsuite/integration-arquillian/test-apps/fuse/customer-app-fuse/pom.xml b/testsuite/integration-arquillian/test-apps/fuse/customer-app-fuse/pom.xml index a7d0ad92fcb7..04c9ea33cd1a 100755 --- a/testsuite/integration-arquillian/test-apps/fuse/customer-app-fuse/pom.xml +++ b/testsuite/integration-arquillian/test-apps/fuse/customer-app-fuse/pom.xml @@ -113,8 +113,8 @@ war - customer-portal - customer-portal + /customer-portal + /customer-portal WEB-INF/lib .,WEB-INF/classes ${project.name} diff --git a/testsuite/integration-arquillian/test-apps/fuse/cxf-jaxrs-fuse7-undertow/pom.xml b/testsuite/integration-arquillian/test-apps/fuse/cxf-jaxrs-fuse7-undertow/pom.xml index 6ae6300c2e72..6d2c671651d6 100755 --- a/testsuite/integration-arquillian/test-apps/fuse/cxf-jaxrs-fuse7-undertow/pom.xml +++ b/testsuite/integration-arquillian/test-apps/fuse/cxf-jaxrs-fuse7-undertow/pom.xml @@ -63,6 +63,12 @@ cxf-rt-transports-http-undertow ${cxf.undertow.version} + + javax.ws.rs + javax.ws.rs-api + 2.1 + compile + diff --git a/testsuite/integration-arquillian/test-apps/fuse/cxf-jaxrs-fuse7-undertow/src/main/java/org/keycloak/example/rs/CxfCustomerService.java b/testsuite/integration-arquillian/test-apps/fuse/cxf-jaxrs-fuse7-undertow/src/main/java/org/keycloak/example/rs/CxfCustomerService.java index f737bc42463c..184391fe0d6b 100644 --- a/testsuite/integration-arquillian/test-apps/fuse/cxf-jaxrs-fuse7-undertow/src/main/java/org/keycloak/example/rs/CxfCustomerService.java +++ b/testsuite/integration-arquillian/test-apps/fuse/cxf-jaxrs-fuse7-undertow/src/main/java/org/keycloak/example/rs/CxfCustomerService.java @@ -17,9 +17,9 @@ package org.keycloak.example.rs; -import jakarta.ws.rs.GET; -import jakarta.ws.rs.Path; -import jakarta.ws.rs.Produces; +import javax.ws.rs.GET; +import javax.ws.rs.Path; +import javax.ws.rs.Produces; import java.util.ArrayList; import java.util.List; diff --git a/testsuite/integration-arquillian/test-apps/fuse/demorealm.json b/testsuite/integration-arquillian/test-apps/fuse/demorealm.json index 1f609e60f2cc..d63cb39663ea 100644 --- a/testsuite/integration-arquillian/test-apps/fuse/demorealm.json +++ b/testsuite/integration-arquillian/test-apps/fuse/demorealm.json @@ -198,7 +198,10 @@ "redirectUris": [ "http://localhost:8181/customer-portal/*" ], - "secret": "password" + "secret": "password", + "attributes": { + "exclude.issuer.from.auth.response": "true" + } }, { "clientId": "product-portal", @@ -208,7 +211,10 @@ "redirectUris": [ "http://localhost:8181/product-portal/*" ], - "secret": "password" + "secret": "password", + "attributes": { + "exclude.issuer.from.auth.response": "true" + } }, { "clientId": "builtin-cxf-app", @@ -218,28 +224,40 @@ "redirectUris": [ "http://localhost:8181/cxf/*" ], - "secret": "password" + "secret": "password", + "attributes": { + "exclude.issuer.from.auth.response": "true" + } }, { "clientId": "custom-cxf-endpoint", "enabled": true, "adminUrl": "http://localhost:8282/PersonServiceCF", "baseUrl": "http://localhost:8282/PersonServiceCF", - "bearerOnly": true + "bearerOnly": true, + "attributes": { + "exclude.issuer.from.auth.response": "true" + } }, { "clientId": "admin-camel-endpoint", "enabled": true, "adminUrl": "http://localhost:8383/admin-camel-endpoint", "baseUrl": "http://localhost:8383/admin-camel-endpoint", - "bearerOnly": true + "bearerOnly": true, + "attributes": { + "exclude.issuer.from.auth.response": "true" + } }, { "clientId": "admin-camel-restdsl", "enabled": true, "adminUrl": "http://localhost:8484/restdsl", "baseUrl": "http://localhost:8484/restdsl", - "bearerOnly": true + "bearerOnly": true, + "attributes": { + "exclude.issuer.from.auth.response": "true" + } }, { "clientId": "ssh-jmx-admin-client", @@ -247,7 +265,10 @@ "publicClient": false, "standardFlowEnabled": false, "directAccessGrantsEnabled": true, - "secret": "password" + "secret": "password", + "attributes": { + "exclude.issuer.from.auth.response": "true" + } }, { "clientId": "external-config", @@ -258,7 +279,10 @@ "http://localhost:8181/external-config", "http://localhost:8181/external-config/*" ], - "secret": "password" + "secret": "password", + "attributes": { + "exclude.issuer.from.auth.response": "true" + } }, { "clientId" : "hawtio-client", @@ -269,7 +293,10 @@ "webOrigins" : [ "http://localhost:8080", "http://localhost:8181", "http://localhost:8081" ], "bearerOnly" : false, "publicClient" : true, - "protocol" : "openid-connect" + "protocol" : "openid-connect", + "attributes": { + "exclude.issuer.from.auth.response": "true" + } } ], diff --git a/testsuite/integration-arquillian/test-apps/fuse/external-config/pom.xml b/testsuite/integration-arquillian/test-apps/fuse/external-config/pom.xml index eb970fb64ea9..ff8421e357d3 100755 --- a/testsuite/integration-arquillian/test-apps/fuse/external-config/pom.xml +++ b/testsuite/integration-arquillian/test-apps/fuse/external-config/pom.xml @@ -116,8 +116,8 @@ war - external-config - external-config + /external-config + /external-config WEB-INF/lib ${project.name} ${project.groupId}.${project.artifactId} diff --git a/testsuite/integration-arquillian/test-apps/fuse/features/src/main/resources/features.xml b/testsuite/integration-arquillian/test-apps/fuse/features/src/main/resources/features.xml index 4d1109c41ca2..7ff60fad40e8 100644 --- a/testsuite/integration-arquillian/test-apps/fuse/features/src/main/resources/features.xml +++ b/testsuite/integration-arquillian/test-apps/fuse/features/src/main/resources/features.xml @@ -20,7 +20,7 @@

The Keycloak / Fuse 7.0 on Undertow example
- pax-http-undertow + pax-web-http-undertow war camel camel-undertow diff --git a/testsuite/integration-arquillian/test-apps/fuse/pom.xml b/testsuite/integration-arquillian/test-apps/fuse/pom.xml index 2989cc4b7980..ec776203ad43 100755 --- a/testsuite/integration-arquillian/test-apps/fuse/pom.xml +++ b/testsuite/integration-arquillian/test-apps/fuse/pom.xml @@ -32,7 +32,7 @@ pom 2.21.2 - 18.0.7 + 18.0.12 customer-app-fuse diff --git a/testsuite/integration-arquillian/test-apps/fuse/product-app-fuse7-undertow/src/main/java/org/keycloak/example/ProductPortalServlet.java b/testsuite/integration-arquillian/test-apps/fuse/product-app-fuse7-undertow/src/main/java/org/keycloak/example/ProductPortalServlet.java index ee023fa88cfd..b3084a2c5307 100644 --- a/testsuite/integration-arquillian/test-apps/fuse/product-app-fuse7-undertow/src/main/java/org/keycloak/example/ProductPortalServlet.java +++ b/testsuite/integration-arquillian/test-apps/fuse/product-app-fuse7-undertow/src/main/java/org/keycloak/example/ProductPortalServlet.java @@ -55,7 +55,7 @@ protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws Se out.println("Product Portal Page"); String logoutUri = KeycloakUriBuilder.fromUri("http://localhost:8080/auth").path(ServiceUrlConstants.TOKEN_SERVICE_LOGOUT_PATH) - .queryParam("redirect_uri", "http://localhost:8181/product-portal").build("demo").toString(); + .build("demo").toString(); String acctUri = KeycloakUriBuilder.fromUri("http://localhost:8080/auth").path(ServiceUrlConstants.ACCOUNT_SERVICE_PATH) .queryParam("referrer", "product-portal").build("demo").toString(); diff --git a/testsuite/integration-arquillian/test-apps/fuse/product-app-fuse7-undertow/src/main/resources/OSGI-INF/blueprint/blueprint.xml b/testsuite/integration-arquillian/test-apps/fuse/product-app-fuse7-undertow/src/main/resources/OSGI-INF/blueprint/blueprint.xml index 2166764892f4..8fc629fd289b 100644 --- a/testsuite/integration-arquillian/test-apps/fuse/product-app-fuse7-undertow/src/main/resources/OSGI-INF/blueprint/blueprint.xml +++ b/testsuite/integration-arquillian/test-apps/fuse/product-app-fuse7-undertow/src/main/resources/OSGI-INF/blueprint/blueprint.xml @@ -42,12 +42,25 @@ - + + + + + + + + /product-portal/* + + + + + \ No newline at end of file diff --git a/testsuite/integration-arquillian/test-apps/fuse/product-app-fuse7-undertow/src/main/resources/keycloak.json b/testsuite/integration-arquillian/test-apps/fuse/product-app-fuse7-undertow/src/main/resources/keycloak.json new file mode 100644 index 000000000000..0808feafef6c --- /dev/null +++ b/testsuite/integration-arquillian/test-apps/fuse/product-app-fuse7-undertow/src/main/resources/keycloak.json @@ -0,0 +1,9 @@ +{ + "realm": "demo", + "resource": "product-portal", + "auth-server-url": "http://localhost:8080/auth", + "ssl-required" : "external", + "credentials": { + "secret": "password" + } +} \ No newline at end of file diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/example/fuse/FuseAdapterTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/example/fuse/FuseAdapterTest.java index d035427285b2..c1d6be746319 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/example/fuse/FuseAdapterTest.java +++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/example/fuse/FuseAdapterTest.java @@ -16,39 +16,13 @@ */ package org.keycloak.testsuite.adapter.example.fuse; -import static org.hamcrest.Matchers.*; -import static org.hamcrest.MatcherAssert.assertThat; -import static org.junit.Assert.assertTrue; -import static org.keycloak.testsuite.auth.page.AuthRealm.DEMO; -import static org.keycloak.testsuite.utils.fuse.FuseUtils.assertCommand; -import static org.keycloak.testsuite.utils.fuse.FuseUtils.getCommandOutput; -import static org.keycloak.testsuite.utils.io.IOUtil.loadRealm; -import static org.keycloak.testsuite.util.URLAssert.assertCurrentUrlStartsWith; -import static org.keycloak.testsuite.util.URLAssert.assertCurrentUrlStartsWithLoginUrlOf; - -import java.io.File; -import java.io.IOException; -import java.util.Arrays; -import java.util.Collections; -import java.util.List; -import java.util.Map; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.TimeoutException; -import javax.management.InstanceNotFoundException; -import javax.management.MBeanException; -import javax.management.MBeanServerConnection; -import javax.management.ObjectName; -import javax.management.ReflectionException; -import javax.management.remote.JMXConnector; -import javax.management.remote.JMXConnectorFactory; -import javax.management.remote.JMXServiceURL; import org.jboss.arquillian.drone.api.annotation.Drone; import org.jboss.arquillian.graphene.page.Page; import org.junit.Assert; import org.junit.Assume; import org.junit.Before; import org.junit.Test; -import org.keycloak.common.Profile; +import org.junit.Ignore; import org.keycloak.protocol.oidc.OIDCLoginProtocolService; import org.keycloak.representations.idm.RealmRepresentation; import org.keycloak.testsuite.adapter.AbstractExampleAdapterTest; @@ -59,21 +33,49 @@ import org.keycloak.testsuite.adapter.page.fuse.CustomerPortalFuseExample; import org.keycloak.testsuite.adapter.page.fuse.ProductPortalFuseExample; import org.keycloak.testsuite.arquillian.annotation.AppServerContainer; -import org.keycloak.testsuite.arquillian.annotation.DisableFeature; import org.keycloak.testsuite.auth.page.AuthRealm; -import org.keycloak.testsuite.pages.LogoutConfirmPage; -import org.keycloak.testsuite.utils.arquillian.ContainerConstants; import org.keycloak.testsuite.auth.page.login.OIDCLogin; +import org.keycloak.testsuite.pages.LogoutConfirmPage; import org.keycloak.testsuite.util.DroneUtils; import org.keycloak.testsuite.util.JavascriptBrowser; import org.keycloak.testsuite.util.WaitUtils; +import org.keycloak.testsuite.utils.arquillian.ContainerConstants; import org.keycloak.testsuite.utils.fuse.FuseUtils.Result; import org.openqa.selenium.By; import org.openqa.selenium.WebDriver; +import javax.management.InstanceNotFoundException; +import javax.management.MBeanException; +import javax.management.MBeanServerConnection; +import javax.management.ObjectName; +import javax.management.ReflectionException; +import javax.management.remote.JMXConnector; +import javax.management.remote.JMXConnectorFactory; +import javax.management.remote.JMXServiceURL; +import java.io.File; +import java.io.IOException; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.allOf; +import static org.hamcrest.Matchers.containsInAnyOrder; +import static org.hamcrest.Matchers.containsString; +import static org.hamcrest.Matchers.not; +import static org.junit.Assert.assertTrue; +import static org.keycloak.testsuite.auth.page.AuthRealm.DEMO; +import static org.keycloak.testsuite.util.URLAssert.assertCurrentUrlStartsWith; +import static org.keycloak.testsuite.util.URLAssert.assertCurrentUrlStartsWithLoginUrlOf; +import static org.keycloak.testsuite.utils.fuse.FuseUtils.assertCommand; +import static org.keycloak.testsuite.utils.fuse.FuseUtils.getCommandOutput; +import static org.keycloak.testsuite.utils.io.IOUtil.loadRealm; + @AppServerContainer(ContainerConstants.APP_SERVER_FUSE63) @AppServerContainer(ContainerConstants.APP_SERVER_FUSE7X) -@DisableFeature(value = Profile.Feature.ACCOUNT2, skipRestart = true) // TODO remove this (KEYCLOAK-16228) public class FuseAdapterTest extends AbstractExampleAdapterTest { @@ -165,6 +167,7 @@ public void hawtio1LoginTest() throws Exception { } @Test + @Ignore @AppServerContainer(value = ContainerConstants.APP_SERVER_FUSE63, skip = true) public void hawtio2LoginTest() throws Exception { @@ -345,9 +348,9 @@ public void testCustomerListingAndAccountManagement() { DroneUtils.getCurrentDriver().navigate().back(); customerListing.clickLogOut(); + logoutConfirmPage.confirmLogout(); - WaitUtils.pause(2500); customerPortal.navigateTo();//needed for phantomjs WaitUtils.waitForPageToLoad(); customerPortal.clickCustomerListingLink(); @@ -374,6 +377,7 @@ public void testAdminInterface() { WaitUtils.waitForPageToLoad(); customerListing.clickLogOut(); logoutConfirmPage.confirmLogout(); + WaitUtils.waitForPageToLoad(); WaitUtils.pause(2500); customerPortal.navigateTo();//needed for phantomjs @@ -403,7 +407,8 @@ public void testProductPortal() { assertThat(productPortal.getProduct2SecuredText(), containsString("Product received: id=2")); productPortal.clickLogOutLink(); + logoutConfirmPage.confirmLogout(); WaitUtils.waitForPageToLoad(); - assertCurrentUrlStartsWithLoginUrlOf(testRealmPage); + assertCurrentUrlStartsWith(testRealmPage); } } diff --git a/testsuite/integration-arquillian/util/src/main/java/org/keycloak/testsuite/utils/fuse/FuseUtils.java b/testsuite/integration-arquillian/util/src/main/java/org/keycloak/testsuite/utils/fuse/FuseUtils.java index 8a44455c5c58..cdda36d7478f 100644 --- a/testsuite/integration-arquillian/util/src/main/java/org/keycloak/testsuite/utils/fuse/FuseUtils.java +++ b/testsuite/integration-arquillian/util/src/main/java/org/keycloak/testsuite/utils/fuse/FuseUtils.java @@ -113,7 +113,7 @@ private static void setUpFuse7() throws IOException { assertCommand(managementUser, managementPassword, "feature:repo-add mvn:org.keycloak/keycloak-osgi-features/" + fuseAdapterVersion + "/xml/features; " + "feature:repo-add mvn:org.keycloak.testsuite/fuse-example-keycloak-features/" + projectVersion + "/xml/features; " + - "feature:install pax-http-undertow; " + + "feature:install pax-web-http-undertow; " + "feature:install keycloak-jaas keycloak-pax-http-undertow; " + "feature:install keycloak-fuse-7.0-example", Result.OK); From a5161f40042b6d32d73aa9c12341dacf72f72490 Mon Sep 17 00:00:00 2001 From: Alexander Schwartz Date: Tue, 2 Apr 2024 19:56:43 +0200 Subject: [PATCH 086/158] Fix lists to be rendered as expected Closes #28377 Signed-off-by: Alexander Schwartz --- docs/guides/server/importExport.adoc | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/docs/guides/server/importExport.adoc b/docs/guides/server/importExport.adoc index fcff6ef9d90f..ac7c1d98917f 100644 --- a/docs/guides/server/importExport.adoc +++ b/docs/guides/server/importExport.adoc @@ -45,13 +45,13 @@ When exporting realms to a directory, the server is going to create separate fil You are also able to configure how users are going to be exported by setting the `--users ` option. The values available for this option are: -* *different_files*: Users export into different json files, depending on the maximum number of users per file set by `--users-per-file`. This is the default value. +`different_files`:: Users export into different json files, depending on the maximum number of users per file set by `--users-per-file`. This is the default value. -* *skip*: Skips exporting users. +`skip`:: Skips exporting users. -* *realm_file*: Users will be exported to the same file as the realm settings. For a realm named "foo", this would be "foo-realm.json" with realm data and users. +`realm_file`:: Users will be exported to the same file as the realm settings. For a realm named "foo", this would be "foo-realm.json" with realm data and users. -* *same_file*: All users are exported to one explicit file. So you will get two json files for a realm, one with realm data and one with users. +`same_file`:: All users are exported to one explicit file. So you will get two json files for a realm, one with realm data and one with users. If you are exporting users using the `different_files` strategy, you can set how many users per file you want by setting the `--users-per-file` option. The default value is `50`. @@ -138,6 +138,7 @@ NOTE: When using the Admin Console export, the realm and the selected resources named `realm-export.json`. Also, all sensitive values like passwords and client secrets will be masked with `+*+` symbols. To export a realm using the Admin Console, perform these steps: + . Select a realm. . Click *Realm settings* in the menu. . Point to the *Action* menu in the top right corner of the realm settings screen, and select *Partial export*. @@ -153,16 +154,18 @@ WARNING: If the realm contains many groups, roles, and clients, the operation ma unresponsive to user requests for a while. Use this feature with caution, especially on a production system. In a similar way, you can import a previously exported realm. Perform these steps: + . Click *Realm settings* in the menu. . Point to the *Action* menu in the top right corner of the realm settings screen, and select *Partial import*. + A prompt appears where you can select the file you want to import. Based on this file, you see the resources you can import along with the realm settings. . Click *Import*. -You can also control what {project_name} should do if the imported resource already exists. These options exist -* `Fail import` - aborts the import. -* `Skip` - skips the duplicate resources without aborting the process -* `Overwrite` - replaces the existing resources with the ones being imported. +You can also control what {project_name} should do if the imported resource already exists. These options exist: + +Fail import:: Abort the import. +Skip:: Skip the duplicate resources without aborting the process +Overwrite:: Replace the existing resources with the ones being imported. NOTE: The Admin Console partial import can also import files created by the CLI `export` command. In other words, full exports created by the CLI can be imported by using the Admin Console. If the file contains users, those users will also be available for importing into the From 261b68927b36bc7ee318edf30ad3bb8dd7ac08fe Mon Sep 17 00:00:00 2001 From: Alexander Schwartz Date: Thu, 4 Apr 2024 10:55:25 +0200 Subject: [PATCH 087/158] Add error details to events to be able to track down root causes Closes #28429 Signed-off-by: Alexander Schwartz --- .../java/org/keycloak/broker/oidc/OIDCIdentityProvider.java | 2 ++ .../events/log/JBossLoggingEventListenerProvider.java | 4 ++++ .../main/java/org/keycloak/protocol/oidc/TokenManager.java | 3 ++- .../keycloak/protocol/oidc/endpoints/LogoutEndpoint.java | 3 +++ .../protocol/oidc/endpoints/TokenRevocationEndpoint.java | 4 ++++ .../protocol/oidc/grants/RefreshTokenGrantType.java | 6 +++++- 6 files changed, 20 insertions(+), 2 deletions(-) diff --git a/services/src/main/java/org/keycloak/broker/oidc/OIDCIdentityProvider.java b/services/src/main/java/org/keycloak/broker/oidc/OIDCIdentityProvider.java index 5f550ad456a6..215763ef9c18 100755 --- a/services/src/main/java/org/keycloak/broker/oidc/OIDCIdentityProvider.java +++ b/services/src/main/java/org/keycloak/broker/oidc/OIDCIdentityProvider.java @@ -885,6 +885,8 @@ final protected BrokeredIdentityContext validateJwt(EventBuilder event, String s return context; } catch (IOException e) { logger.debug("Unable to extract identity from identity token", e); + event.detail(Details.REASON, "Unable to extract identity from identity token: " + e.getMessage()); + event.error(Errors.INVALID_TOKEN); throw new ErrorResponseException(OAuthErrorException.INVALID_TOKEN, "invalid token", Response.Status.BAD_REQUEST); } diff --git a/services/src/main/java/org/keycloak/events/log/JBossLoggingEventListenerProvider.java b/services/src/main/java/org/keycloak/events/log/JBossLoggingEventListenerProvider.java index f8b7ec8e0308..fa9b665e3b50 100755 --- a/services/src/main/java/org/keycloak/events/log/JBossLoggingEventListenerProvider.java +++ b/services/src/main/java/org/keycloak/events/log/JBossLoggingEventListenerProvider.java @@ -94,6 +94,10 @@ private void logEvent(Event event) { sanitize(sb, event.getClientId()); sb.append(", userId="); sanitize(sb, event.getUserId()); + if (event.getSessionId() != null) { + sb.append(", sessionId="); + sanitize(sb, event.getSessionId()); + } sb.append(", ipAddress="); sanitize(sb, event.getIpAddress()); diff --git a/services/src/main/java/org/keycloak/protocol/oidc/TokenManager.java b/services/src/main/java/org/keycloak/protocol/oidc/TokenManager.java index 1c7b5dfb6206..c0694af6f5c3 100755 --- a/services/src/main/java/org/keycloak/protocol/oidc/TokenManager.java +++ b/services/src/main/java/org/keycloak/protocol/oidc/TokenManager.java @@ -1149,8 +1149,9 @@ private void generateRefreshToken(boolean offlineTokenRequested) { if (offlineTokenRequested) { UserSessionManager sessionManager = new UserSessionManager(session); if (!sessionManager.isOfflineTokenAllowed(clientSessionCtx)) { + event.detail(Details.REASON, "Offline tokens not allowed for the user or client"); event.error(Errors.NOT_ALLOWED); - throw new ErrorResponseException("not_allowed", "Offline tokens not allowed for the user or client", Response.Status.BAD_REQUEST); + throw new ErrorResponseException(Errors.NOT_ALLOWED, "Offline tokens not allowed for the user or client", Response.Status.BAD_REQUEST); } refreshToken.type(TokenUtil.TOKEN_TYPE_OFFLINE); if (realm.isOfflineSessionMaxLifespanEnabled()) { diff --git a/services/src/main/java/org/keycloak/protocol/oidc/endpoints/LogoutEndpoint.java b/services/src/main/java/org/keycloak/protocol/oidc/endpoints/LogoutEndpoint.java index 956014f19603..0201b925b68a 100755 --- a/services/src/main/java/org/keycloak/protocol/oidc/endpoints/LogoutEndpoint.java +++ b/services/src/main/java/org/keycloak/protocol/oidc/endpoints/LogoutEndpoint.java @@ -442,6 +442,7 @@ private Response doBrowserLogout(AuthenticationSessionModel logoutSession) { } } catch (OAuthErrorException e) { event.event(EventType.LOGOUT); + event.detail(Details.REASON, e.getDescription()); event.error(Errors.INVALID_TOKEN); return ErrorPage.error(session, null, Response.Status.BAD_REQUEST, Messages.SESSION_NOT_ACTIVE); } @@ -538,9 +539,11 @@ private Response logoutToken() { } catch (OAuthErrorException e) { // KEYCLOAK-6771 Certificate Bound Token if (MtlsHoKTokenUtil.CERT_VERIFY_ERROR_DESC.equals(e.getDescription())) { + event.detail(Details.REASON, e.getDescription()); event.error(Errors.NOT_ALLOWED); throw new CorsErrorResponseException(cors, e.getError(), e.getDescription(), Response.Status.UNAUTHORIZED); } else { + event.detail(Details.REASON, e.getDescription()); event.error(Errors.INVALID_TOKEN); throw new CorsErrorResponseException(cors, e.getError(), e.getDescription(), Response.Status.BAD_REQUEST); } diff --git a/services/src/main/java/org/keycloak/protocol/oidc/endpoints/TokenRevocationEndpoint.java b/services/src/main/java/org/keycloak/protocol/oidc/endpoints/TokenRevocationEndpoint.java index f81f28dc688f..7af0759fa625 100644 --- a/services/src/main/java/org/keycloak/protocol/oidc/endpoints/TokenRevocationEndpoint.java +++ b/services/src/main/java/org/keycloak/protocol/oidc/endpoints/TokenRevocationEndpoint.java @@ -171,6 +171,7 @@ private void checkToken() { String encodedToken = formParams.getFirst(PARAM_TOKEN); if (encodedToken == null) { + event.detail(Details.REASON, "Token not provided"); event.error(Errors.INVALID_REQUEST); throw new CorsErrorResponseException(cors, OAuthErrorException.INVALID_REQUEST, "Token not provided", Response.Status.BAD_REQUEST); @@ -184,6 +185,7 @@ private void checkToken() { } if (!(TokenUtil.TOKEN_TYPE_REFRESH.equals(token.getType()) || TokenUtil.TOKEN_TYPE_OFFLINE.equals(token.getType()) || TokenUtil.TOKEN_TYPE_BEARER.equals(token.getType())|| TokenUtil.TOKEN_TYPE_DPOP.equals(token.getType()))) { + event.detail(Details.REASON, "Unsupported token type"); event.error(Errors.INVALID_TOKEN_TYPE); throw new CorsErrorResponseException(cors, OAuthErrorException.UNSUPPORTED_TOKEN_TYPE, "Unsupported token type", Response.Status.BAD_REQUEST); @@ -193,11 +195,13 @@ private void checkToken() { private void checkIssuedFor() { String issuedFor = token.getIssuedFor(); if (issuedFor == null) { + event.detail(Details.REASON, "Issued for not set"); event.error(Errors.INVALID_TOKEN); throw new CorsErrorResponseException(cors, OAuthErrorException.INVALID_TOKEN, "Invalid token", Response.Status.OK); } if (!client.getClientId().equals(issuedFor)) { + event.detail(Details.REASON, "Unmatching clients"); event.error(Errors.INVALID_REQUEST); throw new CorsErrorResponseException(cors, OAuthErrorException.INVALID_REQUEST, "Unmatching clients", Response.Status.BAD_REQUEST); diff --git a/services/src/main/java/org/keycloak/protocol/oidc/grants/RefreshTokenGrantType.java b/services/src/main/java/org/keycloak/protocol/oidc/grants/RefreshTokenGrantType.java index 882e3c7620a6..efafc320cecb 100644 --- a/services/src/main/java/org/keycloak/protocol/oidc/grants/RefreshTokenGrantType.java +++ b/services/src/main/java/org/keycloak/protocol/oidc/grants/RefreshTokenGrantType.java @@ -25,10 +25,10 @@ import org.keycloak.OAuth2Constants; import org.keycloak.OAuthErrorException; import org.keycloak.common.Profile; +import org.keycloak.events.Details; import org.keycloak.events.Errors; import org.keycloak.events.EventType; import org.keycloak.models.AuthenticatedClientSessionModel; -import org.keycloak.models.KeycloakSession; import org.keycloak.models.UserSessionModel; import org.keycloak.protocol.oidc.TokenManager; import org.keycloak.representations.AccessTokenResponse; @@ -65,6 +65,7 @@ public Response process(Context context) { session.clientPolicy().triggerOnEvent(new TokenRefreshContext(formParams)); refreshToken = formParams.getFirst(OAuth2Constants.REFRESH_TOKEN); } catch (ClientPolicyException cpe) { + event.detail(Details.REASON, cpe.getErrorDetail()); event.error(cpe.getError()); throw new CorsErrorResponseException(cors, cpe.getError(), cpe.getErrorDetail(), cpe.getErrorStatus()); } @@ -91,13 +92,16 @@ public Response process(Context context) { logger.trace(e.getMessage(), e); // KEYCLOAK-6771 Certificate Bound Token if (MtlsHoKTokenUtil.CERT_VERIFY_ERROR_DESC.equals(e.getDescription())) { + event.detail(Details.REASON, e.getDescription()); event.error(Errors.NOT_ALLOWED); throw new CorsErrorResponseException(cors, e.getError(), e.getDescription(), Response.Status.UNAUTHORIZED); } else { + event.detail(Details.REASON, e.getDescription()); event.error(Errors.INVALID_TOKEN); throw new CorsErrorResponseException(cors, e.getError(), e.getDescription(), Response.Status.BAD_REQUEST); } } catch (ClientPolicyException cpe) { + event.detail(Details.REASON, cpe.getErrorDetail()); event.error(cpe.getError()); throw new CorsErrorResponseException(cors, cpe.getError(), cpe.getErrorDetail(), cpe.getErrorStatus()); } From f6af0092a4ed6643851abfa490a2114836ab15f1 Mon Sep 17 00:00:00 2001 From: Pedro Ruivo Date: Thu, 14 Mar 2024 11:47:35 +0000 Subject: [PATCH 088/158] Use new remote-store options in HA guides Fixes #27508 Signed-off-by: Pedro Ruivo Signed-off-by: Alexander Schwartz Co-authored-by: Alexander Schwartz --- ...nnect-keycloak-to-external-infinispan.adoc | 53 +-- .../examples/generated/keycloak-ispn.yaml | 311 +----------------- ...b-infinispan-cache-remote-store-config.xml | 283 ---------------- 3 files changed, 22 insertions(+), 625 deletions(-) delete mode 100644 docs/guides/high-availability/examples/src/kcb-infinispan-cache-remote-store-config.xml diff --git a/docs/guides/high-availability/connect-keycloak-to-external-infinispan.adoc b/docs/guides/high-availability/connect-keycloak-to-external-infinispan.adoc index 831c2fd7979e..b3e661521d8b 100644 --- a/docs/guides/high-availability/connect-keycloak-to-external-infinispan.adoc +++ b/docs/guides/high-availability/connect-keycloak-to-external-infinispan.adoc @@ -4,10 +4,18 @@ <@tmpl.guide title="Connect {project_name} with an external {jdgserver_name}" summary="Building block for an Infinispan deployment on Kubernetes" -tileVisible="false" > +tileVisible="false" +includedOptions="cache-remote-*" > This topic describes advanced {jdgserver_name} configurations for {project_name} on Kubernetes. +== Architecture + +This connects {project_name} to {jdgserver_name} using TCP connections secured by TLS 1.3. +It uses the {project_name}'s truststore to verify {jdgserver_name}'s server certificate. +As {project_name} is deployed using its Operator on OpenShift in the prerequisites listed below, the Operator already added the `service-ca.crt` to the truststore which is used to sign {jdgserver_name}'s server certificates. +In other environments, add the necessary certificates to {project_name}'s truststore. + == Prerequisites * <@links.ha id="deploy-keycloak-kubernetes" /> as it will be extended. @@ -15,35 +23,6 @@ This topic describes advanced {jdgserver_name} configurations for {project_name} == Procedure -. Prepare an {jdgserver_name} Cache configuration XML from the file `cache-ispn.xml` which is part of the {project_name} distribution: -.. For each `distributed-cache` entry, add the tags `` as shown following. -+ -[source,xml,indent=0] ----- -include::examples/src/kcb-infinispan-cache-remote-store-config.xml[tag=keycloak-ispn-remotestore] ----- -<1> New tag `` to connect it to the remote store. -<2> For the address to the remote store, reference two environment variables for host name and port number. -<3> For authentication, reference two environment variables for username and password. -<4> To secure the remote store connection, use the Kubernetes mechanisms of the pre-configured truststore. - -.. Prepare an {jdgserver_name} Cache configuration XML from the file `cache-ispn.xml`, which is part of the {project_name} distribution. -For each `replicated-cache` entry, add the tag `` as shown below. -For additional information on the infinispan configuration options, see the https://docs.jboss.org/infinispan/14.0/configdocs/infinispan-config-14.0.html[infinispan configuration schema reference]. -+ -[source,xml,indent=0] ----- -include::examples/src/kcb-infinispan-cache-remote-store-config.xml[tag=keycloak-ispn-remotestore-work] ----- - -. Place the {jdgserver_name} Cache configuration XML in a ConfigMap. -+ -[source,yaml] ----- -include::examples/generated/keycloak-ispn.yaml[tag=keycloak-ispn-configmap] -... ----- - . Create a Secret with the username and password to connect to the external {jdgserver_name} deployment: + [source,yaml] @@ -55,9 +34,7 @@ include::examples/generated/keycloak-ispn.yaml[tag=keycloak-ispn-secret] + [NOTE] ==== -* The new `additionalOptions` entries starting with `remote-store` used here are not official {project_name} configurations. -Instead, they provide their values to environment variables that are then referenced in the {jdgserver_name} XML configuration. -* All the memory, resource and database configurations are skipped from the CR below as they have been described in <@links.ha id="deploy-keycloak-kubernetes" /> {section} already. +All the memory, resource and database configurations are skipped from the CR below as they have been described in <@links.ha id="deploy-keycloak-kubernetes" /> {section} already. Administrators should leave those configurations untouched. ==== + @@ -65,10 +42,12 @@ Administrators should leave those configurations untouched. ---- include::examples/generated/keycloak-ispn.yaml[tag=keycloak-ispn] ---- -<1> The `name` and `key` of the ConfigMap with the {jdgserver_name} Cache configuration XML created in the previous step. -<2> The hostname and port of the remote cache {jdgserver_name} cluster. -<3> The credentials required, username and password, to access the remote cache {jdgserver_name} cluster. -<4> The `spi-connections-infinispan-quarkus-site-name` is an arbitrary {jdgserver_name} site name which {project_name} needs for its embedded {jdgserver_name} deployment when a remote store is used. +<1> The hostname of the remote {jdgserver_name} cluster. +<2> The port of the remote {jdgserver_name} cluster. +This is optional and it default to `11222`. +<3> The Secret `name` and `key` with the {jdgserver_name} username credential. +<4> The Secret `name` and `key` with the {jdgserver_name} password credential. +<5> The `spi-connections-infinispan-quarkus-site-name` is an arbitrary {jdgserver_name} site name which {project_name} needs for its embedded {jdgserver_name} deployment when a remote store is used. This site-name is related only to the embedded {jdgserver_name} and does not need to match any value from the external {jdgserver_name} deployment. If you are using multiple sites for {project_name} in a cross-DC setup such as <@links.ha id="deploy-infinispan-kubernetes-crossdc" />, the site name must be different in each site. diff --git a/docs/guides/high-availability/examples/generated/keycloak-ispn.yaml b/docs/guides/high-availability/examples/generated/keycloak-ispn.yaml index c0909213724f..b52e8571f24f 100644 --- a/docs/guides/high-availability/examples/generated/keycloak-ispn.yaml +++ b/docs/guides/high-availability/examples/generated/keycloak-ispn.yaml @@ -47,299 +47,6 @@ metadata: namespace: keycloak type: kubernetes.io/tls --- -# Source: keycloak/templates/keycloak-infinispan-configmap.yaml -# tag::keycloak-ispn-configmap[] -apiVersion: v1 -kind: ConfigMap -metadata: - name: kcb-infinispan-cache-config - namespace: keycloak -data: - kcb-infinispan-cache-remote-store-config.xml: | - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ---- # Source: keycloak/templates/keycloak-providers-configmap.yaml apiVersion: v1 kind: ConfigMap @@ -743,12 +450,6 @@ spec: features: enabled: - multi-site # <3> - # tag::keycloak-ispn[] - cache: - configMapFile: - name: kcb-infinispan-cache-config # <1> - key: kcb-infinispan-cache-remote-store-config.xml # <1> - # end::keycloak-ispn[] transaction: xaEnabled: false # <4> # tag::keycloak-ispn[] @@ -765,19 +466,19 @@ spec: - name: http-pool-max-threads # <6> value: "200" # tag::keycloak-ispn[] - - name: remote-store-host # <2> + - name: cache-remote-host # <1> value: "infinispan.keycloak.svc" - - name: remote-store-port # <2> + - name: cache-remote-port # <2> value: "11222" - - name: remote-store-username # <3> + - name: cache-remote-username # <3> secret: name: remote-store-secret key: username - - name: remote-store-password # <3> + - name: cache-remote-password # <4> secret: name: remote-store-secret key: password - - name: spi-connections-infinispan-quarkus-site-name # <4> + - name: spi-connections-infinispan-quarkus-site-name # <5> value: keycloak # end::keycloak-ispn[] - name: db-driver @@ -790,7 +491,7 @@ spec: podTemplate: metadata: annotations: - checksum/config: ebe9b8c121995f449a1a4e339af244b2bb67769af84b3cbdff61159948447e20-4832924b47210161956e3b1718daf07ff52d801545186a76c391485eaf1897d3--dbc855dd9b7f7c0b828760ea8cd7427e8a2f5a5be303fba7dee0c6bbb68258d4-v1.27.0 + checksum/config: 385f54cb8e4bf326f6970aa2a0c8e573d35d9071e69ab2baee252728748bca76-4832924b47210161956e3b1718daf07ff52d801545186a76c391485eaf1897d3--01ba4719c80b6fe911b091a7c05124b64eeece964e09c058ef8f9805daca546b-v1.27.0 spec: containers: - env: diff --git a/docs/guides/high-availability/examples/src/kcb-infinispan-cache-remote-store-config.xml b/docs/guides/high-availability/examples/src/kcb-infinispan-cache-remote-store-config.xml deleted file mode 100644 index bdf643136f3d..000000000000 --- a/docs/guides/high-availability/examples/src/kcb-infinispan-cache-remote-store-config.xml +++ /dev/null @@ -1,283 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - From eda33155aaa6092a70301cd4427dbe47f370b48a Mon Sep 17 00:00:00 2001 From: graziang Date: Tue, 5 Mar 2024 12:29:38 +0100 Subject: [PATCH 089/158] Encode role name parameter in the location header uri The role is encoded to avoid template resolution by the URIBuilder. This fix avoids the exception when creating roles with names containing {patterns}. Closes #27514 Signed-off-by: graziang (cherry picked from commit 39299eeb38df999816b49dab82ac8afe083458c8) --- .../services/resources/admin/RoleContainerResource.java | 3 ++- .../keycloak/testsuite/admin/client/ClientRolesTest.java | 6 ++++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/services/src/main/java/org/keycloak/services/resources/admin/RoleContainerResource.java b/services/src/main/java/org/keycloak/services/resources/admin/RoleContainerResource.java index 2e7e035d0301..22ff3913015d 100755 --- a/services/src/main/java/org/keycloak/services/resources/admin/RoleContainerResource.java +++ b/services/src/main/java/org/keycloak/services/resources/admin/RoleContainerResource.java @@ -24,6 +24,7 @@ import org.eclipse.microprofile.openapi.annotations.tags.Tag; import org.jboss.resteasy.reactive.NoCache; import jakarta.ws.rs.NotFoundException; +import org.keycloak.common.util.Encode; import org.keycloak.events.admin.OperationType; import org.keycloak.events.admin.ResourceType; import org.keycloak.models.ClientModel; @@ -202,7 +203,7 @@ public Response createRole(final RoleRepresentation rep) { adminEvent.operation(OperationType.CREATE).resourcePath(uriInfo, role.getName()).representation(rep).success(); - return Response.created(uriInfo.getAbsolutePathBuilder().path(role.getName()).build()).build(); + return Response.created(uriInfo.getAbsolutePathBuilder().path(Encode.encodePathSegmentAsIs(role.getName())).build()).build(); } catch (ModelDuplicateException e) { throw ErrorResponse.exists("Role with name " + rep.getName() + " already exists"); } diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/client/ClientRolesTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/client/ClientRolesTest.java index 07f60ac0d3d6..618e8fffa5df 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/client/ClientRolesTest.java +++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/client/ClientRolesTest.java @@ -110,6 +110,12 @@ public void createRoleWithSameName() { rolesRsc.create(role); } + @Test + public void createRoleWithNamePattern() { + RoleRepresentation role = RoleBuilder.create().name("role-a-{pattern}").build(); + rolesRsc.create(role); + } + @Test public void testRemoveRole() { RoleRepresentation role2 = makeRole("role2"); From 25b391d566918ca00a41d04cafc41d74e6d9cce2 Mon Sep 17 00:00:00 2001 From: Hynek Mlnarik Date: Fri, 12 Apr 2024 12:52:32 +0200 Subject: [PATCH 090/158] Ensure correct treatment of auth and transient users This commit establishes consistency in retrieval of users and responses between `org.keycloak.admin.ui.rest.UsersResource.getUser(String)` and `org.keycloak.services.resources.admin.UsersResource.user(String)` Fixes: #28666 Signed-off-by: Hynek Mlnarik (cherry picked from commit 146204c5cd35d6849b3aba66758ffec28fd4a8ed) --- .../admin/ui/rest/AdminExtResource.java | 2 +- .../keycloak/admin/ui/rest/UsersResource.java | 23 ++++++++++++++++--- 2 files changed, 21 insertions(+), 4 deletions(-) diff --git a/rest/admin-ui-ext/src/main/java/org/keycloak/admin/ui/rest/AdminExtResource.java b/rest/admin-ui-ext/src/main/java/org/keycloak/admin/ui/rest/AdminExtResource.java index b0c48c862529..3a0492112339 100644 --- a/rest/admin-ui-ext/src/main/java/org/keycloak/admin/ui/rest/AdminExtResource.java +++ b/rest/admin-ui-ext/src/main/java/org/keycloak/admin/ui/rest/AdminExtResource.java @@ -57,6 +57,6 @@ public UIRealmResource realm() { @Path("/users") public UsersResource users() { - return new UsersResource(session); + return new UsersResource(session, auth); } } diff --git a/rest/admin-ui-ext/src/main/java/org/keycloak/admin/ui/rest/UsersResource.java b/rest/admin-ui-ext/src/main/java/org/keycloak/admin/ui/rest/UsersResource.java index f01b69f28caa..fee417a04cbe 100644 --- a/rest/admin-ui-ext/src/main/java/org/keycloak/admin/ui/rest/UsersResource.java +++ b/rest/admin-ui-ext/src/main/java/org/keycloak/admin/ui/rest/UsersResource.java @@ -6,21 +6,38 @@ import org.keycloak.models.KeycloakSession; import org.keycloak.models.RealmModel; import org.keycloak.models.UserModel; +import org.keycloak.models.UserSessionModel; +import org.keycloak.models.light.LightweightUserAdapter; +import org.keycloak.services.resources.admin.permissions.AdminPermissionEvaluator; +import jakarta.ws.rs.ForbiddenException; public class UsersResource { private final KeycloakSession session; - public UsersResource(KeycloakSession session) { + private final AdminPermissionEvaluator auth; + + public UsersResource(KeycloakSession session, AdminPermissionEvaluator auth) { this.session = session; + this.auth = auth; } @Path("{id}") public UserResource getUser(@PathParam("id") String id) { RealmModel realm = session.getContext().getRealm(); - UserModel user = session.users().getUserById(realm, id); + UserModel user = null; + if (LightweightUserAdapter.isLightweightUser(id)) { + UserSessionModel userSession = session.sessions().getUserSession(realm, LightweightUserAdapter.getLightweightUserId(id)); + if (userSession != null) { + user = userSession.getUser(); + } + } else { + user = session.users().getUserById(realm, id); + } if (user == null) { - throw new NotFoundException(); + // we do this to make sure somebody can't phish ids + if (auth.users().canQuery()) throw new NotFoundException("User not found"); + else throw new ForbiddenException(); } return new UserResource(session, user); From 00d8afb6e155bff22990bbcf6ca365c0664ef239 Mon Sep 17 00:00:00 2001 From: agagancarczyk <4890675+agagancarczyk@users.noreply.github.com> Date: Thu, 18 Apr 2024 07:06:08 +0100 Subject: [PATCH 091/158] backport for issue 28514 (#28558) Signed-off-by: Agnieszka Gancarczyk Co-authored-by: Agnieszka Gancarczyk --- .../theme/keycloak.v2/admin/messages/messages_en.properties | 3 ++- .../src/clients/registration/ClientRegistrationList.tsx | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/js/apps/admin-ui/maven-resources/theme/keycloak.v2/admin/messages/messages_en.properties b/js/apps/admin-ui/maven-resources/theme/keycloak.v2/admin/messages/messages_en.properties index 371ff5c3cecb..2b09218fde20 100644 --- a/js/apps/admin-ui/maven-resources/theme/keycloak.v2/admin/messages/messages_en.properties +++ b/js/apps/admin-ui/maven-resources/theme/keycloak.v2/admin/messages/messages_en.properties @@ -3075,4 +3075,5 @@ to the attribute. For that, make sure to use any of the built-in validators to p sendIdTokenOnLogout=Send 'id_token_hint' in logout requests sendIdTokenOnLogoutHelp=If the 'id_token_hint' parameter should be sent in logout requests. sendClientIdOnLogout=Send 'client_id' in logout requests -sendClientIdOnLogoutHelp=If the 'client_id' parameter should be sent in logout requests. \ No newline at end of file +sendClientIdOnLogoutHelp=If the 'client_id' parameter should be sent in logout requests. +searchClientRegistration=Search for policy \ No newline at end of file diff --git a/js/apps/admin-ui/src/clients/registration/ClientRegistrationList.tsx b/js/apps/admin-ui/src/clients/registration/ClientRegistrationList.tsx index 42084b7ca8ef..df9a52137ba4 100644 --- a/js/apps/admin-ui/src/clients/registration/ClientRegistrationList.tsx +++ b/js/apps/admin-ui/src/clients/registration/ClientRegistrationList.tsx @@ -104,7 +104,7 @@ export const ClientRegistrationList = ({ Date: Fri, 15 Mar 2024 08:24:22 -0300 Subject: [PATCH 092/158] Add realm to session context when exporting to prevent NPE when vault is enabled. (#27911) Closes #22617 Signed-off-by: Stefan Guilhen (cherry picked from commit 0e717f735e1b17d2ef10fa7b4764b1dc495d4746) --- .../exportimport/singlefile/SingleFileExportProvider.java | 2 ++ .../exportimport/util/MultipleStepsExportProvider.java | 3 +++ 2 files changed, 5 insertions(+) diff --git a/model/storage-services/src/main/java/org/keycloak/exportimport/singlefile/SingleFileExportProvider.java b/model/storage-services/src/main/java/org/keycloak/exportimport/singlefile/SingleFileExportProvider.java index ee9a037e1f29..6ad575fe661e 100755 --- a/model/storage-services/src/main/java/org/keycloak/exportimport/singlefile/SingleFileExportProvider.java +++ b/model/storage-services/src/main/java/org/keycloak/exportimport/singlefile/SingleFileExportProvider.java @@ -71,6 +71,7 @@ public void exportModel() { @Override protected void runExportImportTask(KeycloakSession session) throws IOException { Stream realms = session.realms().getRealmsStream() + .peek(realm -> session.getContext().setRealm(realm)) .map(realm -> ExportUtils.exportRealm(session, realm, true, true)); writeToFile(realms); @@ -88,6 +89,7 @@ private void exportRealm(final String realmName) { protected void runExportImportTask(KeycloakSession session) throws IOException { RealmModel realm = session.realms().getRealmByName(realmName); Objects.requireNonNull(realm, "realm not found by realm name '" + realmName + "'"); + session.getContext().setRealm(realm); RealmRepresentation realmRep = ExportUtils.exportRealm(session, realm, true, true); writeToFile(realmRep); } diff --git a/model/storage-services/src/main/java/org/keycloak/exportimport/util/MultipleStepsExportProvider.java b/model/storage-services/src/main/java/org/keycloak/exportimport/util/MultipleStepsExportProvider.java index 270dcb710aee..8802d3ac556f 100755 --- a/model/storage-services/src/main/java/org/keycloak/exportimport/util/MultipleStepsExportProvider.java +++ b/model/storage-services/src/main/java/org/keycloak/exportimport/util/MultipleStepsExportProvider.java @@ -95,6 +95,7 @@ protected void exportRealmImpl(final String realmName) { @Override protected void runExportImportTask(KeycloakSession session) throws IOException { RealmModel realm = session.realms().getRealmByName(realmName); + session.getContext().setRealm(realm); RealmRepresentation rep = ExportUtils.exportRealm(session, realm, exportUsersIntoRealmFile, true); writeRealm(realmName + "-realm.json", rep); logger.info("Realm '" + realmName + "' - data exported"); @@ -131,6 +132,7 @@ protected void runExportImportTask(KeycloakSession session) throws IOException { @Override protected void runExportImportTask(KeycloakSession session) throws IOException { RealmModel realm = session.realms().getRealmByName(realmName); + session.getContext().setRealm(realm); usersHolder.users = session.users() .searchForUserStream(realm, Collections.emptyMap(), usersHolder.currentPageStart, usersHolder.currentPageEnd - usersHolder.currentPageStart) .collect(Collectors.toList()); @@ -164,6 +166,7 @@ protected void runExportImportTask(KeycloakSession session) throws IOException { @Override protected void runExportImportTask(KeycloakSession session) throws IOException { RealmModel realm = session.realms().getRealmByName(realmName); + session.getContext().setRealm(realm); federatedUsersHolder.users = UserStorageUtil.userFederatedStorage(session) .getStoredUsersStream(realm, federatedUsersHolder.currentPageStart, federatedUsersHolder.currentPageEnd - federatedUsersHolder.currentPageStart) .collect(Collectors.toList()); From b7c6bdd3e1bf45ba2babbf984905c25012d4a3c1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hynek=20Mlna=C5=99=C3=ADk?= Date: Thu, 4 Apr 2024 09:50:33 +0200 Subject: [PATCH 093/158] Fix navigation with realms with special chars (#28349) Fixes: #16345 Signed-off-by: Hynek Mlnarik (cherry picked from commit 1fbdb62334735767a29a3357a102dd0d11b90ccc) --- js/apps/admin-ui/cypress/e2e/realm_test.spec.ts | 16 ++++++++++++++-- js/apps/admin-ui/src/PageNav.tsx | 3 ++- .../src/context/realm-context/RealmContext.tsx | 2 +- 3 files changed, 17 insertions(+), 4 deletions(-) diff --git a/js/apps/admin-ui/cypress/e2e/realm_test.spec.ts b/js/apps/admin-ui/cypress/e2e/realm_test.spec.ts index 169a70198c31..91084fc06fe8 100644 --- a/js/apps/admin-ui/cypress/e2e/realm_test.spec.ts +++ b/js/apps/admin-ui/cypress/e2e/realm_test.spec.ts @@ -7,6 +7,7 @@ import adminClient from "../support/util/AdminClient"; import { keycloakBefore } from "../support/util/keycloak_hooks"; import RealmSettings from "../support/pages/admin-ui/configure/realm_settings/RealmSettings"; import ModalUtils from "../support/util/ModalUtils"; +import CommonPage from "../support/pages/CommonPage"; const masthead = new Masthead(); const loginPage = new LoginPage(); @@ -14,11 +15,13 @@ const sidebarPage = new SidebarPage(); const createRealmPage = new CreateRealmPage(); const realmSettings = new RealmSettings(); const modalUtils = new ModalUtils(); +const commonPage = new CommonPage(); const testRealmName = "Test-realm-" + uuid(); const newRealmName = "New-Test-realm-" + uuid(); const editedRealmName = "Edited-Test-realm-" + uuid(); const testDisabledName = "Test-Disabled"; +const specialCharsName = "%22-" + uuid(); describe("Realm tests", () => { beforeEach(() => { @@ -28,8 +31,8 @@ describe("Realm tests", () => { after(() => Promise.all( - [testRealmName, newRealmName, editedRealmName].map((realm) => - adminClient.deleteRealm(realm), + [testRealmName, newRealmName, editedRealmName, specialCharsName].map( + (realm) => adminClient.deleteRealm(realm), ), ), ); @@ -117,4 +120,13 @@ describe("Realm tests", () => { .getCurrentRealm() .should("eq", testRealmName); }); + + it("should create realm with special characters", () => { + sidebarPage.goToCreateRealm(); + createRealmPage.fillRealmName(specialCharsName).createRealm(); + + sidebarPage.goToRealm(specialCharsName); + sidebarPage.goToClients(); + commonPage.tableUtils().checkRowItemExists("account"); + }); }); diff --git a/js/apps/admin-ui/src/PageNav.tsx b/js/apps/admin-ui/src/PageNav.tsx index eb169fd251df..b00df3e24bc1 100644 --- a/js/apps/admin-ui/src/PageNav.tsx +++ b/js/apps/admin-ui/src/PageNav.tsx @@ -25,6 +25,7 @@ const LeftNav = ({ title, path, id }: LeftNavProps) => { const { t } = useTranslation(); const { hasAccess } = useAccess(); const { realm } = useRealm(); + const encodedRealm = encodeURIComponent(realm); const route = routes.find( (route) => route.path.replace(/\/:.+?(\?|(?:(?!\/).)*|$)/g, "") === (id || path), @@ -44,7 +45,7 @@ const LeftNav = ({ title, path, id }: LeftNavProps) => {
  • `pf-c-nav__link${isActive ? " pf-m-current" : ""}` } diff --git a/js/apps/admin-ui/src/context/realm-context/RealmContext.tsx b/js/apps/admin-ui/src/context/realm-context/RealmContext.tsx index 91378910354a..08ff295cb4d8 100644 --- a/js/apps/admin-ui/src/context/realm-context/RealmContext.tsx +++ b/js/apps/admin-ui/src/context/realm-context/RealmContext.tsx @@ -23,7 +23,7 @@ export const RealmContextProvider = ({ children }: PropsWithChildren) => { const realmParam = routeMatch?.params.realm; const realm = useMemo( - () => realmParam ?? environment.loginRealm, + () => decodeURIComponent(realmParam ?? environment.loginRealm), [realmParam], ); From 60ea525d1df1c21ffeebcbbe6a3cb36ee9312f08 Mon Sep 17 00:00:00 2001 From: Giuseppe Graziano Date: Fri, 12 Apr 2024 10:49:21 +0200 Subject: [PATCH 094/158] Added new SessionStateMapper Closes #28591 Signed-off-by: Giuseppe Graziano --- js/libs/keycloak-js/src/keycloak.js | 2 +- .../oidc/mappers/SessionStateMapper.java | 97 +++++++++++++++++++ .../org.keycloak.protocol.ProtocolMapper | 3 +- 3 files changed, 100 insertions(+), 2 deletions(-) create mode 100755 services/src/main/java/org/keycloak/protocol/oidc/mappers/SessionStateMapper.java diff --git a/js/libs/keycloak-js/src/keycloak.js b/js/libs/keycloak-js/src/keycloak.js index 333c703164db..e4c18d6869bf 100755 --- a/js/libs/keycloak-js/src/keycloak.js +++ b/js/libs/keycloak-js/src/keycloak.js @@ -1005,7 +1005,7 @@ function Keycloak (config) { if (token) { kc.token = token; kc.tokenParsed = jwtDecode(token); - kc.sessionId = kc.tokenParsed.session_state; + kc.sessionId = kc.tokenParsed.sid; kc.authenticated = true; kc.subject = kc.tokenParsed.sub; kc.realmAccess = kc.tokenParsed.realm_access; diff --git a/services/src/main/java/org/keycloak/protocol/oidc/mappers/SessionStateMapper.java b/services/src/main/java/org/keycloak/protocol/oidc/mappers/SessionStateMapper.java new file mode 100755 index 000000000000..ac4ac07a2c8f --- /dev/null +++ b/services/src/main/java/org/keycloak/protocol/oidc/mappers/SessionStateMapper.java @@ -0,0 +1,97 @@ +/* + * Copyright 2024 Red Hat, Inc. and/or its affiliates + * and other contributors as indicated by the @author tags. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.keycloak.protocol.oidc.mappers; + +import org.jboss.logging.Logger; + +import org.keycloak.models.ClientSessionContext; +import org.keycloak.models.KeycloakSession; +import org.keycloak.models.ProtocolMapperModel; +import org.keycloak.models.UserSessionModel; +import org.keycloak.protocol.oidc.OIDCLoginProtocol; +import org.keycloak.provider.ProviderConfigProperty; +import org.keycloak.representations.IDToken; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * @author Giuseppe Graziano + */ +public class SessionStateMapper extends AbstractOIDCProtocolMapper implements OIDCAccessTokenMapper, OIDCIDTokenMapper, UserInfoTokenMapper, TokenIntrospectionTokenMapper { + + + public static final String PROVIDER_ID = "oidc-session-state-mapper"; + + private static final Logger logger = Logger.getLogger(SessionStateMapper.class); + + private static final List configProperties = new ArrayList<>(); + + static { + OIDCAttributeMapperHelper.addIncludeInTokensConfig(configProperties, SessionStateMapper.class); + } + + public List getConfigProperties() { + return configProperties; + } + + @Override + public String getId() { + return PROVIDER_ID; + } + + @Override + public String getDisplayType() { + return "Session State (session_state)"; + } + + @Override + public String getDisplayCategory() { + return TOKEN_MAPPER_CATEGORY; + } + + @Override + public String getHelpText() { + return "Add Session State (session_state) claim"; + } + + @Override + protected void setClaim(IDToken token, ProtocolMapperModel mappingModel, UserSessionModel userSession, KeycloakSession keycloakSession, + ClientSessionContext clientSessionCtx) { + if (userSession != null) { + token.getOtherClaims().put(IDToken.SESSION_STATE, userSession.getId()); + } + } + + public static ProtocolMapperModel create(String name, boolean accessToken, boolean idToken, boolean userInfo, boolean introspectionEndpoint) { + ProtocolMapperModel mapper = new ProtocolMapperModel(); + mapper.setName(name); + mapper.setProtocolMapper(PROVIDER_ID); + mapper.setProtocol(OIDCLoginProtocol.LOGIN_PROTOCOL); + Map config = new HashMap<>(); + if (accessToken) config.put(OIDCAttributeMapperHelper.INCLUDE_IN_ACCESS_TOKEN, "true"); + if (idToken) config.put(OIDCAttributeMapperHelper.INCLUDE_IN_ID_TOKEN, "true"); + if (userInfo) config.put(OIDCAttributeMapperHelper.INCLUDE_IN_USERINFO, "true"); + if (introspectionEndpoint) config.put(OIDCAttributeMapperHelper.INCLUDE_IN_INTROSPECTION, "true"); + mapper.setConfig(config); + return mapper; + } + +} \ No newline at end of file diff --git a/services/src/main/resources/META-INF/services/org.keycloak.protocol.ProtocolMapper b/services/src/main/resources/META-INF/services/org.keycloak.protocol.ProtocolMapper index 544200a95b0a..6508d71ae91f 100755 --- a/services/src/main/resources/META-INF/services/org.keycloak.protocol.ProtocolMapper +++ b/services/src/main/resources/META-INF/services/org.keycloak.protocol.ProtocolMapper @@ -45,4 +45,5 @@ org.keycloak.protocol.saml.mappers.SAMLAudienceProtocolMapper org.keycloak.protocol.saml.mappers.SAMLAudienceResolveProtocolMapper org.keycloak.protocol.oidc.mappers.ClaimsParameterTokenMapper org.keycloak.protocol.saml.mappers.UserAttributeNameIdMapper -org.keycloak.protocol.oidc.mappers.ClaimsParameterWithValueIdTokenMapper \ No newline at end of file +org.keycloak.protocol.oidc.mappers.ClaimsParameterWithValueIdTokenMapper +org.keycloak.protocol.oidc.mappers.SessionStateMapper \ No newline at end of file From f9119673409a7ed487e022d5249d4899f0507faa Mon Sep 17 00:00:00 2001 From: agagancarczyk <4890675+agagancarczyk@users.noreply.github.com> Date: Thu, 18 Apr 2024 11:06:02 +0100 Subject: [PATCH 095/158] backport for fixing redirect on cancelling role edit (#28601) Signed-off-by: Agnieszka Gancarczyk Co-authored-by: Agnieszka Gancarczyk --- .../admin-ui/src/client-scopes/details/MappingDetails.tsx | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/js/apps/admin-ui/src/client-scopes/details/MappingDetails.tsx b/js/apps/admin-ui/src/client-scopes/details/MappingDetails.tsx index d372ef76e4cc..7143db9e303e 100644 --- a/js/apps/admin-ui/src/client-scopes/details/MappingDetails.tsx +++ b/js/apps/admin-ui/src/client-scopes/details/MappingDetails.tsx @@ -14,9 +14,7 @@ import { FormProvider, useForm } from "react-hook-form"; import { useTranslation } from "react-i18next"; import { Link, useMatch, useNavigate } from "react-router-dom"; import { KeycloakTextInput, TextControl } from "ui-shared"; - import { adminClient } from "../../admin-client"; -import { toClient } from "../../clients/routes/Client"; import { useAlerts } from "../../components/alert/Alerts"; import { useConfirmDialog } from "../../components/confirm-dialog/ConfirmDialog"; import { DynamicComponents } from "../../components/dynamic/DynamicComponents"; @@ -29,6 +27,7 @@ import { useFetch } from "../../utils/useFetch"; import { useParams } from "../../utils/useParams"; import { toClientScope } from "../routes/ClientScope"; import { MapperParams, MapperRoute } from "../routes/Mapper"; +import { toDedicatedScope } from "../../clients/routes/DedicatedScopeDetails"; export default function MappingDetails() { const { t } = useTranslation(); @@ -53,7 +52,7 @@ export default function MappingDetails() { const toDetails = () => isOnClientScope ? toClientScope({ realm, id, tab: "mappers" }) - : toClient({ realm, clientId: id, tab: "mappers" }); + : toDedicatedScope({ realm, clientId: id, tab: "mappers" }); useFetch( async () => { From 047e80445f0e82cf2704943baf425fc6fa16d104 Mon Sep 17 00:00:00 2001 From: rmartinc Date: Thu, 2 Nov 2023 17:11:20 +0100 Subject: [PATCH 096/158] Better management of the CSP header Closes https://github.com/keycloak/keycloak/issues/24568 Signed-off-by: rmartinc (cherry picked from commit 2b769e5129a0db453bb8cc00452c33afdcd2c322) --- .../models/ContentSecurityPolicyBuilder.java | 125 ++++++++++--- .../models/BrowserSecurityHeadersTest.java | 18 ++ .../DefaultSecurityHeadersProvider.java | 14 +- .../oidc/FrontChannelLogoutHandler.java | 1 - .../updaters/ClientAttributeUpdater.java | 5 + .../updaters/RealmAttributeUpdater.java | 5 + .../RPInitiatedFrontChannelLogoutTest.java | 165 ++++++++++++++++++ .../oauth/RPInitiatedLogoutTest.java | 96 ---------- 8 files changed, 304 insertions(+), 125 deletions(-) create mode 100644 testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/forms/RPInitiatedFrontChannelLogoutTest.java diff --git a/server-spi-private/src/main/java/org/keycloak/models/ContentSecurityPolicyBuilder.java b/server-spi-private/src/main/java/org/keycloak/models/ContentSecurityPolicyBuilder.java index b220c1f50f0f..080854e15b88 100644 --- a/server-spi-private/src/main/java/org/keycloak/models/ContentSecurityPolicyBuilder.java +++ b/server-spi-private/src/main/java/org/keycloak/models/ContentSecurityPolicyBuilder.java @@ -1,48 +1,129 @@ +/* + * Copyright 2023 Red Hat, Inc. and/or its affiliates + * and other contributors as indicated by the @author tags. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ package org.keycloak.models; +import java.util.LinkedHashMap; +import java.util.Map; + public class ContentSecurityPolicyBuilder { - private String frameSrc = "'self'"; - private String frameAncestors = "'self'"; - private String objectSrc = "'none'"; + // constants for directive names used in the class + public static final String DIRECTIVE_NAME_FRAME_SRC = "frame-src"; + public static final String DIRECTIVE_NAME_FRAME_ANCESTORS = "frame-ancestors"; + public static final String DIRECTIVE_NAME_OBJECT_SRC = "object-src"; + + // constants for specific directive value keywords + public static final String DIRECTIVE_VALUE_SELF = "'self'"; + public static final String DIRECTIVE_VALUE_NONE = "'none'"; - private boolean first; - private StringBuilder sb; + private final Map directives = new LinkedHashMap<>(); public static ContentSecurityPolicyBuilder create() { - return new ContentSecurityPolicyBuilder(); + return new ContentSecurityPolicyBuilder() + .add(DIRECTIVE_NAME_FRAME_SRC, DIRECTIVE_VALUE_SELF) + .add(DIRECTIVE_NAME_FRAME_ANCESTORS, DIRECTIVE_VALUE_SELF) + .add(DIRECTIVE_NAME_OBJECT_SRC, DIRECTIVE_VALUE_NONE); + } + + public static ContentSecurityPolicyBuilder create(String directives) { + return new ContentSecurityPolicyBuilder().parse(directives); } public ContentSecurityPolicyBuilder frameSrc(String frameSrc) { - this.frameSrc = frameSrc; + if (frameSrc == null) { + directives.remove(DIRECTIVE_NAME_FRAME_SRC); + } else { + put(DIRECTIVE_NAME_FRAME_SRC, frameSrc); + } return this; } + public ContentSecurityPolicyBuilder addFrameSrc(String frameSrc) { + return add(DIRECTIVE_NAME_FRAME_SRC, frameSrc); + } + + public boolean isDefaultFrameAncestors() { + return DIRECTIVE_VALUE_SELF.equals(directives.get(DIRECTIVE_NAME_FRAME_ANCESTORS)); + } + public ContentSecurityPolicyBuilder frameAncestors(String frameancestors) { - this.frameAncestors = frameancestors; + if (frameancestors == null) { + directives.remove(DIRECTIVE_NAME_FRAME_ANCESTORS); + } else { + put(DIRECTIVE_NAME_FRAME_ANCESTORS, frameancestors); + } return this; } - public String build() { - sb = new StringBuilder(); - first = true; - - build("frame-src", frameSrc); - build("frame-ancestors", frameAncestors); - build("object-src", objectSrc); + public ContentSecurityPolicyBuilder addFrameAncestors(String frameancestors) { + return add(DIRECTIVE_NAME_FRAME_ANCESTORS, frameancestors); + } + public String build() { + StringBuilder sb = new StringBuilder(); + if (!directives.isEmpty()) { + for (Map.Entry entry : directives.entrySet()) { + sb.append(entry.getKey()); + if (!entry.getValue().isEmpty()) { + sb.append(" ").append(entry.getValue()); + } + sb.append("; "); + } + sb.setLength(sb.length() - 1); + } return sb.toString(); } - private void build(String k, String v) { - if (v != null) { - if (!first) { - sb.append(" "); - } - first = false; + private ContentSecurityPolicyBuilder put(String name, String value) { + if (name != null && value != null) { + directives.put(name, value); + } + return this; + } - sb.append(k).append(" ").append(v).append(";"); + private ContentSecurityPolicyBuilder add(String name, String value) { + if (name != null && value != null) { + String current = directives.get(name); + if (current != null && !current.isEmpty()) { + value = current + " " + value; + } + directives.put(name, value); } + return this; } + // W3C Working Draft: https://www.w3.org/TR/CSP/ + // Only managing spaces not the other whitespaces defined in the spec + private ContentSecurityPolicyBuilder parse(String value) { + if (value == null) { + return this; + } + String[] values = value.split(";"); + if (values != null) { + for (String directive : values) { + directive = directive.trim(); + int idx = directive.indexOf(' '); + if (idx > 0) { + add(directive.substring(0, idx), directive.substring(idx + 1, directive.length()).trim()); + } else if (!directive.isEmpty()) { + add(directive, ""); + } + } + } + return this; + } } diff --git a/server-spi-private/src/test/java/org/keycloak/models/BrowserSecurityHeadersTest.java b/server-spi-private/src/test/java/org/keycloak/models/BrowserSecurityHeadersTest.java index e4b39afc4aee..cb88f12d0289 100644 --- a/server-spi-private/src/test/java/org/keycloak/models/BrowserSecurityHeadersTest.java +++ b/server-spi-private/src/test/java/org/keycloak/models/BrowserSecurityHeadersTest.java @@ -24,6 +24,24 @@ public void contentSecurityPolicyBuilderTest() { assertEquals("frame-ancestors 'self'; object-src 'none';", ContentSecurityPolicyBuilder.create().frameSrc(null).build()); assertEquals("frame-src 'self'; object-src 'none';", ContentSecurityPolicyBuilder.create().frameAncestors(null).build()); assertEquals("frame-src 'custom-frame-src'; frame-ancestors 'custom-frame-ancestors'; object-src 'none';", ContentSecurityPolicyBuilder.create().frameSrc("'custom-frame-src'").frameAncestors("'custom-frame-ancestors'").build()); + assertEquals("frame-src localhost; frame-ancestors 'self'; object-src 'none';", ContentSecurityPolicyBuilder.create().frameSrc("localhost").build()); + assertEquals("frame-src 'self' localhost; frame-ancestors 'self'; object-src 'none';", + ContentSecurityPolicyBuilder.create().addFrameSrc("localhost").build()); + } + + private void assertParsedDirectives(String directives) { + assertEquals(directives, ContentSecurityPolicyBuilder.create(directives).build()); + } + + @Test + public void parseSecurityPolicyBuilderTest() { + assertParsedDirectives("frame-src 'self'; frame-ancestors 'self'; object-src 'none';"); + assertParsedDirectives("frame-ancestors 'self'; object-src 'none';"); + assertParsedDirectives("frame-src 'self'; object-src 'none';"); + assertParsedDirectives("frame-src 'custom-frame-src'; frame-ancestors 'custom-frame-ancestors'; object-src 'none';"); + assertParsedDirectives("frame-src 'custom-frame-src'; frame-ancestors 'custom-frame-ancestors'; object-src 'none'; style-src 'self';"); + assertParsedDirectives("frame-src 'custom-frame-src'; frame-ancestors 'custom-frame-ancestors'; object-src 'none'; sandbox;"); + assertEquals("frame-src 'custom-frame-src'; sandbox;", ContentSecurityPolicyBuilder.create("frame-src 'custom-frame-src' ; sandbox ; ").build()); } @Test diff --git a/services/src/main/java/org/keycloak/headers/DefaultSecurityHeadersProvider.java b/services/src/main/java/org/keycloak/headers/DefaultSecurityHeadersProvider.java index 58af8967d980..b10b2b28dfdf 100644 --- a/services/src/main/java/org/keycloak/headers/DefaultSecurityHeadersProvider.java +++ b/services/src/main/java/org/keycloak/headers/DefaultSecurityHeadersProvider.java @@ -106,22 +106,24 @@ private void addHtmlHeaders(MultivaluedMap headers) { // TODO This will be refactored as part of introducing a more strict CSP header if (options != null) { - ContentSecurityPolicyBuilder csp = ContentSecurityPolicyBuilder.create(); + ContentSecurityPolicyBuilder csp = ContentSecurityPolicyBuilder.create( + headers.getFirst(CONTENT_SECURITY_POLICY.getHeaderName()).toString()); if (options.isAllowAnyFrameAncestor()) { headers.remove(BrowserSecurityHeaders.X_FRAME_OPTIONS.getHeaderName()); - csp.frameAncestors(null); + if (csp.isDefaultFrameAncestors()) { + // only remove frame ancestors if defined to default 'self' + csp.frameAncestors(null); + } } String allowedFrameSrc = options.getAllowedFrameSrc(); if (allowedFrameSrc != null) { - csp.frameSrc(allowedFrameSrc); + csp.addFrameSrc(allowedFrameSrc); } - if (CONTENT_SECURITY_POLICY.getDefaultValue().equals(headers.getFirst(CONTENT_SECURITY_POLICY.getHeaderName()))) { - headers.putSingle(CONTENT_SECURITY_POLICY.getHeaderName(), csp.build()); - } + headers.putSingle(CONTENT_SECURITY_POLICY.getHeaderName(), csp.build()); } } diff --git a/services/src/main/java/org/keycloak/protocol/oidc/FrontChannelLogoutHandler.java b/services/src/main/java/org/keycloak/protocol/oidc/FrontChannelLogoutHandler.java index bcac389cc67b..f292aa89c0eb 100644 --- a/services/src/main/java/org/keycloak/protocol/oidc/FrontChannelLogoutHandler.java +++ b/services/src/main/java/org/keycloak/protocol/oidc/FrontChannelLogoutHandler.java @@ -67,7 +67,6 @@ private void configureCSP() { allowFrameSrc.append(client.frontChannelLogoutUrl.getAuthority()).append(' '); } - session.getProvider(SecurityHeadersProvider.class).options().allowAnyFrameAncestor(); session.getProvider(SecurityHeadersProvider.class).options().allowFrameSrc(allowFrameSrc.toString()); } diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/updaters/ClientAttributeUpdater.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/updaters/ClientAttributeUpdater.java index bb5d87e3621b..4d0d77c0aa6a 100644 --- a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/updaters/ClientAttributeUpdater.java +++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/updaters/ClientAttributeUpdater.java @@ -68,6 +68,11 @@ public ClientAttributeUpdater setClientId(String clientId) { return this; } + public ClientAttributeUpdater setName(String name) { + this.rep.setName(name); + return this; + } + public ClientAttributeUpdater setAttribute(String name, String value) { this.rep.getAttributes().put(name, value); if (value != null && !this.origRep.getAttributes().containsKey(name)) { diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/updaters/RealmAttributeUpdater.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/updaters/RealmAttributeUpdater.java index 56ad1317501a..505a7d5432a1 100644 --- a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/updaters/RealmAttributeUpdater.java +++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/updaters/RealmAttributeUpdater.java @@ -169,4 +169,9 @@ public RealmAttributeUpdater setSmtpServer(String name, String value) { rep.getSmtpServer().put(name, value); return this; } + + public RealmAttributeUpdater setBrowserSecurityHeader(String name, String value) { + rep.getBrowserSecurityHeaders().put(name, value); + return this; + } } diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/forms/RPInitiatedFrontChannelLogoutTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/forms/RPInitiatedFrontChannelLogoutTest.java new file mode 100644 index 000000000000..fa1f0598849b --- /dev/null +++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/forms/RPInitiatedFrontChannelLogoutTest.java @@ -0,0 +1,165 @@ +/* + * Copyright 2024 Red Hat, Inc. and/or its affiliates + * and other contributors as indicated by the @author tags. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.keycloak.testsuite.forms; + +import org.junit.Assert; +import org.junit.Test; +import org.keycloak.OAuth2Constants; +import org.keycloak.admin.client.resource.ClientsResource; +import org.keycloak.jose.jws.JWSInput; +import org.keycloak.models.BrowserSecurityHeaders; +import org.keycloak.protocol.oidc.OIDCConfigAttributes; +import org.keycloak.representations.IDToken; +import org.keycloak.representations.LogoutToken; +import org.keycloak.representations.idm.ClientRepresentation; +import org.keycloak.representations.idm.RealmRepresentation; +import org.keycloak.testsuite.AbstractTestRealmKeycloakTest; +import org.keycloak.testsuite.updaters.ClientAttributeUpdater; +import org.keycloak.testsuite.updaters.RealmAttributeUpdater; +import org.keycloak.testsuite.util.OAuthClient; + +/** + * + * @author rmartinc + */ +public class RPInitiatedFrontChannelLogoutTest extends AbstractTestRealmKeycloakTest { + + @Override + public void configureTestRealm(RealmRepresentation testRealm) { + // no-op + } + + @Test + public void testFrontChannelLogoutWithPostLogoutRedirectUri() throws Exception { + ClientsResource clients = adminClient.realm(oauth.getRealm()).clients(); + ClientRepresentation rep = clients.findByClientId(oauth.getClientId()).get(0); + rep.setFrontchannelLogout(true); + rep.getAttributes().put(OIDCConfigAttributes.FRONT_CHANNEL_LOGOUT_URI, OAuthClient.APP_ROOT + "/admin/frontchannelLogout"); + clients.get(rep.getId()).update(rep); + try { + oauth.clientSessionState("client-session"); + oauth.doLogin("test-user@localhost", "password"); + String code = oauth.getCurrentQuery().get(OAuth2Constants.CODE); + OAuthClient.AccessTokenResponse tokenResponse = oauth.doAccessTokenRequest(code, "password"); + String idTokenString = tokenResponse.getIdToken(); + String logoutUrl = oauth.getLogoutUrl().idTokenHint(idTokenString) + .postLogoutRedirectUri(OAuthClient.APP_AUTH_ROOT).build(); + driver.navigate().to(logoutUrl); + LogoutToken logoutToken = testingClient.testApp().getFrontChannelLogoutToken(); + Assert.assertNotNull(logoutToken); + + IDToken idToken = new JWSInput(idTokenString).readJsonContent(IDToken.class); + + Assert.assertEquals(logoutToken.getIssuer(), idToken.getIssuer()); + Assert.assertEquals(logoutToken.getSid(), idToken.getSessionId()); + } finally { + rep.setFrontchannelLogout(false); + rep.getAttributes().put(OIDCConfigAttributes.FRONT_CHANNEL_LOGOUT_URI, ""); + clients.get(rep.getId()).update(rep); + } + } + + @Test + public void testFrontChannelLogoutWithoutSessionRequired() throws Exception { + ClientsResource clients = adminClient.realm(oauth.getRealm()).clients(); + ClientRepresentation rep = clients.findByClientId(oauth.getClientId()).get(0); + rep.setFrontchannelLogout(true); + rep.getAttributes().put(OIDCConfigAttributes.FRONT_CHANNEL_LOGOUT_URI, OAuthClient.APP_ROOT + "/admin/frontchannelLogout"); + rep.getAttributes().put(OIDCConfigAttributes.FRONT_CHANNEL_LOGOUT_SESSION_REQUIRED, "false"); + clients.get(rep.getId()).update(rep); + try { + oauth.clientSessionState("client-session"); + oauth.doLogin("test-user@localhost", "password"); + String code = oauth.getCurrentQuery().get(OAuth2Constants.CODE); + OAuthClient.AccessTokenResponse tokenResponse = oauth.doAccessTokenRequest(code, "password"); + String idTokenString = tokenResponse.getIdToken(); + String logoutUrl = oauth.getLogoutUrl().idTokenHint(idTokenString) + .postLogoutRedirectUri(OAuthClient.APP_AUTH_ROOT).build(); + driver.navigate().to(logoutUrl); + LogoutToken logoutToken = testingClient.testApp().getFrontChannelLogoutToken(); + Assert.assertNotNull(logoutToken); + + Assert.assertNull(logoutToken.getIssuer()); + Assert.assertNull(logoutToken.getSid()); + } finally { + rep.setFrontchannelLogout(false); + rep.getAttributes().put(OIDCConfigAttributes.FRONT_CHANNEL_LOGOUT_URI, ""); + rep.getAttributes().put(OIDCConfigAttributes.FRONT_CHANNEL_LOGOUT_SESSION_REQUIRED, "true"); + clients.get(rep.getId()).update(rep); + } + } + + @Test + public void testFrontChannelLogout() throws Exception { + ClientsResource clients = adminClient.realm(oauth.getRealm()).clients(); + ClientRepresentation rep = clients.findByClientId(oauth.getClientId()).get(0); + rep.setName("My Testing App"); + rep.setFrontchannelLogout(true); + rep.getAttributes().put(OIDCConfigAttributes.FRONT_CHANNEL_LOGOUT_URI, OAuthClient.APP_ROOT + "/admin/frontchannelLogout"); + clients.get(rep.getId()).update(rep); + try { + oauth.clientSessionState("client-session"); + oauth.doLogin("test-user@localhost", "password"); + String code = oauth.getCurrentQuery().get(OAuth2Constants.CODE); + OAuthClient.AccessTokenResponse tokenResponse = oauth.doAccessTokenRequest(code, "password"); + String idTokenString = tokenResponse.getIdToken(); + String logoutUrl = oauth.getLogoutUrl().idTokenHint(idTokenString).build(); + driver.navigate().to(logoutUrl); + LogoutToken logoutToken = testingClient.testApp().getFrontChannelLogoutToken(); + org.keycloak.testsuite.Assert.assertNotNull(logoutToken); + IDToken idToken = new JWSInput(idTokenString).readJsonContent(IDToken.class); + org.keycloak.testsuite.Assert.assertEquals(logoutToken.getIssuer(), idToken.getIssuer()); + org.keycloak.testsuite.Assert.assertEquals(logoutToken.getSid(), idToken.getSessionId()); + Assert.assertTrue(driver.getTitle().equals("Logging out")); + Assert.assertTrue(driver.getPageSource().contains("You are logging out from following apps")); + Assert.assertTrue(driver.getPageSource().contains("My Testing App")); + } finally { + rep.setFrontchannelLogout(false); + rep.getAttributes().put(OIDCConfigAttributes.FRONT_CHANNEL_LOGOUT_URI, ""); + clients.get(rep.getId()).update(rep); + } + } + + @Test + public void testFrontChannelLogoutCustomCSP() throws Exception { + try (RealmAttributeUpdater realmUpdater = new RealmAttributeUpdater(adminClient.realm(oauth.getRealm())) + .setBrowserSecurityHeader(BrowserSecurityHeaders.CONTENT_SECURITY_POLICY.getKey(), + "frame-src 'keycloak.org'; frame-ancestors 'self'; object-src 'none'; style-src 'self';") + .update(); + ClientAttributeUpdater clientUpdater = ClientAttributeUpdater.forClient(adminClient, oauth.getRealm(), oauth.getClientId()) + .setName("My Testing App") + .setFrontchannelLogout(true) + .setAttribute(OIDCConfigAttributes.FRONT_CHANNEL_LOGOUT_URI, OAuthClient.APP_ROOT + "/admin/frontchannelLogout") + .update()) { + oauth.clientSessionState("client-session"); + oauth.doLogin("test-user@localhost", "password"); + String code = oauth.getCurrentQuery().get(OAuth2Constants.CODE); + OAuthClient.AccessTokenResponse tokenResponse = oauth.doAccessTokenRequest(code, "password"); + String idTokenString = tokenResponse.getIdToken(); + String logoutUrl = oauth.getLogoutUrl().idTokenHint(idTokenString).build(); + driver.navigate().to(logoutUrl); + LogoutToken logoutToken = testingClient.testApp().getFrontChannelLogoutToken(); + Assert.assertNotNull(logoutToken); + IDToken idToken = new JWSInput(idTokenString).readJsonContent(IDToken.class); + Assert.assertEquals(logoutToken.getIssuer(), idToken.getIssuer()); + Assert.assertEquals(logoutToken.getSid(), idToken.getSessionId()); + Assert.assertTrue(driver.getTitle().equals("Logging out")); + Assert.assertTrue(driver.getPageSource().contains("You are logging out from following apps")); + Assert.assertTrue(driver.getPageSource().contains("My Testing App")); + } + } +} diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/oauth/RPInitiatedLogoutTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/oauth/RPInitiatedLogoutTest.java index a56ebeee1ccc..e5b9bfe16c93 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/oauth/RPInitiatedLogoutTest.java +++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/oauth/RPInitiatedLogoutTest.java @@ -28,18 +28,14 @@ import org.junit.Test; import org.keycloak.OAuth2Constants; import org.keycloak.OAuthErrorException; -import org.keycloak.admin.client.resource.ClientsResource; import org.keycloak.admin.client.resource.UserResource; import org.keycloak.common.util.UriUtils; import org.keycloak.events.Details; import org.keycloak.events.Errors; -import org.keycloak.jose.jws.JWSInput; import org.keycloak.models.Constants; import org.keycloak.models.UserModel; import org.keycloak.protocol.oidc.OIDCConfigAttributes; import org.keycloak.protocol.oidc.OIDCLoginProtocol; -import org.keycloak.representations.IDToken; -import org.keycloak.representations.LogoutToken; import org.keycloak.representations.idm.ClientRepresentation; import org.keycloak.representations.idm.RealmRepresentation; import org.keycloak.testsuite.Assert; @@ -1032,98 +1028,6 @@ public void testIncorrectChangingParameters() throws IOException { events.expectLogoutError(Errors.LOGOUT_FAILED).assertEvent(); } - - @Test - public void testFrontChannelLogoutWithPostLogoutRedirectUri() throws Exception { - ClientsResource clients = adminClient.realm(oauth.getRealm()).clients(); - ClientRepresentation rep = clients.findByClientId(oauth.getClientId()).get(0); - rep.setFrontchannelLogout(true); - rep.getAttributes().put(OIDCConfigAttributes.FRONT_CHANNEL_LOGOUT_URI, oauth.APP_ROOT + "/admin/frontchannelLogout"); - clients.get(rep.getId()).update(rep); - try { - oauth.clientSessionState("client-session"); - oauth.doLogin("test-user@localhost", "password"); - String code = oauth.getCurrentQuery().get(OAuth2Constants.CODE); - OAuthClient.AccessTokenResponse tokenResponse = oauth.doAccessTokenRequest(code, "password"); - String idTokenString = tokenResponse.getIdToken(); - String logoutUrl = oauth.getLogoutUrl().idTokenHint(idTokenString) - .postLogoutRedirectUri(oauth.APP_AUTH_ROOT).build(); - driver.navigate().to(logoutUrl); - LogoutToken logoutToken = testingClient.testApp().getFrontChannelLogoutToken(); - Assert.assertNotNull(logoutToken); - - IDToken idToken = new JWSInput(idTokenString).readJsonContent(IDToken.class); - - Assert.assertEquals(logoutToken.getIssuer(), idToken.getIssuer()); - Assert.assertEquals(logoutToken.getSid(), idToken.getSessionId()); - } finally { - rep.setFrontchannelLogout(false); - rep.getAttributes().put(OIDCConfigAttributes.FRONT_CHANNEL_LOGOUT_URI, ""); - clients.get(rep.getId()).update(rep); - } - } - - @Test - public void testFrontChannelLogoutWithoutSessionRequired() throws Exception { - ClientsResource clients = adminClient.realm(oauth.getRealm()).clients(); - ClientRepresentation rep = clients.findByClientId(oauth.getClientId()).get(0); - rep.setFrontchannelLogout(true); - rep.getAttributes().put(OIDCConfigAttributes.FRONT_CHANNEL_LOGOUT_URI, oauth.APP_ROOT + "/admin/frontchannelLogout"); - rep.getAttributes().put(OIDCConfigAttributes.FRONT_CHANNEL_LOGOUT_SESSION_REQUIRED, "false"); - clients.get(rep.getId()).update(rep); - try { - oauth.clientSessionState("client-session"); - oauth.doLogin("test-user@localhost", "password"); - String code = oauth.getCurrentQuery().get(OAuth2Constants.CODE); - OAuthClient.AccessTokenResponse tokenResponse = oauth.doAccessTokenRequest(code, "password"); - String idTokenString = tokenResponse.getIdToken(); - String logoutUrl = oauth.getLogoutUrl().idTokenHint(idTokenString) - .postLogoutRedirectUri(oauth.APP_AUTH_ROOT).build(); - driver.navigate().to(logoutUrl); - LogoutToken logoutToken = testingClient.testApp().getFrontChannelLogoutToken(); - Assert.assertNotNull(logoutToken); - - Assert.assertNull(logoutToken.getIssuer()); - Assert.assertNull(logoutToken.getSid()); - } finally { - rep.setFrontchannelLogout(false); - rep.getAttributes().put(OIDCConfigAttributes.FRONT_CHANNEL_LOGOUT_URI, ""); - rep.getAttributes().put(OIDCConfigAttributes.FRONT_CHANNEL_LOGOUT_SESSION_REQUIRED, "true"); - clients.get(rep.getId()).update(rep); - } - } - - @Test - public void testFrontChannelLogout() throws Exception { - ClientsResource clients = adminClient.realm(oauth.getRealm()).clients(); - ClientRepresentation rep = clients.findByClientId(oauth.getClientId()).get(0); - rep.setName("My Testing App"); - rep.setFrontchannelLogout(true); - rep.getAttributes().put(OIDCConfigAttributes.FRONT_CHANNEL_LOGOUT_URI, oauth.APP_ROOT + "/admin/frontchannelLogout"); - clients.get(rep.getId()).update(rep); - try { - oauth.clientSessionState("client-session"); - oauth.doLogin("test-user@localhost", "password"); - String code = oauth.getCurrentQuery().get(OAuth2Constants.CODE); - OAuthClient.AccessTokenResponse tokenResponse = oauth.doAccessTokenRequest(code, "password"); - String idTokenString = tokenResponse.getIdToken(); - String logoutUrl = oauth.getLogoutUrl().idTokenHint(idTokenString).build(); - driver.navigate().to(logoutUrl); - LogoutToken logoutToken = testingClient.testApp().getFrontChannelLogoutToken(); - Assert.assertNotNull(logoutToken); - IDToken idToken = new JWSInput(idTokenString).readJsonContent(IDToken.class); - Assert.assertEquals(logoutToken.getIssuer(), idToken.getIssuer()); - Assert.assertEquals(logoutToken.getSid(), idToken.getSessionId()); - assertTrue(driver.getTitle().equals("Logging out")); - assertTrue(driver.getPageSource().contains("You are logging out from following apps")); - assertTrue(driver.getPageSource().contains("My Testing App")); - } finally { - rep.setFrontchannelLogout(false); - rep.getAttributes().put(OIDCConfigAttributes.FRONT_CHANNEL_LOGOUT_URI, ""); - clients.get(rep.getId()).update(rep); - } - } - @Test public void logoutWithIdTokenAndDisabledClientMustWork() throws Exception { OAuthClient.AccessTokenResponse tokenResponse = loginUser(); From 747c435cd4fc5acac0eec297aab40232a2b35a12 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Barto=C5=A1?= Date: Tue, 16 Apr 2024 15:41:32 +0200 Subject: [PATCH 097/158] Emphasize the need for setting container limit MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Closes #28729 Signed-off-by: Martin Bartoš --- docs/documentation/release_notes/topics/24_0_0.adoc | 9 +++++---- .../upgrading/topics/changes/changes-24_0_0.adoc | 11 +++++++++++ docs/guides/server/containers.adoc | 12 ++++++++++-- 3 files changed, 26 insertions(+), 6 deletions(-) diff --git a/docs/documentation/release_notes/topics/24_0_0.adoc b/docs/documentation/release_notes/topics/24_0_0.adoc index 8ca20b429c58..6772329a3574 100644 --- a/docs/documentation/release_notes/topics/24_0_0.adoc +++ b/docs/documentation/release_notes/topics/24_0_0.adoc @@ -430,13 +430,14 @@ mappers would never be used. The supported options were updated to only include - `urn:oasis:names:tc:SAML:2.0:nameid-format:persistent` - `urn:oasis:names:tc:SAML:2.0:nameid-format:transient` -= Different JVM memory settings when running in container += Different JVM memory settings when running in a container Instead of specifying hardcoded values for the initial and maximum heap size, {project_name} uses relative values to the total memory of a container. -The JVM options `-Xms`, and `-Xmx` were replaced by `-XX:InitialRAMPercentage`, and `-XX:MaxRAMPercentage`. +The JVM options `-Xms` and `-Xmx` were replaced by `-XX:InitialRAMPercentage` and `-XX:MaxRAMPercentage`. -For more details, see the -https://www.keycloak.org/server/containers[Running Keycloak in a container] guide. +WARNING: It can significantly impact memory consumption, so executing particular actions might be required. + +For more details, see the link:{upgradingguide_link}[{upgradingguide_name}]. ifeval::[{project_community}==true] = GELF log handler has been deprecated diff --git a/docs/documentation/upgrading/topics/changes/changes-24_0_0.adoc b/docs/documentation/upgrading/topics/changes/changes-24_0_0.adoc index 5eb502c191c3..40b92c707653 100644 --- a/docs/documentation/upgrading/topics/changes/changes-24_0_0.adoc +++ b/docs/documentation/upgrading/topics/changes/changes-24_0_0.adoc @@ -488,6 +488,17 @@ For custom extensions there may be some changes needed: The algorithm that {project_name} uses to sign internal tokens (a JWT which is consumed by {project_name} itself, for example a refresh or action token) is being changed from `HS256` to the more secure `HS512`. A new key provider named `hmac-generated-hs512` is now added for realms. Note that in migrated realms the old `hmac-generated` provider and the old `HS256` key are maintained and still validate tokens issued before the upgrade. The `HS256` provider can be manually deleted when no more old tokens exist following the {adminguide_link}#rotating-keys[rotating keys guidelines]. += Different JVM memory settings when running in a container + +The JVM options `-Xms` and `-Xmx` were replaced by `-XX:InitialRAMPercentage` and `-XX:MaxRAMPercentage` when running in a container. +Instead of the static maximum heap size settings, {project_name} specifies the maximum as 70% of the total container memory. + +As the heap size is dynamically calculated based on the total container memory, you should *always set the memory limit* for the container. + +WARNING: If the memory limit is not set, the memory consumption rapidly increases as the maximum heap size grows up to 70% of the total container memory. + +For more details, see the https://www.keycloak.org/server/containers#_specifying_different_memory_settings[Running Keycloak in a container] guide. + ifeval::[{project_community}==true] = GELF log handler has been deprecated diff --git a/docs/guides/server/containers.adoc b/docs/guides/server/containers.adoc index 9307da618512..c4eb4364a2c0 100644 --- a/docs/guides/server/containers.adoc +++ b/docs/guides/server/containers.adoc @@ -238,17 +238,25 @@ The `-XX:MaxRAMPercentage` option represents the maximum heap size as 70% of the The `-XX:InitialRAMPercentage` option represents the initial heap size as 50% of the total container memory. These values were chosen based on a deeper analysis of {project_name} memory management. +As the heap size is dynamically calculated based on the total container memory, you should *always set the memory limit* for the container. +Previously, the maximum heap size was set to 512 MB, and in order to approach similar values, you should set the memory limit to at least 750 MB. +For smaller production-ready deployments, the recommended memory limit is 2 GB. + The JVM options related to the heap might be overridden by setting the environment variable `JAVA_OPTS_KC_HEAP`. You can find the default values of the `JAVA_OPTS_KC_HEAP` in the source code of the `kc.sh`, or `kc.bat` script. -For example, you can specify the environment variable as follows: + +For example, you can specify the environment variable and memory limit as follows: [source,bash,subs="attributes+"] ---- -podman|docker run --name mykeycloak -p 8080:8080 \ +podman|docker run --name mykeycloak -p 8080:8080 -m 1g \ -e KEYCLOAK_ADMIN=admin -e KEYCLOAK_ADMIN_PASSWORD=change_me \ -e JAVA_OPTS_KC_HEAP="-XX:MaxHeapFreeRatio=30 -XX:MaxRAMPercentage=65" \ quay.io/keycloak/keycloak:{containerlabel} \ start-dev ---- +WARNING: If the memory limit is not set, the memory consumption rapidly increases as the heap size can grow up to 70% of the total container memory. +Once the JVM allocates the memory, it is returned to the OS reluctantly with the current {project_name} GC settings. + From 84cb199557aa26732adda2b087c86a3b03b593ef Mon Sep 17 00:00:00 2001 From: Hynek Mlnarik Date: Fri, 19 Apr 2024 11:08:04 +0200 Subject: [PATCH 098/158] Relax checking of messages Related to: #28873 Fixes: #28911 Signed-off-by: Hynek Mlnarik (cherry picked from commit 4f30400e07b2842de6844a811d0ad3472a89f2b9) --- .../support/pages/admin-ui/Masthead.ts | 29 +++++++++++++------ .../client_details/tabs/SettingsTab.ts | 2 +- 2 files changed, 21 insertions(+), 10 deletions(-) diff --git a/js/apps/admin-ui/cypress/support/pages/admin-ui/Masthead.ts b/js/apps/admin-ui/cypress/support/pages/admin-ui/Masthead.ts index d79f4420d044..fa65da038280 100644 --- a/js/apps/admin-ui/cypress/support/pages/admin-ui/Masthead.ts +++ b/js/apps/admin-ui/cypress/support/pages/admin-ui/Masthead.ts @@ -94,16 +94,27 @@ export default class Masthead extends CommonElements { cy.get("#manage-account").click(); } - checkNotificationMessage(message: string, closeNotification = true) { - this.#getAlertsContainer() - .find(this.#alertMessage) - .should("contain.text", message); - - if (closeNotification) { + checkNotificationMessage(message: string | RegExp, closeNotification = true) { + if (typeof message === "string") { + this.#getAlertsContainer() + .find(this.#alertMessage) + .should("contain.text", message); + + if (closeNotification) { + this.#getAlertsContainer() + .find(`button[title="` + message.replaceAll('"', '\\"') + `"]`) + .last() + .click({ force: true }); + } + } else { this.#getAlertsContainer() - .find(`button[title="` + message.replaceAll('"', '\\"') + `"]`) - .last() - .click({ force: true }); + .find(this.#alertMessage) + .invoke("text") + .should("match", message); + + if (closeNotification) { + this.#getAlertsContainer().find("button").last().click({ force: true }); + } } return this; } diff --git a/js/apps/admin-ui/cypress/support/pages/admin-ui/manage/clients/client_details/tabs/SettingsTab.ts b/js/apps/admin-ui/cypress/support/pages/admin-ui/manage/clients/client_details/tabs/SettingsTab.ts index 0331c6958cfa..9fc1c94e6cfa 100644 --- a/js/apps/admin-ui/cypress/support/pages/admin-ui/manage/clients/client_details/tabs/SettingsTab.ts +++ b/js/apps/admin-ui/cypress/support/pages/admin-ui/manage/clients/client_details/tabs/SettingsTab.ts @@ -221,7 +221,7 @@ export default class SettingsTab extends PageObject { public assertAccessSettings() { const redirectUriError = - "Client could not be updated: A redirect URI is not a valid URI"; + /Client could not be updated:.*(Master SAML Processing URL is not a valid URL|A redirect URI is not a valid URI).*/i; cy.findByTestId(this.#idpInitiatedSsoUrlName).click().type("a"); cy.findByTestId(this.#idpInitiatedSsoRelayState).click().type("b"); From 389c12d3079f2623a03356d59e873bb73e7667d5 Mon Sep 17 00:00:00 2001 From: agagancarczyk <4890675+agagancarczyk@users.noreply.github.com> Date: Fri, 19 Apr 2024 15:55:05 +0100 Subject: [PATCH 099/158] added helpText for importFileHelp (#28562) Signed-off-by: Agnieszka Gancarczyk Co-authored-by: Agnieszka Gancarczyk --- .../theme/keycloak.v2/admin/messages/messages_en.properties | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/js/apps/admin-ui/maven-resources/theme/keycloak.v2/admin/messages/messages_en.properties b/js/apps/admin-ui/maven-resources/theme/keycloak.v2/admin/messages/messages_en.properties index 2b09218fde20..bd9698ef62f1 100644 --- a/js/apps/admin-ui/maven-resources/theme/keycloak.v2/admin/messages/messages_en.properties +++ b/js/apps/admin-ui/maven-resources/theme/keycloak.v2/admin/messages/messages_en.properties @@ -3076,4 +3076,5 @@ sendIdTokenOnLogout=Send 'id_token_hint' in logout requests sendIdTokenOnLogoutHelp=If the 'id_token_hint' parameter should be sent in logout requests. sendClientIdOnLogout=Send 'client_id' in logout requests sendClientIdOnLogoutHelp=If the 'client_id' parameter should be sent in logout requests. -searchClientRegistration=Search for policy \ No newline at end of file +searchClientRegistration=Search for policy +importFileHelp=File to import a key \ No newline at end of file From 7d756704e0bf72801052f58db6578c72a2ec4153 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Barto=C5=A1?= Date: Wed, 24 Apr 2024 12:07:34 +0200 Subject: [PATCH 100/158] NoClassDefFoundError for Apache XML and EAP8 (#28447) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixes #24878 Signed-off-by: Martin Bartoš --- .../keycloak/keycloak-saml-adapter-core-jakarta/main/module.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/distribution/galleon-feature-packs/saml-adapter-galleon-pack/src/main/resources/modules/system/add-ons/keycloak/org/keycloak/keycloak-saml-adapter-core-jakarta/main/module.xml b/distribution/galleon-feature-packs/saml-adapter-galleon-pack/src/main/resources/modules/system/add-ons/keycloak/org/keycloak/keycloak-saml-adapter-core-jakarta/main/module.xml index 0440e67ff9f1..68538bb94398 100755 --- a/distribution/galleon-feature-packs/saml-adapter-galleon-pack/src/main/resources/modules/system/add-ons/keycloak/org/keycloak/keycloak-saml-adapter-core-jakarta/main/module.xml +++ b/distribution/galleon-feature-packs/saml-adapter-galleon-pack/src/main/resources/modules/system/add-ons/keycloak/org/keycloak/keycloak-saml-adapter-core-jakarta/main/module.xml @@ -38,6 +38,7 @@ + From 5d222b706a245d5dc3c959a1b46cde466d9779a4 Mon Sep 17 00:00:00 2001 From: Steven Hawkins Date: Wed, 24 Apr 2024 17:36:05 -0400 Subject: [PATCH 101/158] fix: ensuring test state is clean between tests closes: #27080 Signed-off-by: Steven Hawkins (cherry picked from commit 26dc81a92fbce458d7c4b0cc7e33aef41c488ac7) --- .../integration/BaseOperatorTest.java | 56 +++++++++++++------ .../integration/PodTemplateTest.java | 8 +-- 2 files changed, 43 insertions(+), 21 deletions(-) diff --git a/operator/src/test/java/org/keycloak/operator/testsuite/integration/BaseOperatorTest.java b/operator/src/test/java/org/keycloak/operator/testsuite/integration/BaseOperatorTest.java index 3d4159ad4b78..ae064ecc3a3a 100644 --- a/operator/src/test/java/org/keycloak/operator/testsuite/integration/BaseOperatorTest.java +++ b/operator/src/test/java/org/keycloak/operator/testsuite/integration/BaseOperatorTest.java @@ -22,8 +22,10 @@ import io.fabric8.kubernetes.api.model.NamespaceBuilder; import io.fabric8.kubernetes.api.model.Pod; import io.fabric8.kubernetes.api.model.Secret; +import io.fabric8.kubernetes.api.model.Service; import io.fabric8.kubernetes.api.model.apps.Deployment; import io.fabric8.kubernetes.api.model.apps.StatefulSet; +import io.fabric8.kubernetes.api.model.batch.v1.Job; import io.fabric8.kubernetes.api.model.events.v1.Event; import io.fabric8.kubernetes.api.model.rbac.ClusterRoleBinding; import io.fabric8.kubernetes.api.model.rbac.RoleBinding; @@ -80,6 +82,7 @@ import java.util.List; import java.util.Optional; import java.util.UUID; +import java.util.concurrent.CompletableFuture; import java.util.concurrent.TimeUnit; import java.util.function.Function; import java.util.stream.Collectors; @@ -289,19 +292,39 @@ private static void setDefaultAwaitilityTimings() { } public void cleanup() { - Log.info("Deleting Keycloak CR"); - k8sclient.resources(Keycloak.class).delete(); - Awaitility.await() - .untilAsserted(() -> { - var kcDeployments = k8sclient - .apps() - .statefulSets() - .inNamespace(namespace) - .withLabels(Constants.DEFAULT_LABELS) - .list() - .getItems(); - assertThat(kcDeployments.size()).isZero(); - }); + Log.info("Deleting Keycloak CR"); + + // due to https://github.com/operator-framework/java-operator-sdk/issues/2314 we + // try to ensure that the operator has processed the delete event from root objects + // this can be simplified to just the root deletion after we pick up the fix + // it can be further simplified after https://github.com/fabric8io/kubernetes-client/issues/5838 + // to just a timed foreground deletion + var roots = List.of(Keycloak.class, KeycloakRealmImport.class); + var dependents = List.of(StatefulSet.class, Secret.class, Service.class, Pod.class, Job.class); + + var rootsDeleted = CompletableFuture.allOf(roots.stream() + .map(c -> k8sclient.resources(c).informOnCondition(List::isEmpty)).toArray(CompletableFuture[]::new)); + roots.stream().forEach(c -> k8sclient.resources(c).withGracePeriod(0).delete()); + try { + rootsDeleted.get(1, TimeUnit.MINUTES); + } catch (Exception e) { + // delete event should have arrived quickly because this is a background delete + throw new RuntimeException(e); + } + dependents.stream().map(c -> k8sclient.resources(c).withLabels(Constants.DEFAULT_LABELS)) + .forEach(r -> r.withGracePeriod(0).delete()); + // enforce that the dependents are gone + Awaitility.await().during(5, TimeUnit.SECONDS).until(() -> { + if (dependents.stream().anyMatch( + c -> !k8sclient.resources(c).withLabels(Constants.DEFAULT_LABELS).list().getItems().isEmpty())) { + // the operator must have recreated because it hasn't gotten the keycloak + // deleted event, keep cleaning + dependents.stream().map(c -> k8sclient.resources(c).withLabels(Constants.DEFAULT_LABELS)) + .forEach(r -> r.withGracePeriod(0).delete()); + return false; + } + return true; + }); } @Override @@ -319,6 +342,7 @@ public void afterEach(QuarkusTestMethodContext context) { return; } Log.warnf("Test failed with %s: %s", context.getTestStatus().getTestErrorCause().getMessage(), context.getTestStatus().getTestErrorCause().getClass().getName()); + Log.infof("Secrets %s", k8sclient.secrets().list().getItems().stream().map(s -> s.getMetadata().getName()).collect(Collectors.joining(", "))); logEvents(); savePodLogs(); // provide some helpful entries in the main log as well @@ -328,7 +352,7 @@ public void afterEach(QuarkusTestMethodContext context) { } logFailed(k8sclient.apps().statefulSets().withName(POSTGRESQL_NAME), StatefulSet::getStatus); k8sclient.pods().withLabel("app", "keycloak-realm-import").list().getItems().stream() - .forEach(pod -> logFailed(k8sclient.pods().resource(pod), Pod::getStatus)); + .forEach(pod -> log(k8sclient.pods().resource(pod), Pod::getStatus, false)); } finally { cleanup(); } @@ -342,11 +366,11 @@ private & Loggable> void log(R res } Log.warnf("%s failed to become ready %s", instance.getMetadata().getName(), Serialization.asYaml(statusExtractor.apply(instance))); } else { - Log.infof("%s is ready %s", instance.getMetadata().getName(), Serialization.asYaml(statusExtractor.apply(instance))); + Log.infof("%s is ready %s %s", instance.getMetadata().getName(), resource.isReady(), Serialization.asYaml(statusExtractor.apply(instance))); } try { String log = resource.getLog(); - log = log.substring(Math.max(0, log.length() - 5000)); + log = log.substring(Math.max(0, log.length() - 50000)); Log.warnf("%s log: %s", instance.getMetadata().getName(), log); } catch (KubernetesClientException e) { Log.warnf("No %s log: %s", instance.getMetadata().getName(), e.getMessage()); diff --git a/operator/src/test/java/org/keycloak/operator/testsuite/integration/PodTemplateTest.java b/operator/src/test/java/org/keycloak/operator/testsuite/integration/PodTemplateTest.java index 40ba5adbe48a..2323f178d54f 100644 --- a/operator/src/test/java/org/keycloak/operator/testsuite/integration/PodTemplateTest.java +++ b/operator/src/test/java/org/keycloak/operator/testsuite/integration/PodTemplateTest.java @@ -26,11 +26,12 @@ import io.fabric8.kubernetes.client.utils.Serialization; import io.quarkus.logging.Log; import io.quarkus.test.junit.QuarkusTest; + import org.awaitility.Awaitility; import org.junit.jupiter.api.Test; +import org.keycloak.operator.crds.v2alpha1.deployment.Keycloak; import org.keycloak.operator.testsuite.utils.CRAssert; import org.keycloak.operator.testsuite.utils.K8sUtils; -import org.keycloak.operator.crds.v2alpha1.deployment.Keycloak; import java.util.Collections; @@ -68,10 +69,7 @@ public void testPodTemplateIsMerged() { var keycloakPod = k8sclient .pods() .inNamespace(namespace) - .withLabel("app", "keycloak") - .list() - .getItems() - .get(0); + .withName("example-podtemplate-kc-0").get(); var logs = k8sclient .pods() From 8ae7c8ff3a4f5992cf6add3fc201ba485609169d Mon Sep 17 00:00:00 2001 From: Alexander Schwartz Date: Thu, 25 Apr 2024 17:13:39 +0200 Subject: [PATCH 102/158] Moving admin user creation to the Quarkus startup phase Closes #29072 Signed-off-by: Alexander Schwartz --- .../quarkus/runtime/KeycloakMain.java | 32 ------------------ .../jaxrs/QuarkusKeycloakApplication.java | 33 ++++++++++++++++++- 2 files changed, 32 insertions(+), 33 deletions(-) diff --git a/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/KeycloakMain.java b/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/KeycloakMain.java index f91e3670758c..ca76823fe968 100644 --- a/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/KeycloakMain.java +++ b/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/KeycloakMain.java @@ -37,17 +37,11 @@ import io.quarkus.runtime.Quarkus; import org.jboss.logging.Logger; -import org.keycloak.Config; -import org.keycloak.models.KeycloakSessionFactory; -import org.keycloak.models.utils.KeycloakModelUtils; import org.keycloak.quarkus.runtime.cli.ExecutionExceptionHandler; import org.keycloak.quarkus.runtime.cli.PropertyException; import org.keycloak.quarkus.runtime.cli.Picocli; import org.keycloak.common.Version; import org.keycloak.quarkus.runtime.cli.command.Start; -import org.keycloak.services.ServicesLogger; -import org.keycloak.services.managers.ApplianceBootstrap; -import org.keycloak.services.resources.KeycloakApplication; import io.quarkus.runtime.QuarkusApplication; import io.quarkus.runtime.annotations.QuarkusMain; @@ -59,9 +53,6 @@ @ApplicationScoped public class KeycloakMain implements QuarkusApplication { - private static final String KEYCLOAK_ADMIN_ENV_VAR = "KEYCLOAK_ADMIN"; - private static final String KEYCLOAK_ADMIN_PASSWORD_ENV_VAR = "KEYCLOAK_ADMIN_PASSWORD"; - public static void main(String[] args) { System.setProperty("kc.version", Version.VERSION); List cliArgs = null; @@ -140,10 +131,6 @@ public static void start(ExecutionExceptionHandler errorHandler, PrintWriter err */ @Override public int run(String... args) throws Exception { - if (!isImportExportMode()) { - createAdminUser(); - } - if (isDevProfile()) { Logger.getLogger(KeycloakMain.class).warnf("Running the server in development mode. DO NOT use this configuration in production."); } @@ -161,23 +148,4 @@ public int run(String... args) throws Exception { return exitCode; } - private void createAdminUser() { - String adminUserName = System.getenv(KEYCLOAK_ADMIN_ENV_VAR); - String adminPassword = System.getenv(KEYCLOAK_ADMIN_PASSWORD_ENV_VAR); - - if ((adminUserName == null || adminUserName.trim().length() == 0) - || (adminPassword == null || adminPassword.trim().length() == 0)) { - return; - } - - KeycloakSessionFactory sessionFactory = KeycloakApplication.getSessionFactory(); - - try { - KeycloakModelUtils.runJobInTransaction(sessionFactory, session -> { - new ApplianceBootstrap(session).createMasterRealmUser(adminUserName, adminPassword); - }); - } catch (Throwable t) { - ServicesLogger.LOGGER.addUserFailed(t, adminUserName, Config.getAdminRealm()); - } - } } diff --git a/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/integration/jaxrs/QuarkusKeycloakApplication.java b/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/integration/jaxrs/QuarkusKeycloakApplication.java index 18840350d7ab..5a0a8773ae96 100644 --- a/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/integration/jaxrs/QuarkusKeycloakApplication.java +++ b/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/integration/jaxrs/QuarkusKeycloakApplication.java @@ -20,14 +20,17 @@ import java.util.HashSet; import java.util.Set; +import org.keycloak.Config; import jakarta.enterprise.event.Observes; import jakarta.ws.rs.ApplicationPath; -import org.keycloak.config.HostnameOptions; import org.keycloak.models.KeycloakSessionFactory; +import org.keycloak.models.utils.KeycloakModelUtils; import org.keycloak.platform.Platform; import org.keycloak.quarkus.runtime.integration.QuarkusKeycloakSessionFactory; import org.keycloak.quarkus.runtime.integration.QuarkusPlatform; +import org.keycloak.services.ServicesLogger; +import org.keycloak.services.managers.ApplianceBootstrap; import org.keycloak.quarkus.runtime.services.resources.DebugHostnameSettingsResource; import org.keycloak.services.resources.KeycloakApplication; @@ -35,15 +38,23 @@ import io.quarkus.runtime.StartupEvent; import io.smallrye.common.annotation.Blocking; +import static org.keycloak.quarkus.runtime.Environment.isImportExportMode; + @ApplicationPath("/") @Blocking public class QuarkusKeycloakApplication extends KeycloakApplication { + private static final String KEYCLOAK_ADMIN_ENV_VAR = "KEYCLOAK_ADMIN"; + private static final String KEYCLOAK_ADMIN_PASSWORD_ENV_VAR = "KEYCLOAK_ADMIN_PASSWORD"; + void onStartupEvent(@Observes StartupEvent event) { QuarkusPlatform platform = (QuarkusPlatform) Platform.getPlatform(); platform.started(); QuarkusPlatform.exitOnError(); startup(); + if (!isImportExportMode()) { + createAdminUser(); + } } void onShutdownEvent(@Observes ShutdownEvent event) { @@ -62,6 +73,26 @@ protected void loadConfig() { // no need to load config provider because we force quarkus impl } + private void createAdminUser() { + String adminUserName = System.getenv(KEYCLOAK_ADMIN_ENV_VAR); + String adminPassword = System.getenv(KEYCLOAK_ADMIN_PASSWORD_ENV_VAR); + + if ((adminUserName == null || adminUserName.trim().length() == 0) + || (adminPassword == null || adminPassword.trim().length() == 0)) { + return; + } + + KeycloakSessionFactory sessionFactory = KeycloakApplication.getSessionFactory(); + + try { + KeycloakModelUtils.runJobInTransaction(sessionFactory, session -> { + new ApplianceBootstrap(session).createMasterRealmUser(adminUserName, adminPassword); + }); + } catch (Throwable t) { + ServicesLogger.LOGGER.addUserFailed(t, adminUserName, Config.getAdminRealm()); + } + } + @Override public Set getSingletons() { return Set.of(); From 5cf920fcdc506d86a8a6c2236562f7306ad3fd7e Mon Sep 17 00:00:00 2001 From: Alexander Schwartz Date: Mon, 29 Apr 2024 16:47:16 +0200 Subject: [PATCH 103/158] Fix product name usage for downstream documentation Closes #29154 Signed-off-by: Alexander Schwartz --- .../concepts-infinispan-cli-batch.adoc | 2 +- .../high-availability/deploy-aurora-multi-az.adoc | 2 +- .../deploy-aws-route53-loadbalancer.adoc | 4 ++-- .../deploy-infinispan-kubernetes-crossdc.adoc | 12 ++++++------ docs/guides/high-availability/introduction.adoc | 2 +- .../partials/infinispan/infinispan-attributes.adoc | 2 +- .../partials/infinispan/infinispan-credentials.adoc | 4 ++-- .../infinispan/infinispan-install-operator.adoc | 2 +- .../infinispan/infinispan-prerequisites.adoc | 2 +- 9 files changed, 16 insertions(+), 16 deletions(-) diff --git a/docs/guides/high-availability/concepts-infinispan-cli-batch.adoc b/docs/guides/high-availability/concepts-infinispan-cli-batch.adoc index 51902fc456aa..08b3735e06e8 100644 --- a/docs/guides/high-availability/concepts-infinispan-cli-batch.adoc +++ b/docs/guides/high-availability/concepts-infinispan-cli-batch.adoc @@ -49,6 +49,6 @@ NOTE: Modifying a `Batch` CR instance has no effect. Batch operations are "`one- == Further reading -For more information, see the link:{infinispan-operator-docs}#batch-cr[{jdgserver_name} Operator Batch CR documentation]. +For more information, see the link:{infinispan-operator-docs}#batch-cr[{jdgserver_name} Operator `Batch` CR documentation]. diff --git a/docs/guides/high-availability/deploy-aurora-multi-az.adoc b/docs/guides/high-availability/deploy-aurora-multi-az.adoc index 8f0a7ccd6566..c8f0eb0625e7 100644 --- a/docs/guides/high-availability/deploy-aurora-multi-az.adoc +++ b/docs/guides/high-availability/deploy-aurora-multi-az.adoc @@ -52,7 +52,7 @@ include::partials/aurora/aurora-verify-peering-connections.adoc[] == Deploying {project_name} Now that an Aurora database has been established and linked with all of your ROSA clusters, the next step is to deploy {project_name} as described in the <@links.ha id="deploy-keycloak-kubernetes" /> {section} with the JDBC url configured to use the Aurora database writer endpoint. -To do this, create a `{project_name}` CR with the following adjustments: +To do this, create a `Keycloak` CR with the following adjustments: . Update `spec.db.url` to be `jdbc:aws-wrapper:postgresql://$HOST:5432/keycloak` where `$HOST` is the <>. diff --git a/docs/guides/high-availability/deploy-aws-route53-loadbalancer.adoc b/docs/guides/high-availability/deploy-aws-route53-loadbalancer.adoc index 32922cf889cc..86d22453d33d 100644 --- a/docs/guides/high-availability/deploy-aws-route53-loadbalancer.adoc +++ b/docs/guides/high-availability/deploy-aws-route53-loadbalancer.adoc @@ -217,13 +217,13 @@ For both the Primary and Backup cluster, perform the following steps: + .. Log in to the ROSA cluster + -.. Ensure the {project_name} CR has the following configuration +.. Ensure the `Keycloak` CR has the following configuration + [source,yaml] ---- <#noparse> apiVersion: k8s.keycloak.org/v2alpha1 -kind: {project_name} +kind: Keycloak metadata: name: keycloak spec: diff --git a/docs/guides/high-availability/deploy-infinispan-kubernetes-crossdc.adoc b/docs/guides/high-availability/deploy-infinispan-kubernetes-crossdc.adoc index 1b37f755f8ac..ac945bf09200 100644 --- a/docs/guides/high-availability/deploy-infinispan-kubernetes-crossdc.adoc +++ b/docs/guides/high-availability/deploy-infinispan-kubernetes-crossdc.adoc @@ -143,7 +143,7 @@ The {infinispan-operator-docs}#setting-up-xsite[Setting Up Cross-Site] documenta + A basic example is provided in this {section} using the credentials, tokens, and TLS Keystore/Truststore created by the commands from the previous steps. + -.The {jdgserver_name} CR for `{site-a}` +.The `Infinispan` CR for `{site-a}` [source,yaml] ---- include::examples/generated/ispn-site-a.yaml[tag=infinispan-crossdc] @@ -163,10 +163,10 @@ include::examples/generated/ispn-site-a.yaml[tag=infinispan-crossdc] <13> The {ocp} API URL for the remote site. <14> The secret with the access toke to authenticate into the remote site. + -For `{site-b}`, the {jdgserver_name} CR looks similar to the above. +For `{site-b}`, the `Infinispan` CR looks similar to the above. Note the differences in point 4, 11 and 13. + -.The {jdgserver_name} CR for `{site-b}` +.The `Infinispan` CR for `{site-b}` [source,yaml] ---- include::examples/generated/ispn-site-b.yaml[tag=infinispan-crossdc] @@ -179,7 +179,7 @@ include::examples/generated/ispn-site-b.yaml[tag=infinispan-crossdc] The {jdgserver_name} {infinispan-operator-docs}#creating-caches[Cache CR] allows deploying the caches in the {jdgserver_name} cluster. Cross-site needs to be enabled per cache as documented by {infinispan-xsite-docs}[Cross Site Documentation]. The documentation contains more details about the options used by this {section}. -The following example shows the Cache CR for `{site-a}`. +The following example shows the `Cache` CR for `{site-a}`. + .sessions in `{site-a}` [source,yaml] @@ -191,7 +191,7 @@ Set this for the caches `sessions`, `authenticationSessions`, `offlineSessions`, <2> The remote site name. <3> The cross-site communication, in this case, `SYNC`. + -For `{site-b}`, the Cache CR is similar except in point 2. +For `{site-b}`, the `Cache` CR is similar except in point 2. + .session in `{site-b}` [source,yaml] @@ -217,6 +217,6 @@ kubectl wait --for condition=CrossSiteViewFormed --timeout=300s infinispans.infi == Next steps -After infinispan is deployed and running, use the procedure in the <@links.ha id="connect-keycloak-to-external-infinispan"/> {section} to connect your {project_name} cluster with the {jdgserver_name} cluster. +After {jdgserver_name} is deployed and running, use the procedure in the <@links.ha id="connect-keycloak-to-external-infinispan"/> {section} to connect your {project_name} cluster with the {jdgserver_name} cluster. diff --git a/docs/guides/high-availability/introduction.adoc b/docs/guides/high-availability/introduction.adoc index f4296d03eb0e..bfbf02d28515 100644 --- a/docs/guides/high-availability/introduction.adoc +++ b/docs/guides/high-availability/introduction.adoc @@ -5,7 +5,7 @@ title="Multi-site deployments" summary="Connect multiple {project_name} deployments in different sites to increase the overall availability" > -{project_name} supports deployments that consist of multiple {project_name} instances that connect to each other using its embedded Infinispan; load balancers can distribute the load evenly across those instances. +{project_name} supports deployments that consist of multiple {project_name} instances that connect to each other using its Infinispan caches; load balancers can distribute the load evenly across those instances. Those setups are intended for a transparent network on a single site. The {project_name} high-availability guide goes one step further to describe setups across multiple sites. diff --git a/docs/guides/high-availability/partials/infinispan/infinispan-attributes.adoc b/docs/guides/high-availability/partials/infinispan/infinispan-attributes.adoc index 081f39907abc..686f3bda5738 100644 --- a/docs/guides/high-availability/partials/infinispan/infinispan-attributes.adoc +++ b/docs/guides/high-availability/partials/infinispan/infinispan-attributes.adoc @@ -20,6 +20,6 @@ // Other common attributes :ocp: OpenShift -:ispn-operator: Infinispan Operator +:ispn-operator: {jdgserver_name} Operator :site-a: Site-A :site-b: Site-B diff --git a/docs/guides/high-availability/partials/infinispan/infinispan-credentials.adoc b/docs/guides/high-availability/partials/infinispan/infinispan-credentials.adoc index e03d78b46c60..86d0d0b99bc0 100644 --- a/docs/guides/high-availability/partials/infinispan/infinispan-credentials.adoc +++ b/docs/guides/high-availability/partials/infinispan/infinispan-credentials.adoc @@ -1,7 +1,7 @@ [[infinispan-credentials]] -. Configure the credential to access the Infinispan cluster. +. Configure the credential to access the {jdgserver_name} cluster. + -{project_name} needs this credential to be able to authenticate with the Infinispan cluster. +{project_name} needs this credential to be able to authenticate with the {jdgserver_name} cluster. The following `identities.yaml` file sets the username and password with admin permissions + [source,yaml,subs="+attributes"] diff --git a/docs/guides/high-availability/partials/infinispan/infinispan-install-operator.adoc b/docs/guides/high-availability/partials/infinispan/infinispan-install-operator.adoc index 1f60df0e79d2..906c333d33ef 100644 --- a/docs/guides/high-availability/partials/infinispan/infinispan-install-operator.adoc +++ b/docs/guides/high-availability/partials/infinispan/infinispan-install-operator.adoc @@ -1 +1 @@ -. Install the https://infinispan.org/docs/infinispan-operator/main/operator.html#installation[Infinispan Operator] +. Install the {infinispan-operator-docs}#installation[{jdgserver_name} Operator] diff --git a/docs/guides/high-availability/partials/infinispan/infinispan-prerequisites.adoc b/docs/guides/high-availability/partials/infinispan/infinispan-prerequisites.adoc index c490694eda4f..4aacec9d0963 100644 --- a/docs/guides/high-availability/partials/infinispan/infinispan-prerequisites.adoc +++ b/docs/guides/high-availability/partials/infinispan/infinispan-prerequisites.adoc @@ -1,2 +1,2 @@ * OpenShift or Kubernetes cluster running -* Understanding of the https://infinispan.org/docs/infinispan-operator/main/operator.html[Infinispan Operator] +* Understanding of the {infinispan-operator-docs}[{jdgserver_name} Operator] From 80f13803d1f11c7a437e91a69f9a12b9a8e134d3 Mon Sep 17 00:00:00 2001 From: Erik Jan de Wit Date: Tue, 30 Apr 2024 09:00:39 +0200 Subject: [PATCH 104/158] fixed change calculation (#28350) fixes: #28187 Signed-off-by: Erik Jan de Wit --- .../src/authentication/execution-model.ts | 25 +++++++++---------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/js/apps/admin-ui/src/authentication/execution-model.ts b/js/apps/admin-ui/src/authentication/execution-model.ts index 89e063c059d2..672fbfd00d18 100644 --- a/js/apps/admin-ui/src/authentication/execution-model.ts +++ b/js/apps/admin-ui/src/authentication/execution-model.ts @@ -98,17 +98,15 @@ export class ExecutionList { return found; } - #getParentNodes(level?: number) { - for (let index = 0; index < this.#list.length; index++) { - const ex = this.#list[index]; - if ( - index + 1 < this.#list.length && - this.#list[index + 1].level! > ex.level! && - ex.level! + 1 === level - ) { - return ex; + #getParentNodes(level: number, index: number) { + let parent = undefined; + for (let i = 0; i < index; i++) { + const ex = this.#list[i]; + if (level - 1 === ex.level) { + parent = ex; } } + return parent; } getChange( @@ -117,13 +115,14 @@ export class ExecutionList { ) { const currentOrder = this.order(); const newLocIndex = order.findIndex((id) => id === changed.id); - const oldLocation = - currentOrder[currentOrder.findIndex((ex) => ex.id === changed.id)]; + const oldLocIndex = currentOrder.findIndex((ex) => ex.id === changed.id); + const oldLocation = currentOrder[oldLocIndex]; const newLocation = currentOrder[newLocIndex]; - if (newLocation.level !== oldLocation.level) { + const currentParent = this.#getParentNodes(oldLocation.level!, oldLocIndex); + const parent = this.#getParentNodes(newLocation.level!, newLocIndex); + if (currentParent?.id !== parent?.id) { if (newLocation.level! > 0) { - const parent = this.#getParentNodes(newLocation.level); return new LevelChange( parent?.executionList?.length || 0, newLocation.index!, From e70173c4567eb4bb5a01c78b258812828ae10d8d Mon Sep 17 00:00:00 2001 From: Erik Jan de Wit Date: Tue, 30 Apr 2024 09:01:04 +0200 Subject: [PATCH 105/158] filter out parent group if it doesn't match (#28397) fixes: #28079 Signed-off-by: Erik Jan de Wit --- .../components/group/GroupPickerDialog.tsx | 26 ++++++++++--------- 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/js/apps/admin-ui/src/components/group/GroupPickerDialog.tsx b/js/apps/admin-ui/src/components/group/GroupPickerDialog.tsx index ad7fbb3429fd..6980434f44f2 100644 --- a/js/apps/admin-ui/src/components/group/GroupPickerDialog.tsx +++ b/js/apps/admin-ui/src/components/group/GroupPickerDialog.tsx @@ -226,18 +226,20 @@ export const GroupPickerDialog = ({ .slice(groupId ? first : 0, max + (groupId ? first : 0)) .map((group: SelectableGroup) => ( - + {(!isSearching || group.name?.includes(filter)) && ( + + )} {isSearching && group.subGroups?.map((g) => ( Date: Tue, 30 Apr 2024 12:31:10 +0200 Subject: [PATCH 106/158] Filter dedicated client scopes when searching (#28433) (#28468) Closes #28431 Signed-off-by: Jon Koops (cherry picked from commit 7cbe6095711a523f4902983984fdaf7d69868cc2) --- .../admin-ui/cypress/e2e/clients_test.spec.ts | 4 ++-- .../cypress/e2e/realm_roles_test.spec.ts | 2 +- .../e2e/user_fed_ldap_mapper_test.spec.ts | 2 +- js/apps/admin-ui/cypress/e2e/users_test.spec.ts | 2 +- .../support/pages/admin-ui/ListingPage.ts | 6 +++++- .../pages/admin-ui/components/TablePage.ts | 8 +++----- .../admin-ui/src/clients/scopes/ClientScopes.tsx | 16 +++++++++------- 7 files changed, 22 insertions(+), 18 deletions(-) diff --git a/js/apps/admin-ui/cypress/e2e/clients_test.spec.ts b/js/apps/admin-ui/cypress/e2e/clients_test.spec.ts index a245740e4381..082774290687 100644 --- a/js/apps/admin-ui/cypress/e2e/clients_test.spec.ts +++ b/js/apps/admin-ui/cypress/e2e/clients_test.spec.ts @@ -102,7 +102,7 @@ describe("Clients test", () => { commonPage .tableUtils() .checkRowItemExists(clientScopeName + 0) - .checkRowItemsEqualTo(2); + .checkRowItemsEqualTo(1); }); it("Should search non-existent client scope by name", () => { @@ -187,7 +187,7 @@ describe("Clients test", () => { commonPage.modalUtils().confirmModal(); commonPage.masthead().checkNotificationMessage(msgScopeMappingRemoved); commonPage.tableToolbarUtils().searchItem(itemName, false); - commonPage.tableUtils().checkRowItemExists(itemName, false); + listingPage.assertNoResults(); }); it("Should remove multiple client scopes from search bar", () => { diff --git a/js/apps/admin-ui/cypress/e2e/realm_roles_test.spec.ts b/js/apps/admin-ui/cypress/e2e/realm_roles_test.spec.ts index a354aa8a1d6c..b53de502a482 100644 --- a/js/apps/admin-ui/cypress/e2e/realm_roles_test.spec.ts +++ b/js/apps/admin-ui/cypress/e2e/realm_roles_test.spec.ts @@ -151,7 +151,7 @@ describe("Realm roles test", () => { it("Should search non-existent associated role by name", () => { const itemName = "non-existent-associated-role"; listingPage.searchItem(itemName, false); - cy.findByTestId(listingPage.emptyState).should("exist"); + listingPage.assertNoResults(); }); it("Should hide inherited roles test", () => { diff --git a/js/apps/admin-ui/cypress/e2e/user_fed_ldap_mapper_test.spec.ts b/js/apps/admin-ui/cypress/e2e/user_fed_ldap_mapper_test.spec.ts index c0df367fe065..737c4323351c 100644 --- a/js/apps/admin-ui/cypress/e2e/user_fed_ldap_mapper_test.spec.ts +++ b/js/apps/admin-ui/cypress/e2e/user_fed_ldap_mapper_test.spec.ts @@ -264,7 +264,7 @@ describe("User Fed LDAP mapper tests", () => { providersPage.clickExistingCard(ldapName); providersPage.goToMappers(); listingPage.searchItem(nonexistingSearchTerm, false); - cy.findByTestId(listingPage.emptyState).should("exist"); + listingPage.assertNoResults(); }); // *** test cleanup *** diff --git a/js/apps/admin-ui/cypress/e2e/users_test.spec.ts b/js/apps/admin-ui/cypress/e2e/users_test.spec.ts index 3f019a2af9d7..a9e9eb080c21 100644 --- a/js/apps/admin-ui/cypress/e2e/users_test.spec.ts +++ b/js/apps/admin-ui/cypress/e2e/users_test.spec.ts @@ -127,7 +127,7 @@ describe("User creation", () => { it("Search non-existing user test", () => { listingPage.searchItem("user_DNE"); - cy.findByTestId(listingPage.emptyState).should("exist"); + listingPage.assertNoResults(); }); it("User details test", () => { diff --git a/js/apps/admin-ui/cypress/support/pages/admin-ui/ListingPage.ts b/js/apps/admin-ui/cypress/support/pages/admin-ui/ListingPage.ts index 1f10227eebb1..5d05ebde4a5b 100644 --- a/js/apps/admin-ui/cypress/support/pages/admin-ui/ListingPage.ts +++ b/js/apps/admin-ui/cypress/support/pages/admin-ui/ListingPage.ts @@ -34,7 +34,7 @@ export default class ListingPage extends CommonElements { #itemsRows = "table:visible"; #deleteUserButton = "delete-user-btn"; #emptyListImg = '[role="tabpanel"]:not([hidden]) [data-testid="empty-state"]'; - public emptyState = "empty-state"; + #emptyState = "empty-state"; #itemRowDrpDwn = ".pf-c-dropdown__toggle"; #itemRowSelect = ".pf-c-select__toggle:nth-child(1)"; #itemRowSelectItem = ".pf-c-select__menu-item"; @@ -401,6 +401,10 @@ export default class ListingPage extends CommonElements { return this; } + assertNoResults() { + cy.findByTestId(this.#emptyState).should("exist"); + } + assertDefaultResource() { this.assertResource("Default Resource"); return this; diff --git a/js/apps/admin-ui/cypress/support/pages/admin-ui/components/TablePage.ts b/js/apps/admin-ui/cypress/support/pages/admin-ui/components/TablePage.ts index 9b42844ff915..9de6fd45cb06 100644 --- a/js/apps/admin-ui/cypress/support/pages/admin-ui/components/TablePage.ts +++ b/js/apps/admin-ui/cypress/support/pages/admin-ui/components/TablePage.ts @@ -5,14 +5,12 @@ export default class TablePage extends CommonElements { #tableRowItemChckBx: string; #tableHeaderRowItem: string; #tableInModal: boolean; - static tableSelector = "table[aria-label]"; + static tableSelector = ".pf-c-table"; constructor(parentElement?: string) { super(parentElement ?? TablePage.tableSelector + ":visible"); - this.#tableRowItem = - this.parentSelector + "tbody tr[data-ouia-component-type]"; - this.#tableHeaderRowItem = - this.parentSelector + "thead tr[data-ouia-component-type]"; + this.#tableRowItem = this.parentSelector + "tbody tr"; + this.#tableHeaderRowItem = this.parentSelector + "thead tr"; this.#tableRowItemChckBx = ".pf-c-table__check"; this.#tableInModal = false; } diff --git a/js/apps/admin-ui/src/clients/scopes/ClientScopes.tsx b/js/apps/admin-ui/src/clients/scopes/ClientScopes.tsx index b4c69a885a73..45f0901c0be5 100644 --- a/js/apps/admin-ui/src/clients/scopes/ClientScopes.tsx +++ b/js/apps/admin-ui/src/clients/scopes/ClientScopes.tsx @@ -165,21 +165,19 @@ export const ClientScopes = ({ return row; }); - const rows = [...optional, ...defaultScopes]; + let rows = [...optional, ...defaultScopes]; const names = rows.map((row) => row.name); + setRest( clientScopes .filter((scope) => !names.includes(scope.name)) .filter((scope) => scope.protocol === protocol), ); - const filter = - searchType === "name" ? nameFilter(search) : typeFilter(searchTypeType); - const firstNum = Number(first); - const page = localeSort(rows.filter(filter), mapByKey("name")); + rows = localeSort(rows, mapByKey("name")); if (isViewer) { - page.unshift({ + rows.unshift({ id: DEDICATED_ROW, name: t("dedicatedScopeName", { clientName }), type: AllClientScopes.none, @@ -187,7 +185,11 @@ export const ClientScopes = ({ }); } - return page.slice(firstNum, firstNum + Number(max)); + const filter = + searchType === "name" ? nameFilter(search) : typeFilter(searchTypeType); + const firstNum = Number(first); + + return rows.filter(filter).slice(firstNum, firstNum + Number(max)); }; const [toggleDeleteDialog, DeleteConfirm] = useConfirmDialog({ From 3f2951dcd51a1d318cb0d0c6be2269adb3165a6d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=A1clav=20Muzik=C3=A1=C5=99?= Date: Thu, 2 May 2024 09:36:03 +0200 Subject: [PATCH 107/158] Upgrade to Quarkus 3.8.4 (#28885) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * quarkus-next: java.util.NoSuchElementException: No value present causes quarkus-server build failure (#28857) * resolveFileLogLocation transformer method now checks the location value presence Closes: #28856 Signed-off-by: Peter Zaoral (cherry picked from commit f9e68cdc541a9f638ba16bab02bfdb2465d91af0) * Upgrade to Quarkus 3.8.4 Closes #28880 Signed-off-by: Václav Muzikář * Make PropertyMapper to use Keycloak options' default values (#29030) * improved a condition in PropertyMapper.java Related to: #28856 Signed-off-by: Peter Zaoral (cherry picked from commit 86b280349b17aa401308c5a4ef21e726d795233f) --------- Signed-off-by: Václav Muzikář Co-authored-by: Peter Zaoral --- pom.xml | 4 ++-- .../configuration/mappers/LoggingPropertyMappers.java | 8 +------- .../runtime/configuration/mappers/PropertyMapper.java | 2 +- 3 files changed, 4 insertions(+), 10 deletions(-) diff --git a/pom.xml b/pom.xml index 0379b0805f1d..85b4cb56cfbe 100644 --- a/pom.xml +++ b/pom.xml @@ -45,8 +45,8 @@ jboss-snapshots-repository https://s01.oss.sonatype.org/content/repositories/snapshots/ - 3.8.3 - 3.8.3 + 3.8.4 + 3.8.4 ${timestamp} diff --git a/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/configuration/mappers/LoggingPropertyMappers.java b/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/configuration/mappers/LoggingPropertyMappers.java index 965ee4853a20..dd155eb38a71 100644 --- a/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/configuration/mappers/LoggingPropertyMappers.java +++ b/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/configuration/mappers/LoggingPropertyMappers.java @@ -151,13 +151,7 @@ private static BiFunction, ConfigSourceInterceptorContext, Opti } private static Optional resolveFileLogLocation(Optional value, ConfigSourceInterceptorContext configSourceInterceptorContext) { - String location = value.get(); - - if (location.endsWith(File.separator)) { - return of(location + LoggingOptions.DEFAULT_LOG_FILENAME); - } - - return value; + return value.map(location -> location.endsWith(File.separator) ? location + LoggingOptions.DEFAULT_LOG_FILENAME : location); } private static Level toLevel(String categoryLevel) throws IllegalArgumentException { diff --git a/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/configuration/mappers/PropertyMapper.java b/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/configuration/mappers/PropertyMapper.java index cbd7093e37e1..e9651c66b6e3 100644 --- a/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/configuration/mappers/PropertyMapper.java +++ b/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/configuration/mappers/PropertyMapper.java @@ -104,7 +104,7 @@ ConfigValue getConfigValue(String name, ConfigSourceInterceptorContext context) // try to obtain the value for the property we want to map first ConfigValue config = convertValue(context.proceed(from)); - if (config == null) { + if (config == null || config.getValue() == null) { if (mapFrom != null) { // if the property we want to map depends on another one, we use the value from the other property to call the mapper String parentKey = MicroProfileConfigProvider.NS_KEYCLOAK_PREFIX + mapFrom; From 2b6c331097c22461efde6a8e097ecaa739c1db5a Mon Sep 17 00:00:00 2001 From: Pedro Ruivo Date: Thu, 25 Apr 2024 09:16:37 +0200 Subject: [PATCH 108/158] Use cache.compute() method to improve the replace retry loop This commit only adds the ReplaceFunction to it can be backwards compatible. Closes #29073 Signed-off-by: Pedro Ruivo --- ...ltInfinispanConnectionProviderFactory.java | 11 +-- .../org/keycloak/marshalling/Marshalling.java | 25 +++++++ .../infinispan/changes/ReplaceFunction.java | 73 +++++++++++++++++++ .../infinispan/CacheManagerFactory.java | 8 +- 4 files changed, 102 insertions(+), 15 deletions(-) create mode 100644 model/infinispan/src/main/java/org/keycloak/marshalling/Marshalling.java create mode 100644 model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/changes/ReplaceFunction.java diff --git a/model/infinispan/src/main/java/org/keycloak/connections/infinispan/DefaultInfinispanConnectionProviderFactory.java b/model/infinispan/src/main/java/org/keycloak/connections/infinispan/DefaultInfinispanConnectionProviderFactory.java index 95a4652b8223..659ffb464a12 100755 --- a/model/infinispan/src/main/java/org/keycloak/connections/infinispan/DefaultInfinispanConnectionProviderFactory.java +++ b/model/infinispan/src/main/java/org/keycloak/connections/infinispan/DefaultInfinispanConnectionProviderFactory.java @@ -25,7 +25,6 @@ import org.infinispan.configuration.global.GlobalConfigurationBuilder; import org.infinispan.eviction.EvictionStrategy; import org.infinispan.eviction.EvictionType; -import org.infinispan.jboss.marshalling.core.JBossUserMarshaller; import org.infinispan.manager.DefaultCacheManager; import org.infinispan.manager.EmbeddedCacheManager; import org.infinispan.persistence.remote.configuration.RemoteStoreConfigurationBuilder; @@ -38,6 +37,7 @@ import org.keycloak.cluster.ClusterProvider; import org.keycloak.cluster.ManagedCacheManagerProvider; import org.keycloak.cluster.infinispan.KeycloakHotRodMarshallerFactory; +import org.keycloak.marshalling.Marshalling; import org.keycloak.models.KeycloakSession; import org.keycloak.models.KeycloakSessionFactory; import org.keycloak.models.cache.infinispan.ClearCacheEvent; @@ -46,12 +46,9 @@ import org.keycloak.models.utils.KeycloakModelUtils; import org.keycloak.models.utils.PostMigrationEvent; import org.keycloak.provider.InvalidationHandler.ObjectType; -import org.keycloak.provider.ProviderConfigProperty; -import org.keycloak.provider.ProviderConfigurationBuilder; import org.keycloak.provider.ProviderEvent; import java.util.Iterator; -import java.util.List; import java.util.ServiceLoader; import java.util.concurrent.TimeUnit; import java.util.concurrent.locks.Lock; @@ -254,11 +251,7 @@ protected EmbeddedCacheManager initEmbedded() { gcb.jmx().domain(InfinispanConnectionProvider.JMX_DOMAIN).enable(); } - // For Infinispan 10, we go with the JBoss marshalling. - // TODO: This should be replaced later with the marshalling recommended by infinispan. Probably protostream. - // See https://infinispan.org/docs/stable/titles/developing/developing.html#marshalling for the details - gcb.serialization().marshaller(new JBossUserMarshaller()); - + Marshalling.configure(gcb); EmbeddedCacheManager cacheManager = new DefaultCacheManager(gcb.build()); if (useKeycloakTimeService) { setTimeServiceToKeycloakTime(cacheManager); diff --git a/model/infinispan/src/main/java/org/keycloak/marshalling/Marshalling.java b/model/infinispan/src/main/java/org/keycloak/marshalling/Marshalling.java new file mode 100644 index 000000000000..e710f09def92 --- /dev/null +++ b/model/infinispan/src/main/java/org/keycloak/marshalling/Marshalling.java @@ -0,0 +1,25 @@ +package org.keycloak.marshalling; + +import org.infinispan.configuration.global.GlobalConfigurationBuilder; +import org.infinispan.jboss.marshalling.core.JBossUserMarshaller; +import org.keycloak.models.sessions.infinispan.changes.ReplaceFunction; + +@SuppressWarnings("removal") +public final class Marshalling { + + private Marshalling() { + } + + // Note: Min ID is 2500 + public static final Integer REPLACE_FUNCTION_ID = 2500; + + // For Infinispan 10, we go with the JBoss marshalling. + // TODO: This should be replaced later with the marshalling recommended by infinispan. Probably protostream. + // See https://infinispan.org/docs/stable/titles/developing/developing.html#marshalling for the details + public static void configure(GlobalConfigurationBuilder builder) { + builder.serialization() + .marshaller(new JBossUserMarshaller()) + .addAdvancedExternalizer(ReplaceFunction.INSTANCE); + } + +} diff --git a/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/changes/ReplaceFunction.java b/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/changes/ReplaceFunction.java new file mode 100644 index 000000000000..10aad0343329 --- /dev/null +++ b/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/changes/ReplaceFunction.java @@ -0,0 +1,73 @@ +package org.keycloak.models.sessions.infinispan.changes; + +import java.io.IOException; +import java.io.ObjectInput; +import java.io.ObjectOutput; +import java.util.Objects; +import java.util.Set; +import java.util.UUID; +import java.util.function.BiFunction; + +import org.infinispan.commons.marshall.AdvancedExternalizer; +import org.infinispan.commons.marshall.MarshallUtil; +import org.keycloak.marshalling.Marshalling; +import org.keycloak.models.sessions.infinispan.entities.SessionEntity; + +/** + * Performs an entity replacement in Infinispan, using its versions instead of equality. + * + * @param The Infinispan key type. + * @param The Infinispan value type (Keycloak entity) + */ +public class ReplaceFunction implements BiFunction, SessionEntityWrapper> { + + @SuppressWarnings({"removal", "rawtypes"}) + public static final AdvancedExternalizer INSTANCE = new Externalizer(); + private final UUID expectedVersion; + private final SessionEntityWrapper newValue; + + public ReplaceFunction(UUID expectedVersion, SessionEntityWrapper newValue) { + this.expectedVersion = Objects.requireNonNull(expectedVersion); + this.newValue = Objects.requireNonNull(newValue); + } + + @Override + public SessionEntityWrapper apply(K key, SessionEntityWrapper currentValue) { + assert currentValue != null; + return expectedVersion.equals(currentValue.getVersion()) ? newValue : currentValue; + } + + @SuppressWarnings({"removal", "rawtypes"}) + private static class Externalizer implements AdvancedExternalizer { + + private static final SessionEntityWrapper.ExternalizerImpl EXTERNALIZER = new SessionEntityWrapper.ExternalizerImpl(); + private static final byte VERSION_1 = 1; + + @Override + public Set> getTypeClasses() { + return Set.of(ReplaceFunction.class); + } + + @Override + public Integer getId() { + return Marshalling.REPLACE_FUNCTION_ID; + } + + @Override + public void writeObject(ObjectOutput output, ReplaceFunction object) throws IOException { + output.writeByte(VERSION_1); + MarshallUtil.marshallUUID(object.expectedVersion, output, false); + EXTERNALIZER.writeObject(output, object.newValue); + } + + @Override + public ReplaceFunction readObject(ObjectInput input) throws IOException, ClassNotFoundException { + var version = input.readByte(); + if (version != VERSION_1) { + throw new IOException("Invalid version: " + version); + } + //noinspection unchecked + return new ReplaceFunction(MarshallUtil.unmarshallUUID(input, false), EXTERNALIZER.readObject(input)); + } + } +} diff --git a/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/storage/legacy/infinispan/CacheManagerFactory.java b/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/storage/legacy/infinispan/CacheManagerFactory.java index 025e2ef84654..5feba1cb37c9 100644 --- a/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/storage/legacy/infinispan/CacheManagerFactory.java +++ b/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/storage/legacy/infinispan/CacheManagerFactory.java @@ -30,7 +30,6 @@ import org.infinispan.configuration.global.GlobalConfiguration; import org.infinispan.configuration.parsing.ConfigurationBuilderHolder; import org.infinispan.configuration.parsing.ParserRegistry; -import org.infinispan.jboss.marshalling.core.JBossUserMarshaller; import org.infinispan.manager.DefaultCacheManager; import org.infinispan.metrics.config.MicrometerMeterRegisterConfigurationBuilder; import org.infinispan.persistence.remote.configuration.ExhaustedAction; @@ -41,6 +40,7 @@ import org.jgroups.protocols.UDP; import org.jgroups.util.TLS; import org.jgroups.util.TLSClientAuth; +import org.keycloak.marshalling.Marshalling; import org.keycloak.quarkus.runtime.configuration.Configuration; import javax.net.ssl.SSLContext; @@ -112,11 +112,7 @@ private DefaultCacheManager startCacheManager() { builder.getGlobalConfigurationBuilder().module(MicrometerMeterRegisterConfigurationBuilder.class).meterRegistry(Metrics.globalRegistry); } - // For Infinispan 10, we go with the JBoss marshalling. - // TODO: This should be replaced later with the marshalling recommended by infinispan. Probably protostream. - // See https://infinispan.org/docs/stable/titles/developing/developing.html#marshalling for the details - builder.getGlobalConfigurationBuilder().serialization().marshaller(new JBossUserMarshaller()); - + Marshalling.configure(builder.getGlobalConfigurationBuilder()); return new DefaultCacheManager(builder, isStartEagerly()); } From 217987bac4382232e9375bc320f52bd4a9756747 Mon Sep 17 00:00:00 2001 From: Paul Muriel Biya-Bi Date: Fri, 3 May 2024 06:22:17 -0400 Subject: [PATCH 109/158] Fix client name help grammatical error (#29095) Closes #29094 Signed-off-by: Paul Muriel Biya-Bi --- .../theme/keycloak.v2/admin/messages/messages_en.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/apps/admin-ui/maven-resources/theme/keycloak.v2/admin/messages/messages_en.properties b/js/apps/admin-ui/maven-resources/theme/keycloak.v2/admin/messages/messages_en.properties index bd9698ef62f1..ec93b61e83c8 100644 --- a/js/apps/admin-ui/maven-resources/theme/keycloak.v2/admin/messages/messages_en.properties +++ b/js/apps/admin-ui/maven-resources/theme/keycloak.v2/admin/messages/messages_en.properties @@ -2126,7 +2126,7 @@ configureMappingDescription=Choose any of the mappings from this table keystorePassword=Keystore password mapperTypeHardcodedLdapRoleMapperHelp=Users imported from LDAP will be automatically added into this configured role. more={{count}} more -clientNameHelp=Specifies display name of the client. For example 'My Client'. Supports keys for localized values as well. For example\: ${my_client} +clientNameHelp=Specifies the display name of the client. For example 'My Client'. Supports keys for localized values as well. For example\: ${my_client} mappersList=Mappers list rootUrl=Root URL realmExplain=A realm manages a set of users, credentials, roles, and groups. A user belongs to and logs into a realm. Realms are isolated from one another and can only manage and authenticate the users that they control. From bda30ddb5aed52e2523679c286a97e07b35c8496 Mon Sep 17 00:00:00 2001 From: Alexander Schwartz Date: Mon, 29 Apr 2024 10:44:20 +0200 Subject: [PATCH 110/158] Run validation of email addresses only for new and changed email addresses Closes #29133 Signed-off-by: Alexander Schwartz --- .../userprofile/validator/DuplicateEmailValidator.java | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/services/src/main/java/org/keycloak/userprofile/validator/DuplicateEmailValidator.java b/services/src/main/java/org/keycloak/userprofile/validator/DuplicateEmailValidator.java index 5b8e6cab9b77..469f0404c909 100644 --- a/services/src/main/java/org/keycloak/userprofile/validator/DuplicateEmailValidator.java +++ b/services/src/main/java/org/keycloak/userprofile/validator/DuplicateEmailValidator.java @@ -18,6 +18,7 @@ import jakarta.ws.rs.core.Response; import java.util.List; +import java.util.Objects; import org.keycloak.models.KeycloakSession; import org.keycloak.models.RealmModel; @@ -62,10 +63,11 @@ public ValidationContext validate(Object input, String inputHint, ValidationCont KeycloakSession session = context.getSession(); RealmModel realm = session.getContext().getRealm(); + UserModel user = UserProfileAttributeValidationContext.from(context).getAttributeContext().getUser(); - if (!realm.isDuplicateEmailsAllowed()) { + // Only check if duplicate email addresses are not allowed, and the user is either new or changed their email address + if (!realm.isDuplicateEmailsAllowed() && (user == null || !Objects.equals(user.getFirstAttribute(inputHint), value))) { UserModel userByEmail = session.users().getUserByEmail(realm, value); - UserModel user = UserProfileAttributeValidationContext.from(context).getAttributeContext().getUser(); // check for duplicated email if (userByEmail != null && (user == null || !userByEmail.getId().equals(user.getId()))) { context.addError(new ValidationError(ID, inputHint, Messages.EMAIL_EXISTS) From efdad6408ccf43a0395d418b804e0c88147137b0 Mon Sep 17 00:00:00 2001 From: Douglas Palmer Date: Sun, 5 May 2024 22:57:34 -0700 Subject: [PATCH 111/158] Broken link in documentation (#29273) Closes #29233 Signed-off-by: Douglas Palmer --- docs/documentation/server_development/topics/extensions.adoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/documentation/server_development/topics/extensions.adoc b/docs/documentation/server_development/topics/extensions.adoc index f8d5180fac1c..d53525b98581 100644 --- a/docs/documentation/server_development/topics/extensions.adoc +++ b/docs/documentation/server_development/topics/extensions.adoc @@ -146,7 +146,7 @@ EntityManager em = session.getProvider(JpaConnectionProvider.class).getEntityMan Company myCompany = em.find(Company.class, "123"); ---- -The methods `getChangelogLocation` and `getFactoryId` are important to support automatic updating of your entities by Liquibase. https://www.liquibase.com/community[Liquibase] +The methods `getChangelogLocation` and `getFactoryId` are important to support automatic updating of your entities by Liquibase. https://www.liquibase.com/community/contributors[Liquibase] is a framework for updating the database schema, which {project_name} internally uses to create the DB schema and update the DB schema among versions. You may need to use it as well and create a changelog for your entities. Note that versioning of your own Liquibase changelog is independent of {project_name} versions. In other words, when you update to a new {project_name} version, you are not forced to update your From 1e47a0bfa219abfc41e6a942904ab63d7e2139e1 Mon Sep 17 00:00:00 2001 From: AndyMunro Date: Tue, 30 Apr 2024 17:45:58 -0400 Subject: [PATCH 112/158] Corrections to HA Guide Closes #29183 Signed-off-by: AndyMunro (cherry picked from commit dc84823239e35b70d27b7d86f74d1331bc73f2e1) --- .../high-availability/bblocks-active-passive-sync.adoc | 2 +- .../high-availability/concepts-active-passive-sync.adoc | 6 +++--- .../high-availability/concepts-infinispan-cli-batch.adoc | 2 +- .../connect-keycloak-to-external-infinispan.adoc | 4 ++-- .../deploy-infinispan-kubernetes-crossdc.adoc | 2 +- .../partials/infinispan/infinispan-credentials.adoc | 2 +- 6 files changed, 9 insertions(+), 9 deletions(-) diff --git a/docs/guides/high-availability/bblocks-active-passive-sync.adoc b/docs/guides/high-availability/bblocks-active-passive-sync.adoc index 345941d0ed10..6b45af958f33 100644 --- a/docs/guides/high-availability/bblocks-active-passive-sync.adoc +++ b/docs/guides/high-availability/bblocks-active-passive-sync.adoc @@ -41,7 +41,7 @@ A synchronously replicated database across two sites. == {jdgserver_name} -An {jdgserver_name} deployment which leverages the {jdgserver_name}'s Cross-DC functionality. +A deployment of {jdgserver_name} that leverages the {jdgserver_name}'s Cross-DC functionality. *Blueprint:* <@links.ha id="deploy-infinispan-kubernetes-crossdc" /> using the {jdgserver_name} Operator, and connect the two sites using {jdgserver_name}'s Gossip Router. diff --git a/docs/guides/high-availability/concepts-active-passive-sync.adoc b/docs/guides/high-availability/concepts-active-passive-sync.adoc index 486bca09f248..08caba5c10ad 100644 --- a/docs/guides/high-availability/concepts-active-passive-sync.adoc +++ b/docs/guides/high-availability/concepts-active-passive-sync.adoc @@ -15,11 +15,11 @@ Use this setup to be able to fail over automatically in the event of a site fail Two independent {project_name} deployments running in different sites are connected with a low latency network connection. Users, realms, clients, offline sessions, and other entities are stored in a database that is replicated synchronously across the two sites. -The data is also cached in the {project_name} embedded {jdgserver_name} as local caches. +The data is also cached in the {project_name} Infinispan caches as local caches. When the data is changed in one {project_name} instance, that data is updated in the database, and an invalidation message is sent to the other site using the replicated `work` cache. -Session-related data is stored in the replicated caches of the embedded {jdgserver_name} of {project_name}, and forwarded to the external {jdgserver_name}, which forwards information to the external {jdgserver_name} running synchronously in the other site. -As session data of the external {jdgserver_name} is also cached in the embedded {jdgserver_name}, invalidation messages of the replicated `work` cache are needed for invalidation. +Session-related data is stored in the replicated caches of the Infinispan caches of {project_name}, and forwarded to the external {jdgserver_name}, which forwards information to the external {jdgserver_name} running synchronously in the other site. +As session data of the external {jdgserver_name} is also cached in the Infinispan caches, invalidation messages of the replicated `work` cache are needed for invalidation. In the following paragraphs and diagrams, references to deploying {jdgserver_name} apply to the external {jdgserver_name}. diff --git a/docs/guides/high-availability/concepts-infinispan-cli-batch.adoc b/docs/guides/high-availability/concepts-infinispan-cli-batch.adoc index 08b3735e06e8..5e6497435015 100644 --- a/docs/guides/high-availability/concepts-infinispan-cli-batch.adoc +++ b/docs/guides/high-availability/concepts-infinispan-cli-batch.adoc @@ -19,7 +19,7 @@ For human interactions, the CLI shell might still be a better fit. == Example -The following `Batch` CR takes an {jdgserver_name} site offline as described in the operational procedure <@links.ha id="operate-switch-over" />. +The following `Batch` CR takes a site offline as described in the operational procedure <@links.ha id="operate-switch-over" />. [source,yaml,subs="+attributes"] ---- diff --git a/docs/guides/high-availability/connect-keycloak-to-external-infinispan.adoc b/docs/guides/high-availability/connect-keycloak-to-external-infinispan.adoc index b3e661521d8b..41379d2e1b23 100644 --- a/docs/guides/high-availability/connect-keycloak-to-external-infinispan.adoc +++ b/docs/guides/high-availability/connect-keycloak-to-external-infinispan.adoc @@ -47,8 +47,8 @@ include::examples/generated/keycloak-ispn.yaml[tag=keycloak-ispn] This is optional and it default to `11222`. <3> The Secret `name` and `key` with the {jdgserver_name} username credential. <4> The Secret `name` and `key` with the {jdgserver_name} password credential. -<5> The `spi-connections-infinispan-quarkus-site-name` is an arbitrary {jdgserver_name} site name which {project_name} needs for its embedded {jdgserver_name} deployment when a remote store is used. -This site-name is related only to the embedded {jdgserver_name} and does not need to match any value from the external {jdgserver_name} deployment. +<5> The `spi-connections-infinispan-quarkus-site-name` is an arbitrary {jdgserver_name} site name which {project_name} needs for its Infinispan caches deployment when a remote store is used. +This site-name is related only to the Infinispan caches and does not need to match any value from the external {jdgserver_name} deployment. If you are using multiple sites for {project_name} in a cross-DC setup such as <@links.ha id="deploy-infinispan-kubernetes-crossdc" />, the site name must be different in each site. diff --git a/docs/guides/high-availability/deploy-infinispan-kubernetes-crossdc.adoc b/docs/guides/high-availability/deploy-infinispan-kubernetes-crossdc.adoc index ac945bf09200..bc2b02ea68fb 100644 --- a/docs/guides/high-availability/deploy-infinispan-kubernetes-crossdc.adoc +++ b/docs/guides/high-availability/deploy-infinispan-kubernetes-crossdc.adoc @@ -137,7 +137,7 @@ kubectl -n {ns} create secret generic {ts-secret} \ + NOTE: Keystore and Truststore must be uploaded in both {ocp} clusters. -. Create an {jdgserver_name} Cluster with Cross-Site enabled +. Create a Cluster for {jdgserver_name} with Cross-Site enabled + The {infinispan-operator-docs}#setting-up-xsite[Setting Up Cross-Site] documentation provides all the information on how to create and configure your {jdgserver_name} cluster with cross-site enabled, including the previous steps. + diff --git a/docs/guides/high-availability/partials/infinispan/infinispan-credentials.adoc b/docs/guides/high-availability/partials/infinispan/infinispan-credentials.adoc index 86d0d0b99bc0..6f1e55570bab 100644 --- a/docs/guides/high-availability/partials/infinispan/infinispan-credentials.adoc +++ b/docs/guides/high-availability/partials/infinispan/infinispan-credentials.adoc @@ -32,4 +32,4 @@ include::../../examples/generated/ispn-single.yaml[tag=infinispan-credentials] kubectl create secret generic connect-secret --from-file=identities.yaml ---- + -Check https://infinispan.org/docs/infinispan-operator/main/operator.html#configuring-authentication[Configuring Authentication] documentation for more details. +Check the {infinispan-operator-docs}#configuring-authentication[Configuring Authentication] documentation for more details. From 9d6923936e5d46f938bcdbd15dd31af6d4e16338 Mon Sep 17 00:00:00 2001 From: Pedro Ruivo Date: Mon, 6 May 2024 16:27:07 +0100 Subject: [PATCH 113/158] Retry fetching event from remote cache Closes #28303 Signed-off-by: Pedro Ruivo Signed-off-by: Alexander Schwartz Co-authored-by: Alexander Schwartz --- .../java/org/keycloak/common/util/Retry.java | 6 +- .../InfinispanNotificationsManager.java | 69 ++++++++++--------- 2 files changed, 41 insertions(+), 34 deletions(-) diff --git a/common/src/main/java/org/keycloak/common/util/Retry.java b/common/src/main/java/org/keycloak/common/util/Retry.java index 05894afb358b..d3225b9b9c6e 100644 --- a/common/src/main/java/org/keycloak/common/util/Retry.java +++ b/common/src/main/java/org/keycloak/common/util/Retry.java @@ -18,7 +18,7 @@ package org.keycloak.common.util; import java.time.Duration; -import java.util.Random; +import java.util.concurrent.ThreadLocalRandom; /** * @author Stian Thorgersen @@ -125,8 +125,8 @@ public static int executeWithBackoff(AdvancedRunnable runnable, ThrowableCallbac } } - private static int computeBackoffInterval(int base, int iteration) { - return new Random().nextInt(computeIterationBase(base, iteration)); + public static int computeBackoffInterval(int base, int iteration) { + return ThreadLocalRandom.current().nextInt(computeIterationBase(base, iteration)); } private static int computeIterationBase(int base, int iteration) { diff --git a/model/infinispan/src/main/java/org/keycloak/cluster/infinispan/InfinispanNotificationsManager.java b/model/infinispan/src/main/java/org/keycloak/cluster/infinispan/InfinispanNotificationsManager.java index 7f9bc7bd1e84..d0907221d925 100644 --- a/model/infinispan/src/main/java/org/keycloak/cluster/infinispan/InfinispanNotificationsManager.java +++ b/model/infinispan/src/main/java/org/keycloak/cluster/infinispan/InfinispanNotificationsManager.java @@ -26,6 +26,7 @@ import java.util.concurrent.ExecutorService; import java.util.concurrent.RejectedExecutionException; import java.util.concurrent.TimeUnit; +import java.util.function.Supplier; import org.infinispan.Cache; import org.infinispan.client.hotrod.RemoteCache; @@ -36,6 +37,7 @@ import org.infinispan.client.hotrod.event.ClientCacheEntryCreatedEvent; import org.infinispan.client.hotrod.event.ClientCacheEntryModifiedEvent; import org.infinispan.client.hotrod.event.ClientCacheEntryRemovedEvent; +import org.infinispan.client.hotrod.exceptions.HotRodClientException; import org.infinispan.context.Flag; import org.infinispan.notifications.Listener; import org.infinispan.notifications.cachelistener.annotation.CacheEntryCreated; @@ -54,7 +56,6 @@ import org.keycloak.connections.infinispan.DefaultInfinispanConnectionProviderFactory; import org.keycloak.executors.ExecutorsProvider; import org.keycloak.models.KeycloakSession; -import org.infinispan.client.hotrod.exceptions.HotRodClientException; import static org.keycloak.cluster.infinispan.InfinispanClusterProvider.TASK_KEY_PREFIX; @@ -67,13 +68,16 @@ public class InfinispanNotificationsManager { protected static final Logger logger = Logger.getLogger(InfinispanNotificationsManager.class); + private static final int BACKOFF_BASE_MILLIS = 10; + private static final int MAX_BACKOFF_RETRIES = 10; + private final ConcurrentMultivaluedHashMap listeners = new ConcurrentMultivaluedHashMap<>(); private final ConcurrentMap taskCallbacks = new ConcurrentHashMap<>(); private final Cache workCache; - private final RemoteCache workRemoteCache; + private final RemoteCache workRemoteCache; private final String myAddress; @@ -81,8 +85,7 @@ public class InfinispanNotificationsManager { private final ExecutorService listenersExecutor; - - protected InfinispanNotificationsManager(Cache workCache, RemoteCache workRemoteCache, String myAddress, String mySite, ExecutorService listenersExecutor) { + protected InfinispanNotificationsManager(Cache workCache, RemoteCache workRemoteCache, String myAddress, String mySite, ExecutorService listenersExecutor) { this.workCache = workCache; this.workRemoteCache = workRemoteCache; this.myAddress = myAddress; @@ -93,7 +96,7 @@ protected InfinispanNotificationsManager(Cache workCache, // Create and init manager including all listeners etc public static InfinispanNotificationsManager create(KeycloakSession session, Cache workCache, String myAddress, String mySite, Set remoteStores) { - RemoteCache workRemoteCache = null; + RemoteCache workRemoteCache = null; if (!remoteStores.isEmpty()) { RemoteStore remoteStore = remoteStores.iterator().next(); @@ -189,12 +192,12 @@ public void cacheEntryCreated(CacheEntryCreatedEvent event @CacheEntryModified public void cacheEntryModified(CacheEntryModifiedEvent event) { - eventReceived(event.getKey(), event.getValue()); + eventReceived(event.getKey(), event.getNewValue()); } @CacheEntryRemoved public void cacheEntryRemoved(CacheEntryRemovedEvent event) { - taskFinished(event.getKey(), true); + taskFinished(event.getKey()); } } @@ -203,31 +206,28 @@ public void cacheEntryRemoved(CacheEntryRemovedEvent event @ClientListener public class HotRodListener { - private final RemoteCache remoteCache; + private final RemoteCache remoteCache; - public HotRodListener(RemoteCache remoteCache) { + public HotRodListener(RemoteCache remoteCache) { this.remoteCache = remoteCache; } @ClientCacheEntryCreated - public void created(ClientCacheEntryCreatedEvent event) { - String key = event.getKey().toString(); - hotrodEventReceived(key); + public void created(ClientCacheEntryCreatedEvent event) { + hotrodEventReceived(event.getKey()); } @ClientCacheEntryModified - public void updated(ClientCacheEntryModifiedEvent event) { - String key = event.getKey().toString(); - hotrodEventReceived(key); + public void updated(ClientCacheEntryModifiedEvent event) { + hotrodEventReceived(event.getKey()); } @ClientCacheEntryRemoved - public void removed(ClientCacheEntryRemovedEvent event) { - String key = event.getKey().toString(); - taskFinished(key, true); + public void removed(ClientCacheEntryRemovedEvent event) { + taskFinished(event.getKey()); } @@ -235,11 +235,22 @@ private void hotrodEventReceived(String key) { // TODO: Look at CacheEventConverter stuff to possibly include value in the event and avoid additional remoteCache request try { listenersExecutor.submit(() -> { - Object value = DefaultInfinispanConnectionProviderFactory.runWithReadLockOnCacheManager(() -> - // We've seen deadlocks in Infinispan 14.x when shutting down Infinispan concurrently, therefore wrapping this - remoteCache.get(key) - ); - eventReceived(key, (Serializable) value); + Supplier fetchEvent = () -> remoteCache.get(key); + Serializable event = DefaultInfinispanConnectionProviderFactory.runWithReadLockOnCacheManager(fetchEvent); + int iteration = 0; + // Event might have been generated from a node which is more up-to-date, so the fetch might return null. + // Retry until we find a node that is up-to-date and has the entry. + while (event == null && iteration < MAX_BACKOFF_RETRIES) { + ++iteration; + try { + Thread.sleep(Retry.computeBackoffInterval(BACKOFF_BASE_MILLIS, iteration)); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + return; + } + event = DefaultInfinispanConnectionProviderFactory.runWithReadLockOnCacheManager(fetchEvent); + } + eventReceived(key, event); }); } catch (RejectedExecutionException ree) { @@ -254,11 +265,10 @@ private void hotrodEventReceived(String key) { } } } - } private void eventReceived(String key, Serializable obj) { - if (!(obj instanceof WrapperClusterEvent)) { + if (!(obj instanceof WrapperClusterEvent event)) { // Items with the TASK_KEY_PREFIX might be gone fast once the locking is complete, therefore, don't log them. // It is still good to have the warning in case of real events return null because they have been, for example, expired if (obj == null && !key.startsWith(TASK_KEY_PREFIX)) { @@ -267,8 +277,6 @@ private void eventReceived(String key, Serializable obj) { return; } - WrapperClusterEvent event = (WrapperClusterEvent) obj; - if (event.isIgnoreSender()) { if (this.myAddress.equals(event.getSender())) { return; @@ -298,16 +306,15 @@ private void eventReceived(String key, Serializable obj) { } - void taskFinished(String taskKey, boolean success) { + void taskFinished(String taskKey) { TaskCallback callback = taskCallbacks.remove(taskKey); if (callback != null) { if (logger.isDebugEnabled()) { - logger.debugf("Finished task '%s' with '%b'", taskKey, success); + logger.debugf("Finished task '%s' with '%b'", taskKey, true); } - callback.setSuccess(success); + callback.setSuccess(true); callback.getTaskCompletedLatch().countDown(); } - } } From 6994dcfa6a376a4f0d8184a7f13b031a8845505b Mon Sep 17 00:00:00 2001 From: Pedro Igor Date: Tue, 7 May 2024 10:13:22 -0300 Subject: [PATCH 114/158] Additional note on release and upgrade guides about partial update on user attributes (#29358) Closes #28220 Signed-off-by: Pedro Igor --- docs/documentation/release_notes/index.adoc | 3 +++ docs/documentation/release_notes/topics/24_0_4.adoc | 6 ++++++ .../upgrading/topics/changes/changes-24_0_4.adoc | 10 ++++++++++ .../upgrading/topics/changes/changes.adoc | 4 ++++ 4 files changed, 23 insertions(+) create mode 100644 docs/documentation/release_notes/topics/24_0_4.adoc create mode 100644 docs/documentation/upgrading/topics/changes/changes-24_0_4.adoc diff --git a/docs/documentation/release_notes/index.adoc b/docs/documentation/release_notes/index.adoc index 0480468bfaf7..8259516f4e75 100644 --- a/docs/documentation/release_notes/index.adoc +++ b/docs/documentation/release_notes/index.adoc @@ -13,6 +13,9 @@ include::topics/templates/document-attributes.adoc[] :release_header_latest_link: {releasenotes_link_latest} include::topics/templates/release-header.adoc[] +== {project_name_full} 24.0.4 +include::topics/24_0_4.adoc[leveloffset=2] + == {project_name_full} 24.0.1 include::topics/24_0_1.adoc[leveloffset=2] diff --git a/docs/documentation/release_notes/topics/24_0_4.adoc b/docs/documentation/release_notes/topics/24_0_4.adoc new file mode 100644 index 000000000000..33b329eec6bc --- /dev/null +++ b/docs/documentation/release_notes/topics/24_0_4.adoc @@ -0,0 +1,6 @@ += Partial update to user attributes when updating users through the Admin User API is no longer supported + +When updating user attributes through the Admin User API, you cannot execute partial updates when updating the +user attributes, including the root attributes like `username`, `email`, `firstName`, and `lastName`. + +For more details, see the link:{upgradingguide_link}[{upgradingguide_name}]. \ No newline at end of file diff --git a/docs/documentation/upgrading/topics/changes/changes-24_0_4.adoc b/docs/documentation/upgrading/topics/changes/changes-24_0_4.adoc new file mode 100644 index 000000000000..a90c36f7f596 --- /dev/null +++ b/docs/documentation/upgrading/topics/changes/changes-24_0_4.adoc @@ -0,0 +1,10 @@ += Partial update to user attributes when updating users through the Admin User API is no longer supported + +When updating user attributes through the Admin User API, you cannot execute partial updates when updating the +user attributes, including the root attributes like `username`, `email`, `firstName`, and `lastName`. + +If you are updating user attributes through the Admin User API without passing all the attributes that the administrator +has write permissions, the missing attributes will be removed. On the other hand, if an attribute is marked as read-only for +administrators, not sending the attribute won't remove it. + +See the link:{adminguide_link}#user-profile[User Profile Documentation] for the details about the user profile settings. \ No newline at end of file diff --git a/docs/documentation/upgrading/topics/changes/changes.adoc b/docs/documentation/upgrading/topics/changes/changes.adoc index 62f7cbd487de..1c353b4870ce 100644 --- a/docs/documentation/upgrading/topics/changes/changes.adoc +++ b/docs/documentation/upgrading/topics/changes/changes.adoc @@ -1,6 +1,10 @@ [[migration-changes]] == Migration Changes +=== Migrating to 24.0.4 + +include::changes-24_0_4.adoc[leveloffset=3] + === Migrating to 24.0.3 include::changes-24_0_3.adoc[leveloffset=3] From d83ae90ae158392bacdc1aa9821e8feeb72ddbac Mon Sep 17 00:00:00 2001 From: Erik Jan de Wit Date: Mon, 15 Apr 2024 14:32:28 +0200 Subject: [PATCH 115/158] use subGroupCount instead (#28719) fixes: #28684 Signed-off-by: Erik Jan de Wit (cherry picked from commit 03c2629afc32f98185a5b6a29b3234f8ece70883) --- js/apps/admin-ui/src/clients/authorization/policy/Group.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/apps/admin-ui/src/clients/authorization/policy/Group.tsx b/js/apps/admin-ui/src/clients/authorization/policy/Group.tsx index 6b3ff84b5de1..0b933f30eb41 100644 --- a/js/apps/admin-ui/src/clients/authorization/policy/Group.tsx +++ b/js/apps/admin-ui/src/clients/authorization/policy/Group.tsx @@ -157,7 +157,7 @@ export const Group = () => { name="extendChildren" isChecked={field.value} onChange={field.onChange} - isDisabled={group.subGroups?.length === 0} + isDisabled={group.subGroupCount === 0} /> )} /> From 36b7b8bdbda8612a594dd696cd35ff4f9231d4fa Mon Sep 17 00:00:00 2001 From: Pedro Ruivo Date: Wed, 8 May 2024 10:54:29 +0100 Subject: [PATCH 116/158] Use cache.compute() method to improve the replace retry loop Final commit to enable the new function. Closes #29073 Signed-off-by: Pedro Ruivo --- .../InfinispanChangelogBasedTransaction.java | 51 +++++++------------ 1 file changed, 19 insertions(+), 32 deletions(-) diff --git a/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/changes/InfinispanChangelogBasedTransaction.java b/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/changes/InfinispanChangelogBasedTransaction.java index 2df4073a2f69..b60c14093dc2 100644 --- a/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/changes/InfinispanChangelogBasedTransaction.java +++ b/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/changes/InfinispanChangelogBasedTransaction.java @@ -223,45 +223,32 @@ private void runOperationInCluster(K key, MergedUpdate task, SessionEntityWr private void replace(K key, MergedUpdate task, SessionEntityWrapper oldVersionEntity, long lifespanMs, long maxIdleTimeMs) { - boolean replaced = false; + SessionEntityWrapper oldVersion = oldVersionEntity; + SessionEntityWrapper returnValue = null; int iteration = 0; - V session = oldVersionEntity.getEntity(); - - while (!replaced && iteration < InfinispanUtil.MAXIMUM_REPLACE_RETRIES) { - iteration++; - - SessionEntityWrapper newVersionEntity = generateNewVersionAndWrapEntity(session, oldVersionEntity.getLocalMetadata()); - - // Atomic cluster-aware replace - replaced = CacheDecorators.skipCacheStoreIfRemoteCacheIsEnabled(cache).replace(key, oldVersionEntity, newVersionEntity, lifespanMs, TimeUnit.MILLISECONDS, maxIdleTimeMs, TimeUnit.MILLISECONDS); - - // Replace fail. Need to load latest entity from cache, apply updates again and try to replace in cache again - if (!replaced) { - if (logger.isDebugEnabled()) { - logger.debugf("Replace failed for entity: %s, old version %s, new version %s. Will try again", key, oldVersionEntity.getVersion(), newVersionEntity.getVersion()); - } - - oldVersionEntity = cache.get(key); - - if (oldVersionEntity == null) { - logger.debugf("Entity %s not found. Maybe removed in the meantime. Replace task will be ignored", key); - return; - } - - session = oldVersionEntity.getEntity(); + V session = oldVersion.getEntity(); + var writeCache = CacheDecorators.skipCacheStoreIfRemoteCacheIsEnabled(cache); + while (iteration++ < InfinispanUtil.MAXIMUM_REPLACE_RETRIES) { + SessionEntityWrapper newVersionEntity = generateNewVersionAndWrapEntity(session, oldVersion.getLocalMetadata()); + returnValue = writeCache.computeIfPresent(key, new ReplaceFunction<>(oldVersion.getVersion(), newVersionEntity), lifespanMs, TimeUnit.MILLISECONDS, maxIdleTimeMs, TimeUnit.MILLISECONDS); + + if (returnValue == null) { + logger.debugf("Entity %s not found. Maybe removed in the meantime. Replace task will be ignored", key); + return; + } - task.runUpdate(session); - } else { + if (returnValue.getVersion().equals(newVersionEntity.getVersion())) { if (logger.isTraceEnabled()) { - logger.tracef("Replace SUCCESS for entity: %s . old version: %s, new version: %s, Lifespan: %d ms, MaxIdle: %d ms", key, oldVersionEntity.getVersion(), newVersionEntity.getVersion(), task.getLifespanMs(), task.getMaxIdleTimeMs()); + logger.tracef("Replace SUCCESS for entity: %s . old version: %s, new version: %s, Lifespan: %d ms, MaxIdle: %d ms", key, oldVersion.getVersion(), newVersionEntity.getVersion(), task.getLifespanMs(), task.getMaxIdleTimeMs()); } + return; } - } - if (!replaced) { - logger.warnf("Failed to replace entity '%s' in cache '%s'", key, cache.getName()); + oldVersion = returnValue; + session = oldVersion.getEntity(); + task.runUpdate(session); } - + logger.warnf("Failed to replace entity '%s' in cache '%s'. Expected: %s, Current: %s", key, cache.getName(), oldVersion, returnValue); } From 06b7d4bc2fc8b11e0193efa7e79c621896d9df5b Mon Sep 17 00:00:00 2001 From: AndyMunro Date: Mon, 6 May 2024 15:59:25 -0400 Subject: [PATCH 117/158] Update create realm topics to replace Master Closes #29280 Signed-off-by: AndyMunro (cherry picked from commit a7398b232c36bb55e8293d9d67269eac05ea702e) --- .../server_admin/images/add-realm-menu.png | Bin 53963 -> 65450 bytes .../topics/realms/proc-creating-a-realm.adoc | 4 +--- .../templates/realm-config.adoc | 5 ++--- docs/guides/images/add-realm.png | Bin 10929 -> 8263 bytes 4 files changed, 3 insertions(+), 6 deletions(-) diff --git a/docs/documentation/server_admin/images/add-realm-menu.png b/docs/documentation/server_admin/images/add-realm-menu.png index 4462459bbd56fe448793443fd7aaa7b13bcc2237..cf8a82cd7354e327c417e3905c1f071ee0854605 100644 GIT binary patch literal 65450 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 P@FgoPe>(A$&b|K)R!-6N literal 53963 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 WLQEjciLrhH{7H(+ij=+63-~|PMAldU diff --git a/docs/documentation/server_admin/topics/realms/proc-creating-a-realm.adoc b/docs/documentation/server_admin/topics/realms/proc-creating-a-realm.adoc index cf66e7cbbe2f..633e4a2c59d6 100644 --- a/docs/documentation/server_admin/topics/realms/proc-creating-a-realm.adoc +++ b/docs/documentation/server_admin/topics/realms/proc-creating-a-realm.adoc @@ -11,9 +11,7 @@ realm and only be able to interact with customer-facing apps. .Procedure -. Point to the top of the left pane. - -. Click *Create Realm*. +. Click *{project_name}* next to *master realm*, then click *Create Realm*. + .Add realm menu image:images/add-realm-menu.png[Add realm menu] diff --git a/docs/guides/getting-started/templates/realm-config.adoc b/docs/guides/getting-started/templates/realm-config.adoc index 86f1b72aea1a..0842aef267d3 100644 --- a/docs/guides/getting-started/templates/realm-config.adoc +++ b/docs/guides/getting-started/templates/realm-config.adoc @@ -11,7 +11,7 @@ includes a single realm, called `master`. Use this realm only for managing {proj Use these steps to create the first realm. . Open the {links-admin-console}. -. Click the word *master* in the top-left corner, then click *Create Realm*. +. Click *{project_name}* next to *master realm*, then click *Create Realm*. . Enter `myrealm` in the *Realm name* field. . Click *Create*. @@ -21,8 +21,7 @@ image::add-realm.png[Add realm] Initially, the realm has no users. Use these steps to create a user: -. Open the {links-admin-console}. -. Click the word *master* in the top-left corner, then click *myrealm*. +. Verify that you are still in the *myrealm* realm, which is shown above the word *Manage*. . Click *Users* in the left-hand menu. . Click *Add user*. . Fill in the form with the following values: diff --git a/docs/guides/images/add-realm.png b/docs/guides/images/add-realm.png index 75bd6410ff45567cde4cb3cdd10389514030609a..eb9a194b0a658ed4c16e725c1b398d5814098451 100644 GIT binary patch literal 8263 zcmbuEbySs6m&Pw5N=i2fSENf?S}r9mDUEaqNOve8EueIFcS|c*TJX{(NL{+5dk){M zHEU+peCwN8!yj?aO7S}sX-tpGO44lM% zcao%&xv`72gFUT=wVgRc34D0QPpf3?Nz46=o0pc0ONfV8h>M?ALWx#dN`s%W#TEjg z$&!TWFud4_g8yTK_Q{tAAJoGn4Y(c>5prHU4a3yZwLreq9n(~h zaIpA<$0E?8n`J=k+)QFoknbirz>IGxt=fr?C;g(}`pP&>h�ZLCC^oe7j3!+oa(o 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 literal 10929 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 Kv4S_cU;Yov9OOy> From 19a232c7a43b41feccb8a9b3f3fe039fe16b0b3e Mon Sep 17 00:00:00 2001 From: rmartinc Date: Sat, 11 May 2024 18:35:34 +0200 Subject: [PATCH 118/158] Allow empty CSP header in headers provider Closes #29458 Signed-off-by: rmartinc (cherry picked from commit 2cc051346df2cca7ed438db852a52a7123740600) --- .../DefaultSecurityHeadersProvider.java | 21 ++++++++++--------- .../oauth/LoginStatusIframeEndpointTest.java | 20 ++++++++++++++++++ 2 files changed, 31 insertions(+), 10 deletions(-) diff --git a/services/src/main/java/org/keycloak/headers/DefaultSecurityHeadersProvider.java b/services/src/main/java/org/keycloak/headers/DefaultSecurityHeadersProvider.java index b10b2b28dfdf..385446435688 100644 --- a/services/src/main/java/org/keycloak/headers/DefaultSecurityHeadersProvider.java +++ b/services/src/main/java/org/keycloak/headers/DefaultSecurityHeadersProvider.java @@ -106,24 +106,25 @@ private void addHtmlHeaders(MultivaluedMap headers) { // TODO This will be refactored as part of introducing a more strict CSP header if (options != null) { - ContentSecurityPolicyBuilder csp = ContentSecurityPolicyBuilder.create( - headers.getFirst(CONTENT_SECURITY_POLICY.getHeaderName()).toString()); - if (options.isAllowAnyFrameAncestor()) { headers.remove(BrowserSecurityHeaders.X_FRAME_OPTIONS.getHeaderName()); + } - if (csp.isDefaultFrameAncestors()) { + Object cspVal = headers.getFirst(CONTENT_SECURITY_POLICY.getHeaderName()); + if (cspVal != null) { + ContentSecurityPolicyBuilder csp = ContentSecurityPolicyBuilder.create(cspVal.toString()); + if (options.isAllowAnyFrameAncestor() && csp.isDefaultFrameAncestors()) { // only remove frame ancestors if defined to default 'self' csp.frameAncestors(null); } - } - String allowedFrameSrc = options.getAllowedFrameSrc(); - if (allowedFrameSrc != null) { - csp.addFrameSrc(allowedFrameSrc); - } + String allowedFrameSrc = options.getAllowedFrameSrc(); + if (allowedFrameSrc != null) { + csp.addFrameSrc(allowedFrameSrc); + } - headers.putSingle(CONTENT_SECURITY_POLICY.getHeaderName(), csp.build()); + headers.putSingle(CONTENT_SECURITY_POLICY.getHeaderName(), csp.build()); + } } } diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/oauth/LoginStatusIframeEndpointTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/oauth/LoginStatusIframeEndpointTest.java index 86298bee50c6..528377eda97a 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/oauth/LoginStatusIframeEndpointTest.java +++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/oauth/LoginStatusIframeEndpointTest.java @@ -40,7 +40,12 @@ import org.keycloak.testsuite.ActionURIUtils; import org.keycloak.testsuite.oidc.PkceGenerator; import org.keycloak.testsuite.runonserver.ServerVersion; +import org.keycloak.testsuite.updaters.RealmAttributeUpdater; +import org.keycloak.testsuite.util.AdminClientUtil; +import org.keycloak.testsuite.util.RealmBuilder; +import jakarta.ws.rs.client.Client; +import jakarta.ws.rs.core.Response; import java.io.IOException; import java.net.URLEncoder; import java.util.Collections; @@ -200,8 +205,23 @@ public void checkIframeCache() throws IOException { } } + @Test + public void checkEmptyCsp() throws Exception { + try (RealmAttributeUpdater realmUpdater = new RealmAttributeUpdater(adminClient.realm("test")) + .setBrowserSecurityHeader(BrowserSecurityHeaders.CONTENT_SECURITY_POLICY.getKey(), "") + .update(); + Client client = AdminClientUtil.createResteasyClient(); + Response response = client.target(suiteContext.getAuthServerInfo().getContextRoot() + + "/auth/realms/test/protocol/openid-connect/login-status-iframe.html").request().get()) { + assertEquals(Response.Status.OK.getStatusCode(), response.getStatus()); + assertNull(response.getHeaderString(BrowserSecurityHeaders.CONTENT_SECURITY_POLICY.getKey())); + assertNull(response.getHeaderString(BrowserSecurityHeaders.X_FRAME_OPTIONS.getHeaderName())); + } + } + @Override public void addTestRealms(List testRealms) { + testRealms.add(RealmBuilder.create().name("test").build()); } } From e3570496c89efef24a98eba7658cf4a6625fce87 Mon Sep 17 00:00:00 2001 From: Alexander Schwartz Date: Tue, 14 May 2024 11:37:52 +0200 Subject: [PATCH 119/158] Retrieve UUID from LDAP in same context (#29484) This should avoid out-of-sync problems in distributed LDAP environments. Closes #29206 Signed-off-by: Alexander Schwartz --- .../idm/store/ldap/LDAPIdentityStore.java | 23 +------------------ .../idm/store/ldap/LDAPOperationManager.java | 22 ++++++++++++------ 2 files changed, 16 insertions(+), 29 deletions(-) diff --git a/federation/ldap/src/main/java/org/keycloak/storage/ldap/idm/store/ldap/LDAPIdentityStore.java b/federation/ldap/src/main/java/org/keycloak/storage/ldap/idm/store/ldap/LDAPIdentityStore.java index be463f42f85b..adf890c3eb5a 100644 --- a/federation/ldap/src/main/java/org/keycloak/storage/ldap/idm/store/ldap/LDAPIdentityStore.java +++ b/federation/ldap/src/main/java/org/keycloak/storage/ldap/idm/store/ldap/LDAPIdentityStore.java @@ -48,7 +48,6 @@ import java.io.IOException; import java.util.ArrayList; -import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.LinkedHashSet; @@ -100,8 +99,7 @@ public void add(LDAPObject ldapObject) { } BasicAttributes ldapAttributes = extractAttributesForSaving(ldapObject, true); - this.operationManager.createSubContext(ldapObject.getDn().getLdapName(), ldapAttributes); - ldapObject.setUuid(getEntryIdentifier(ldapObject)); + ldapObject.setUuid(operationManager.createSubContext(ldapObject.getDn().getLdapName(), ldapAttributes)); if (logger.isDebugEnabled()) { logger.debugf("Type with identifier [%s] and dn [%s] successfully added to LDAP store.", ldapObject.getUuid(), ldapObject.getDn()); @@ -603,23 +601,4 @@ private BasicAttribute createBinaryBasicAttribute(String attrName, Set a return attr; } - protected String getEntryIdentifier(final LDAPObject ldapObject) { - try { - // we need this to retrieve the entry's identifier from the ldap server - String uuidAttrName = getConfig().getUuidLDAPAttributeName(); - - List search = this.operationManager.search(ldapObject.getDn().getLdapName(), - new LDAPQueryConditionsBuilder().present(LDAPConstants.OBJECT_CLASS), - Arrays.asList(uuidAttrName), SearchControls.OBJECT_SCOPE); - Attribute id = search.get(0).getAttributes().get(getConfig().getUuidLDAPAttributeName()); - - if (id == null) { - throw new ModelException("Could not retrieve identifier for entry [" + ldapObject.getDn().toString() + "]."); - } - - return this.operationManager.decodeEntryUUID(id.get()); - } catch (NamingException ne) { - throw new ModelException("Could not retrieve identifier for entry [" + ldapObject.getDn().toString() + "]."); - } - } } diff --git a/federation/ldap/src/main/java/org/keycloak/storage/ldap/idm/store/ldap/LDAPOperationManager.java b/federation/ldap/src/main/java/org/keycloak/storage/ldap/idm/store/ldap/LDAPOperationManager.java index 4e9fda9572ac..8f61aaad41d1 100644 --- a/federation/ldap/src/main/java/org/keycloak/storage/ldap/idm/store/ldap/LDAPOperationManager.java +++ b/federation/ldap/src/main/java/org/keycloak/storage/ldap/idm/store/ldap/LDAPOperationManager.java @@ -600,7 +600,7 @@ public void modifyAttributes(final LdapName dn, final ModificationItem[] mods, L } } - public void createSubContext(final LdapName name, final Attributes attributes) { + public String createSubContext(final LdapName name, final Attributes attributes) { try { if (logger.isTraceEnabled()) { logger.tracef("Creating entry [%s] with attributes: [", name); @@ -622,14 +622,22 @@ public void createSubContext(final LdapName name, final Attributes attributes) { logger.tracef("]"); } - execute(new LdapOperation() { + return execute(new LdapOperation<>() { @Override - public Void execute(LdapContext context) throws NamingException { + public String execute(LdapContext context) throws NamingException { DirContext subcontext = context.createSubcontext(name, attributes); - - subcontext.close(); - - return null; + try { + String uuidLDAPAttributeName = config.getUuidLDAPAttributeName(); + Attribute id = subcontext.getAttributes("", new String[]{uuidLDAPAttributeName}).get(uuidLDAPAttributeName); + if (id == null) { + throw new ModelException("Could not retrieve identifier for entry [" + name + "]."); + } + return decodeEntryUUID(id.get()); + } catch (NamingException ne) { + throw new ModelException("Could not retrieve identifier for entry [" + name + "].", ne); + } finally { + subcontext.close(); + } } From 57d924de22fb363a80b75735ffd7d8aa5d41f2a6 Mon Sep 17 00:00:00 2001 From: Hynek Mlnarik Date: Mon, 13 May 2024 10:34:35 +0200 Subject: [PATCH 120/158] Only store videos of failed Cypress tests `cypressSplit` function overrides the `after:spec` trigger which is used for removing videos of successful tests. Fixes: #29471 Signed-off-by: Hynek Mlnarik (cherry picked from commit 7daa2a047164afc194c38f90020e2a1a6550ba1b) --- js/apps/admin-ui/cypress.config.js | 29 +++++++++++++++++++++++++---- 1 file changed, 25 insertions(+), 4 deletions(-) diff --git a/js/apps/admin-ui/cypress.config.js b/js/apps/admin-ui/cypress.config.js index f80724266854..6e342c69cd50 100644 --- a/js/apps/admin-ui/cypress.config.js +++ b/js/apps/admin-ui/cypress.config.js @@ -1,6 +1,7 @@ import { defineConfig } from "cypress"; import cypressSplit from "cypress-split"; import fs from "node:fs"; +import { isAsyncFunction } from "node:util/types"; const isCI = process.env.CI === "true"; @@ -23,8 +24,10 @@ export default defineConfig({ slowTestThreshold: 30000, specPattern: "cypress/e2e/**/*.{js,jsx,ts,tsx}", setupNodeEvents(on, config) { - on("after:spec", (spec, results) => { - if (results.video) { + // after:spec collides with cypressSplit function below and is overridden there + + function afterSpecRemoveSuccessfulVideos(spec, results) { + if (results?.video) { // Do we have failures for any retry attempts? const failures = results.tests.some((test) => test.attempts.some((attempt) => attempt.state === "failed"), @@ -35,9 +38,27 @@ export default defineConfig({ fs.unlinkSync(results.video); } } - }); + } + + function chainedOn(event, callback) { + if (event === "after:spec") { + if (isAsyncFunction(callback)) { + on(event, async (spec, results) => { + afterSpecRemoveSuccessfulVideos(spec, results); + await callback(spec, results); + }); + } else { + on(event, (spec, results) => { + afterSpecRemoveSuccessfulVideos(spec, results); + callback(spec, results); + }); + } + } else { + on(event, callback); + } + } - cypressSplit(on, config); + cypressSplit(chainedOn, config); return config; }, From 18e3a69a33a833396dcd2aa5573a1fb25a332f84 Mon Sep 17 00:00:00 2001 From: Alexander Schwartz Date: Tue, 14 May 2024 12:38:37 +0200 Subject: [PATCH 121/158] Defer initialization of JGroups after logging is set up by Quarkus Closes #29129 Signed-off-by: Alexander Schwartz --- .../keycloak/quarkus/deployment/CacheBuildSteps.java | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/quarkus/deployment/src/main/java/org/keycloak/quarkus/deployment/CacheBuildSteps.java b/quarkus/deployment/src/main/java/org/keycloak/quarkus/deployment/CacheBuildSteps.java index 7675d915e983..13b45bbda338 100644 --- a/quarkus/deployment/src/main/java/org/keycloak/quarkus/deployment/CacheBuildSteps.java +++ b/quarkus/deployment/src/main/java/org/keycloak/quarkus/deployment/CacheBuildSteps.java @@ -25,6 +25,9 @@ import java.nio.file.Path; import java.nio.file.Paths; import java.util.stream.Collectors; + +import io.quarkus.deployment.builditem.ShutdownContextBuildItem; +import io.quarkus.deployment.logging.LoggingSetupBuildItem; import jakarta.enterprise.context.ApplicationScoped; import org.infinispan.commons.util.FileLookupFactory; import org.keycloak.config.MetricsOptions; @@ -39,12 +42,16 @@ import io.quarkus.deployment.annotations.Consume; import io.quarkus.deployment.annotations.ExecutionTime; import io.quarkus.deployment.annotations.Record; -import io.quarkus.deployment.builditem.ShutdownContextBuildItem; public class CacheBuildSteps { @Consume(ConfigBuildItem.class) - @Record(ExecutionTime.STATIC_INIT) + // Consume LoggingSetupBuildItem.class and record RUNTIME_INIT are necessary to ensure that logging is set up before the caches are initialized. + // This is to prevent the class TP in JGroups to pick up the trace logging at start up. While the logs will not appear on the console, + // they will still be created and use CPU cycles and create garbage collection. + // See: https://issues.redhat.com/browse/JGRP-2130 for the JGroups discussion, and https://github.com/keycloak/keycloak/issues/29129 for the issue Keycloak had with this. + @Consume(LoggingSetupBuildItem.class) + @Record(ExecutionTime.RUNTIME_INIT) @BuildStep void configureInfinispan(KeycloakRecorder recorder, BuildProducer syntheticBeanBuildItems, ShutdownContextBuildItem shutdownContext) { String configFile = getConfigValue("kc.spi-connections-infinispan-quarkus-config-file").getValue(); From 779e2019f3fbc6a53a604a68c228450802860482 Mon Sep 17 00:00:00 2001 From: Alexander Schwartz Date: Tue, 14 May 2024 16:31:10 +0200 Subject: [PATCH 122/158] Clean generated files for the admin Java client Closes #29525 Signed-off-by: Alexander Schwartz --- integration/admin-client/pom.xml | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/integration/admin-client/pom.xml b/integration/admin-client/pom.xml index b1b9a79cfa2c..5aae036ebdbb 100755 --- a/integration/admin-client/pom.xml +++ b/integration/admin-client/pom.xml @@ -137,6 +137,20 @@ + + maven-clean-plugin + + + + + src + + **/*.java + + + + + From 2c397d7827cf331074fa560f438a6d2af7f98844 Mon Sep 17 00:00:00 2001 From: Hynek Mlnarik Date: Wed, 15 May 2024 12:37:50 +0200 Subject: [PATCH 123/158] Remove the video file only if it exists Fixes: #29554 Signed-off-by: Hynek Mlnarik --- js/apps/admin-ui/cypress.config.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/apps/admin-ui/cypress.config.js b/js/apps/admin-ui/cypress.config.js index 6e342c69cd50..9f8b1969eb0f 100644 --- a/js/apps/admin-ui/cypress.config.js +++ b/js/apps/admin-ui/cypress.config.js @@ -35,7 +35,7 @@ export default defineConfig({ if (!failures) { // delete the video if the spec passed and no tests retried - fs.unlinkSync(results.video); + fs.rmSync(results.video, { force: true }); } } } From 3fb06c077b3e0ada68ca9e540635b87f8155cd0a Mon Sep 17 00:00:00 2001 From: Hynek Mlnarik Date: Wed, 15 May 2024 14:32:54 +0200 Subject: [PATCH 124/158] Keep correct format of config fields Fixes: #29314 Signed-off-by: Hynek Mlnarik (cherry picked from commit ef4a246aa92f1637e454244783c64c3bc4e7d9b2) --- .../src/identity-providers/add/DetailSettings.tsx | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/js/apps/admin-ui/src/identity-providers/add/DetailSettings.tsx b/js/apps/admin-ui/src/identity-providers/add/DetailSettings.tsx index 0f724be18b4a..0f660cf7184e 100644 --- a/js/apps/admin-ui/src/identity-providers/add/DetailSettings.tsx +++ b/js/apps/admin-ui/src/identity-providers/add/DetailSettings.tsx @@ -323,10 +323,12 @@ export default function DetailSettings() { const save = async (savedProvider?: IdentityProviderRepresentation) => { const p = savedProvider || getValues(); + const origAuthnContextClassRefs = p.config?.authnContextClassRefs; if (p.config?.authnContextClassRefs) p.config.authnContextClassRefs = JSON.stringify( p.config.authnContextClassRefs, ); + const origAuthnContextDeclRefs = p.config?.authnContextDeclRefs; if (p.config?.authnContextDeclRefs) p.config.authnContextDeclRefs = JSON.stringify( p.config.authnContextDeclRefs, @@ -342,6 +344,12 @@ export default function DetailSettings() { providerId, }, ); + if (origAuthnContextClassRefs) { + p.config!.authnContextClassRefs = origAuthnContextClassRefs; + } + if (origAuthnContextDeclRefs) { + p.config!.authnContextDeclRefs = origAuthnContextDeclRefs; + } reset(p); addAlert(t("updateSuccessIdentityProvider"), AlertVariant.success); } catch (error) { From f93266618518233aed3714bd00c980a694fae3eb Mon Sep 17 00:00:00 2001 From: vramik Date: Thu, 16 May 2024 12:43:27 +0200 Subject: [PATCH 125/158] Add a note to the migration guide about index name length for Oracle database Closes #29594 Signed-off-by: vramik (cherry picked from commit 35df0140eeeef9af687292b57c5b07d4eef85c99) --- .../documentation/upgrading/topics/changes/changes-24_0_0.adoc | 3 +++ 1 file changed, 3 insertions(+) diff --git a/docs/documentation/upgrading/topics/changes/changes-24_0_0.adoc b/docs/documentation/upgrading/topics/changes/changes-24_0_0.adoc index 40b92c707653..fc0a54fe3536 100644 --- a/docs/documentation/upgrading/topics/changes/changes-24_0_0.adoc +++ b/docs/documentation/upgrading/topics/changes/changes-24_0_0.adoc @@ -313,6 +313,9 @@ This change adds new indexes on the tables `USER_ATTRIBUTE` and `FED_USER_ATTRIB If those tables contain more than 300000 entries, {project_name} will skip the index creation by default during the automatic schema migration and instead log the SQL statement on the console during migration to be applied manually after {project_name}'s startup. See the link:{upgradingguide_link}[{upgradingguide_name}] for details on how to configure a different limit. +NOTE: The newly added indexes `USER_ATTR_LONG_VALUES_LOWER_CASE` and `FED_USER_ATTR_LONG_VALUES_LOWER_CASE` may exceed the maximum limit of 30 characters set by Oracle, +in case the database is running in compatibility mode. Since Oracle version 12.2, there is a support for longer index names. + == Additional migration steps for LDAP This is for installations that match all the following criteria: From f44b2c3176b4f4897900e1cd99c1f947b1d95e1b Mon Sep 17 00:00:00 2001 From: Alex Szczuczko Date: Fri, 17 May 2024 08:01:50 -0600 Subject: [PATCH 126/158] Add chmod to ADD examples in docs (#29648) Closes #29625 Signed-off-by: Alex Szczuczko --- docs/guides/server/containers.adoc | 2 +- docs/guides/server/db.adoc | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/guides/server/containers.adoc b/docs/guides/server/containers.adoc index c4eb4364a2c0..a6b9a5221fd7 100644 --- a/docs/guides/server/containers.adoc +++ b/docs/guides/server/containers.adoc @@ -72,7 +72,7 @@ FROM quay.io/keycloak/keycloak:{containerlabel} as builder ... # Add the provider JAR file to the providers directory -ADD --chown=keycloak:keycloak /opt/keycloak/providers/myprovider.jar +ADD --chown=keycloak:keycloak --chmod=644 /opt/keycloak/providers/myprovider.jar ... diff --git a/docs/guides/server/db.adoc b/docs/guides/server/db.adoc index 986fb18aa4cf..a5554838e260 100644 --- a/docs/guides/server/db.adoc +++ b/docs/guides/server/db.adoc @@ -59,8 +59,8 @@ A minimal Dockerfile to build an image which can be used with the {project_name} [source,dockerfile,subs="attributes+"] ---- FROM quay.io/keycloak/keycloak:{containerlabel} -ADD --chown=keycloak:keycloak https://repo1.maven.org/maven2/com/oracle/database/jdbc/ojdbc11/${properties["oracle-jdbc.version"]}/ojdbc11-${properties["oracle-jdbc.version"]}.jar /opt/keycloak/providers/ojdbc11.jar -ADD --chown=keycloak:keycloak https://repo1.maven.org/maven2/com/oracle/database/nls/orai18n/${properties["oracle-jdbc.version"]}/orai18n-${properties["oracle-jdbc.version"]}.jar /opt/keycloak/providers/orai18n.jar +ADD --chown=keycloak:keycloak --chmod=644 https://repo1.maven.org/maven2/com/oracle/database/jdbc/ojdbc11/${properties["oracle-jdbc.version"]}/ojdbc11-${properties["oracle-jdbc.version"]}.jar /opt/keycloak/providers/ojdbc11.jar +ADD --chown=keycloak:keycloak --chmod=644 https://repo1.maven.org/maven2/com/oracle/database/nls/orai18n/${properties["oracle-jdbc.version"]}/orai18n-${properties["oracle-jdbc.version"]}.jar /opt/keycloak/providers/orai18n.jar # Setting the build parameter for the database: ENV KC_DB=oracle # Add all other build parameters needed, for example enable health and metrics: @@ -95,7 +95,7 @@ A minimal Dockerfile to build an image which can be used with the {project_name} [source,dockerfile,subs="attributes+"] ---- FROM quay.io/keycloak/keycloak:{containerlabel} -ADD --chown=keycloak:keycloak https://repo1.maven.org/maven2/com/microsoft/sqlserver/mssql-jdbc/${properties["mssql-jdbc.version"]}/mssql-jdbc-${properties["mssql-jdbc.version"]}.jar /opt/keycloak/providers/mssql-jdbc.jar +ADD --chown=keycloak:keycloak --chmod=644 https://repo1.maven.org/maven2/com/microsoft/sqlserver/mssql-jdbc/${properties["mssql-jdbc.version"]}/mssql-jdbc-${properties["mssql-jdbc.version"]}.jar /opt/keycloak/providers/mssql-jdbc.jar # Setting the build parameter for the database: ENV KC_DB=mssql # Add all other build parameters needed, for example enable health and metrics: From 7589ad28f18b63f825c300627a59084dd87c4b29 Mon Sep 17 00:00:00 2001 From: Tomas Ondrusko <67582554+tondrusk@users.noreply.github.com> Date: Wed, 27 Mar 2024 10:34:26 +0100 Subject: [PATCH 127/158] Remove Twitter workaround (#28232) Relates to #23252 Signed-off-by: Tomas Ondrusko (cherry picked from commit 3160116a566bcb90aa9d34f77ef09083b8069d13) --- .../test/java/org/keycloak/testsuite/broker/SocialLoginTest.java | 1 - 1 file changed, 1 deletion(-) diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/SocialLoginTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/SocialLoginTest.java index 7a155855de7a..6a240b71ccb3 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/SocialLoginTest.java +++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/SocialLoginTest.java @@ -389,7 +389,6 @@ public void githubPrivateEmailLogin() throws InterruptedException { public void twitterLogin() { setTestProvider(TWITTER); performLogin(); - navigateToLoginPage(); assertUpdateProfile(false, false, true); appPage.assertCurrent(); } From 76353a255c6169e829b1b782efd1f28812bf938f Mon Sep 17 00:00:00 2001 From: stianst Date: Tue, 21 May 2024 07:45:43 +0200 Subject: [PATCH 128/158] Fix release labels --- .github/workflows/label.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/label.yml b/.github/workflows/label.yml index 5b3a17282bcd..92be95950bb8 100644 --- a/.github/workflows/label.yml +++ b/.github/workflows/label.yml @@ -31,7 +31,7 @@ jobs: BACKPORT_LABEL="backport/main" elif [[ "$GITHUB_BASE_REF" = release/* ]]; then MAJOR_MINOR="$(echo $GITHUB_BASE_REF | cut -d '/' -f 2)" - LAST_MICRO="$(gh api /repos/$GITHUB_REPOSITORY/tags --jq .[].name | sort -n -r | grep $MAJOR_MINOR | head -n 1 | cut -d '.' -f 3)" + LAST_MICRO="$(gh api /repos/$GITHUB_REPOSITORY/tags --jq .[].name | sort -V -r | grep $MAJOR_MINOR | head -n 1 | cut -d '.' -f 3)" NEXT_MICRO="$(($LAST_MICRO + 1))" LABEL="release/$MAJOR_MINOR.$NEXT_MICRO" BACKPORT_LABEL="backport/$MAJOR_MINOR" From d9f0c84b797525eac55914db5f81a8133ef5f9b1 Mon Sep 17 00:00:00 2001 From: Ricardo Martin Date: Tue, 21 May 2024 08:26:19 +0200 Subject: [PATCH 129/158] Missing auth checks in some admin endpoints (#166) Closes keycloak/keycloak-private#156 Signed-off-by: rmartinc --- .../admin/TestLdapConnectionResource.java | 3 +- .../keycloak/admin/ui/rest/UserResource.java | 8 +++-- .../keycloak/admin/ui/rest/UsersResource.java | 2 +- .../ClientRegistrationPolicyResource.java | 1 + .../testsuite/admin/PermissionsTest.java | 35 +++++++++++++++++-- 5 files changed, 42 insertions(+), 7 deletions(-) diff --git a/federation/ldap/src/main/java/org/keycloak/services/resources/admin/TestLdapConnectionResource.java b/federation/ldap/src/main/java/org/keycloak/services/resources/admin/TestLdapConnectionResource.java index 3dcbe238185e..d0e52dd0bb0d 100644 --- a/federation/ldap/src/main/java/org/keycloak/services/resources/admin/TestLdapConnectionResource.java +++ b/federation/ldap/src/main/java/org/keycloak/services/resources/admin/TestLdapConnectionResource.java @@ -16,9 +16,7 @@ */ package org.keycloak.services.resources.admin; -import org.jboss.logging.Logger; import org.jboss.resteasy.reactive.NoCache; -import org.keycloak.common.ClientConnection; import org.keycloak.models.KeycloakSession; import org.keycloak.models.LDAPConstants; import org.keycloak.models.RealmModel; @@ -89,6 +87,7 @@ public Response testLDAPConnection(@FormParam("action") String action, @FormPara @NoCache @Consumes(MediaType.APPLICATION_JSON) public Response testLDAPConnection(TestLdapConnectionRepresentation config) { + auth.realm().requireManageRealm(); try { LDAPServerCapabilitiesManager.testLDAP(config, session, realm); return Response.noContent().build(); diff --git a/rest/admin-ui-ext/src/main/java/org/keycloak/admin/ui/rest/UserResource.java b/rest/admin-ui-ext/src/main/java/org/keycloak/admin/ui/rest/UserResource.java index a37dcd6b039b..73e22a6d95ad 100644 --- a/rest/admin-ui-ext/src/main/java/org/keycloak/admin/ui/rest/UserResource.java +++ b/rest/admin-ui-ext/src/main/java/org/keycloak/admin/ui/rest/UserResource.java @@ -17,9 +17,9 @@ import jakarta.ws.rs.core.MediaType; import org.jboss.resteasy.reactive.NoCache; import org.keycloak.models.KeycloakSession; -import org.keycloak.models.RealmModel; import org.keycloak.models.UserModel; import org.keycloak.representations.userprofile.config.UPConfig; +import org.keycloak.services.resources.admin.permissions.AdminPermissionEvaluator; import org.keycloak.userprofile.UserProfile; import org.keycloak.userprofile.UserProfileProvider; import org.keycloak.utils.StringUtil; @@ -30,10 +30,12 @@ public class UserResource { private final KeycloakSession session; + private final AdminPermissionEvaluator auth; private final UserModel user; - public UserResource(KeycloakSession session, UserModel user) { + public UserResource(KeycloakSession session, AdminPermissionEvaluator auth, UserModel user) { this.session = session; + this.auth = auth; this.user = user; } @@ -42,6 +44,8 @@ public UserResource(KeycloakSession session, UserModel user) { @NoCache @Produces(MediaType.APPLICATION_JSON) public Map> getUnmanagedAttributes() { + auth.users().requireView(user); + UserProfileProvider provider = session.getProvider(UserProfileProvider.class); UserProfile profile = provider.create(USER_API, user); diff --git a/rest/admin-ui-ext/src/main/java/org/keycloak/admin/ui/rest/UsersResource.java b/rest/admin-ui-ext/src/main/java/org/keycloak/admin/ui/rest/UsersResource.java index fee417a04cbe..41ea079a86a4 100644 --- a/rest/admin-ui-ext/src/main/java/org/keycloak/admin/ui/rest/UsersResource.java +++ b/rest/admin-ui-ext/src/main/java/org/keycloak/admin/ui/rest/UsersResource.java @@ -40,6 +40,6 @@ public UserResource getUser(@PathParam("id") String id) { else throw new ForbiddenException(); } - return new UserResource(session, user); + return new UserResource(session, auth, user); } } diff --git a/services/src/main/java/org/keycloak/services/resources/admin/ClientRegistrationPolicyResource.java b/services/src/main/java/org/keycloak/services/resources/admin/ClientRegistrationPolicyResource.java index 86693805d381..fc1f15ee5c3b 100644 --- a/services/src/main/java/org/keycloak/services/resources/admin/ClientRegistrationPolicyResource.java +++ b/services/src/main/java/org/keycloak/services/resources/admin/ClientRegistrationPolicyResource.java @@ -74,6 +74,7 @@ public ClientRegistrationPolicyResource(KeycloakSession session, AdminPermission @Tag(name = KeycloakOpenAPI.Admin.Tags.CLIENT_REGISTRATION_POLICY) @Operation( summary="Base path for retrieve providers with the configProperties properly filled") public Stream getProviders() { + auth.realm().requireViewRealm(); return session.getKeycloakSessionFactory().getProviderFactoriesStream(ClientRegistrationPolicy.class) .map((ProviderFactory factory) -> { ClientRegistrationPolicyFactory clientRegFactory = (ClientRegistrationPolicyFactory) factory; diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/PermissionsTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/PermissionsTest.java index 3830e8537898..5487dd6af1c7 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/PermissionsTest.java +++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/PermissionsTest.java @@ -24,6 +24,7 @@ import org.junit.Test; import org.keycloak.admin.client.Keycloak; import org.keycloak.admin.client.resource.AuthorizationResource; +import org.keycloak.admin.client.resource.BearerAuthFilter; import org.keycloak.admin.client.resource.RealmResource; import org.keycloak.common.Profile; import org.keycloak.models.AdminRoles; @@ -51,6 +52,7 @@ import org.keycloak.representations.idm.RequiredActionProviderRepresentation; import org.keycloak.representations.idm.RequiredActionProviderSimpleRepresentation; import org.keycloak.representations.idm.RoleRepresentation; +import org.keycloak.representations.idm.TestLdapConnectionRepresentation; import org.keycloak.representations.idm.UserRepresentation; import org.keycloak.representations.idm.authorization.PolicyRepresentation; import org.keycloak.representations.idm.authorization.ResourcePermissionRepresentation; @@ -73,6 +75,8 @@ import org.keycloak.userprofile.DeclarativeUserProfileProvider; import jakarta.ws.rs.ClientErrorException; +import jakarta.ws.rs.client.Client; +import jakarta.ws.rs.core.MediaType; import jakarta.ws.rs.core.Response; import java.lang.reflect.Method; import java.util.Arrays; @@ -375,7 +379,11 @@ public void invoke(RealmResource realm) { invoke(new InvocationWithResponse() { public void invoke(RealmResource realm, AtomicReference response) { - response.set(realm.testLDAPConnection("nosuch", "nosuch", "nosuch", "nosuch", "nosuch", "nosuch")); + TestLdapConnectionRepresentation config = new TestLdapConnectionRepresentation( + "nosuch", "nosuch", "nosuch", "nosuch", "nosuch", "nosuch"); + response.set(realm.testLDAPConnection(config.getAction(), config.getConnectionUrl(), config.getBindDn(), + config.getBindCredential(), config.getUseTruststoreSpi(), config.getConnectionTimeout())); + response.set(realm.testLDAPConnection(config)); } }, Resource.REALM, true); @@ -1458,6 +1466,21 @@ public void invoke(RealmResource realm) { realm.users().get(user.getId()).toRepresentation(); } }, Resource.USER, false); + invoke(new InvocationWithResponse() { + public void invoke(RealmResource realm, AtomicReference response) { + // no-op + } + public void invoke(Keycloak keycloak, RealmResource realm, AtomicReference response) { + try (Client client = Keycloak.getClientProvider().newRestEasyClient(null, null, true)) { + Response resp = client.target(suiteContext.getAuthServerInfo().getContextRoot().toString() + "/auth") + .path("/admin/realms/" + realm.toRepresentation().getRealm() + "/ui-ext/users/" + user.getId() + "/unmanagedAttributes") + .register(new BearerAuthFilter(keycloak.tokenManager())) + .request(MediaType.APPLICATION_JSON) + .get(); + response.set(resp); + } + } + }, Resource.USER, false); invoke(new Invocation() { public void invoke(RealmResource realm) { realm.users().get(user.getId()).update(user); @@ -1757,6 +1780,11 @@ public void invoke(RealmResource realm) { realm.components().query("nosuch"); } }, Resource.REALM, false); + invoke(new Invocation() { + public void invoke(RealmResource realm) { + realm.clientRegistrationPolicy().getProviders(); + } + }, Resource.REALM, false); invoke(new InvocationWithResponse() { public void invoke(RealmResource realm, AtomicReference response) { response.set(realm.components().add(new ComponentRepresentation())); @@ -1945,7 +1973,7 @@ private void invoke(InvocationWithResponse invocation, Keycloak client, boolean int statusCode; try { AtomicReference responseReference = new AtomicReference<>(); - invocation.invoke(client.realm(REALM_NAME), responseReference); + invocation.invoke(client, client.realm(REALM_NAME), responseReference); Response response = responseReference.get(); if (response != null) { statusCode = response.getStatus(); @@ -2054,6 +2082,9 @@ public interface InvocationWithResponse { void invoke(RealmResource realm, AtomicReference response); + default void invoke(Keycloak keycloak, RealmResource realm, AtomicReference response) { + invoke(realm, response); + } } private void assertGettersEmpty(RealmRepresentation rep) { From 2191cc26ae6deb52eeaf74046027b65804d16fd0 Mon Sep 17 00:00:00 2001 From: Giuseppe Graziano Date: Tue, 21 May 2024 08:29:17 +0200 Subject: [PATCH 130/158] Encrypted KC_RESTART cookie and removed sensitive notes (#167) Closes #keycloak/keycloak-private#162 Signed-off-by: Giuseppe Graziano --- docs/documentation/release_notes/index.adoc | 3 + .../release_notes/topics/24_0_5.adoc | 5 ++ .../jose/jws/DefaultTokenManager.java | 4 + .../keycloak/protocol/RestartLoginCookie.java | 38 +++++++- .../request/AuthzEndpointRequestParser.java | 5 ++ .../keycloak/testsuite/util/OAuthClient.java | 6 +- .../testsuite/forms/RestartCookieTest.java | 88 +++++++++++++++++++ .../keys/FallbackKeyProviderTest.java | 2 +- 8 files changed, 145 insertions(+), 6 deletions(-) create mode 100644 docs/documentation/release_notes/topics/24_0_5.adoc diff --git a/docs/documentation/release_notes/index.adoc b/docs/documentation/release_notes/index.adoc index 8259516f4e75..32cbb0575ad1 100644 --- a/docs/documentation/release_notes/index.adoc +++ b/docs/documentation/release_notes/index.adoc @@ -13,6 +13,9 @@ include::topics/templates/document-attributes.adoc[] :release_header_latest_link: {releasenotes_link_latest} include::topics/templates/release-header.adoc[] +== {project_name_full} 24.0.5 +include::topics/24_0_5.adoc[leveloffset=2] + == {project_name_full} 24.0.4 include::topics/24_0_4.adoc[leveloffset=2] diff --git a/docs/documentation/release_notes/topics/24_0_5.adoc b/docs/documentation/release_notes/topics/24_0_5.adoc new file mode 100644 index 000000000000..9af7da2adc84 --- /dev/null +++ b/docs/documentation/release_notes/topics/24_0_5.adoc @@ -0,0 +1,5 @@ += Security issue with PAR clients using client_secret_post based authentication + +This release contains the fix of the important security issue affecting some OIDC confidential clients using PAR (Pushed authorization request). In case you use OIDC confidential clients together +with PAR and you use client authentication based on `client_id` and `client_secret` sent as parameters in the HTTP request body (method `client_secret_post` specified in the OIDC specification), it is +highly encouraged to rotate the client secrets of your clients after upgrading to this version. diff --git a/services/src/main/java/org/keycloak/jose/jws/DefaultTokenManager.java b/services/src/main/java/org/keycloak/jose/jws/DefaultTokenManager.java index 4bd94b5b3528..a5a2703a7c33 100644 --- a/services/src/main/java/org/keycloak/jose/jws/DefaultTokenManager.java +++ b/services/src/main/java/org/keycloak/jose/jws/DefaultTokenManager.java @@ -273,6 +273,8 @@ private String getEncryptedToken(TokenCategory category, String encodedToken) { public String cekManagementAlgorithm(TokenCategory category) { if (category == null) return null; switch (category) { + case INTERNAL: + return Algorithm.AES; case ID: case LOGOUT: return getCekManagementAlgorithm(OIDCConfigAttributes.ID_TOKEN_ENCRYPTED_RESPONSE_ALG); @@ -300,6 +302,8 @@ public String encryptAlgorithm(TokenCategory category) { switch (category) { case ID: return getEncryptAlgorithm(OIDCConfigAttributes.ID_TOKEN_ENCRYPTED_RESPONSE_ENC, JWEConstants.A128CBC_HS256); + case INTERNAL: + return JWEConstants.A128CBC_HS256; case LOGOUT: return getEncryptAlgorithm(OIDCConfigAttributes.ID_TOKEN_ENCRYPTED_RESPONSE_ENC); case AUTHORIZATION_RESPONSE: diff --git a/services/src/main/java/org/keycloak/protocol/RestartLoginCookie.java b/services/src/main/java/org/keycloak/protocol/RestartLoginCookie.java index d676a5121feb..9432bfee92fc 100644 --- a/services/src/main/java/org/keycloak/protocol/RestartLoginCookie.java +++ b/services/src/main/java/org/keycloak/protocol/RestartLoginCookie.java @@ -23,13 +23,17 @@ import org.keycloak.TokenCategory; import org.keycloak.cookie.CookieProvider; import org.keycloak.cookie.CookieType; +import org.keycloak.crypto.KeyUse; import org.keycloak.models.ClientModel; import org.keycloak.models.KeycloakSession; import org.keycloak.models.RealmModel; import org.keycloak.services.managers.AuthenticationSessionManager; import org.keycloak.sessions.AuthenticationSessionModel; import org.keycloak.sessions.RootAuthenticationSessionModel; +import org.keycloak.util.TokenUtil; +import javax.crypto.SecretKey; +import java.nio.charset.StandardCharsets; import java.util.HashMap; import java.util.Map; @@ -118,7 +122,7 @@ public RestartLoginCookie(AuthenticationSessionModel authSession) { public static void setRestartCookie(KeycloakSession session, AuthenticationSessionModel authSession) { RestartLoginCookie restart = new RestartLoginCookie(authSession); - String encoded = session.tokens().encode(restart); + String encoded = encodeAndEncrypt(session, restart); session.getProvider(CookieProvider.class).set(CookieType.AUTH_RESTART, encoded); } @@ -138,7 +142,7 @@ public static String getRestartCookie(KeycloakSession session){ public static AuthenticationSessionModel restartSession(KeycloakSession session, RealmModel realm, RootAuthenticationSessionModel rootSession, String expectedClientId, String encodedCookie) throws Exception { - RestartLoginCookie cookie = session.tokens().decode(encodedCookie, RestartLoginCookie.class); + RestartLoginCookie cookie = decryptAndDecode(session, encodedCookie); if (cookie == null) { logger.debug("Failed to verify encoded RestartLoginCookie"); return null; @@ -169,6 +173,36 @@ public static AuthenticationSessionModel restartSession(KeycloakSession session, return authSession; } + private static RestartLoginCookie decryptAndDecode(KeycloakSession session, String encodedToken) { + try { + String sigAlgorithm = session.tokens().signatureAlgorithm(TokenCategory.INTERNAL); + String algAlgorithm = session.tokens().cekManagementAlgorithm(TokenCategory.INTERNAL); + SecretKey encKey = session.keys().getActiveKey(session.getContext().getRealm(), KeyUse.ENC, algAlgorithm).getSecretKey(); + SecretKey signKey = session.keys().getActiveKey(session.getContext().getRealm(), KeyUse.SIG, sigAlgorithm).getSecretKey(); + + byte[] contentBytes = TokenUtil.jweDirectVerifyAndDecode(encKey, signKey, encodedToken); + String jwt = new String(contentBytes, StandardCharsets.UTF_8); + return session.tokens().decode(jwt, RestartLoginCookie.class); + } catch (Exception e) { + // Might be the cookie from the older version + return session.tokens().decode(encodedToken, RestartLoginCookie.class); + } + } + + private static String encodeAndEncrypt(KeycloakSession session, RestartLoginCookie cookie) { + try { + String sigAlgorithm = session.tokens().signatureAlgorithm(cookie.getCategory()); + String algAlgorithm = session.tokens().cekManagementAlgorithm(cookie.getCategory()); + SecretKey encKey = session.keys().getActiveKey(session.getContext().getRealm(), KeyUse.ENC, algAlgorithm).getSecretKey(); + SecretKey signKey = session.keys().getActiveKey(session.getContext().getRealm(), KeyUse.SIG, sigAlgorithm).getSecretKey(); + + String encodedJwt = session.tokens().encode(cookie); + return TokenUtil.jweDirectEncode(encKey, signKey, encodedJwt.getBytes(StandardCharsets.UTF_8)); + } catch (Exception e) { + throw new RuntimeException("Error encoding cookie.", e); + } + } + @Override public TokenCategory getCategory() { return TokenCategory.INTERNAL; diff --git a/services/src/main/java/org/keycloak/protocol/oidc/endpoints/request/AuthzEndpointRequestParser.java b/services/src/main/java/org/keycloak/protocol/oidc/endpoints/request/AuthzEndpointRequestParser.java index 8f696ea0ba7a..246d40978383 100644 --- a/services/src/main/java/org/keycloak/protocol/oidc/endpoints/request/AuthzEndpointRequestParser.java +++ b/services/src/main/java/org/keycloak/protocol/oidc/endpoints/request/AuthzEndpointRequestParser.java @@ -73,6 +73,11 @@ public abstract class AuthzEndpointRequestParser { // https://tools.ietf.org/html/rfc7636#section-6.1 KNOWN_REQ_PARAMS.add(OIDCLoginProtocol.CODE_CHALLENGE_PARAM); KNOWN_REQ_PARAMS.add(OIDCLoginProtocol.CODE_CHALLENGE_METHOD_PARAM); + + // Those are not OAuth/OIDC parameters, but they should never be added to the additionalRequestParameters + KNOWN_REQ_PARAMS.add(OAuth2Constants.CLIENT_ASSERTION_TYPE); + KNOWN_REQ_PARAMS.add(OAuth2Constants.CLIENT_ASSERTION); + KNOWN_REQ_PARAMS.add(OAuth2Constants.CLIENT_SECRET); } public void parseRequest(AuthorizationEndpointRequest request) { diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/util/OAuthClient.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/util/OAuthClient.java index 501cd2414f9e..5b74459a4989 100644 --- a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/util/OAuthClient.java +++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/util/OAuthClient.java @@ -1198,10 +1198,10 @@ public ParResponse doPushedAuthorizationRequest(String clientId, String clientSe parameters.add(new BasicNameValuePair(OIDCLoginProtocol.RESPONSE_MODE_PARAM, responseMode)); } if (clientId != null && clientSecret != null) { - String authorization = BasicAuthHelper.createHeader(clientId, clientSecret); - post.setHeader("Authorization", authorization); + parameters.add(new BasicNameValuePair(OAuth2Constants.CLIENT_ID, clientId)); + parameters.add(new BasicNameValuePair(OAuth2Constants.CLIENT_SECRET, clientSecret)); } - if (clientId != null) { + else if (clientId != null) { parameters.add(new BasicNameValuePair(OAuth2Constants.CLIENT_ID, clientId)); } if (redirectUri != null) { diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/forms/RestartCookieTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/forms/RestartCookieTest.java index 228abc1441e5..88f403ad6430 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/forms/RestartCookieTest.java +++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/forms/RestartCookieTest.java @@ -18,12 +18,20 @@ package org.keycloak.testsuite.forms; import jakarta.ws.rs.core.Response; + import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.util.HashSet; +import java.util.Set; + import org.jboss.arquillian.graphene.page.Page; import org.junit.Rule; import org.junit.Test; +import org.keycloak.OAuth2Constants; +import org.keycloak.TokenCategory; import org.keycloak.common.util.MultivaluedHashMap; import org.keycloak.crypto.Algorithm; +import org.keycloak.crypto.KeyUse; import org.keycloak.events.Details; import org.keycloak.events.Errors; import org.keycloak.jose.jws.JWSBuilder; @@ -32,17 +40,26 @@ import org.keycloak.keys.KeyProvider; import org.keycloak.models.KeyManager; import org.keycloak.models.KeycloakSession; +import org.keycloak.models.ParConfig; import org.keycloak.models.RealmModel; import org.keycloak.models.utils.DefaultKeyProviders; import org.keycloak.protocol.RestartLoginCookie; +import org.keycloak.protocol.oidc.endpoints.AuthorizationEndpoint; import org.keycloak.representations.idm.ComponentRepresentation; import org.keycloak.representations.idm.RealmRepresentation; import org.keycloak.testsuite.AbstractTestRealmKeycloakTest; import org.keycloak.testsuite.Assert; import org.keycloak.testsuite.AssertEvents; import org.keycloak.testsuite.pages.LoginPage; +import org.keycloak.testsuite.util.ClientBuilder; +import org.keycloak.testsuite.util.OAuthClient; +import org.keycloak.util.TokenUtil; import org.openqa.selenium.Cookie; +import javax.crypto.SecretKey; + +import static org.junit.Assert.assertEquals; + /** * @author Marek Posolda */ @@ -76,6 +93,16 @@ public class RestartCookieTest extends AbstractTestRealmKeycloakTest { " }\n" + "}"; + public static final Set sensitiveNotes = new HashSet<>(); + static { + sensitiveNotes.add(OAuth2Constants.CLIENT_ASSERTION_TYPE); + sensitiveNotes.add(OAuth2Constants.CLIENT_ASSERTION); + sensitiveNotes.add(OAuth2Constants.CLIENT_SECRET); + sensitiveNotes.add(AuthorizationEndpoint.LOGIN_SESSION_NOTE_ADDITIONAL_REQ_PARAMS_PREFIX + OAuth2Constants.CLIENT_ASSERTION_TYPE); + sensitiveNotes.add(AuthorizationEndpoint.LOGIN_SESSION_NOTE_ADDITIONAL_REQ_PARAMS_PREFIX + OAuth2Constants.CLIENT_ASSERTION); + sensitiveNotes.add(AuthorizationEndpoint.LOGIN_SESSION_NOTE_ADDITIONAL_REQ_PARAMS_PREFIX + OAuth2Constants.CLIENT_SECRET); + } + @Override public void configureTestRealm(RealmRepresentation testRealm) { } @@ -99,6 +126,67 @@ protected void afterAbstractKeycloakTestRealmImport() { } } + @Test + public void testRestartCookie() { + loginPage.open(); + String restartCookie = loginPage.getDriver().manage().getCookieNamed(RestartLoginCookie.KC_RESTART).getValue(); + assertRestartCookie(restartCookie); + } + + @Test + public void testRestartCookieWithPar() { + String clientId = "par-confidential-client"; + adminClient.realm("test").clients().create(ClientBuilder.create() + .clientId("par-confidential-client") + .secret("secret") + .redirectUris(oauth.getRedirectUri() + "/*") + .attribute(ParConfig.REQUIRE_PUSHED_AUTHORIZATION_REQUESTS, "true") + .build()); + + oauth.clientId(clientId); + String requestUri = null; + try { + OAuthClient.ParResponse pResp = oauth.doPushedAuthorizationRequest(clientId, "secret"); + assertEquals(201, pResp.getStatusCode()); + requestUri = pResp.getRequestUri(); + } + catch (Exception e) { + Assert.fail(); + } + + oauth.redirectUri(null); + oauth.scope(null); + oauth.responseType(null); + oauth.requestUri(requestUri); + String state = oauth.stateParamRandom().getState(); + oauth.stateParamHardcoded(state); + + oauth.openLoginForm(); + String restartCookie = loginPage.getDriver().manage().getCookieNamed(RestartLoginCookie.KC_RESTART).getValue(); + assertRestartCookie(restartCookie); + } + + private void assertRestartCookie(String restartCookie) { + getTestingClient() + .server(TEST_REALM_NAME) + .run(session -> + { + try { + String sigAlgorithm = session.tokens().signatureAlgorithm(TokenCategory.INTERNAL); + String encAlgorithm = session.tokens().cekManagementAlgorithm(TokenCategory.INTERNAL); + SecretKey encKey = session.keys().getActiveKey(session.getContext().getRealm(), KeyUse.ENC, encAlgorithm).getSecretKey(); + SecretKey signKey = session.keys().getActiveKey(session.getContext().getRealm(), KeyUse.SIG, sigAlgorithm).getSecretKey(); + + byte[] contentBytes = TokenUtil.jweDirectVerifyAndDecode(encKey, signKey, restartCookie); + String jwt = new String(contentBytes, StandardCharsets.UTF_8); + RestartLoginCookie restartLoginCookie = session.tokens().decode(jwt, RestartLoginCookie.class); + Assert.assertFalse(restartLoginCookie.getNotes().keySet().stream().anyMatch(sensitiveNotes::contains)); + } catch (Exception e) { + Assert.fail(); + } + }); + } + // KEYCLOAK-5440 -- migration from Keycloak 3.1.0 @Test public void testRestartCookieBackwardsCompatible_Keycloak25() throws IOException { diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/keys/FallbackKeyProviderTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/keys/FallbackKeyProviderTest.java index 5212318c45e6..ba11ae46afec 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/keys/FallbackKeyProviderTest.java +++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/keys/FallbackKeyProviderTest.java @@ -82,7 +82,7 @@ public void fallbackAfterDeletingAllKeysInRealm() { Assert.assertEquals(AppPage.RequestType.AUTH_RESPONSE, appPage.getRequestType()); providers = realmsResouce().realm("test").components().query(realmId, "org.keycloak.keys.KeyProvider"); - assertProviders(providers, "fallback-RS256", "fallback-" + Constants.INTERNAL_SIGNATURE_ALGORITHM); + assertProviders(providers, "fallback-RS256", "fallback-AES", "fallback-" + Constants.INTERNAL_SIGNATURE_ALGORITHM); } @Test From a9740b7c358e2a611f1447e8b696c35cc4f5ad7a Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 4 Jun 2024 05:24:31 +0000 Subject: [PATCH 131/158] Set version to 24.0.5 --- adapters/oidc/adapter-core/pom.xml | 2 +- adapters/oidc/installed/pom.xml | 2 +- adapters/oidc/jakarta-servlet-filter/pom.xml | 2 +- adapters/oidc/jaxrs-oauth-client/pom.xml | 2 +- adapters/oidc/jetty/jetty-core/pom.xml | 2 +- adapters/oidc/jetty/jetty9.4/pom.xml | 2 +- adapters/oidc/jetty/pom.xml | 2 +- adapters/oidc/js/pom.xml | 2 +- adapters/oidc/pom.xml | 2 +- adapters/oidc/servlet-filter/pom.xml | 2 +- adapters/oidc/spring-boot-adapter-core/pom.xml | 2 +- adapters/oidc/spring-boot-container-bundle/pom.xml | 2 +- adapters/oidc/spring-boot2/pom.xml | 2 +- adapters/oidc/spring-security/pom.xml | 2 +- adapters/oidc/tomcat/pom.xml | 2 +- adapters/oidc/tomcat/tomcat-core/pom.xml | 2 +- adapters/oidc/tomcat/tomcat/pom.xml | 2 +- adapters/oidc/undertow/pom.xml | 2 +- adapters/oidc/wildfly-elytron/pom.xml | 2 +- adapters/oidc/wildfly/pom.xml | 2 +- adapters/oidc/wildfly/wildfly-subsystem/pom.xml | 2 +- adapters/pom.xml | 2 +- adapters/saml/core-jakarta/pom.xml | 2 +- adapters/saml/core-public/pom.xml | 2 +- adapters/saml/core/pom.xml | 2 +- adapters/saml/jakarta-servlet-filter/pom.xml | 2 +- adapters/saml/jetty/jetty-core/pom.xml | 2 +- adapters/saml/jetty/jetty9.4/pom.xml | 2 +- adapters/saml/jetty/pom.xml | 2 +- adapters/saml/pom.xml | 2 +- adapters/saml/servlet-filter/pom.xml | 2 +- adapters/saml/tomcat/pom.xml | 2 +- adapters/saml/tomcat/tomcat-core/pom.xml | 2 +- adapters/saml/tomcat/tomcat/pom.xml | 2 +- adapters/saml/undertow/pom.xml | 2 +- adapters/saml/wildfly-elytron-jakarta/pom.xml | 2 +- adapters/saml/wildfly-elytron/pom.xml | 2 +- adapters/saml/wildfly/pom.xml | 2 +- adapters/saml/wildfly/wildfly-jakarta-subsystem/pom.xml | 2 +- adapters/saml/wildfly/wildfly-subsystem/pom.xml | 2 +- adapters/spi/adapter-spi/pom.xml | 2 +- adapters/spi/jakarta-servlet-adapter-spi/pom.xml | 2 +- adapters/spi/jboss-adapter-core/pom.xml | 2 +- adapters/spi/jetty-adapter-spi/pom.xml | 2 +- adapters/spi/pom.xml | 2 +- adapters/spi/servlet-adapter-spi/pom.xml | 2 +- adapters/spi/tomcat-adapter-spi/pom.xml | 2 +- adapters/spi/undertow-adapter-spi/pom.xml | 2 +- authz/client/pom.xml | 2 +- authz/policy-enforcer/pom.xml | 2 +- authz/policy/common/pom.xml | 2 +- authz/policy/pom.xml | 2 +- authz/pom.xml | 2 +- boms/adapter/pom.xml | 2 +- boms/misc/pom.xml | 2 +- boms/pom.xml | 2 +- boms/spi/pom.xml | 2 +- common/pom.xml | 2 +- core/pom.xml | 2 +- crypto/default/pom.xml | 2 +- crypto/elytron/pom.xml | 2 +- crypto/fips1402/pom.xml | 2 +- crypto/pom.xml | 2 +- dependencies/pom.xml | 2 +- dependencies/server-all/pom.xml | 2 +- dependencies/server-min/pom.xml | 2 +- distribution/adapters/pom.xml | 2 +- distribution/adapters/tomcat-adapter-zip/pom.xml | 2 +- distribution/adapters/wildfly-adapter/pom.xml | 2 +- distribution/api-docs-dist/pom.xml | 2 +- distribution/downloads/pom.xml | 2 +- distribution/feature-packs/adapter-feature-pack/pom.xml | 2 +- distribution/feature-packs/pom.xml | 2 +- distribution/galleon-feature-packs/pom.xml | 2 +- .../pom.xml | 2 +- .../saml-adapter-galleon-pack/pom.xml | 2 +- distribution/licenses-common/pom.xml | 2 +- distribution/maven-plugins/licenses-processor/pom.xml | 2 +- distribution/maven-plugins/pom.xml | 2 +- distribution/pom.xml | 2 +- distribution/saml-adapters/pom.xml | 2 +- distribution/saml-adapters/tomcat-adapter-zip/pom.xml | 2 +- distribution/saml-adapters/wildfly-adapter/pom.xml | 2 +- .../wildfly-adapter/wildfly-adapter-jakarta-zip/pom.xml | 2 +- .../wildfly-adapter/wildfly-adapter-zip/pom.xml | 2 +- .../wildfly-adapter/wildfly-jakarta-modules/pom.xml | 2 +- .../saml-adapters/wildfly-adapter/wildfly-modules/pom.xml | 2 +- docs/documentation/aggregation/pom.xml | 2 +- docs/documentation/api_documentation/pom.xml | 2 +- docs/documentation/authorization_services/pom.xml | 2 +- docs/documentation/dist/pom.xml | 2 +- docs/documentation/header-maven-plugin/pom.xml | 4 ++-- docs/documentation/pom.xml | 4 ++-- docs/documentation/release_notes/pom.xml | 2 +- docs/documentation/securing_apps/pom.xml | 2 +- docs/documentation/server_admin/pom.xml | 2 +- docs/documentation/server_development/pom.xml | 2 +- docs/documentation/tests/pom.xml | 2 +- .../topics/templates/document-attributes.adoc | 8 ++++---- docs/documentation/upgrading/pom.xml | 2 +- docs/guides/pom.xml | 2 +- docs/maven-plugin/pom.xml | 2 +- docs/pom.xml | 2 +- examples/admin-client/pom.xml | 2 +- examples/js-console/pom.xml | 2 +- examples/kerberos/pom.xml | 2 +- examples/ldap/pom.xml | 2 +- examples/pom.xml | 2 +- examples/providers/authenticator/pom.xml | 2 +- examples/providers/pom.xml | 2 +- examples/providers/rest/pom.xml | 2 +- examples/saml/pom.xml | 2 +- examples/saml/servlet-filter/pom.xml | 2 +- examples/themes/pom.xml | 2 +- federation/kerberos/pom.xml | 2 +- federation/ldap/pom.xml | 2 +- federation/pom.xml | 2 +- federation/sssd/pom.xml | 2 +- integration/admin-client-jee/pom.xml | 2 +- integration/admin-client/pom.xml | 2 +- integration/client-cli/admin-cli/pom.xml | 2 +- integration/client-cli/client-cli-dist/pom.xml | 2 +- integration/client-cli/client-registration-cli/pom.xml | 2 +- integration/client-cli/pom.xml | 2 +- integration/client-registration/pom.xml | 2 +- integration/pom.xml | 2 +- js/apps/account-ui/pom.xml | 2 +- js/apps/admin-ui/pom.xml | 2 +- js/libs/keycloak-admin-client/package.json | 2 +- js/libs/keycloak-admin-client/pom.xml | 2 +- js/libs/keycloak-js/package.json | 2 +- js/libs/keycloak-js/pom.xml | 2 +- js/pom.xml | 2 +- misc/keycloak-test-helper/pom.xml | 2 +- misc/pom.xml | 2 +- .../keycloak-spring-boot-starter/pom.xml | 2 +- misc/spring-boot-starter/pom.xml | 2 +- model/infinispan/pom.xml | 2 +- model/jpa/pom.xml | 2 +- model/legacy/pom.xml | 2 +- model/pom.xml | 2 +- model/storage-private/pom.xml | 2 +- model/storage-services/pom.xml | 2 +- model/storage/pom.xml | 2 +- operator/pom.xml | 2 +- pom.xml | 4 ++-- quarkus/config-api/pom.xml | 2 +- quarkus/container/Dockerfile | 2 +- quarkus/deployment/pom.xml | 2 +- quarkus/dist/pom.xml | 2 +- quarkus/pom.xml | 2 +- quarkus/runtime/pom.xml | 2 +- quarkus/server/pom.xml | 2 +- quarkus/tests/integration/pom.xml | 2 +- quarkus/tests/junit5/pom.xml | 2 +- quarkus/tests/pom.xml | 2 +- rest/admin-ui-ext/pom.xml | 2 +- rest/pom.xml | 2 +- saml-core-api/pom.xml | 2 +- saml-core/pom.xml | 2 +- server-spi-private/pom.xml | 2 +- server-spi/pom.xml | 2 +- services/pom.xml | 2 +- testsuite/db-allocator-plugin/pom.xml | 2 +- testsuite/integration-arquillian/pom.xml | 2 +- .../integration-arquillian/servers/adapter-spi/pom.xml | 2 +- .../servers/adapter-spi/undertow-adapter-jakarta/pom.xml | 2 +- .../adapter-spi/undertow-adapter-saml-jakarta/pom.xml | 2 +- .../adapter-spi/undertow-adapter-spi-jakarta/pom.xml | 2 +- .../servers/app-server/app-server-spi/pom.xml | 2 +- .../servers/app-server/jboss/eap/pom.xml | 2 +- .../servers/app-server/jboss/eap6/pom.xml | 2 +- .../servers/app-server/jboss/galleon/pom.xml | 2 +- .../servers/app-server/jboss/pom.xml | 2 +- .../servers/app-server/jboss/wildfly/pom.xml | 2 +- .../servers/app-server/jetty/94/pom.xml | 2 +- .../servers/app-server/jetty/common/pom.xml | 2 +- .../servers/app-server/jetty/pom.xml | 2 +- .../servers/app-server/karaf/fuse63/pom.xml | 2 +- .../servers/app-server/karaf/fuse7x/pom.xml | 2 +- .../servers/app-server/karaf/pom.xml | 2 +- .../integration-arquillian/servers/app-server/pom.xml | 2 +- .../servers/app-server/tomcat/common/pom.xml | 2 +- .../servers/app-server/tomcat/pom.xml | 2 +- .../servers/app-server/tomcat/tomcat8/pom.xml | 2 +- .../servers/app-server/tomcat/tomcat9/pom.xml | 2 +- .../servers/app-server/undertow/pom.xml | 2 +- .../integration-arquillian/servers/auth-server/pom.xml | 2 +- .../servers/auth-server/quarkus/pom.xml | 2 +- .../servers/auth-server/services/pom.xml | 2 +- .../services/testsuite-providers-deployment/pom.xml | 2 +- .../auth-server/services/testsuite-providers/pom.xml | 2 +- .../servers/auth-server/undertow/pom.xml | 2 +- .../servers/cache-server/infinispan/datagrid/pom.xml | 2 +- .../servers/cache-server/infinispan/infinispan/pom.xml | 2 +- .../servers/cache-server/infinispan/pom.xml | 2 +- .../servers/cache-server/legacy/datagrid/pom.xml | 2 +- .../servers/cache-server/legacy/infinispan/pom.xml | 2 +- .../servers/cache-server/legacy/pom.xml | 2 +- .../integration-arquillian/servers/cache-server/pom.xml | 2 +- .../integration-arquillian/servers/migration/pom.xml | 2 +- testsuite/integration-arquillian/servers/pom.xml | 2 +- .../test-apps/app-profile-jee/pom.xml | 2 +- .../test-apps/cors/angular-product/pom.xml | 2 +- .../test-apps/cors/database-service/pom.xml | 2 +- testsuite/integration-arquillian/test-apps/cors/pom.xml | 2 +- .../test-apps/fuse/camel-fuse7-undertow/pom.xml | 2 +- .../integration-arquillian/test-apps/fuse/camel/pom.xml | 2 +- .../test-apps/fuse/customer-app-fuse/pom.xml | 2 +- .../test-apps/fuse/cxf-jaxrs-fuse7-undertow/pom.xml | 2 +- .../test-apps/fuse/cxf-jaxrs/pom.xml | 2 +- .../test-apps/fuse/cxf-jaxws-fuse7-undertow/pom.xml | 2 +- .../test-apps/fuse/cxf-jaxws/pom.xml | 2 +- .../test-apps/fuse/external-config/pom.xml | 2 +- .../test-apps/fuse/features/pom.xml | 2 +- testsuite/integration-arquillian/test-apps/fuse/pom.xml | 2 +- .../test-apps/fuse/product-app-fuse/pom.xml | 2 +- .../test-apps/fuse/product-app-fuse7-undertow/pom.xml | 2 +- .../test-apps/hello-world-authz-service/pom.xml | 2 +- testsuite/integration-arquillian/test-apps/pom.xml | 2 +- .../test-apps/servlet-authz/pom.xml | 2 +- .../test-apps/servlet-policy-enforcer/pom.xml | 2 +- .../test-apps/servlets-jakarta/pom.xml | 2 +- .../integration-arquillian/test-apps/servlets/pom.xml | 2 +- .../test-apps/spring-boot-adapter-app/pom.xml | 2 +- .../test-apps/test-apps-dist/pom.xml | 2 +- testsuite/integration-arquillian/tests/base/pom.xml | 2 +- .../tests/other/adapters/jboss/pom.xml | 2 +- .../tests/other/adapters/karaf/fuse61/pom.xml | 2 +- .../tests/other/adapters/karaf/fuse62/pom.xml | 2 +- .../tests/other/adapters/karaf/karaf3/pom.xml | 2 +- .../tests/other/adapters/karaf/pom.xml | 2 +- .../integration-arquillian/tests/other/adapters/pom.xml | 2 +- .../tests/other/adapters/was/pom.xml | 2 +- .../tests/other/adapters/was/was8/pom.xml | 2 +- .../tests/other/adapters/wls/pom.xml | 2 +- .../tests/other/adapters/wls/wls12/pom.xml | 2 +- .../integration-arquillian/tests/other/base-ui/pom.xml | 2 +- .../tests/other/jpa-performance/pom.xml | 2 +- .../tests/other/mod_auth_mellon/pom.xml | 2 +- testsuite/integration-arquillian/tests/other/pom.xml | 2 +- .../tests/other/springboot-tests/pom.xml | 2 +- testsuite/integration-arquillian/tests/other/sssd/pom.xml | 2 +- .../integration-arquillian/tests/other/webauthn/pom.xml | 2 +- testsuite/integration-arquillian/tests/pom.xml | 2 +- testsuite/integration-arquillian/util/pom.xml | 2 +- testsuite/model/pom.xml | 2 +- testsuite/pom.xml | 2 +- testsuite/utils/pom.xml | 2 +- themes/pom.xml | 2 +- util/embedded-ldap/pom.xml | 2 +- util/pom.xml | 2 +- 252 files changed, 258 insertions(+), 258 deletions(-) diff --git a/adapters/oidc/adapter-core/pom.xml b/adapters/oidc/adapter-core/pom.xml index 2baddd35d190..98c8f1103eeb 100755 --- a/adapters/oidc/adapter-core/pom.xml +++ b/adapters/oidc/adapter-core/pom.xml @@ -21,7 +21,7 @@ keycloak-parent org.keycloak - 999.0.0-SNAPSHOT + 24.0.5 ../../../pom.xml 4.0.0 diff --git a/adapters/oidc/installed/pom.xml b/adapters/oidc/installed/pom.xml index 985b81d485f2..663ce2ab7c00 100755 --- a/adapters/oidc/installed/pom.xml +++ b/adapters/oidc/installed/pom.xml @@ -21,7 +21,7 @@ keycloak-parent org.keycloak - 999.0.0-SNAPSHOT + 24.0.5 ../../../pom.xml 4.0.0 diff --git a/adapters/oidc/jakarta-servlet-filter/pom.xml b/adapters/oidc/jakarta-servlet-filter/pom.xml index c2105f8e1e94..2927ea3dc583 100755 --- a/adapters/oidc/jakarta-servlet-filter/pom.xml +++ b/adapters/oidc/jakarta-servlet-filter/pom.xml @@ -21,7 +21,7 @@ keycloak-parent org.keycloak - 999.0.0-SNAPSHOT + 24.0.5 ../../../pom.xml 4.0.0 diff --git a/adapters/oidc/jaxrs-oauth-client/pom.xml b/adapters/oidc/jaxrs-oauth-client/pom.xml index ef98434d3561..803dc6d86a25 100755 --- a/adapters/oidc/jaxrs-oauth-client/pom.xml +++ b/adapters/oidc/jaxrs-oauth-client/pom.xml @@ -21,7 +21,7 @@ keycloak-parent org.keycloak - 999.0.0-SNAPSHOT + 24.0.5 ../../../pom.xml 4.0.0 diff --git a/adapters/oidc/jetty/jetty-core/pom.xml b/adapters/oidc/jetty/jetty-core/pom.xml index f36d9780af3f..265589ab9184 100755 --- a/adapters/oidc/jetty/jetty-core/pom.xml +++ b/adapters/oidc/jetty/jetty-core/pom.xml @@ -21,7 +21,7 @@ keycloak-parent org.keycloak - 999.0.0-SNAPSHOT + 24.0.5 ../../../../pom.xml 4.0.0 diff --git a/adapters/oidc/jetty/jetty9.4/pom.xml b/adapters/oidc/jetty/jetty9.4/pom.xml index 3ddd0b2fa930..32107f3ae6e3 100644 --- a/adapters/oidc/jetty/jetty9.4/pom.xml +++ b/adapters/oidc/jetty/jetty9.4/pom.xml @@ -21,7 +21,7 @@ keycloak-parent org.keycloak - 999.0.0-SNAPSHOT + 24.0.5 ../../../../pom.xml 4.0.0 diff --git a/adapters/oidc/jetty/pom.xml b/adapters/oidc/jetty/pom.xml index 30b8b7706bea..f7db7183aeba 100755 --- a/adapters/oidc/jetty/pom.xml +++ b/adapters/oidc/jetty/pom.xml @@ -20,7 +20,7 @@ keycloak-parent org.keycloak - 999.0.0-SNAPSHOT + 24.0.5 ../../../pom.xml Keycloak Jetty Integration diff --git a/adapters/oidc/js/pom.xml b/adapters/oidc/js/pom.xml index e9bdb2780847..239530971ff9 100644 --- a/adapters/oidc/js/pom.xml +++ b/adapters/oidc/js/pom.xml @@ -5,7 +5,7 @@ keycloak-parent org.keycloak - 999.0.0-SNAPSHOT + 24.0.5 ../../../pom.xml diff --git a/adapters/oidc/pom.xml b/adapters/oidc/pom.xml index f4e37273be18..9a9bf517edae 100755 --- a/adapters/oidc/pom.xml +++ b/adapters/oidc/pom.xml @@ -20,7 +20,7 @@ keycloak-parent org.keycloak - 999.0.0-SNAPSHOT + 24.0.5 ../../pom.xml Keycloak OIDC Client Adapter Modules diff --git a/adapters/oidc/servlet-filter/pom.xml b/adapters/oidc/servlet-filter/pom.xml index 1e26928aa9b0..78eff3edfff7 100755 --- a/adapters/oidc/servlet-filter/pom.xml +++ b/adapters/oidc/servlet-filter/pom.xml @@ -21,7 +21,7 @@ keycloak-parent org.keycloak - 999.0.0-SNAPSHOT + 24.0.5 ../../../pom.xml 4.0.0 diff --git a/adapters/oidc/spring-boot-adapter-core/pom.xml b/adapters/oidc/spring-boot-adapter-core/pom.xml index ea1f9c6a6736..77b6884e0742 100755 --- a/adapters/oidc/spring-boot-adapter-core/pom.xml +++ b/adapters/oidc/spring-boot-adapter-core/pom.xml @@ -21,7 +21,7 @@ keycloak-parent org.keycloak - 999.0.0-SNAPSHOT + 24.0.5 ../../../pom.xml 4.0.0 diff --git a/adapters/oidc/spring-boot-container-bundle/pom.xml b/adapters/oidc/spring-boot-container-bundle/pom.xml index fd0be595f8d6..8b1675dbf164 100644 --- a/adapters/oidc/spring-boot-container-bundle/pom.xml +++ b/adapters/oidc/spring-boot-container-bundle/pom.xml @@ -4,7 +4,7 @@ keycloak-parent org.keycloak - 999.0.0-SNAPSHOT + 24.0.5 ../../../pom.xml spring-boot-container-bundle diff --git a/adapters/oidc/spring-boot2/pom.xml b/adapters/oidc/spring-boot2/pom.xml index 86115f6004fa..5d9f1d495e30 100755 --- a/adapters/oidc/spring-boot2/pom.xml +++ b/adapters/oidc/spring-boot2/pom.xml @@ -21,7 +21,7 @@ keycloak-parent org.keycloak - 999.0.0-SNAPSHOT + 24.0.5 ../../../pom.xml 4.0.0 diff --git a/adapters/oidc/spring-security/pom.xml b/adapters/oidc/spring-security/pom.xml index c85b8180a240..9ccd5ad9584d 100644 --- a/adapters/oidc/spring-security/pom.xml +++ b/adapters/oidc/spring-security/pom.xml @@ -21,7 +21,7 @@ keycloak-parent org.keycloak - 999.0.0-SNAPSHOT + 24.0.5 ../../../pom.xml 4.0.0 diff --git a/adapters/oidc/tomcat/pom.xml b/adapters/oidc/tomcat/pom.xml index e79897be4b29..111f9ac4268d 100755 --- a/adapters/oidc/tomcat/pom.xml +++ b/adapters/oidc/tomcat/pom.xml @@ -20,7 +20,7 @@ keycloak-parent org.keycloak - 999.0.0-SNAPSHOT + 24.0.5 ../../../pom.xml Keycloak Tomcat Integration diff --git a/adapters/oidc/tomcat/tomcat-core/pom.xml b/adapters/oidc/tomcat/tomcat-core/pom.xml index b7859c0d7d20..44efbeae2879 100755 --- a/adapters/oidc/tomcat/tomcat-core/pom.xml +++ b/adapters/oidc/tomcat/tomcat-core/pom.xml @@ -21,7 +21,7 @@ keycloak-tomcat-integration-pom org.keycloak - 999.0.0-SNAPSHOT + 24.0.5 ../pom.xml 4.0.0 diff --git a/adapters/oidc/tomcat/tomcat/pom.xml b/adapters/oidc/tomcat/tomcat/pom.xml index 2a5d96149038..ab5440f4f80e 100755 --- a/adapters/oidc/tomcat/tomcat/pom.xml +++ b/adapters/oidc/tomcat/tomcat/pom.xml @@ -21,7 +21,7 @@ keycloak-tomcat-integration-pom org.keycloak - 999.0.0-SNAPSHOT + 24.0.5 ../pom.xml 4.0.0 diff --git a/adapters/oidc/undertow/pom.xml b/adapters/oidc/undertow/pom.xml index 6d179ae84d86..e5c78bd41c9b 100755 --- a/adapters/oidc/undertow/pom.xml +++ b/adapters/oidc/undertow/pom.xml @@ -21,7 +21,7 @@ keycloak-parent org.keycloak - 999.0.0-SNAPSHOT + 24.0.5 ../../../pom.xml 4.0.0 diff --git a/adapters/oidc/wildfly-elytron/pom.xml b/adapters/oidc/wildfly-elytron/pom.xml index 1d8f5c9b5b86..67e1b508db5e 100755 --- a/adapters/oidc/wildfly-elytron/pom.xml +++ b/adapters/oidc/wildfly-elytron/pom.xml @@ -22,7 +22,7 @@ keycloak-parent org.keycloak - 999.0.0-SNAPSHOT + 24.0.5 ../../../pom.xml 4.0.0 diff --git a/adapters/oidc/wildfly/pom.xml b/adapters/oidc/wildfly/pom.xml index 173bf616d01a..f97c86702747 100755 --- a/adapters/oidc/wildfly/pom.xml +++ b/adapters/oidc/wildfly/pom.xml @@ -20,7 +20,7 @@ keycloak-parent org.keycloak - 999.0.0-SNAPSHOT + 24.0.5 ../../../pom.xml Keycloak WildFly Integration diff --git a/adapters/oidc/wildfly/wildfly-subsystem/pom.xml b/adapters/oidc/wildfly/wildfly-subsystem/pom.xml index 15f36da7fef0..374a99212a92 100755 --- a/adapters/oidc/wildfly/wildfly-subsystem/pom.xml +++ b/adapters/oidc/wildfly/wildfly-subsystem/pom.xml @@ -21,7 +21,7 @@ org.keycloak keycloak-parent - 999.0.0-SNAPSHOT + 24.0.5 ../../../../pom.xml diff --git a/adapters/pom.xml b/adapters/pom.xml index fac0534171c2..b3be935dc570 100755 --- a/adapters/pom.xml +++ b/adapters/pom.xml @@ -20,7 +20,7 @@ keycloak-parent org.keycloak - 999.0.0-SNAPSHOT + 24.0.5 ../pom.xml Keycloak Adapters diff --git a/adapters/saml/core-jakarta/pom.xml b/adapters/saml/core-jakarta/pom.xml index 2b4f82342130..7e84cd087a7f 100644 --- a/adapters/saml/core-jakarta/pom.xml +++ b/adapters/saml/core-jakarta/pom.xml @@ -6,7 +6,7 @@ keycloak-parent org.keycloak - 999.0.0-SNAPSHOT + 24.0.5 ../../../pom.xml diff --git a/adapters/saml/core-public/pom.xml b/adapters/saml/core-public/pom.xml index a5e4e0aa3f9b..4a5dc064e031 100755 --- a/adapters/saml/core-public/pom.xml +++ b/adapters/saml/core-public/pom.xml @@ -21,7 +21,7 @@ keycloak-parent org.keycloak - 999.0.0-SNAPSHOT + 24.0.5 ../../../pom.xml 4.0.0 diff --git a/adapters/saml/core/pom.xml b/adapters/saml/core/pom.xml index d72de7b0ee37..1d2e67fc39e7 100755 --- a/adapters/saml/core/pom.xml +++ b/adapters/saml/core/pom.xml @@ -21,7 +21,7 @@ keycloak-parent org.keycloak - 999.0.0-SNAPSHOT + 24.0.5 ../../../pom.xml 4.0.0 diff --git a/adapters/saml/jakarta-servlet-filter/pom.xml b/adapters/saml/jakarta-servlet-filter/pom.xml index 03b33d337b4c..1deea262f7ff 100755 --- a/adapters/saml/jakarta-servlet-filter/pom.xml +++ b/adapters/saml/jakarta-servlet-filter/pom.xml @@ -21,7 +21,7 @@ keycloak-parent org.keycloak - 999.0.0-SNAPSHOT + 24.0.5 ../../../pom.xml 4.0.0 diff --git a/adapters/saml/jetty/jetty-core/pom.xml b/adapters/saml/jetty/jetty-core/pom.xml index 7ad707523966..a849b825f709 100755 --- a/adapters/saml/jetty/jetty-core/pom.xml +++ b/adapters/saml/jetty/jetty-core/pom.xml @@ -21,7 +21,7 @@ keycloak-parent org.keycloak - 999.0.0-SNAPSHOT + 24.0.5 ../../../../pom.xml 4.0.0 diff --git a/adapters/saml/jetty/jetty9.4/pom.xml b/adapters/saml/jetty/jetty9.4/pom.xml index 91e5589f0b4c..be927cb1009a 100644 --- a/adapters/saml/jetty/jetty9.4/pom.xml +++ b/adapters/saml/jetty/jetty9.4/pom.xml @@ -21,7 +21,7 @@ keycloak-parent org.keycloak - 999.0.0-SNAPSHOT + 24.0.5 ../../../../pom.xml 4.0.0 diff --git a/adapters/saml/jetty/pom.xml b/adapters/saml/jetty/pom.xml index dda79695de4a..d02d240b4981 100755 --- a/adapters/saml/jetty/pom.xml +++ b/adapters/saml/jetty/pom.xml @@ -20,7 +20,7 @@ keycloak-parent org.keycloak - 999.0.0-SNAPSHOT + 24.0.5 ../../../pom.xml Keycloak SAML Jetty Integration diff --git a/adapters/saml/pom.xml b/adapters/saml/pom.xml index 443ad2a468d0..22ffb0c623a1 100755 --- a/adapters/saml/pom.xml +++ b/adapters/saml/pom.xml @@ -20,7 +20,7 @@ keycloak-parent org.keycloak - 999.0.0-SNAPSHOT + 24.0.5 ../../pom.xml Keycloak SAML Client Adapter Modules diff --git a/adapters/saml/servlet-filter/pom.xml b/adapters/saml/servlet-filter/pom.xml index 5fc027042952..385d9ab3b589 100755 --- a/adapters/saml/servlet-filter/pom.xml +++ b/adapters/saml/servlet-filter/pom.xml @@ -21,7 +21,7 @@ keycloak-parent org.keycloak - 999.0.0-SNAPSHOT + 24.0.5 ../../../pom.xml 4.0.0 diff --git a/adapters/saml/tomcat/pom.xml b/adapters/saml/tomcat/pom.xml index 70bd25315d01..ad79e20cb08b 100755 --- a/adapters/saml/tomcat/pom.xml +++ b/adapters/saml/tomcat/pom.xml @@ -20,7 +20,7 @@ keycloak-parent org.keycloak - 999.0.0-SNAPSHOT + 24.0.5 ../../../pom.xml Keycloak SAML Tomcat Integration diff --git a/adapters/saml/tomcat/tomcat-core/pom.xml b/adapters/saml/tomcat/tomcat-core/pom.xml index 9844e59f090d..505f93971efe 100755 --- a/adapters/saml/tomcat/tomcat-core/pom.xml +++ b/adapters/saml/tomcat/tomcat-core/pom.xml @@ -21,7 +21,7 @@ keycloak-saml-tomcat-integration-pom org.keycloak - 999.0.0-SNAPSHOT + 24.0.5 ../pom.xml 4.0.0 diff --git a/adapters/saml/tomcat/tomcat/pom.xml b/adapters/saml/tomcat/tomcat/pom.xml index 0dd64f673b34..db02a09eeb61 100755 --- a/adapters/saml/tomcat/tomcat/pom.xml +++ b/adapters/saml/tomcat/tomcat/pom.xml @@ -21,7 +21,7 @@ keycloak-saml-tomcat-integration-pom org.keycloak - 999.0.0-SNAPSHOT + 24.0.5 ../pom.xml 4.0.0 diff --git a/adapters/saml/undertow/pom.xml b/adapters/saml/undertow/pom.xml index b4d5480a78c1..79776568f129 100755 --- a/adapters/saml/undertow/pom.xml +++ b/adapters/saml/undertow/pom.xml @@ -21,7 +21,7 @@ keycloak-parent org.keycloak - 999.0.0-SNAPSHOT + 24.0.5 ../../../pom.xml 4.0.0 diff --git a/adapters/saml/wildfly-elytron-jakarta/pom.xml b/adapters/saml/wildfly-elytron-jakarta/pom.xml index f8dc3b5d5d88..46a73c4d71d2 100755 --- a/adapters/saml/wildfly-elytron-jakarta/pom.xml +++ b/adapters/saml/wildfly-elytron-jakarta/pom.xml @@ -21,7 +21,7 @@ keycloak-parent org.keycloak - 999.0.0-SNAPSHOT + 24.0.5 ../../../pom.xml 4.0.0 diff --git a/adapters/saml/wildfly-elytron/pom.xml b/adapters/saml/wildfly-elytron/pom.xml index cc78c6e482b1..647588752094 100755 --- a/adapters/saml/wildfly-elytron/pom.xml +++ b/adapters/saml/wildfly-elytron/pom.xml @@ -21,7 +21,7 @@ keycloak-parent org.keycloak - 999.0.0-SNAPSHOT + 24.0.5 ../../../pom.xml 4.0.0 diff --git a/adapters/saml/wildfly/pom.xml b/adapters/saml/wildfly/pom.xml index 02f49d830727..54ea545ebd51 100755 --- a/adapters/saml/wildfly/pom.xml +++ b/adapters/saml/wildfly/pom.xml @@ -20,7 +20,7 @@ keycloak-parent org.keycloak - 999.0.0-SNAPSHOT + 24.0.5 ../../../pom.xml Keycloak SAML Wildfly Integration diff --git a/adapters/saml/wildfly/wildfly-jakarta-subsystem/pom.xml b/adapters/saml/wildfly/wildfly-jakarta-subsystem/pom.xml index 15e52a641515..c19ad9284602 100755 --- a/adapters/saml/wildfly/wildfly-jakarta-subsystem/pom.xml +++ b/adapters/saml/wildfly/wildfly-jakarta-subsystem/pom.xml @@ -21,7 +21,7 @@ org.keycloak keycloak-parent - 999.0.0-SNAPSHOT + 24.0.5 ../../../../pom.xml diff --git a/adapters/saml/wildfly/wildfly-subsystem/pom.xml b/adapters/saml/wildfly/wildfly-subsystem/pom.xml index 90b38b0f7462..66a5593fec9c 100755 --- a/adapters/saml/wildfly/wildfly-subsystem/pom.xml +++ b/adapters/saml/wildfly/wildfly-subsystem/pom.xml @@ -21,7 +21,7 @@ org.keycloak keycloak-parent - 999.0.0-SNAPSHOT + 24.0.5 ../../../../pom.xml diff --git a/adapters/spi/adapter-spi/pom.xml b/adapters/spi/adapter-spi/pom.xml index aab344d284be..cf3996c05e85 100755 --- a/adapters/spi/adapter-spi/pom.xml +++ b/adapters/spi/adapter-spi/pom.xml @@ -21,7 +21,7 @@ keycloak-parent org.keycloak - 999.0.0-SNAPSHOT + 24.0.5 ../../../pom.xml 4.0.0 diff --git a/adapters/spi/jakarta-servlet-adapter-spi/pom.xml b/adapters/spi/jakarta-servlet-adapter-spi/pom.xml index a5d8d48e4729..24caa4ed7578 100755 --- a/adapters/spi/jakarta-servlet-adapter-spi/pom.xml +++ b/adapters/spi/jakarta-servlet-adapter-spi/pom.xml @@ -21,7 +21,7 @@ keycloak-parent org.keycloak - 999.0.0-SNAPSHOT + 24.0.5 ../../../pom.xml 4.0.0 diff --git a/adapters/spi/jboss-adapter-core/pom.xml b/adapters/spi/jboss-adapter-core/pom.xml index a184ae5e0d8a..87dd226c548b 100755 --- a/adapters/spi/jboss-adapter-core/pom.xml +++ b/adapters/spi/jboss-adapter-core/pom.xml @@ -21,7 +21,7 @@ keycloak-parent org.keycloak - 999.0.0-SNAPSHOT + 24.0.5 ../../../pom.xml 4.0.0 diff --git a/adapters/spi/jetty-adapter-spi/pom.xml b/adapters/spi/jetty-adapter-spi/pom.xml index 17a0654fd008..128079d12f0b 100755 --- a/adapters/spi/jetty-adapter-spi/pom.xml +++ b/adapters/spi/jetty-adapter-spi/pom.xml @@ -21,7 +21,7 @@ keycloak-parent org.keycloak - 999.0.0-SNAPSHOT + 24.0.5 ../../../pom.xml 4.0.0 diff --git a/adapters/spi/pom.xml b/adapters/spi/pom.xml index 6bd05d9b412a..83426f598757 100755 --- a/adapters/spi/pom.xml +++ b/adapters/spi/pom.xml @@ -20,7 +20,7 @@ keycloak-parent org.keycloak - 999.0.0-SNAPSHOT + 24.0.5 ../../pom.xml Keycloak Client Adapter SPI Modules diff --git a/adapters/spi/servlet-adapter-spi/pom.xml b/adapters/spi/servlet-adapter-spi/pom.xml index 2d8f0ca1f6dd..f463a3b92f31 100755 --- a/adapters/spi/servlet-adapter-spi/pom.xml +++ b/adapters/spi/servlet-adapter-spi/pom.xml @@ -21,7 +21,7 @@ keycloak-parent org.keycloak - 999.0.0-SNAPSHOT + 24.0.5 ../../../pom.xml 4.0.0 diff --git a/adapters/spi/tomcat-adapter-spi/pom.xml b/adapters/spi/tomcat-adapter-spi/pom.xml index 0588a9826061..904fc2053870 100755 --- a/adapters/spi/tomcat-adapter-spi/pom.xml +++ b/adapters/spi/tomcat-adapter-spi/pom.xml @@ -21,7 +21,7 @@ keycloak-parent org.keycloak - 999.0.0-SNAPSHOT + 24.0.5 ../../../pom.xml 4.0.0 diff --git a/adapters/spi/undertow-adapter-spi/pom.xml b/adapters/spi/undertow-adapter-spi/pom.xml index 884b93c77400..a4a91e338505 100755 --- a/adapters/spi/undertow-adapter-spi/pom.xml +++ b/adapters/spi/undertow-adapter-spi/pom.xml @@ -21,7 +21,7 @@ keycloak-parent org.keycloak - 999.0.0-SNAPSHOT + 24.0.5 ../../../pom.xml 4.0.0 diff --git a/authz/client/pom.xml b/authz/client/pom.xml index d410042e7ec0..f8b16c6b7fce 100644 --- a/authz/client/pom.xml +++ b/authz/client/pom.xml @@ -7,7 +7,7 @@ org.keycloak keycloak-authz-parent - 999.0.0-SNAPSHOT + 24.0.5 ../pom.xml diff --git a/authz/policy-enforcer/pom.xml b/authz/policy-enforcer/pom.xml index 096833adf698..63d64e2beea4 100755 --- a/authz/policy-enforcer/pom.xml +++ b/authz/policy-enforcer/pom.xml @@ -21,7 +21,7 @@ org.keycloak keycloak-authz-parent - 999.0.0-SNAPSHOT + 24.0.5 ../pom.xml 4.0.0 diff --git a/authz/policy/common/pom.xml b/authz/policy/common/pom.xml index ec7c0f5ddc4d..8bd9a5022abb 100644 --- a/authz/policy/common/pom.xml +++ b/authz/policy/common/pom.xml @@ -25,7 +25,7 @@ org.keycloak keycloak-authz-provider-parent - 999.0.0-SNAPSHOT + 24.0.5 ../pom.xml diff --git a/authz/policy/pom.xml b/authz/policy/pom.xml index 48091592faad..cae95a5a23e2 100644 --- a/authz/policy/pom.xml +++ b/authz/policy/pom.xml @@ -7,7 +7,7 @@ org.keycloak keycloak-authz-parent - 999.0.0-SNAPSHOT + 24.0.5 ../pom.xml diff --git a/authz/pom.xml b/authz/pom.xml index 31224da50309..477c28ecd3ff 100644 --- a/authz/pom.xml +++ b/authz/pom.xml @@ -7,7 +7,7 @@ org.keycloak keycloak-parent - 999.0.0-SNAPSHOT + 24.0.5 ../pom.xml diff --git a/boms/adapter/pom.xml b/boms/adapter/pom.xml index 417c7107fde5..f1fac6ff25a6 100644 --- a/boms/adapter/pom.xml +++ b/boms/adapter/pom.xml @@ -22,7 +22,7 @@ org.keycloak.bom keycloak-bom-parent - 999.0.0-SNAPSHOT + 24.0.5 org.keycloak.bom diff --git a/boms/misc/pom.xml b/boms/misc/pom.xml index 7305aa3bb610..47b917d59b73 100644 --- a/boms/misc/pom.xml +++ b/boms/misc/pom.xml @@ -22,7 +22,7 @@ org.keycloak.bom keycloak-bom-parent - 999.0.0-SNAPSHOT + 24.0.5 org.keycloak.bom diff --git a/boms/pom.xml b/boms/pom.xml index c67416d7afa0..520b43a00937 100644 --- a/boms/pom.xml +++ b/boms/pom.xml @@ -27,7 +27,7 @@ org.keycloak.bom keycloak-bom-parent - 999.0.0-SNAPSHOT + 24.0.5 pom diff --git a/boms/spi/pom.xml b/boms/spi/pom.xml index 339c18bb3033..e118390570c0 100644 --- a/boms/spi/pom.xml +++ b/boms/spi/pom.xml @@ -23,7 +23,7 @@ org.keycloak.bom keycloak-bom-parent - 999.0.0-SNAPSHOT + 24.0.5 org.keycloak.bom diff --git a/common/pom.xml b/common/pom.xml index 5f644cbc0af2..a9c4cb09e558 100755 --- a/common/pom.xml +++ b/common/pom.xml @@ -21,7 +21,7 @@ keycloak-parent org.keycloak - 999.0.0-SNAPSHOT + 24.0.5 ../pom.xml 4.0.0 diff --git a/core/pom.xml b/core/pom.xml index c057066ccc49..b63dcee53711 100755 --- a/core/pom.xml +++ b/core/pom.xml @@ -21,7 +21,7 @@ keycloak-parent org.keycloak - 999.0.0-SNAPSHOT + 24.0.5 ../pom.xml 4.0.0 diff --git a/crypto/default/pom.xml b/crypto/default/pom.xml index 2b0cf1fb234f..9d60cf92b7a9 100644 --- a/crypto/default/pom.xml +++ b/crypto/default/pom.xml @@ -21,7 +21,7 @@ keycloak-crypto-parent org.keycloak - 999.0.0-SNAPSHOT + 24.0.5 ../pom.xml 4.0.0 diff --git a/crypto/elytron/pom.xml b/crypto/elytron/pom.xml index 51b7d6dbeac7..499551674b64 100644 --- a/crypto/elytron/pom.xml +++ b/crypto/elytron/pom.xml @@ -21,7 +21,7 @@ keycloak-crypto-parent org.keycloak - 999.0.0-SNAPSHOT + 24.0.5 ../pom.xml 4.0.0 diff --git a/crypto/fips1402/pom.xml b/crypto/fips1402/pom.xml index 7691a0f12880..9a53632f0e22 100644 --- a/crypto/fips1402/pom.xml +++ b/crypto/fips1402/pom.xml @@ -21,7 +21,7 @@ keycloak-crypto-parent org.keycloak - 999.0.0-SNAPSHOT + 24.0.5 ../pom.xml 4.0.0 diff --git a/crypto/pom.xml b/crypto/pom.xml index de2f6d339653..2b0ed4d2bba9 100644 --- a/crypto/pom.xml +++ b/crypto/pom.xml @@ -20,7 +20,7 @@ keycloak-parent org.keycloak - 999.0.0-SNAPSHOT + 24.0.5 ../pom.xml Keycloak Crypto Parent diff --git a/dependencies/pom.xml b/dependencies/pom.xml index 019146bf55c3..66f60000ad34 100755 --- a/dependencies/pom.xml +++ b/dependencies/pom.xml @@ -21,7 +21,7 @@ keycloak-parent org.keycloak - 999.0.0-SNAPSHOT + 24.0.5 4.0.0 diff --git a/dependencies/server-all/pom.xml b/dependencies/server-all/pom.xml index c25827c2a373..5d711bb37a17 100755 --- a/dependencies/server-all/pom.xml +++ b/dependencies/server-all/pom.xml @@ -21,7 +21,7 @@ keycloak-dependencies-parent org.keycloak - 999.0.0-SNAPSHOT + 24.0.5 4.0.0 diff --git a/dependencies/server-min/pom.xml b/dependencies/server-min/pom.xml index 8749833cd6fd..4ab8a7db3c15 100755 --- a/dependencies/server-min/pom.xml +++ b/dependencies/server-min/pom.xml @@ -21,7 +21,7 @@ keycloak-dependencies-parent org.keycloak - 999.0.0-SNAPSHOT + 24.0.5 4.0.0 diff --git a/distribution/adapters/pom.xml b/distribution/adapters/pom.xml index 2ae54a9b8a74..729fb2edf1fb 100755 --- a/distribution/adapters/pom.xml +++ b/distribution/adapters/pom.xml @@ -20,7 +20,7 @@ keycloak-distribution-parent org.keycloak - 999.0.0-SNAPSHOT + 24.0.5 Adapters Distribution Parent diff --git a/distribution/adapters/tomcat-adapter-zip/pom.xml b/distribution/adapters/tomcat-adapter-zip/pom.xml index c6f4d90b26c6..9a47ce58ba59 100755 --- a/distribution/adapters/tomcat-adapter-zip/pom.xml +++ b/distribution/adapters/tomcat-adapter-zip/pom.xml @@ -21,7 +21,7 @@ keycloak-parent org.keycloak - 999.0.0-SNAPSHOT + 24.0.5 ../../../pom.xml diff --git a/distribution/adapters/wildfly-adapter/pom.xml b/distribution/adapters/wildfly-adapter/pom.xml index 96ad4d4de431..0bd83e1d89bb 100644 --- a/distribution/adapters/wildfly-adapter/pom.xml +++ b/distribution/adapters/wildfly-adapter/pom.xml @@ -21,7 +21,7 @@ keycloak-adapters-distribution-parent org.keycloak - 999.0.0-SNAPSHOT + 24.0.5 diff --git a/distribution/api-docs-dist/pom.xml b/distribution/api-docs-dist/pom.xml index dc67e12f110e..1170c0555a7a 100755 --- a/distribution/api-docs-dist/pom.xml +++ b/distribution/api-docs-dist/pom.xml @@ -21,7 +21,7 @@ keycloak-distribution-parent org.keycloak - 999.0.0-SNAPSHOT + 24.0.5 keycloak-api-docs-dist diff --git a/distribution/downloads/pom.xml b/distribution/downloads/pom.xml index 3651b049e9b5..e676e1792512 100755 --- a/distribution/downloads/pom.xml +++ b/distribution/downloads/pom.xml @@ -21,7 +21,7 @@ keycloak-distribution-parent org.keycloak - 999.0.0-SNAPSHOT + 24.0.5 keycloak-dist-downloads diff --git a/distribution/feature-packs/adapter-feature-pack/pom.xml b/distribution/feature-packs/adapter-feature-pack/pom.xml index cd9b1baf68a9..67d8050c9e86 100755 --- a/distribution/feature-packs/adapter-feature-pack/pom.xml +++ b/distribution/feature-packs/adapter-feature-pack/pom.xml @@ -19,7 +19,7 @@ org.keycloak feature-packs-parent - 999.0.0-SNAPSHOT + 24.0.5 diff --git a/distribution/feature-packs/pom.xml b/distribution/feature-packs/pom.xml index 4c66eecf261f..70fcce625843 100644 --- a/distribution/feature-packs/pom.xml +++ b/distribution/feature-packs/pom.xml @@ -20,7 +20,7 @@ keycloak-distribution-parent org.keycloak - 999.0.0-SNAPSHOT + 24.0.5 Feature Pack Builds diff --git a/distribution/galleon-feature-packs/pom.xml b/distribution/galleon-feature-packs/pom.xml index fc16e8511026..411a7d9035f2 100644 --- a/distribution/galleon-feature-packs/pom.xml +++ b/distribution/galleon-feature-packs/pom.xml @@ -20,7 +20,7 @@ keycloak-distribution-parent org.keycloak - 999.0.0-SNAPSHOT + 24.0.5 Galleon Feature Pack Builds diff --git a/distribution/galleon-feature-packs/saml-adapter-galleon-pack-layer-metadata-tests/pom.xml b/distribution/galleon-feature-packs/saml-adapter-galleon-pack-layer-metadata-tests/pom.xml index d9d706e53e88..6bcff79a6c84 100644 --- a/distribution/galleon-feature-packs/saml-adapter-galleon-pack-layer-metadata-tests/pom.xml +++ b/distribution/galleon-feature-packs/saml-adapter-galleon-pack-layer-metadata-tests/pom.xml @@ -19,7 +19,7 @@ org.keycloak galleon-feature-packs-parent - 999.0.0-SNAPSHOT + 24.0.5 4.0.0 diff --git a/distribution/galleon-feature-packs/saml-adapter-galleon-pack/pom.xml b/distribution/galleon-feature-packs/saml-adapter-galleon-pack/pom.xml index d46987e74272..a0bf0e24bd28 100644 --- a/distribution/galleon-feature-packs/saml-adapter-galleon-pack/pom.xml +++ b/distribution/galleon-feature-packs/saml-adapter-galleon-pack/pom.xml @@ -19,7 +19,7 @@ org.keycloak galleon-feature-packs-parent - 999.0.0-SNAPSHOT + 24.0.5 4.0.0 diff --git a/distribution/licenses-common/pom.xml b/distribution/licenses-common/pom.xml index bf615a63ee6f..f42352601d99 100644 --- a/distribution/licenses-common/pom.xml +++ b/distribution/licenses-common/pom.xml @@ -20,7 +20,7 @@ keycloak-distribution-parent org.keycloak - 999.0.0-SNAPSHOT + 24.0.5 keycloak-distribution-licenses-common diff --git a/distribution/maven-plugins/licenses-processor/pom.xml b/distribution/maven-plugins/licenses-processor/pom.xml index 547d24d96374..198f2952a96d 100644 --- a/distribution/maven-plugins/licenses-processor/pom.xml +++ b/distribution/maven-plugins/licenses-processor/pom.xml @@ -20,7 +20,7 @@ keycloak-distribution-maven-plugins-parent org.keycloak - 999.0.0-SNAPSHOT + 24.0.5 keycloak-distribution-licenses-maven-plugin diff --git a/distribution/maven-plugins/pom.xml b/distribution/maven-plugins/pom.xml index fe9b7db2e36c..277bdd07691e 100644 --- a/distribution/maven-plugins/pom.xml +++ b/distribution/maven-plugins/pom.xml @@ -20,7 +20,7 @@ keycloak-distribution-parent org.keycloak - 999.0.0-SNAPSHOT + 24.0.5 keycloak-distribution-maven-plugins-parent diff --git a/distribution/pom.xml b/distribution/pom.xml index d5ba3d6a1533..8f87bc041018 100755 --- a/distribution/pom.xml +++ b/distribution/pom.xml @@ -20,7 +20,7 @@ keycloak-parent org.keycloak - 999.0.0-SNAPSHOT + 24.0.5 ../pom.xml diff --git a/distribution/saml-adapters/pom.xml b/distribution/saml-adapters/pom.xml index 14e68e4f3511..622619c6818a 100755 --- a/distribution/saml-adapters/pom.xml +++ b/distribution/saml-adapters/pom.xml @@ -20,7 +20,7 @@ keycloak-distribution-parent org.keycloak - 999.0.0-SNAPSHOT + 24.0.5 SAML Adapters Distribution Parent diff --git a/distribution/saml-adapters/tomcat-adapter-zip/pom.xml b/distribution/saml-adapters/tomcat-adapter-zip/pom.xml index 3710b43611de..cb5cfb562b57 100755 --- a/distribution/saml-adapters/tomcat-adapter-zip/pom.xml +++ b/distribution/saml-adapters/tomcat-adapter-zip/pom.xml @@ -21,7 +21,7 @@ keycloak-parent org.keycloak - 999.0.0-SNAPSHOT + 24.0.5 ../../../pom.xml diff --git a/distribution/saml-adapters/wildfly-adapter/pom.xml b/distribution/saml-adapters/wildfly-adapter/pom.xml index ab52d865dab5..5b646b4ca8fc 100755 --- a/distribution/saml-adapters/wildfly-adapter/pom.xml +++ b/distribution/saml-adapters/wildfly-adapter/pom.xml @@ -20,7 +20,7 @@ keycloak-parent org.keycloak - 999.0.0-SNAPSHOT + 24.0.5 ../../../pom.xml Keycloak Wildfly SAML Adapter diff --git a/distribution/saml-adapters/wildfly-adapter/wildfly-adapter-jakarta-zip/pom.xml b/distribution/saml-adapters/wildfly-adapter/wildfly-adapter-jakarta-zip/pom.xml index 794389bd1605..07bd7858593d 100755 --- a/distribution/saml-adapters/wildfly-adapter/wildfly-adapter-jakarta-zip/pom.xml +++ b/distribution/saml-adapters/wildfly-adapter/wildfly-adapter-jakarta-zip/pom.xml @@ -21,7 +21,7 @@ keycloak-parent org.keycloak - 999.0.0-SNAPSHOT + 24.0.5 ../../../../pom.xml diff --git a/distribution/saml-adapters/wildfly-adapter/wildfly-adapter-zip/pom.xml b/distribution/saml-adapters/wildfly-adapter/wildfly-adapter-zip/pom.xml index d503885a0fb5..c7d41f401274 100755 --- a/distribution/saml-adapters/wildfly-adapter/wildfly-adapter-zip/pom.xml +++ b/distribution/saml-adapters/wildfly-adapter/wildfly-adapter-zip/pom.xml @@ -21,7 +21,7 @@ keycloak-parent org.keycloak - 999.0.0-SNAPSHOT + 24.0.5 ../../../../pom.xml diff --git a/distribution/saml-adapters/wildfly-adapter/wildfly-jakarta-modules/pom.xml b/distribution/saml-adapters/wildfly-adapter/wildfly-jakarta-modules/pom.xml index 634ddcbee246..bd65d8951d88 100755 --- a/distribution/saml-adapters/wildfly-adapter/wildfly-jakarta-modules/pom.xml +++ b/distribution/saml-adapters/wildfly-adapter/wildfly-jakarta-modules/pom.xml @@ -25,7 +25,7 @@ keycloak-parent org.keycloak - 999.0.0-SNAPSHOT + 24.0.5 ../../../../pom.xml diff --git a/distribution/saml-adapters/wildfly-adapter/wildfly-modules/pom.xml b/distribution/saml-adapters/wildfly-adapter/wildfly-modules/pom.xml index 68cc6b70703f..beba9d3516f2 100755 --- a/distribution/saml-adapters/wildfly-adapter/wildfly-modules/pom.xml +++ b/distribution/saml-adapters/wildfly-adapter/wildfly-modules/pom.xml @@ -25,7 +25,7 @@ keycloak-parent org.keycloak - 999.0.0-SNAPSHOT + 24.0.5 ../../../../pom.xml diff --git a/docs/documentation/aggregation/pom.xml b/docs/documentation/aggregation/pom.xml index af92b7cddec8..fb100caeab43 100644 --- a/docs/documentation/aggregation/pom.xml +++ b/docs/documentation/aggregation/pom.xml @@ -5,7 +5,7 @@ org.keycloak.documentation documentation-parent - 999.0.0-SNAPSHOT + 24.0.5 ../pom.xml diff --git a/docs/documentation/api_documentation/pom.xml b/docs/documentation/api_documentation/pom.xml index 580cefbb335e..02cab5851dfb 100644 --- a/docs/documentation/api_documentation/pom.xml +++ b/docs/documentation/api_documentation/pom.xml @@ -5,7 +5,7 @@ org.keycloak.documentation documentation-parent - 999.0.0-SNAPSHOT + 24.0.5 ../pom.xml diff --git a/docs/documentation/authorization_services/pom.xml b/docs/documentation/authorization_services/pom.xml index 6e1042aa8410..0e78b6d44cc2 100644 --- a/docs/documentation/authorization_services/pom.xml +++ b/docs/documentation/authorization_services/pom.xml @@ -5,7 +5,7 @@ org.keycloak.documentation documentation-parent - 999.0.0-SNAPSHOT + 24.0.5 ../pom.xml diff --git a/docs/documentation/dist/pom.xml b/docs/documentation/dist/pom.xml index 7ae91bdb25a5..c4fb6f92fff7 100644 --- a/docs/documentation/dist/pom.xml +++ b/docs/documentation/dist/pom.xml @@ -5,7 +5,7 @@ org.keycloak.documentation documentation-parent - 999.0.0-SNAPSHOT + 24.0.5 ../pom.xml diff --git a/docs/documentation/header-maven-plugin/pom.xml b/docs/documentation/header-maven-plugin/pom.xml index ff6ef9310a40..cd84d508d0a1 100644 --- a/docs/documentation/header-maven-plugin/pom.xml +++ b/docs/documentation/header-maven-plugin/pom.xml @@ -5,12 +5,12 @@ documentation-parent org.keycloak.documentation - 999.0.0-SNAPSHOT + 24.0.5 org.keycloak.documentation header-maven-plugin - 999.0.0-SNAPSHOT + 24.0.5 maven-plugin github-maven-plugin diff --git a/docs/documentation/pom.xml b/docs/documentation/pom.xml index d228d63f1adb..d1d956635565 100644 --- a/docs/documentation/pom.xml +++ b/docs/documentation/pom.xml @@ -5,14 +5,14 @@ keycloak-docs-parent org.keycloak - 999.0.0-SNAPSHOT + 24.0.5 ../pom.xml Keycloak Documentation Parent org.keycloak.documentation documentation-parent - 999.0.0-SNAPSHOT + 24.0.5 pom diff --git a/docs/documentation/release_notes/pom.xml b/docs/documentation/release_notes/pom.xml index 57d62bebae50..0d8affabaf21 100644 --- a/docs/documentation/release_notes/pom.xml +++ b/docs/documentation/release_notes/pom.xml @@ -5,7 +5,7 @@ org.keycloak.documentation documentation-parent - 999.0.0-SNAPSHOT + 24.0.5 ../pom.xml diff --git a/docs/documentation/securing_apps/pom.xml b/docs/documentation/securing_apps/pom.xml index 6d8dce060ebe..98643276daa3 100644 --- a/docs/documentation/securing_apps/pom.xml +++ b/docs/documentation/securing_apps/pom.xml @@ -5,7 +5,7 @@ org.keycloak.documentation documentation-parent - 999.0.0-SNAPSHOT + 24.0.5 ../pom.xml diff --git a/docs/documentation/server_admin/pom.xml b/docs/documentation/server_admin/pom.xml index cb0c3fdffef2..4bce6539b2ad 100644 --- a/docs/documentation/server_admin/pom.xml +++ b/docs/documentation/server_admin/pom.xml @@ -5,7 +5,7 @@ org.keycloak.documentation documentation-parent - 999.0.0-SNAPSHOT + 24.0.5 ../pom.xml diff --git a/docs/documentation/server_development/pom.xml b/docs/documentation/server_development/pom.xml index 6e0ec2e820c6..ed375e1fb643 100644 --- a/docs/documentation/server_development/pom.xml +++ b/docs/documentation/server_development/pom.xml @@ -5,7 +5,7 @@ org.keycloak.documentation documentation-parent - 999.0.0-SNAPSHOT + 24.0.5 ../pom.xml diff --git a/docs/documentation/tests/pom.xml b/docs/documentation/tests/pom.xml index 3c2559aef269..d43dc8130b4e 100644 --- a/docs/documentation/tests/pom.xml +++ b/docs/documentation/tests/pom.xml @@ -60,7 +60,7 @@ org.keycloak.documentation documentation-parent - 999.0.0-SNAPSHOT + 24.0.5 ../pom.xml diff --git a/docs/documentation/topics/templates/document-attributes.adoc b/docs/documentation/topics/templates/document-attributes.adoc index 6bc252fbb001..1d0955088b43 100644 --- a/docs/documentation/topics/templates/document-attributes.adoc +++ b/docs/documentation/topics/templates/document-attributes.adoc @@ -2,10 +2,10 @@ :project_name_full: Keycloak :project_community: true :project_product: false -:project_version: DEV -:project_versionMvn: 999.0.0-SNAPSHOT -:project_versionNpm: 999.0.0-SNAPSHOT -:project_versionDoc: DEV +:project_version: 24.0.5 +:project_versionMvn: 24.0.5 +:project_versionNpm: 24.0.5 +:project_versionDoc: 24.0.5 :archivebasename: keycloak :archivedownloadurl: https://github.com/keycloak/keycloak/releases/download/{project_version}/keycloak-{project_version}.zip diff --git a/docs/documentation/upgrading/pom.xml b/docs/documentation/upgrading/pom.xml index 9c081109a538..8fa348237047 100644 --- a/docs/documentation/upgrading/pom.xml +++ b/docs/documentation/upgrading/pom.xml @@ -5,7 +5,7 @@ org.keycloak.documentation documentation-parent - 999.0.0-SNAPSHOT + 24.0.5 ../pom.xml diff --git a/docs/guides/pom.xml b/docs/guides/pom.xml index 050466394dcb..3eb19330d37f 100644 --- a/docs/guides/pom.xml +++ b/docs/guides/pom.xml @@ -19,7 +19,7 @@ keycloak-docs-parent org.keycloak - 999.0.0-SNAPSHOT + 24.0.5 ../pom.xml diff --git a/docs/maven-plugin/pom.xml b/docs/maven-plugin/pom.xml index 2c07a98d449c..1fcde54db620 100644 --- a/docs/maven-plugin/pom.xml +++ b/docs/maven-plugin/pom.xml @@ -20,7 +20,7 @@ keycloak-docs-parent org.keycloak - 999.0.0-SNAPSHOT + 24.0.5 ../pom.xml diff --git a/docs/pom.xml b/docs/pom.xml index 35f5dfa64ff6..84223a01b3c9 100755 --- a/docs/pom.xml +++ b/docs/pom.xml @@ -19,7 +19,7 @@ keycloak-parent org.keycloak - 999.0.0-SNAPSHOT + 24.0.5 ../pom.xml Keycloak Docs Parent diff --git a/examples/admin-client/pom.xml b/examples/admin-client/pom.xml index 713d1db1d74c..68e4454bcaf8 100755 --- a/examples/admin-client/pom.xml +++ b/examples/admin-client/pom.xml @@ -22,7 +22,7 @@ keycloak-examples-parent org.keycloak - 999.0.0-SNAPSHOT + 24.0.5 Keycloak Examples - Admin Client diff --git a/examples/js-console/pom.xml b/examples/js-console/pom.xml index e26e650e56f2..ecfccacbb942 100755 --- a/examples/js-console/pom.xml +++ b/examples/js-console/pom.xml @@ -21,7 +21,7 @@ keycloak-examples-parent org.keycloak - 999.0.0-SNAPSHOT + 24.0.5 4.0.0 diff --git a/examples/kerberos/pom.xml b/examples/kerberos/pom.xml index 985373bde0d7..d53dec85d1f9 100755 --- a/examples/kerberos/pom.xml +++ b/examples/kerberos/pom.xml @@ -22,7 +22,7 @@ keycloak-examples-parent org.keycloak - 999.0.0-SNAPSHOT + 24.0.5 Keycloak Examples - Kerberos Credential Delegation diff --git a/examples/ldap/pom.xml b/examples/ldap/pom.xml index 9e2bd4c20513..56d809229983 100644 --- a/examples/ldap/pom.xml +++ b/examples/ldap/pom.xml @@ -22,7 +22,7 @@ keycloak-examples-parent org.keycloak - 999.0.0-SNAPSHOT + 24.0.5 4.0.0 diff --git a/examples/pom.xml b/examples/pom.xml index b2a2186f7b9a..b37575352346 100755 --- a/examples/pom.xml +++ b/examples/pom.xml @@ -20,7 +20,7 @@ keycloak-parent org.keycloak - 999.0.0-SNAPSHOT + 24.0.5 Keycloak Examples diff --git a/examples/providers/authenticator/pom.xml b/examples/providers/authenticator/pom.xml index dc87b6705f50..40d5cafb4327 100755 --- a/examples/providers/authenticator/pom.xml +++ b/examples/providers/authenticator/pom.xml @@ -20,7 +20,7 @@ keycloak-examples-providers-parent org.keycloak - 999.0.0-SNAPSHOT + 24.0.5 Authenticator Example diff --git a/examples/providers/pom.xml b/examples/providers/pom.xml index 198116d57c1d..aca94c6d0c58 100755 --- a/examples/providers/pom.xml +++ b/examples/providers/pom.xml @@ -20,7 +20,7 @@ keycloak-examples-parent org.keycloak - 999.0.0-SNAPSHOT + 24.0.5 Provider Examples diff --git a/examples/providers/rest/pom.xml b/examples/providers/rest/pom.xml index dead2d35943f..7c632a0638b8 100755 --- a/examples/providers/rest/pom.xml +++ b/examples/providers/rest/pom.xml @@ -20,7 +20,7 @@ keycloak-examples-providers-parent org.keycloak - 999.0.0-SNAPSHOT + 24.0.5 REST Example diff --git a/examples/saml/pom.xml b/examples/saml/pom.xml index c65cff5ac2d5..88dd9df0955a 100755 --- a/examples/saml/pom.xml +++ b/examples/saml/pom.xml @@ -20,7 +20,7 @@ keycloak-examples-parent org.keycloak - 999.0.0-SNAPSHOT + 24.0.5 SAML Examples diff --git a/examples/saml/servlet-filter/pom.xml b/examples/saml/servlet-filter/pom.xml index 3b48824ae0a6..bf92416a29fe 100755 --- a/examples/saml/servlet-filter/pom.xml +++ b/examples/saml/servlet-filter/pom.xml @@ -22,7 +22,7 @@ keycloak-examples-saml-parent org.keycloak - 999.0.0-SNAPSHOT + 24.0.5 saml-servlet-filter diff --git a/examples/themes/pom.xml b/examples/themes/pom.xml index d4190e3ac7a9..7a539c2ec805 100755 --- a/examples/themes/pom.xml +++ b/examples/themes/pom.xml @@ -20,7 +20,7 @@ keycloak-examples-parent org.keycloak - 999.0.0-SNAPSHOT + 24.0.5 Themes Examples diff --git a/federation/kerberos/pom.xml b/federation/kerberos/pom.xml index 95e483b06bf3..8c53c532ce20 100755 --- a/federation/kerberos/pom.xml +++ b/federation/kerberos/pom.xml @@ -20,7 +20,7 @@ keycloak-parent org.keycloak - 999.0.0-SNAPSHOT + 24.0.5 ../../pom.xml 4.0.0 diff --git a/federation/ldap/pom.xml b/federation/ldap/pom.xml index 6d976016a891..71b38dbd7db6 100755 --- a/federation/ldap/pom.xml +++ b/federation/ldap/pom.xml @@ -20,7 +20,7 @@ keycloak-parent org.keycloak - 999.0.0-SNAPSHOT + 24.0.5 ../../pom.xml 4.0.0 diff --git a/federation/pom.xml b/federation/pom.xml index 314e274a73b3..78cd2251dfc7 100755 --- a/federation/pom.xml +++ b/federation/pom.xml @@ -22,7 +22,7 @@ keycloak-parent org.keycloak - 999.0.0-SNAPSHOT + 24.0.5 ../pom.xml 4.0.0 diff --git a/federation/sssd/pom.xml b/federation/sssd/pom.xml index 72e1c71365b5..3b56d8f9ecc0 100644 --- a/federation/sssd/pom.xml +++ b/federation/sssd/pom.xml @@ -4,7 +4,7 @@ keycloak-parent org.keycloak - 999.0.0-SNAPSHOT + 24.0.5 ../../pom.xml 4.0.0 diff --git a/integration/admin-client-jee/pom.xml b/integration/admin-client-jee/pom.xml index 510faebcafb2..5bfa5abc959d 100755 --- a/integration/admin-client-jee/pom.xml +++ b/integration/admin-client-jee/pom.xml @@ -22,7 +22,7 @@ keycloak-integration-parent org.keycloak - 999.0.0-SNAPSHOT + 24.0.5 4.0.0 diff --git a/integration/admin-client/pom.xml b/integration/admin-client/pom.xml index 5aae036ebdbb..1b52402084d0 100755 --- a/integration/admin-client/pom.xml +++ b/integration/admin-client/pom.xml @@ -22,7 +22,7 @@ keycloak-integration-parent org.keycloak - 999.0.0-SNAPSHOT + 24.0.5 4.0.0 diff --git a/integration/client-cli/admin-cli/pom.xml b/integration/client-cli/admin-cli/pom.xml index c0d8ef6358f2..030e418412e6 100755 --- a/integration/client-cli/admin-cli/pom.xml +++ b/integration/client-cli/admin-cli/pom.xml @@ -21,7 +21,7 @@ keycloak-client-cli-parent org.keycloak - 999.0.0-SNAPSHOT + 24.0.5 4.0.0 diff --git a/integration/client-cli/client-cli-dist/pom.xml b/integration/client-cli/client-cli-dist/pom.xml index 5712241f7dd8..33b8f2807ae2 100755 --- a/integration/client-cli/client-cli-dist/pom.xml +++ b/integration/client-cli/client-cli-dist/pom.xml @@ -21,7 +21,7 @@ keycloak-client-cli-parent org.keycloak - 999.0.0-SNAPSHOT + 24.0.5 keycloak-client-cli-dist diff --git a/integration/client-cli/client-registration-cli/pom.xml b/integration/client-cli/client-registration-cli/pom.xml index 2072efa6c751..623936e0e6ef 100755 --- a/integration/client-cli/client-registration-cli/pom.xml +++ b/integration/client-cli/client-registration-cli/pom.xml @@ -21,7 +21,7 @@ keycloak-client-cli-parent org.keycloak - 999.0.0-SNAPSHOT + 24.0.5 4.0.0 diff --git a/integration/client-cli/pom.xml b/integration/client-cli/pom.xml index 7ebc9baa4c07..12687dc1d642 100644 --- a/integration/client-cli/pom.xml +++ b/integration/client-cli/pom.xml @@ -20,7 +20,7 @@ keycloak-integration-parent org.keycloak - 999.0.0-SNAPSHOT + 24.0.5 Keycloak Client CLI diff --git a/integration/client-registration/pom.xml b/integration/client-registration/pom.xml index 9546266379ba..74931ab581f7 100755 --- a/integration/client-registration/pom.xml +++ b/integration/client-registration/pom.xml @@ -21,7 +21,7 @@ keycloak-integration-parent org.keycloak - 999.0.0-SNAPSHOT + 24.0.5 4.0.0 diff --git a/integration/pom.xml b/integration/pom.xml index dc6dde5da414..ef82922c4e68 100755 --- a/integration/pom.xml +++ b/integration/pom.xml @@ -20,7 +20,7 @@ keycloak-parent org.keycloak - 999.0.0-SNAPSHOT + 24.0.5 ../pom.xml Keycloak Integration diff --git a/js/apps/account-ui/pom.xml b/js/apps/account-ui/pom.xml index 10a8081fbee7..7e4d5f0390a3 100644 --- a/js/apps/account-ui/pom.xml +++ b/js/apps/account-ui/pom.xml @@ -7,7 +7,7 @@ keycloak-js-parent org.keycloak - 999.0.0-SNAPSHOT + 24.0.5 ../../pom.xml diff --git a/js/apps/admin-ui/pom.xml b/js/apps/admin-ui/pom.xml index 2407dc3daca8..2216f49637ef 100644 --- a/js/apps/admin-ui/pom.xml +++ b/js/apps/admin-ui/pom.xml @@ -7,7 +7,7 @@ keycloak-js-parent org.keycloak - 999.0.0-SNAPSHOT + 24.0.5 ../../pom.xml diff --git a/js/libs/keycloak-admin-client/package.json b/js/libs/keycloak-admin-client/package.json index e76b417cc15b..2e493971a030 100644 --- a/js/libs/keycloak-admin-client/package.json +++ b/js/libs/keycloak-admin-client/package.json @@ -1,6 +1,6 @@ { "name": "@keycloak/keycloak-admin-client", - "version": "999.0.0-SNAPSHOT", + "version": "24.0.5", "description": "A client to interact with Keycloak's Administration API", "type": "module", "main": "lib/index.js", diff --git a/js/libs/keycloak-admin-client/pom.xml b/js/libs/keycloak-admin-client/pom.xml index d6b8c82c4e4a..2b7b892ef549 100644 --- a/js/libs/keycloak-admin-client/pom.xml +++ b/js/libs/keycloak-admin-client/pom.xml @@ -5,7 +5,7 @@ keycloak-js-parent org.keycloak - 999.0.0-SNAPSHOT + 24.0.5 ../../pom.xml diff --git a/js/libs/keycloak-js/package.json b/js/libs/keycloak-js/package.json index ebdbbe4a1ada..b15ba8e52462 100644 --- a/js/libs/keycloak-js/package.json +++ b/js/libs/keycloak-js/package.json @@ -1,6 +1,6 @@ { "name": "keycloak-js", - "version": "999.0.0-SNAPSHOT", + "version": "24.0.5", "description": "A client-side JavaScript OpenID Connect library that can be used to secure web applications", "main": "./dist/keycloak.js", "module": "./dist/keycloak.mjs", diff --git a/js/libs/keycloak-js/pom.xml b/js/libs/keycloak-js/pom.xml index 2a6543760c11..7cf3af46394f 100644 --- a/js/libs/keycloak-js/pom.xml +++ b/js/libs/keycloak-js/pom.xml @@ -5,7 +5,7 @@ keycloak-js-parent org.keycloak - 999.0.0-SNAPSHOT + 24.0.5 ../../pom.xml diff --git a/js/pom.xml b/js/pom.xml index c959b2e6f757..42ed38c2482d 100644 --- a/js/pom.xml +++ b/js/pom.xml @@ -5,7 +5,7 @@ keycloak-parent org.keycloak - 999.0.0-SNAPSHOT + 24.0.5 ../pom.xml diff --git a/misc/keycloak-test-helper/pom.xml b/misc/keycloak-test-helper/pom.xml index aaed6d3a2de6..26787621dc7b 100644 --- a/misc/keycloak-test-helper/pom.xml +++ b/misc/keycloak-test-helper/pom.xml @@ -6,7 +6,7 @@ keycloak-misc-parent org.keycloak - 999.0.0-SNAPSHOT + 24.0.5 org.keycloak keycloak-test-helper diff --git a/misc/pom.xml b/misc/pom.xml index 8c813fdc5074..f3c60abbe3a0 100644 --- a/misc/pom.xml +++ b/misc/pom.xml @@ -3,7 +3,7 @@ keycloak-parent org.keycloak - 999.0.0-SNAPSHOT + 24.0.5 Keycloak Misc diff --git a/misc/spring-boot-starter/keycloak-spring-boot-starter/pom.xml b/misc/spring-boot-starter/keycloak-spring-boot-starter/pom.xml index d3a97a4168c5..4417d8670d34 100644 --- a/misc/spring-boot-starter/keycloak-spring-boot-starter/pom.xml +++ b/misc/spring-boot-starter/keycloak-spring-boot-starter/pom.xml @@ -4,7 +4,7 @@ org.keycloak keycloak-spring-boot-starter-parent - 999.0.0-SNAPSHOT + 24.0.5 keycloak-spring-boot-starter Keycloak :: Spring :: Boot :: Default :: Starter diff --git a/misc/spring-boot-starter/pom.xml b/misc/spring-boot-starter/pom.xml index b94cafa4a6d0..26ae9be8adc7 100644 --- a/misc/spring-boot-starter/pom.xml +++ b/misc/spring-boot-starter/pom.xml @@ -5,7 +5,7 @@ keycloak-misc-parent org.keycloak - 999.0.0-SNAPSHOT + 24.0.5 org.keycloak keycloak-spring-boot-starter-parent diff --git a/model/infinispan/pom.xml b/model/infinispan/pom.xml index 79ffa042d282..df45b120a833 100755 --- a/model/infinispan/pom.xml +++ b/model/infinispan/pom.xml @@ -21,7 +21,7 @@ keycloak-model-pom org.keycloak - 999.0.0-SNAPSHOT + 24.0.5 17 diff --git a/model/jpa/pom.xml b/model/jpa/pom.xml index ac4e28f787e9..546110a3acc4 100755 --- a/model/jpa/pom.xml +++ b/model/jpa/pom.xml @@ -21,7 +21,7 @@ keycloak-model-pom org.keycloak - 999.0.0-SNAPSHOT + 24.0.5 4.0.0 diff --git a/model/legacy/pom.xml b/model/legacy/pom.xml index 476a1c6c8b91..86d6b0fece0e 100644 --- a/model/legacy/pom.xml +++ b/model/legacy/pom.xml @@ -3,7 +3,7 @@ keycloak-model-pom org.keycloak - 999.0.0-SNAPSHOT + 24.0.5 4.0.0 diff --git a/model/pom.xml b/model/pom.xml index 97b9188ac581..c344be30a444 100755 --- a/model/pom.xml +++ b/model/pom.xml @@ -20,7 +20,7 @@ keycloak-parent org.keycloak - 999.0.0-SNAPSHOT + 24.0.5 ../pom.xml Keycloak Model Parent diff --git a/model/storage-private/pom.xml b/model/storage-private/pom.xml index 851e136ede0a..62dc91188a0d 100644 --- a/model/storage-private/pom.xml +++ b/model/storage-private/pom.xml @@ -3,7 +3,7 @@ keycloak-model-pom org.keycloak - 999.0.0-SNAPSHOT + 24.0.5 4.0.0 diff --git a/model/storage-services/pom.xml b/model/storage-services/pom.xml index 8d7acffc7fa9..dc35300af08c 100644 --- a/model/storage-services/pom.xml +++ b/model/storage-services/pom.xml @@ -3,7 +3,7 @@ keycloak-model-pom org.keycloak - 999.0.0-SNAPSHOT + 24.0.5 4.0.0 diff --git a/model/storage/pom.xml b/model/storage/pom.xml index bbdb17870d33..ca6a53e57761 100644 --- a/model/storage/pom.xml +++ b/model/storage/pom.xml @@ -3,7 +3,7 @@ keycloak-model-pom org.keycloak - 999.0.0-SNAPSHOT + 24.0.5 4.0.0 diff --git a/operator/pom.xml b/operator/pom.xml index 340445f62086..987d95e803c4 100644 --- a/operator/pom.xml +++ b/operator/pom.xml @@ -7,7 +7,7 @@ keycloak-parent org.keycloak - 999.0.0-SNAPSHOT + 24.0.5 ../pom.xml diff --git a/pom.xml b/pom.xml index 85b4cb56cfbe..c0853d6026c4 100644 --- a/pom.xml +++ b/pom.xml @@ -31,11 +31,11 @@ org.keycloak keycloak-parent - 999.0.0-SNAPSHOT + 24.0.5 pom - 999.0.0-SNAPSHOT + 24.0.5 1.5.8 diff --git a/quarkus/config-api/pom.xml b/quarkus/config-api/pom.xml index 8b9737609a17..31d2c6bc0fd1 100755 --- a/quarkus/config-api/pom.xml +++ b/quarkus/config-api/pom.xml @@ -21,7 +21,7 @@ keycloak-quarkus-parent org.keycloak - 999.0.0-SNAPSHOT + 24.0.5 ../pom.xml 4.0.0 diff --git a/quarkus/container/Dockerfile b/quarkus/container/Dockerfile index 0fe77a56d356..1ebc911dd0eb 100644 --- a/quarkus/container/Dockerfile +++ b/quarkus/container/Dockerfile @@ -1,6 +1,6 @@ FROM registry.access.redhat.com/ubi9 AS ubi-micro-build -ENV KEYCLOAK_VERSION 999.0.0-SNAPSHOT +ENV KEYCLOAK_VERSION 24.0.5 ARG KEYCLOAK_DIST=https://github.com/keycloak/keycloak/releases/download/$KEYCLOAK_VERSION/keycloak-$KEYCLOAK_VERSION.tar.gz RUN dnf install -y tar gzip diff --git a/quarkus/deployment/pom.xml b/quarkus/deployment/pom.xml index fcad78bde1a7..ba87acfb491f 100644 --- a/quarkus/deployment/pom.xml +++ b/quarkus/deployment/pom.xml @@ -5,7 +5,7 @@ keycloak-quarkus-parent org.keycloak - 999.0.0-SNAPSHOT + 24.0.5 ../pom.xml 4.0.0 diff --git a/quarkus/dist/pom.xml b/quarkus/dist/pom.xml index 4189fb8967a4..266cc97d855a 100755 --- a/quarkus/dist/pom.xml +++ b/quarkus/dist/pom.xml @@ -21,7 +21,7 @@ keycloak-quarkus-parent org.keycloak - 999.0.0-SNAPSHOT + 24.0.5 keycloak-quarkus-dist diff --git a/quarkus/pom.xml b/quarkus/pom.xml index 4c0a5c76ca8f..613e600270a2 100644 --- a/quarkus/pom.xml +++ b/quarkus/pom.xml @@ -20,7 +20,7 @@ keycloak-parent org.keycloak - 999.0.0-SNAPSHOT + 24.0.5 ../pom.xml Keycloak Quarkus Parent diff --git a/quarkus/runtime/pom.xml b/quarkus/runtime/pom.xml index 24494b6e03fa..a55ababdf71a 100644 --- a/quarkus/runtime/pom.xml +++ b/quarkus/runtime/pom.xml @@ -5,7 +5,7 @@ keycloak-quarkus-parent org.keycloak - 999.0.0-SNAPSHOT + 24.0.5 ../pom.xml 4.0.0 diff --git a/quarkus/server/pom.xml b/quarkus/server/pom.xml index 4cd6f2f88493..61e4f56d941d 100644 --- a/quarkus/server/pom.xml +++ b/quarkus/server/pom.xml @@ -7,7 +7,7 @@ keycloak-quarkus-parent org.keycloak - 999.0.0-SNAPSHOT + 24.0.5 ../pom.xml diff --git a/quarkus/tests/integration/pom.xml b/quarkus/tests/integration/pom.xml index 1876cbaa773a..59b2aa85e502 100644 --- a/quarkus/tests/integration/pom.xml +++ b/quarkus/tests/integration/pom.xml @@ -24,7 +24,7 @@ keycloak-quarkus-test-parent org.keycloak - 999.0.0-SNAPSHOT + 24.0.5 ../pom.xml diff --git a/quarkus/tests/junit5/pom.xml b/quarkus/tests/junit5/pom.xml index 94b7d0e77557..de3537d77694 100644 --- a/quarkus/tests/junit5/pom.xml +++ b/quarkus/tests/junit5/pom.xml @@ -24,7 +24,7 @@ keycloak-quarkus-test-parent org.keycloak - 999.0.0-SNAPSHOT + 24.0.5 ../pom.xml diff --git a/quarkus/tests/pom.xml b/quarkus/tests/pom.xml index 434605e1b9f2..ffb29da694f7 100644 --- a/quarkus/tests/pom.xml +++ b/quarkus/tests/pom.xml @@ -24,7 +24,7 @@ keycloak-quarkus-parent org.keycloak - 999.0.0-SNAPSHOT + 24.0.5 ../pom.xml diff --git a/rest/admin-ui-ext/pom.xml b/rest/admin-ui-ext/pom.xml index caac25c83318..eec2d94ed45c 100644 --- a/rest/admin-ui-ext/pom.xml +++ b/rest/admin-ui-ext/pom.xml @@ -22,7 +22,7 @@ org.keycloak keycloak-rest-parent - 999.0.0-SNAPSHOT + 24.0.5 keycloak-rest-admin-ui-ext diff --git a/rest/pom.xml b/rest/pom.xml index c4d975748c08..43a268003667 100644 --- a/rest/pom.xml +++ b/rest/pom.xml @@ -22,7 +22,7 @@ keycloak-parent org.keycloak - 999.0.0-SNAPSHOT + 24.0.5 Keycloak Administration UI diff --git a/saml-core-api/pom.xml b/saml-core-api/pom.xml index 392c6b495ff3..c5e4ca9e7a82 100755 --- a/saml-core-api/pom.xml +++ b/saml-core-api/pom.xml @@ -21,7 +21,7 @@ keycloak-parent org.keycloak - 999.0.0-SNAPSHOT + 24.0.5 ../pom.xml 4.0.0 diff --git a/saml-core/pom.xml b/saml-core/pom.xml index 6d1097dc5595..c8a0b22180ee 100755 --- a/saml-core/pom.xml +++ b/saml-core/pom.xml @@ -21,7 +21,7 @@ keycloak-parent org.keycloak - 999.0.0-SNAPSHOT + 24.0.5 ../pom.xml 4.0.0 diff --git a/server-spi-private/pom.xml b/server-spi-private/pom.xml index cafe65717241..4f5df05d5ce3 100755 --- a/server-spi-private/pom.xml +++ b/server-spi-private/pom.xml @@ -21,7 +21,7 @@ keycloak-parent org.keycloak - 999.0.0-SNAPSHOT + 24.0.5 ../pom.xml 4.0.0 diff --git a/server-spi/pom.xml b/server-spi/pom.xml index 3bc3dcb6d970..a98f89fd5ff1 100755 --- a/server-spi/pom.xml +++ b/server-spi/pom.xml @@ -21,7 +21,7 @@ keycloak-parent org.keycloak - 999.0.0-SNAPSHOT + 24.0.5 ../pom.xml 4.0.0 diff --git a/services/pom.xml b/services/pom.xml index 42fe00efb82e..be841425ad74 100755 --- a/services/pom.xml +++ b/services/pom.xml @@ -21,7 +21,7 @@ keycloak-parent org.keycloak - 999.0.0-SNAPSHOT + 24.0.5 ../pom.xml 4.0.0 diff --git a/testsuite/db-allocator-plugin/pom.xml b/testsuite/db-allocator-plugin/pom.xml index c82daf8462b7..5002dca33b78 100644 --- a/testsuite/db-allocator-plugin/pom.xml +++ b/testsuite/db-allocator-plugin/pom.xml @@ -22,7 +22,7 @@ keycloak-testsuite-pom org.keycloak - 999.0.0-SNAPSHOT + 24.0.5 4.0.0 diff --git a/testsuite/integration-arquillian/pom.xml b/testsuite/integration-arquillian/pom.xml index b8e4b1024784..765dca22ffda 100644 --- a/testsuite/integration-arquillian/pom.xml +++ b/testsuite/integration-arquillian/pom.xml @@ -22,7 +22,7 @@ org.keycloak keycloak-testsuite-pom - 999.0.0-SNAPSHOT + 24.0.5 ../pom.xml diff --git a/testsuite/integration-arquillian/servers/adapter-spi/pom.xml b/testsuite/integration-arquillian/servers/adapter-spi/pom.xml index 7f641d873404..998ff58c05f5 100644 --- a/testsuite/integration-arquillian/servers/adapter-spi/pom.xml +++ b/testsuite/integration-arquillian/servers/adapter-spi/pom.xml @@ -5,7 +5,7 @@ integration-arquillian-servers org.keycloak.testsuite - 999.0.0-SNAPSHOT + 24.0.5 4.0.0 pom diff --git a/testsuite/integration-arquillian/servers/adapter-spi/undertow-adapter-jakarta/pom.xml b/testsuite/integration-arquillian/servers/adapter-spi/undertow-adapter-jakarta/pom.xml index 7539a2caf5a0..eecae41d07a4 100644 --- a/testsuite/integration-arquillian/servers/adapter-spi/undertow-adapter-jakarta/pom.xml +++ b/testsuite/integration-arquillian/servers/adapter-spi/undertow-adapter-jakarta/pom.xml @@ -5,7 +5,7 @@ integration-arquillian-servers-adapter-spi org.keycloak.testsuite - 999.0.0-SNAPSHOT + 24.0.5 ../pom.xml 4.0.0 diff --git a/testsuite/integration-arquillian/servers/adapter-spi/undertow-adapter-saml-jakarta/pom.xml b/testsuite/integration-arquillian/servers/adapter-spi/undertow-adapter-saml-jakarta/pom.xml index 27455823417e..8aa9289d60a9 100644 --- a/testsuite/integration-arquillian/servers/adapter-spi/undertow-adapter-saml-jakarta/pom.xml +++ b/testsuite/integration-arquillian/servers/adapter-spi/undertow-adapter-saml-jakarta/pom.xml @@ -5,7 +5,7 @@ integration-arquillian-servers-adapter-spi org.keycloak.testsuite - 999.0.0-SNAPSHOT + 24.0.5 ../pom.xml 4.0.0 diff --git a/testsuite/integration-arquillian/servers/adapter-spi/undertow-adapter-spi-jakarta/pom.xml b/testsuite/integration-arquillian/servers/adapter-spi/undertow-adapter-spi-jakarta/pom.xml index 3fe965d90478..9dc178a35d90 100644 --- a/testsuite/integration-arquillian/servers/adapter-spi/undertow-adapter-spi-jakarta/pom.xml +++ b/testsuite/integration-arquillian/servers/adapter-spi/undertow-adapter-spi-jakarta/pom.xml @@ -5,7 +5,7 @@ integration-arquillian-servers-adapter-spi org.keycloak.testsuite - 999.0.0-SNAPSHOT + 24.0.5 ../pom.xml 4.0.0 diff --git a/testsuite/integration-arquillian/servers/app-server/app-server-spi/pom.xml b/testsuite/integration-arquillian/servers/app-server/app-server-spi/pom.xml index a00eae01d9ec..214d7b1620fe 100644 --- a/testsuite/integration-arquillian/servers/app-server/app-server-spi/pom.xml +++ b/testsuite/integration-arquillian/servers/app-server/app-server-spi/pom.xml @@ -21,7 +21,7 @@ org.keycloak.testsuite integration-arquillian-servers-app-server - 999.0.0-SNAPSHOT + 24.0.5 4.0.0 diff --git a/testsuite/integration-arquillian/servers/app-server/jboss/eap/pom.xml b/testsuite/integration-arquillian/servers/app-server/jboss/eap/pom.xml index 34afca828528..c0d82ff28af8 100644 --- a/testsuite/integration-arquillian/servers/app-server/jboss/eap/pom.xml +++ b/testsuite/integration-arquillian/servers/app-server/jboss/eap/pom.xml @@ -21,7 +21,7 @@ org.keycloak.testsuite integration-arquillian-servers-app-server-jboss - 999.0.0-SNAPSHOT + 24.0.5 4.0.0 diff --git a/testsuite/integration-arquillian/servers/app-server/jboss/eap6/pom.xml b/testsuite/integration-arquillian/servers/app-server/jboss/eap6/pom.xml index e5bbcdd78b13..c8cacfdb58f7 100644 --- a/testsuite/integration-arquillian/servers/app-server/jboss/eap6/pom.xml +++ b/testsuite/integration-arquillian/servers/app-server/jboss/eap6/pom.xml @@ -21,7 +21,7 @@ org.keycloak.testsuite integration-arquillian-servers-app-server-jboss - 999.0.0-SNAPSHOT + 24.0.5 4.0.0 diff --git a/testsuite/integration-arquillian/servers/app-server/jboss/galleon/pom.xml b/testsuite/integration-arquillian/servers/app-server/jboss/galleon/pom.xml index 50ab5e2be1b6..5d4f5b3cbbcb 100644 --- a/testsuite/integration-arquillian/servers/app-server/jboss/galleon/pom.xml +++ b/testsuite/integration-arquillian/servers/app-server/jboss/galleon/pom.xml @@ -5,7 +5,7 @@ org.keycloak.testsuite integration-arquillian-servers-app-server-jboss - 999.0.0-SNAPSHOT + 24.0.5 4.0.0 diff --git a/testsuite/integration-arquillian/servers/app-server/jboss/pom.xml b/testsuite/integration-arquillian/servers/app-server/jboss/pom.xml index 30fa7cb29d50..92fed290cf22 100644 --- a/testsuite/integration-arquillian/servers/app-server/jboss/pom.xml +++ b/testsuite/integration-arquillian/servers/app-server/jboss/pom.xml @@ -22,7 +22,7 @@ org.keycloak.testsuite integration-arquillian-servers-app-server - 999.0.0-SNAPSHOT + 24.0.5 4.0.0 diff --git a/testsuite/integration-arquillian/servers/app-server/jboss/wildfly/pom.xml b/testsuite/integration-arquillian/servers/app-server/jboss/wildfly/pom.xml index 4932980a169d..a23527015fa8 100644 --- a/testsuite/integration-arquillian/servers/app-server/jboss/wildfly/pom.xml +++ b/testsuite/integration-arquillian/servers/app-server/jboss/wildfly/pom.xml @@ -21,7 +21,7 @@ org.keycloak.testsuite integration-arquillian-servers-app-server-jboss - 999.0.0-SNAPSHOT + 24.0.5 4.0.0 diff --git a/testsuite/integration-arquillian/servers/app-server/jetty/94/pom.xml b/testsuite/integration-arquillian/servers/app-server/jetty/94/pom.xml index 14fc2f005fa7..b6075b0a6e17 100644 --- a/testsuite/integration-arquillian/servers/app-server/jetty/94/pom.xml +++ b/testsuite/integration-arquillian/servers/app-server/jetty/94/pom.xml @@ -18,7 +18,7 @@ org.keycloak.testsuite integration-arquillian-servers-app-server-jetty - 999.0.0-SNAPSHOT + 24.0.5 4.0.0 diff --git a/testsuite/integration-arquillian/servers/app-server/jetty/common/pom.xml b/testsuite/integration-arquillian/servers/app-server/jetty/common/pom.xml index ffb12cebace8..d73a59f8d127 100644 --- a/testsuite/integration-arquillian/servers/app-server/jetty/common/pom.xml +++ b/testsuite/integration-arquillian/servers/app-server/jetty/common/pom.xml @@ -18,7 +18,7 @@ org.keycloak.testsuite integration-arquillian-servers-app-server-jetty - 999.0.0-SNAPSHOT + 24.0.5 4.0.0 diff --git a/testsuite/integration-arquillian/servers/app-server/jetty/pom.xml b/testsuite/integration-arquillian/servers/app-server/jetty/pom.xml index 1c938b754ea7..aeacdeb5a1ab 100644 --- a/testsuite/integration-arquillian/servers/app-server/jetty/pom.xml +++ b/testsuite/integration-arquillian/servers/app-server/jetty/pom.xml @@ -18,7 +18,7 @@ org.keycloak.testsuite integration-arquillian-servers-app-server - 999.0.0-SNAPSHOT + 24.0.5 4.0.0 diff --git a/testsuite/integration-arquillian/servers/app-server/karaf/fuse63/pom.xml b/testsuite/integration-arquillian/servers/app-server/karaf/fuse63/pom.xml index b57b22e505b3..364850609576 100644 --- a/testsuite/integration-arquillian/servers/app-server/karaf/fuse63/pom.xml +++ b/testsuite/integration-arquillian/servers/app-server/karaf/fuse63/pom.xml @@ -21,7 +21,7 @@ org.keycloak.testsuite integration-arquillian-servers-app-server-karaf - 999.0.0-SNAPSHOT + 24.0.5 4.0.0 diff --git a/testsuite/integration-arquillian/servers/app-server/karaf/fuse7x/pom.xml b/testsuite/integration-arquillian/servers/app-server/karaf/fuse7x/pom.xml index 7b2e45786b9e..674f1ff2ae7e 100644 --- a/testsuite/integration-arquillian/servers/app-server/karaf/fuse7x/pom.xml +++ b/testsuite/integration-arquillian/servers/app-server/karaf/fuse7x/pom.xml @@ -21,7 +21,7 @@ org.keycloak.testsuite integration-arquillian-servers-app-server-karaf - 999.0.0-SNAPSHOT + 24.0.5 4.0.0 diff --git a/testsuite/integration-arquillian/servers/app-server/karaf/pom.xml b/testsuite/integration-arquillian/servers/app-server/karaf/pom.xml index c36e3c40911c..5a20450a2c56 100644 --- a/testsuite/integration-arquillian/servers/app-server/karaf/pom.xml +++ b/testsuite/integration-arquillian/servers/app-server/karaf/pom.xml @@ -21,7 +21,7 @@ org.keycloak.testsuite integration-arquillian-servers-app-server - 999.0.0-SNAPSHOT + 24.0.5 4.0.0 diff --git a/testsuite/integration-arquillian/servers/app-server/pom.xml b/testsuite/integration-arquillian/servers/app-server/pom.xml index 3ee99ab0d9cc..027d43473702 100644 --- a/testsuite/integration-arquillian/servers/app-server/pom.xml +++ b/testsuite/integration-arquillian/servers/app-server/pom.xml @@ -21,7 +21,7 @@ org.keycloak.testsuite integration-arquillian-servers - 999.0.0-SNAPSHOT + 24.0.5 4.0.0 diff --git a/testsuite/integration-arquillian/servers/app-server/tomcat/common/pom.xml b/testsuite/integration-arquillian/servers/app-server/tomcat/common/pom.xml index a0019ea56da5..7110f5a8d3c7 100644 --- a/testsuite/integration-arquillian/servers/app-server/tomcat/common/pom.xml +++ b/testsuite/integration-arquillian/servers/app-server/tomcat/common/pom.xml @@ -5,7 +5,7 @@ integration-arquillian-servers-app-server-tomcat org.keycloak.testsuite - 999.0.0-SNAPSHOT + 24.0.5 4.0.0 diff --git a/testsuite/integration-arquillian/servers/app-server/tomcat/pom.xml b/testsuite/integration-arquillian/servers/app-server/tomcat/pom.xml index ceb6f924d843..d5b0a763568d 100644 --- a/testsuite/integration-arquillian/servers/app-server/tomcat/pom.xml +++ b/testsuite/integration-arquillian/servers/app-server/tomcat/pom.xml @@ -21,7 +21,7 @@ org.keycloak.testsuite integration-arquillian-servers-app-server - 999.0.0-SNAPSHOT + 24.0.5 4.0.0 diff --git a/testsuite/integration-arquillian/servers/app-server/tomcat/tomcat8/pom.xml b/testsuite/integration-arquillian/servers/app-server/tomcat/tomcat8/pom.xml index ebf2b1cf6bef..5b93904b3f72 100644 --- a/testsuite/integration-arquillian/servers/app-server/tomcat/tomcat8/pom.xml +++ b/testsuite/integration-arquillian/servers/app-server/tomcat/tomcat8/pom.xml @@ -21,7 +21,7 @@ org.keycloak.testsuite integration-arquillian-servers-app-server-tomcat - 999.0.0-SNAPSHOT + 24.0.5 4.0.0 diff --git a/testsuite/integration-arquillian/servers/app-server/tomcat/tomcat9/pom.xml b/testsuite/integration-arquillian/servers/app-server/tomcat/tomcat9/pom.xml index cbb1c5b0bccf..93c38fb3eb09 100644 --- a/testsuite/integration-arquillian/servers/app-server/tomcat/tomcat9/pom.xml +++ b/testsuite/integration-arquillian/servers/app-server/tomcat/tomcat9/pom.xml @@ -21,7 +21,7 @@ org.keycloak.testsuite integration-arquillian-servers-app-server-tomcat - 999.0.0-SNAPSHOT + 24.0.5 4.0.0 diff --git a/testsuite/integration-arquillian/servers/app-server/undertow/pom.xml b/testsuite/integration-arquillian/servers/app-server/undertow/pom.xml index 1f708815e3f4..02f57c947f59 100644 --- a/testsuite/integration-arquillian/servers/app-server/undertow/pom.xml +++ b/testsuite/integration-arquillian/servers/app-server/undertow/pom.xml @@ -18,7 +18,7 @@ org.keycloak.testsuite integration-arquillian-servers-app-server - 999.0.0-SNAPSHOT + 24.0.5 4.0.0 diff --git a/testsuite/integration-arquillian/servers/auth-server/pom.xml b/testsuite/integration-arquillian/servers/auth-server/pom.xml index a5ed91a6c734..7fa63212b9a7 100644 --- a/testsuite/integration-arquillian/servers/auth-server/pom.xml +++ b/testsuite/integration-arquillian/servers/auth-server/pom.xml @@ -21,7 +21,7 @@ org.keycloak.testsuite integration-arquillian-servers - 999.0.0-SNAPSHOT + 24.0.5 4.0.0 diff --git a/testsuite/integration-arquillian/servers/auth-server/quarkus/pom.xml b/testsuite/integration-arquillian/servers/auth-server/quarkus/pom.xml index 6bbd8ab3e41e..7c41b59971e9 100644 --- a/testsuite/integration-arquillian/servers/auth-server/quarkus/pom.xml +++ b/testsuite/integration-arquillian/servers/auth-server/quarkus/pom.xml @@ -5,7 +5,7 @@ integration-arquillian-servers-auth-server org.keycloak.testsuite - 999.0.0-SNAPSHOT + 24.0.5 4.0.0 diff --git a/testsuite/integration-arquillian/servers/auth-server/services/pom.xml b/testsuite/integration-arquillian/servers/auth-server/services/pom.xml index a096973d211c..7b4ed55b1bc5 100644 --- a/testsuite/integration-arquillian/servers/auth-server/services/pom.xml +++ b/testsuite/integration-arquillian/servers/auth-server/services/pom.xml @@ -21,7 +21,7 @@ org.keycloak.testsuite integration-arquillian-servers-auth-server - 999.0.0-SNAPSHOT + 24.0.5 4.0.0 diff --git a/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers-deployment/pom.xml b/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers-deployment/pom.xml index 20063ee9442d..3d20f4aa2e9d 100644 --- a/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers-deployment/pom.xml +++ b/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers-deployment/pom.xml @@ -24,7 +24,7 @@ org.keycloak.testsuite integration-arquillian-servers-auth-server-services - 999.0.0-SNAPSHOT + 24.0.5 integration-arquillian-testsuite-providers-deployment diff --git a/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/pom.xml b/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/pom.xml index 0d0277216cc4..48f02cb6365d 100644 --- a/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/pom.xml +++ b/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/pom.xml @@ -24,7 +24,7 @@ org.keycloak.testsuite integration-arquillian-servers-auth-server-services - 999.0.0-SNAPSHOT + 24.0.5 integration-arquillian-testsuite-providers diff --git a/testsuite/integration-arquillian/servers/auth-server/undertow/pom.xml b/testsuite/integration-arquillian/servers/auth-server/undertow/pom.xml index cdfe83d80212..21ab6ad782f3 100644 --- a/testsuite/integration-arquillian/servers/auth-server/undertow/pom.xml +++ b/testsuite/integration-arquillian/servers/auth-server/undertow/pom.xml @@ -21,7 +21,7 @@ org.keycloak.testsuite integration-arquillian-servers-auth-server - 999.0.0-SNAPSHOT + 24.0.5 4.0.0 diff --git a/testsuite/integration-arquillian/servers/cache-server/infinispan/datagrid/pom.xml b/testsuite/integration-arquillian/servers/cache-server/infinispan/datagrid/pom.xml index 39ee8e345f63..a32d5af3bf0e 100644 --- a/testsuite/integration-arquillian/servers/cache-server/infinispan/datagrid/pom.xml +++ b/testsuite/integration-arquillian/servers/cache-server/infinispan/datagrid/pom.xml @@ -21,7 +21,7 @@ org.keycloak.testsuite integration-arquillian-servers-cache-server-infinispan - 999.0.0-SNAPSHOT + 24.0.5 4.0.0 diff --git a/testsuite/integration-arquillian/servers/cache-server/infinispan/infinispan/pom.xml b/testsuite/integration-arquillian/servers/cache-server/infinispan/infinispan/pom.xml index 493512a20957..5aae62989faa 100644 --- a/testsuite/integration-arquillian/servers/cache-server/infinispan/infinispan/pom.xml +++ b/testsuite/integration-arquillian/servers/cache-server/infinispan/infinispan/pom.xml @@ -21,7 +21,7 @@ org.keycloak.testsuite integration-arquillian-servers-cache-server-infinispan - 999.0.0-SNAPSHOT + 24.0.5 4.0.0 diff --git a/testsuite/integration-arquillian/servers/cache-server/infinispan/pom.xml b/testsuite/integration-arquillian/servers/cache-server/infinispan/pom.xml index 80b68a9d8278..e5b7956ed593 100644 --- a/testsuite/integration-arquillian/servers/cache-server/infinispan/pom.xml +++ b/testsuite/integration-arquillian/servers/cache-server/infinispan/pom.xml @@ -24,7 +24,7 @@ org.keycloak.testsuite integration-arquillian-servers-cache-server - 999.0.0-SNAPSHOT + 24.0.5 pom diff --git a/testsuite/integration-arquillian/servers/cache-server/legacy/datagrid/pom.xml b/testsuite/integration-arquillian/servers/cache-server/legacy/datagrid/pom.xml index 56494399a8e5..60bcb47d1852 100644 --- a/testsuite/integration-arquillian/servers/cache-server/legacy/datagrid/pom.xml +++ b/testsuite/integration-arquillian/servers/cache-server/legacy/datagrid/pom.xml @@ -21,7 +21,7 @@ org.keycloak.testsuite integration-arquillian-servers-cache-server-legacy - 999.0.0-SNAPSHOT + 24.0.5 4.0.0 diff --git a/testsuite/integration-arquillian/servers/cache-server/legacy/infinispan/pom.xml b/testsuite/integration-arquillian/servers/cache-server/legacy/infinispan/pom.xml index cb0f8056bfb1..fc50819cee70 100644 --- a/testsuite/integration-arquillian/servers/cache-server/legacy/infinispan/pom.xml +++ b/testsuite/integration-arquillian/servers/cache-server/legacy/infinispan/pom.xml @@ -21,7 +21,7 @@ org.keycloak.testsuite integration-arquillian-servers-cache-server-legacy - 999.0.0-SNAPSHOT + 24.0.5 4.0.0 diff --git a/testsuite/integration-arquillian/servers/cache-server/legacy/pom.xml b/testsuite/integration-arquillian/servers/cache-server/legacy/pom.xml index d5f7c46fe695..c93a7554227f 100644 --- a/testsuite/integration-arquillian/servers/cache-server/legacy/pom.xml +++ b/testsuite/integration-arquillian/servers/cache-server/legacy/pom.xml @@ -21,7 +21,7 @@ org.keycloak.testsuite integration-arquillian-servers-cache-server - 999.0.0-SNAPSHOT + 24.0.5 4.0.0 diff --git a/testsuite/integration-arquillian/servers/cache-server/pom.xml b/testsuite/integration-arquillian/servers/cache-server/pom.xml index 22779a9af5e2..6e83be7253ff 100644 --- a/testsuite/integration-arquillian/servers/cache-server/pom.xml +++ b/testsuite/integration-arquillian/servers/cache-server/pom.xml @@ -21,7 +21,7 @@ org.keycloak.testsuite integration-arquillian-servers - 999.0.0-SNAPSHOT + 24.0.5 4.0.0 diff --git a/testsuite/integration-arquillian/servers/migration/pom.xml b/testsuite/integration-arquillian/servers/migration/pom.xml index 94cf6e625c65..7b6d115c0138 100644 --- a/testsuite/integration-arquillian/servers/migration/pom.xml +++ b/testsuite/integration-arquillian/servers/migration/pom.xml @@ -21,7 +21,7 @@ org.keycloak.testsuite integration-arquillian-servers - 999.0.0-SNAPSHOT + 24.0.5 4.0.0 diff --git a/testsuite/integration-arquillian/servers/pom.xml b/testsuite/integration-arquillian/servers/pom.xml index 70c336cdd481..d2d055ab5ecc 100644 --- a/testsuite/integration-arquillian/servers/pom.xml +++ b/testsuite/integration-arquillian/servers/pom.xml @@ -21,7 +21,7 @@ org.keycloak.testsuite integration-arquillian - 999.0.0-SNAPSHOT + 24.0.5 4.0.0 diff --git a/testsuite/integration-arquillian/test-apps/app-profile-jee/pom.xml b/testsuite/integration-arquillian/test-apps/app-profile-jee/pom.xml index d25c47c91ca4..beaaee718206 100644 --- a/testsuite/integration-arquillian/test-apps/app-profile-jee/pom.xml +++ b/testsuite/integration-arquillian/test-apps/app-profile-jee/pom.xml @@ -5,7 +5,7 @@ org.keycloak.testsuite integration-arquillian-test-apps - 999.0.0-SNAPSHOT + 24.0.5 keycloak-test-app-profile-jee diff --git a/testsuite/integration-arquillian/test-apps/cors/angular-product/pom.xml b/testsuite/integration-arquillian/test-apps/cors/angular-product/pom.xml index ef95d30e4f3e..f893f0700b63 100755 --- a/testsuite/integration-arquillian/test-apps/cors/angular-product/pom.xml +++ b/testsuite/integration-arquillian/test-apps/cors/angular-product/pom.xml @@ -21,7 +21,7 @@ org.keycloak.testsuite integration-arquillian-test-apps-cors-parent - 999.0.0-SNAPSHOT + 24.0.5 4.0.0 diff --git a/testsuite/integration-arquillian/test-apps/cors/database-service/pom.xml b/testsuite/integration-arquillian/test-apps/cors/database-service/pom.xml index e6bb20846a94..c8f62d845d3e 100755 --- a/testsuite/integration-arquillian/test-apps/cors/database-service/pom.xml +++ b/testsuite/integration-arquillian/test-apps/cors/database-service/pom.xml @@ -21,7 +21,7 @@ org.keycloak.testsuite integration-arquillian-test-apps-cors-parent - 999.0.0-SNAPSHOT + 24.0.5 4.0.0 diff --git a/testsuite/integration-arquillian/test-apps/cors/pom.xml b/testsuite/integration-arquillian/test-apps/cors/pom.xml index ce8f9da676d2..165e09043e4f 100644 --- a/testsuite/integration-arquillian/test-apps/cors/pom.xml +++ b/testsuite/integration-arquillian/test-apps/cors/pom.xml @@ -5,7 +5,7 @@ integration-arquillian-test-apps org.keycloak.testsuite - 999.0.0-SNAPSHOT + 24.0.5 4.0.0 diff --git a/testsuite/integration-arquillian/test-apps/fuse/camel-fuse7-undertow/pom.xml b/testsuite/integration-arquillian/test-apps/fuse/camel-fuse7-undertow/pom.xml index debf59b9f149..ea10944e6e4e 100755 --- a/testsuite/integration-arquillian/test-apps/fuse/camel-fuse7-undertow/pom.xml +++ b/testsuite/integration-arquillian/test-apps/fuse/camel-fuse7-undertow/pom.xml @@ -21,7 +21,7 @@ integration-arquillian-test-apps-fuse-parent org.keycloak.testsuite - 999.0.0-SNAPSHOT + 24.0.5 4.0.0 diff --git a/testsuite/integration-arquillian/test-apps/fuse/camel/pom.xml b/testsuite/integration-arquillian/test-apps/fuse/camel/pom.xml index c55d5b01db2b..9f8cbf318269 100755 --- a/testsuite/integration-arquillian/test-apps/fuse/camel/pom.xml +++ b/testsuite/integration-arquillian/test-apps/fuse/camel/pom.xml @@ -21,7 +21,7 @@ integration-arquillian-test-apps-fuse-parent org.keycloak.testsuite - 999.0.0-SNAPSHOT + 24.0.5 4.0.0 diff --git a/testsuite/integration-arquillian/test-apps/fuse/customer-app-fuse/pom.xml b/testsuite/integration-arquillian/test-apps/fuse/customer-app-fuse/pom.xml index 04c9ea33cd1a..da9c147c3123 100755 --- a/testsuite/integration-arquillian/test-apps/fuse/customer-app-fuse/pom.xml +++ b/testsuite/integration-arquillian/test-apps/fuse/customer-app-fuse/pom.xml @@ -21,7 +21,7 @@ integration-arquillian-test-apps-fuse-parent org.keycloak.testsuite - 999.0.0-SNAPSHOT + 24.0.5 4.0.0 diff --git a/testsuite/integration-arquillian/test-apps/fuse/cxf-jaxrs-fuse7-undertow/pom.xml b/testsuite/integration-arquillian/test-apps/fuse/cxf-jaxrs-fuse7-undertow/pom.xml index 6d2c671651d6..b1066bb91e57 100755 --- a/testsuite/integration-arquillian/test-apps/fuse/cxf-jaxrs-fuse7-undertow/pom.xml +++ b/testsuite/integration-arquillian/test-apps/fuse/cxf-jaxrs-fuse7-undertow/pom.xml @@ -21,7 +21,7 @@ integration-arquillian-test-apps-fuse-parent org.keycloak.testsuite - 999.0.0-SNAPSHOT + 24.0.5 4.0.0 diff --git a/testsuite/integration-arquillian/test-apps/fuse/cxf-jaxrs/pom.xml b/testsuite/integration-arquillian/test-apps/fuse/cxf-jaxrs/pom.xml index 2ab0bcf48be1..19f3b50b4015 100755 --- a/testsuite/integration-arquillian/test-apps/fuse/cxf-jaxrs/pom.xml +++ b/testsuite/integration-arquillian/test-apps/fuse/cxf-jaxrs/pom.xml @@ -21,7 +21,7 @@ integration-arquillian-test-apps-fuse-parent org.keycloak.testsuite - 999.0.0-SNAPSHOT + 24.0.5 4.0.0 diff --git a/testsuite/integration-arquillian/test-apps/fuse/cxf-jaxws-fuse7-undertow/pom.xml b/testsuite/integration-arquillian/test-apps/fuse/cxf-jaxws-fuse7-undertow/pom.xml index 714b0d3f383b..e9e57c5c7f03 100755 --- a/testsuite/integration-arquillian/test-apps/fuse/cxf-jaxws-fuse7-undertow/pom.xml +++ b/testsuite/integration-arquillian/test-apps/fuse/cxf-jaxws-fuse7-undertow/pom.xml @@ -21,7 +21,7 @@ integration-arquillian-test-apps-fuse-parent org.keycloak.testsuite - 999.0.0-SNAPSHOT + 24.0.5 4.0.0 diff --git a/testsuite/integration-arquillian/test-apps/fuse/cxf-jaxws/pom.xml b/testsuite/integration-arquillian/test-apps/fuse/cxf-jaxws/pom.xml index 67547eb4295e..b2694b3e81bb 100755 --- a/testsuite/integration-arquillian/test-apps/fuse/cxf-jaxws/pom.xml +++ b/testsuite/integration-arquillian/test-apps/fuse/cxf-jaxws/pom.xml @@ -21,7 +21,7 @@ integration-arquillian-test-apps-fuse-parent org.keycloak.testsuite - 999.0.0-SNAPSHOT + 24.0.5 4.0.0 diff --git a/testsuite/integration-arquillian/test-apps/fuse/external-config/pom.xml b/testsuite/integration-arquillian/test-apps/fuse/external-config/pom.xml index ff8421e357d3..5b15bcb8bdc1 100755 --- a/testsuite/integration-arquillian/test-apps/fuse/external-config/pom.xml +++ b/testsuite/integration-arquillian/test-apps/fuse/external-config/pom.xml @@ -21,7 +21,7 @@ integration-arquillian-test-apps-fuse-parent org.keycloak.testsuite - 999.0.0-SNAPSHOT + 24.0.5 Keycloak Examples - External Config diff --git a/testsuite/integration-arquillian/test-apps/fuse/features/pom.xml b/testsuite/integration-arquillian/test-apps/fuse/features/pom.xml index f5efd8bad550..0f24cfed7803 100755 --- a/testsuite/integration-arquillian/test-apps/fuse/features/pom.xml +++ b/testsuite/integration-arquillian/test-apps/fuse/features/pom.xml @@ -21,7 +21,7 @@ integration-arquillian-test-apps-fuse-parent org.keycloak.testsuite - 999.0.0-SNAPSHOT + 24.0.5 4.0.0 diff --git a/testsuite/integration-arquillian/test-apps/fuse/pom.xml b/testsuite/integration-arquillian/test-apps/fuse/pom.xml index ec776203ad43..23027122d141 100755 --- a/testsuite/integration-arquillian/test-apps/fuse/pom.xml +++ b/testsuite/integration-arquillian/test-apps/fuse/pom.xml @@ -20,7 +20,7 @@ integration-arquillian-test-apps org.keycloak.testsuite - 999.0.0-SNAPSHOT + 24.0.5 Fuse Test Applications diff --git a/testsuite/integration-arquillian/test-apps/fuse/product-app-fuse/pom.xml b/testsuite/integration-arquillian/test-apps/fuse/product-app-fuse/pom.xml index 8e85c189e528..8e7daa1982a3 100755 --- a/testsuite/integration-arquillian/test-apps/fuse/product-app-fuse/pom.xml +++ b/testsuite/integration-arquillian/test-apps/fuse/product-app-fuse/pom.xml @@ -21,7 +21,7 @@ integration-arquillian-test-apps-fuse-parent org.keycloak.testsuite - 999.0.0-SNAPSHOT + 24.0.5 4.0.0 diff --git a/testsuite/integration-arquillian/test-apps/fuse/product-app-fuse7-undertow/pom.xml b/testsuite/integration-arquillian/test-apps/fuse/product-app-fuse7-undertow/pom.xml index d35dfd83c2a5..5bc3825303b5 100755 --- a/testsuite/integration-arquillian/test-apps/fuse/product-app-fuse7-undertow/pom.xml +++ b/testsuite/integration-arquillian/test-apps/fuse/product-app-fuse7-undertow/pom.xml @@ -21,7 +21,7 @@ integration-arquillian-test-apps-fuse-parent org.keycloak.testsuite - 999.0.0-SNAPSHOT + 24.0.5 4.0.0 diff --git a/testsuite/integration-arquillian/test-apps/hello-world-authz-service/pom.xml b/testsuite/integration-arquillian/test-apps/hello-world-authz-service/pom.xml index 6fe8f7897bb4..e725617663ec 100755 --- a/testsuite/integration-arquillian/test-apps/hello-world-authz-service/pom.xml +++ b/testsuite/integration-arquillian/test-apps/hello-world-authz-service/pom.xml @@ -24,7 +24,7 @@ org.keycloak.testsuite integration-arquillian-test-apps - 999.0.0-SNAPSHOT + 24.0.5 hello-world-authz-service diff --git a/testsuite/integration-arquillian/test-apps/pom.xml b/testsuite/integration-arquillian/test-apps/pom.xml index 320a3133d23d..028a362cdb25 100644 --- a/testsuite/integration-arquillian/test-apps/pom.xml +++ b/testsuite/integration-arquillian/test-apps/pom.xml @@ -5,7 +5,7 @@ integration-arquillian org.keycloak.testsuite - 999.0.0-SNAPSHOT + 24.0.5 4.0.0 diff --git a/testsuite/integration-arquillian/test-apps/servlet-authz/pom.xml b/testsuite/integration-arquillian/test-apps/servlet-authz/pom.xml index a2b12820e213..8ed2df40dade 100755 --- a/testsuite/integration-arquillian/test-apps/servlet-authz/pom.xml +++ b/testsuite/integration-arquillian/test-apps/servlet-authz/pom.xml @@ -6,7 +6,7 @@ org.keycloak.testsuite integration-arquillian-test-apps - 999.0.0-SNAPSHOT + 24.0.5 servlet-authz-app diff --git a/testsuite/integration-arquillian/test-apps/servlet-policy-enforcer/pom.xml b/testsuite/integration-arquillian/test-apps/servlet-policy-enforcer/pom.xml index a5fb0fe51153..abb954b67099 100755 --- a/testsuite/integration-arquillian/test-apps/servlet-policy-enforcer/pom.xml +++ b/testsuite/integration-arquillian/test-apps/servlet-policy-enforcer/pom.xml @@ -24,7 +24,7 @@ org.keycloak.testsuite integration-arquillian-test-apps - 999.0.0-SNAPSHOT + 24.0.5 servlet-policy-enforcer diff --git a/testsuite/integration-arquillian/test-apps/servlets-jakarta/pom.xml b/testsuite/integration-arquillian/test-apps/servlets-jakarta/pom.xml index ff0e3f1d8c4a..a566d88fe4ca 100644 --- a/testsuite/integration-arquillian/test-apps/servlets-jakarta/pom.xml +++ b/testsuite/integration-arquillian/test-apps/servlets-jakarta/pom.xml @@ -6,7 +6,7 @@ org.keycloak.testsuite integration-arquillian-test-apps - 999.0.0-SNAPSHOT + 24.0.5 integration-arquillian-test-apps-servlets-jakarta diff --git a/testsuite/integration-arquillian/test-apps/servlets/pom.xml b/testsuite/integration-arquillian/test-apps/servlets/pom.xml index 88e482771d6a..9d93fc53efcb 100644 --- a/testsuite/integration-arquillian/test-apps/servlets/pom.xml +++ b/testsuite/integration-arquillian/test-apps/servlets/pom.xml @@ -5,7 +5,7 @@ integration-arquillian-test-apps org.keycloak.testsuite - 999.0.0-SNAPSHOT + 24.0.5 4.0.0 diff --git a/testsuite/integration-arquillian/test-apps/spring-boot-adapter-app/pom.xml b/testsuite/integration-arquillian/test-apps/spring-boot-adapter-app/pom.xml index 08ac13d183d6..020f21f90ad3 100644 --- a/testsuite/integration-arquillian/test-apps/spring-boot-adapter-app/pom.xml +++ b/testsuite/integration-arquillian/test-apps/spring-boot-adapter-app/pom.xml @@ -5,7 +5,7 @@ org.keycloak.testsuite integration-arquillian-test-apps - 999.0.0-SNAPSHOT + 24.0.5 4.0.0 diff --git a/testsuite/integration-arquillian/test-apps/test-apps-dist/pom.xml b/testsuite/integration-arquillian/test-apps/test-apps-dist/pom.xml index e937de7a6752..961e777bbf2a 100644 --- a/testsuite/integration-arquillian/test-apps/test-apps-dist/pom.xml +++ b/testsuite/integration-arquillian/test-apps/test-apps-dist/pom.xml @@ -5,7 +5,7 @@ integration-arquillian-test-apps org.keycloak.testsuite - 999.0.0-SNAPSHOT + 24.0.5 4.0.0 diff --git a/testsuite/integration-arquillian/tests/base/pom.xml b/testsuite/integration-arquillian/tests/base/pom.xml index 2b0db696463c..46a19ebf6ab6 100644 --- a/testsuite/integration-arquillian/tests/base/pom.xml +++ b/testsuite/integration-arquillian/tests/base/pom.xml @@ -21,7 +21,7 @@ org.keycloak.testsuite integration-arquillian-tests - 999.0.0-SNAPSHOT + 24.0.5 4.0.0 diff --git a/testsuite/integration-arquillian/tests/other/adapters/jboss/pom.xml b/testsuite/integration-arquillian/tests/other/adapters/jboss/pom.xml index 4a6cdb684327..0f2c45e68b97 100644 --- a/testsuite/integration-arquillian/tests/other/adapters/jboss/pom.xml +++ b/testsuite/integration-arquillian/tests/other/adapters/jboss/pom.xml @@ -24,7 +24,7 @@ org.keycloak.testsuite integration-arquillian-tests-adapters - 999.0.0-SNAPSHOT + 24.0.5 integration-arquillian-tests-adapters-jboss diff --git a/testsuite/integration-arquillian/tests/other/adapters/karaf/fuse61/pom.xml b/testsuite/integration-arquillian/tests/other/adapters/karaf/fuse61/pom.xml index 165ab3386bbf..6dcee61df8c0 100644 --- a/testsuite/integration-arquillian/tests/other/adapters/karaf/fuse61/pom.xml +++ b/testsuite/integration-arquillian/tests/other/adapters/karaf/fuse61/pom.xml @@ -24,7 +24,7 @@ org.keycloak.testsuite integration-arquillian-tests-adapters-karaf - 999.0.0-SNAPSHOT + 24.0.5 integration-arquillian-tests-adapters-fuse61 diff --git a/testsuite/integration-arquillian/tests/other/adapters/karaf/fuse62/pom.xml b/testsuite/integration-arquillian/tests/other/adapters/karaf/fuse62/pom.xml index 012da49743bc..c66732beabfc 100644 --- a/testsuite/integration-arquillian/tests/other/adapters/karaf/fuse62/pom.xml +++ b/testsuite/integration-arquillian/tests/other/adapters/karaf/fuse62/pom.xml @@ -24,7 +24,7 @@ org.keycloak.testsuite integration-arquillian-tests-adapters-karaf - 999.0.0-SNAPSHOT + 24.0.5 integration-arquillian-tests-adapters-fuse62 diff --git a/testsuite/integration-arquillian/tests/other/adapters/karaf/karaf3/pom.xml b/testsuite/integration-arquillian/tests/other/adapters/karaf/karaf3/pom.xml index e4eb4dc31252..dbe6e0ed2c56 100644 --- a/testsuite/integration-arquillian/tests/other/adapters/karaf/karaf3/pom.xml +++ b/testsuite/integration-arquillian/tests/other/adapters/karaf/karaf3/pom.xml @@ -24,7 +24,7 @@ org.keycloak.testsuite integration-arquillian-tests-adapters-karaf - 999.0.0-SNAPSHOT + 24.0.5 integration-arquillian-tests-adapters-karaf3 diff --git a/testsuite/integration-arquillian/tests/other/adapters/karaf/pom.xml b/testsuite/integration-arquillian/tests/other/adapters/karaf/pom.xml index c11a2e159adf..2c0ee171876c 100644 --- a/testsuite/integration-arquillian/tests/other/adapters/karaf/pom.xml +++ b/testsuite/integration-arquillian/tests/other/adapters/karaf/pom.xml @@ -24,7 +24,7 @@ org.keycloak.testsuite integration-arquillian-tests-adapters - 999.0.0-SNAPSHOT + 24.0.5 integration-arquillian-tests-adapters-karaf diff --git a/testsuite/integration-arquillian/tests/other/adapters/pom.xml b/testsuite/integration-arquillian/tests/other/adapters/pom.xml index f64034d58652..f23388b512e5 100644 --- a/testsuite/integration-arquillian/tests/other/adapters/pom.xml +++ b/testsuite/integration-arquillian/tests/other/adapters/pom.xml @@ -24,7 +24,7 @@ org.keycloak.testsuite integration-arquillian-tests-other - 999.0.0-SNAPSHOT + 24.0.5 integration-arquillian-tests-adapters diff --git a/testsuite/integration-arquillian/tests/other/adapters/was/pom.xml b/testsuite/integration-arquillian/tests/other/adapters/was/pom.xml index 97a7a6f1f33a..31d5fffca1ac 100644 --- a/testsuite/integration-arquillian/tests/other/adapters/was/pom.xml +++ b/testsuite/integration-arquillian/tests/other/adapters/was/pom.xml @@ -24,7 +24,7 @@ org.keycloak.testsuite integration-arquillian-tests-adapters - 999.0.0-SNAPSHOT + 24.0.5 integration-arquillian-tests-adapters-was diff --git a/testsuite/integration-arquillian/tests/other/adapters/was/was8/pom.xml b/testsuite/integration-arquillian/tests/other/adapters/was/was8/pom.xml index 6bb112f1325b..2966a93ae6c6 100644 --- a/testsuite/integration-arquillian/tests/other/adapters/was/was8/pom.xml +++ b/testsuite/integration-arquillian/tests/other/adapters/was/was8/pom.xml @@ -24,7 +24,7 @@ org.keycloak.testsuite integration-arquillian-tests-adapters-was - 999.0.0-SNAPSHOT + 24.0.5 integration-arquillian-tests-adapters-was8 diff --git a/testsuite/integration-arquillian/tests/other/adapters/wls/pom.xml b/testsuite/integration-arquillian/tests/other/adapters/wls/pom.xml index 4cdabe88b6ea..3fae907682bb 100644 --- a/testsuite/integration-arquillian/tests/other/adapters/wls/pom.xml +++ b/testsuite/integration-arquillian/tests/other/adapters/wls/pom.xml @@ -24,7 +24,7 @@ org.keycloak.testsuite integration-arquillian-tests-adapters - 999.0.0-SNAPSHOT + 24.0.5 integration-arquillian-tests-adapters-wls diff --git a/testsuite/integration-arquillian/tests/other/adapters/wls/wls12/pom.xml b/testsuite/integration-arquillian/tests/other/adapters/wls/wls12/pom.xml index 925b66590116..10d9979d0d15 100644 --- a/testsuite/integration-arquillian/tests/other/adapters/wls/wls12/pom.xml +++ b/testsuite/integration-arquillian/tests/other/adapters/wls/wls12/pom.xml @@ -24,7 +24,7 @@ org.keycloak.testsuite integration-arquillian-tests-adapters-wls - 999.0.0-SNAPSHOT + 24.0.5 integration-arquillian-tests-adapters-wls12 diff --git a/testsuite/integration-arquillian/tests/other/base-ui/pom.xml b/testsuite/integration-arquillian/tests/other/base-ui/pom.xml index 1ce0c878c4ba..e85f842bb01f 100644 --- a/testsuite/integration-arquillian/tests/other/base-ui/pom.xml +++ b/testsuite/integration-arquillian/tests/other/base-ui/pom.xml @@ -22,7 +22,7 @@ integration-arquillian-tests-other org.keycloak.testsuite - 999.0.0-SNAPSHOT + 24.0.5 4.0.0 diff --git a/testsuite/integration-arquillian/tests/other/jpa-performance/pom.xml b/testsuite/integration-arquillian/tests/other/jpa-performance/pom.xml index a4715a5937be..f84cbd857bbf 100644 --- a/testsuite/integration-arquillian/tests/other/jpa-performance/pom.xml +++ b/testsuite/integration-arquillian/tests/other/jpa-performance/pom.xml @@ -24,7 +24,7 @@ org.keycloak.testsuite integration-arquillian-tests-other - 999.0.0-SNAPSHOT + 24.0.5 integration-arquillian-tests-jpa-performance diff --git a/testsuite/integration-arquillian/tests/other/mod_auth_mellon/pom.xml b/testsuite/integration-arquillian/tests/other/mod_auth_mellon/pom.xml index d6ce88890d05..2105c3cfab01 100644 --- a/testsuite/integration-arquillian/tests/other/mod_auth_mellon/pom.xml +++ b/testsuite/integration-arquillian/tests/other/mod_auth_mellon/pom.xml @@ -24,7 +24,7 @@ org.keycloak.testsuite integration-arquillian-tests-other - 999.0.0-SNAPSHOT + 24.0.5 integration-arquillian-tests-other-mod_auth_mellon diff --git a/testsuite/integration-arquillian/tests/other/pom.xml b/testsuite/integration-arquillian/tests/other/pom.xml index 4e8f7f9a10de..de45b6402f57 100644 --- a/testsuite/integration-arquillian/tests/other/pom.xml +++ b/testsuite/integration-arquillian/tests/other/pom.xml @@ -24,7 +24,7 @@ org.keycloak.testsuite integration-arquillian-tests - 999.0.0-SNAPSHOT + 24.0.5 integration-arquillian-tests-other diff --git a/testsuite/integration-arquillian/tests/other/springboot-tests/pom.xml b/testsuite/integration-arquillian/tests/other/springboot-tests/pom.xml index cd3ea630c6d7..358a6ec4e086 100644 --- a/testsuite/integration-arquillian/tests/other/springboot-tests/pom.xml +++ b/testsuite/integration-arquillian/tests/other/springboot-tests/pom.xml @@ -5,7 +5,7 @@ integration-arquillian-tests-other org.keycloak.testsuite - 999.0.0-SNAPSHOT + 24.0.5 4.0.0 diff --git a/testsuite/integration-arquillian/tests/other/sssd/pom.xml b/testsuite/integration-arquillian/tests/other/sssd/pom.xml index abf95fde1c14..42c4f84a8cc6 100644 --- a/testsuite/integration-arquillian/tests/other/sssd/pom.xml +++ b/testsuite/integration-arquillian/tests/other/sssd/pom.xml @@ -5,7 +5,7 @@ integration-arquillian-tests-other org.keycloak.testsuite - 999.0.0-SNAPSHOT + 24.0.5 4.0.0 diff --git a/testsuite/integration-arquillian/tests/other/webauthn/pom.xml b/testsuite/integration-arquillian/tests/other/webauthn/pom.xml index 4d23a83daab8..a8e70f4f283c 100644 --- a/testsuite/integration-arquillian/tests/other/webauthn/pom.xml +++ b/testsuite/integration-arquillian/tests/other/webauthn/pom.xml @@ -5,7 +5,7 @@ org.keycloak.testsuite integration-arquillian-tests-other - 999.0.0-SNAPSHOT + 24.0.5 4.0.0 jar diff --git a/testsuite/integration-arquillian/tests/pom.xml b/testsuite/integration-arquillian/tests/pom.xml index 18d9ef82a558..e2dbba1f5cb4 100644 --- a/testsuite/integration-arquillian/tests/pom.xml +++ b/testsuite/integration-arquillian/tests/pom.xml @@ -24,7 +24,7 @@ org.keycloak.testsuite integration-arquillian - 999.0.0-SNAPSHOT + 24.0.5 pom diff --git a/testsuite/integration-arquillian/util/pom.xml b/testsuite/integration-arquillian/util/pom.xml index 1f762ab47abf..3126226b047d 100644 --- a/testsuite/integration-arquillian/util/pom.xml +++ b/testsuite/integration-arquillian/util/pom.xml @@ -21,7 +21,7 @@ org.keycloak.testsuite integration-arquillian - 999.0.0-SNAPSHOT + 24.0.5 4.0.0 diff --git a/testsuite/model/pom.xml b/testsuite/model/pom.xml index 7f8ae395ef98..722dea9b6970 100644 --- a/testsuite/model/pom.xml +++ b/testsuite/model/pom.xml @@ -4,7 +4,7 @@ org.keycloak keycloak-testsuite-pom - 999.0.0-SNAPSHOT + 24.0.5 ../pom.xml diff --git a/testsuite/pom.xml b/testsuite/pom.xml index b8f8a84e8bf0..d5358ffbc51b 100755 --- a/testsuite/pom.xml +++ b/testsuite/pom.xml @@ -21,7 +21,7 @@ keycloak-parent org.keycloak - 999.0.0-SNAPSHOT + 24.0.5 ../pom.xml 4.0.0 diff --git a/testsuite/utils/pom.xml b/testsuite/utils/pom.xml index 3f1f3f586ba4..1121b82b0230 100755 --- a/testsuite/utils/pom.xml +++ b/testsuite/utils/pom.xml @@ -21,7 +21,7 @@ keycloak-testsuite-pom org.keycloak - 999.0.0-SNAPSHOT + 24.0.5 4.0.0 diff --git a/themes/pom.xml b/themes/pom.xml index 280ddde2115f..d5c0c67f87c8 100755 --- a/themes/pom.xml +++ b/themes/pom.xml @@ -3,7 +3,7 @@ keycloak-parent org.keycloak - 999.0.0-SNAPSHOT + 24.0.5 4.0.0 diff --git a/util/embedded-ldap/pom.xml b/util/embedded-ldap/pom.xml index 1e10ff4e357b..ed5e8ade7f91 100644 --- a/util/embedded-ldap/pom.xml +++ b/util/embedded-ldap/pom.xml @@ -21,7 +21,7 @@ keycloak-parent org.keycloak - 999.0.0-SNAPSHOT + 24.0.5 ../../pom.xml 4.0.0 diff --git a/util/pom.xml b/util/pom.xml index 8b716dbbd2e5..8aaa239ac4aa 100644 --- a/util/pom.xml +++ b/util/pom.xml @@ -20,7 +20,7 @@ keycloak-parent org.keycloak - 999.0.0-SNAPSHOT + 24.0.5 ../pom.xml From ebcd6229410f492c0099156dbeb18841e7be4fb7 Mon Sep 17 00:00:00 2001 From: Georg Romstorfer Date: Wed, 7 Apr 2021 16:39:27 +0200 Subject: [PATCH 132/158] .ci-docker.yml (squashed 24.0.2) Update docker-container to Java 17. Update ci-docker.yml Update ci-docker.yml Rename master branch to main Change repo to primesign/keycloak add ci-docker.yml Disable image cache --- .github/workflows/ci-docker.yml | 67 +++++++++++++++++++++++++++++++++ 1 file changed, 67 insertions(+) create mode 100644 .github/workflows/ci-docker.yml diff --git a/.github/workflows/ci-docker.yml b/.github/workflows/ci-docker.yml new file mode 100644 index 000000000000..df3d8ed7d120 --- /dev/null +++ b/.github/workflows/ci-docker.yml @@ -0,0 +1,67 @@ +name: Docker CI + +on: + workflow_dispatch: + push: + branches: + - main + +env: + DEFAULT_JDK_VERSION: 17 + +concurrency: + # Only run once for latest commit per ref and cancel other (previous) runs. + group: docker-ci-${{ github.ref }} + cancel-in-progress: true + +jobs: + build: + name: Build and push docker image + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - uses: actions/setup-java@v3 + with: + distribution: 'temurin' + java-version: ${{ env.DEFAULT_JDK_VERSION }} + cache: 'maven' + + - name: Build Keycloak + run: | + mvn clean install -DskipTestsuite -DskipExamples -DskipTests + mvn -f quarkus/pom.xml clean install -DskipTests + + - name: Set up environment + run: cat release-details >> $GITHUB_ENV + + - name: Copy keycloak artifact + run: cp quarkus/dist/target/keycloak-${{env.VERSION}}.tar.gz quarkus/container/ + + - name: Set up QEMU + uses: docker/setup-qemu-action@v1 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v1 + + - name: Login to GitHub container registry + uses: docker/login-action@v1 + with: + registry: ghcr.io + username: primesign-services + password: ${{ secrets.CR_TOKEN }} + + - name: Build and push + uses: docker/build-push-action@v2 + with: + context: quarkus/container + platforms: linux/amd64,linux/arm64 + push: ${{ github.event_name != 'pull_request' }} + build-args: KEYCLOAK_DIST=keycloak-${{env.VERSION}}.tar.gz + tags: | + ghcr.io/primesign/keycloak:latest + ghcr.io/primesign/keycloak:${{env.VERSION}} + ghcr.io/primesign/keycloak:${{env.SHORT_VERSION}} + + - name: Remove keycloak artifacts before caching + if: steps.cache.outputs.cache-hit != 'true' + run: rm -rf ~/.m2/repository/org/keycloak \ No newline at end of file From 08b68245756b69cb9543662d97c11b9924f4c8ac Mon Sep 17 00:00:00 2001 From: Patrick Weiner Date: Mon, 23 Oct 2023 13:41:07 +0200 Subject: [PATCH 133/158] set-version.sh (squashed 24.0.2) update setVersion.sh to update everything Update set-version.sh --- set-version.sh | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/set-version.sh b/set-version.sh index 21886a202163..28443058ed1b 100755 --- a/set-version.sh +++ b/set-version.sh @@ -33,3 +33,14 @@ echo "$(jq '. += {"version": "'$NEW_NPM_VERSION'"}' js/libs/keycloak-admin-clien echo "New Mvn Version: $NEW_VERSION" >&2 echo "New NPM Version: $NEW_NPM_VERSION" >&2 + +# js-ci.yml remove 999 +sed -i -E 's/keycloak-[0-9]+\.[0-9]+\.[0-9]+(-PS-[0-9]+){0,1}(-SNAPSHOT){0,1}\.tar.gz/keycloak-'$NEW_VERSION'.tar.gz/g' .github/workflows/js-ci.yml +sed -i -E 's/keycloak-[0-9]+\.[0-9]+\.[0-9]+(-PS-[0-9]+){0,1}(-SNAPSHOT){0,1}/keycloak-'$NEW_VERSION'/g' .github/workflows/js-ci.yml + + +cat < release-details +VERSION=$NEW_VERSION +SHORT_VERSION=$NEW_VERSION +NPM_VERSION=$NEW_VERSION +EOT From 073f80a2a811216ffa32b13813dfc6e4f5e59109 Mon Sep 17 00:00:00 2001 From: Patrick Weiner Date: Thu, 19 Jan 2023 10:06:37 +0100 Subject: [PATCH 134/158] release-details --- release-details | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 release-details diff --git a/release-details b/release-details new file mode 100644 index 000000000000..f18a6e339320 --- /dev/null +++ b/release-details @@ -0,0 +1,3 @@ +VERSION=21.1.1 +SHORT_VERSION=21.1.1 +NPM_VERSION=21.1.1 \ No newline at end of file From 9d53aee585720a42f65aa7cd76ab94b1e3e0090e Mon Sep 17 00:00:00 2001 From: Patrick Weiner Date: Tue, 13 Sep 2022 10:48:44 +0200 Subject: [PATCH 135/158] don't build documentation and remove dependabot.yml to disable dependabot entirely, as we get version updates for packages as we rebase on to newer versions of keycloak. --- .github/dependabot.yml | 36 ----------------------------- .github/workflows/documentation.yml | 1 + 2 files changed, 1 insertion(+), 36 deletions(-) delete mode 100644 .github/dependabot.yml diff --git a/.github/dependabot.yml b/.github/dependabot.yml deleted file mode 100644 index 3f1406802f78..000000000000 --- a/.github/dependabot.yml +++ /dev/null @@ -1,36 +0,0 @@ -version: 2 -updates: - - package-ecosystem: github-actions - directory: / - open-pull-requests-limit: 999 - rebase-strategy: disabled - schedule: - interval: daily - time: "00:00" - timezone: Etc/GMT - labels: - - area/dependencies - - area/ci - - package-ecosystem: npm - directory: /themes/src/main/resources/theme/keycloak/common/resources - schedule: - interval: daily - time: "00:00" - timezone: Etc/GMT - open-pull-requests-limit: 999 - rebase-strategy: disabled - labels: - - area/dependencies - - team/ui - - package-ecosystem: npm - directory: js - open-pull-requests-limit: 999 - rebase-strategy: disabled - versioning-strategy: increase - schedule: - interval: daily - time: "00:00" - timezone: Etc/GMT - labels: - - area/dependencies - - team/ui diff --git a/.github/workflows/documentation.yml b/.github/workflows/documentation.yml index 9e4bb7b6d5a6..39068b710a55 100644 --- a/.github/workflows/documentation.yml +++ b/.github/workflows/documentation.yml @@ -6,6 +6,7 @@ on: - main - dependabot/** - quarkus-next + - fb-* pull_request: workflow_dispatch: From 477e72cf0abbdfd7cb48abe4ec7acf36aa9931b6 Mon Sep 17 00:00:00 2001 From: Patrick Weiner Date: Wed, 30 Jun 2021 10:47:50 +0200 Subject: [PATCH 136/158] Add regex redirect-uri client policy --- .../oidc/endpoints/AuthorizationEndpoint.java | 7 ++ .../condition/ClientIdsCondition.java | 80 ++++++++++++++++++ .../condition/ClientIdsConditionFactory.java | 52 ++++++++++++ .../executor/RegexRedirectUriExecutor.java | 83 +++++++++++++++++++ .../RegexRedirectUriExecutorFactory.java | 50 +++++++++++ ...ition.ClientPolicyConditionProviderFactory | 3 +- ...ecutor.ClientPolicyExecutorProviderFactory | 1 + .../testsuite/util/ClientPoliciesUtil.java | 14 ++++ 8 files changed, 289 insertions(+), 1 deletion(-) create mode 100644 services/src/main/java/org/keycloak/services/clientpolicy/condition/ClientIdsCondition.java create mode 100644 services/src/main/java/org/keycloak/services/clientpolicy/condition/ClientIdsConditionFactory.java create mode 100644 services/src/main/java/org/keycloak/services/clientpolicy/executor/RegexRedirectUriExecutor.java create mode 100644 services/src/main/java/org/keycloak/services/clientpolicy/executor/RegexRedirectUriExecutorFactory.java diff --git a/services/src/main/java/org/keycloak/protocol/oidc/endpoints/AuthorizationEndpoint.java b/services/src/main/java/org/keycloak/protocol/oidc/endpoints/AuthorizationEndpoint.java index 74441c97f22c..4d6c01263228 100755 --- a/services/src/main/java/org/keycloak/protocol/oidc/endpoints/AuthorizationEndpoint.java +++ b/services/src/main/java/org/keycloak/protocol/oidc/endpoints/AuthorizationEndpoint.java @@ -64,6 +64,8 @@ import java.util.Map; import java.util.function.BiConsumer; +import static org.keycloak.OAuthErrorException.INVALID_REDIRECT_URI; + /** * @author Stian Thorgersen */ @@ -185,6 +187,11 @@ private Response process(final MultivaluedMap params) { try { session.clientPolicy().triggerOnEvent(new AuthorizationRequestContext(parsedResponseType, request, redirectUri, params)); } catch (ClientPolicyException cpe) { + if (cpe.getError().equals(INVALID_REDIRECT_URI)) { + event.error(Errors.INVALID_REDIRECT_URI); + throw new ErrorPageException(session, authenticationSession, Response.Status.BAD_REQUEST, Messages.INVALID_PARAMETER, + OIDCLoginProtocol.REDIRECT_URI_PARAM); + } return redirectErrorToClient(parsedResponseMode, cpe.getError(), cpe.getErrorDetail()); } diff --git a/services/src/main/java/org/keycloak/services/clientpolicy/condition/ClientIdsCondition.java b/services/src/main/java/org/keycloak/services/clientpolicy/condition/ClientIdsCondition.java new file mode 100644 index 000000000000..ab3224bfee00 --- /dev/null +++ b/services/src/main/java/org/keycloak/services/clientpolicy/condition/ClientIdsCondition.java @@ -0,0 +1,80 @@ +package org.keycloak.services.clientpolicy.condition; + +import org.jboss.logging.Logger; +import org.keycloak.models.ClientModel; +import org.keycloak.models.KeycloakSession; +import org.keycloak.representations.idm.ClientPolicyConditionConfigurationRepresentation; +import org.keycloak.services.clientpolicy.ClientPolicyContext; +import org.keycloak.services.clientpolicy.ClientPolicyException; +import org.keycloak.services.clientpolicy.ClientPolicyVote; + +import java.util.List; + +public class ClientIdsCondition extends AbstractClientPolicyConditionProvider { + + private static final Logger logger = Logger.getLogger(ClientIdsCondition.class); + + public ClientIdsCondition(KeycloakSession session) { + super(session); + } + + @Override public Class getConditionConfigurationClass() { + return ClientIdsCondition.Configuration.class; + } + + @Override public String getProviderId() { + return ClientIdsConditionFactory.PROVIDER_ID; + } + + @Override public ClientPolicyVote applyPolicy(ClientPolicyContext context) throws ClientPolicyException { + switch (context.getEvent()) { + case AUTHORIZATION_REQUEST: + case TOKEN_REQUEST: + case TOKEN_REFRESH: + case TOKEN_REVOKE: + case TOKEN_INTROSPECT: + case USERINFO_REQUEST: + case LOGOUT_REQUEST: + if (clientIdMatched(session.getContext().getClient())) + return ClientPolicyVote.YES; + return ClientPolicyVote.NO; + default: + return ClientPolicyVote.ABSTAIN; + } + } + + private boolean clientIdMatched(ClientModel client) { + if (client == null || client.getClientId() == null) + return false; + + List configuredClientIds = configuration.getClientIds(); + + if (configuredClientIds == null) + return false; + + String clientId = client.getClientId(); + + for (String configuredClientId : configuredClientIds) { + if (clientId.equals(configuredClientId)) { + return true; + } + } + + return false; + + } + + public static class Configuration extends ClientPolicyConditionConfigurationRepresentation { + + protected List clientIds; + + public List getClientIds() { + return clientIds; + } + + public void setClientIds(List clientIds) { + this.clientIds = clientIds; + } + } + +} diff --git a/services/src/main/java/org/keycloak/services/clientpolicy/condition/ClientIdsConditionFactory.java b/services/src/main/java/org/keycloak/services/clientpolicy/condition/ClientIdsConditionFactory.java new file mode 100644 index 000000000000..e1eab4e4e942 --- /dev/null +++ b/services/src/main/java/org/keycloak/services/clientpolicy/condition/ClientIdsConditionFactory.java @@ -0,0 +1,52 @@ +package org.keycloak.services.clientpolicy.condition; + +import org.keycloak.Config; +import org.keycloak.models.KeycloakSession; +import org.keycloak.models.KeycloakSessionFactory; +import org.keycloak.provider.ProviderConfigProperty; + +import java.util.ArrayList; +import java.util.List; + +public class ClientIdsConditionFactory implements ClientPolicyConditionProviderFactory { + + public static final String PROVIDER_ID = "client-ids"; + public static final String CLIENT_IDS = "clientIds"; + + private static final List configProperties = new ArrayList(); + + static { + ProviderConfigProperty property; + property = new ProviderConfigProperty(CLIENT_IDS, PROVIDER_ID + ".label", PROVIDER_ID + "-condition.tooltip", + ProviderConfigProperty.MULTIVALUED_STRING_TYPE, null); + configProperties.add(property); + } + + @Override public ClientPolicyConditionProvider create(KeycloakSession session) { + return new ClientIdsCondition(session); + } + + @Override public void init(Config.Scope config) { + } + + @Override public void postInit(KeycloakSessionFactory factory) { + } + + @Override public void close() { + } + + @Override public String getId() { + return PROVIDER_ID; + } + + @Override public String getHelpText() { + return "The condition checks whether one of the specified client ids matches the client-id of the client to determine whether the policy is applied."; + } + + @Override public List getConfigProperties() { + return configProperties; + } + +} + + diff --git a/services/src/main/java/org/keycloak/services/clientpolicy/executor/RegexRedirectUriExecutor.java b/services/src/main/java/org/keycloak/services/clientpolicy/executor/RegexRedirectUriExecutor.java new file mode 100644 index 000000000000..b918dd07f8b1 --- /dev/null +++ b/services/src/main/java/org/keycloak/services/clientpolicy/executor/RegexRedirectUriExecutor.java @@ -0,0 +1,83 @@ +package org.keycloak.services.clientpolicy.executor; + +import com.fasterxml.jackson.annotation.JsonProperty; +import org.jboss.logging.Logger; +import org.keycloak.representations.idm.ClientPolicyExecutorConfigurationRepresentation; +import org.keycloak.services.clientpolicy.ClientPolicyContext; +import org.keycloak.services.clientpolicy.ClientPolicyEvent; +import org.keycloak.services.clientpolicy.ClientPolicyException; +import org.keycloak.services.clientpolicy.context.AuthorizationRequestContext; + +import java.util.List; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import static org.keycloak.OAuthErrorException.INVALID_REDIRECT_URI; +import static org.keycloak.services.clientpolicy.executor.RegexRedirectUriExecutorFactory.REGEX_PATTERNS_CONFIG_FIELD; + +public class RegexRedirectUriExecutor + implements ClientPolicyExecutorProvider { + + private static final Logger logger = Logger.getLogger(RegexRedirectUriExecutor.class); + + private Configuration configuration; + + public RegexRedirectUriExecutor() { + } + + public void setupConfiguration(Configuration config) { + this.configuration = config; + } + + public Class getExecutorConfigurationClass() { + return Configuration.class; + } + + public String getProviderId() { + return "regex-redirect-uri"; + } + + public void executeOnEvent(ClientPolicyContext context) throws ClientPolicyException { + if (context.getEvent().equals(ClientPolicyEvent.AUTHORIZATION_REQUEST)) { + this.checkRedirectUri(((AuthorizationRequestContext) context).getRedirectUri()); + } + } + + private void checkRedirectUri(String redirectUri) throws ClientPolicyException { + if (redirectUri != null && !redirectUri.isEmpty()) { + logger.tracev("Redirect URI = {0}", redirectUri); + + List patterns = this.configuration.getRedirectUriRegexPatterns(); + + for (String pattern : patterns) { + Pattern regexPattern = Pattern.compile(pattern); + Matcher matcher = regexPattern.matcher(redirectUri); + if (matcher.matches()) { + return; + } + } + + throw new ClientPolicyException(INVALID_REDIRECT_URI, "Invalid redirect_uri"); + + } else { + throw new ClientPolicyException(INVALID_REDIRECT_URI, "no redirect_uri specified."); + } + } + + public static class Configuration extends ClientPolicyExecutorConfigurationRepresentation { + @JsonProperty(REGEX_PATTERNS_CONFIG_FIELD) + protected List redirectUriRegexPatterns; + + public Configuration() { + } + + public List getRedirectUriRegexPatterns() { + return redirectUriRegexPatterns; + } + + public void setRedirectUriRegexPatterns(List redirectUriRegexPatterns) { + this.redirectUriRegexPatterns = redirectUriRegexPatterns; + } + } + +} diff --git a/services/src/main/java/org/keycloak/services/clientpolicy/executor/RegexRedirectUriExecutorFactory.java b/services/src/main/java/org/keycloak/services/clientpolicy/executor/RegexRedirectUriExecutorFactory.java new file mode 100644 index 000000000000..989944f9858f --- /dev/null +++ b/services/src/main/java/org/keycloak/services/clientpolicy/executor/RegexRedirectUriExecutorFactory.java @@ -0,0 +1,50 @@ +package org.keycloak.services.clientpolicy.executor; + +import org.keycloak.Config; +import org.keycloak.models.KeycloakSession; +import org.keycloak.models.KeycloakSessionFactory; +import org.keycloak.provider.ProviderConfigProperty; + +import java.util.ArrayList; +import java.util.List; + +public class RegexRedirectUriExecutorFactory implements ClientPolicyExecutorProviderFactory { + public static final String PROVIDER_ID = "regex-redirect-uri"; + public static final String REGEX_PATTERNS_CONFIG_FIELD = "redirect-uri-regex-patterns"; + private List configProperties = new ArrayList(); + + public RegexRedirectUriExecutorFactory() { + } + + public ClientPolicyExecutorProvider create(KeycloakSession session) { + return new RegexRedirectUriExecutor(); + } + + public void init(Config.Scope config) { + } + + public void postInit(KeycloakSessionFactory factory) { + ProviderConfigProperty regexPatterns = + new ProviderConfigProperty(REGEX_PATTERNS_CONFIG_FIELD, "Redirect URI Regex Patterns", + "Regex-Patterns with which the redirect-URI is checked against", + "MultivaluedString", null); + + this.configProperties.add(regexPatterns); + } + + public void close() { + } + + public String getId() { + return PROVIDER_ID; + } + + public String getHelpText() { + return "Checks if the redirect URI matches a configured regex pattern."; + } + + public List getConfigProperties() { + return this.configProperties; + } + +} diff --git a/services/src/main/resources/META-INF/services/org.keycloak.services.clientpolicy.condition.ClientPolicyConditionProviderFactory b/services/src/main/resources/META-INF/services/org.keycloak.services.clientpolicy.condition.ClientPolicyConditionProviderFactory index 0b5e3cffdece..9a9312fdeb7c 100644 --- a/services/src/main/resources/META-INF/services/org.keycloak.services.clientpolicy.condition.ClientPolicyConditionProviderFactory +++ b/services/src/main/resources/META-INF/services/org.keycloak.services.clientpolicy.condition.ClientPolicyConditionProviderFactory @@ -5,4 +5,5 @@ org.keycloak.services.clientpolicy.condition.ClientAccessTypeConditionFactory org.keycloak.services.clientpolicy.condition.ClientUpdaterSourceHostsConditionFactory org.keycloak.services.clientpolicy.condition.ClientUpdaterSourceGroupsConditionFactory org.keycloak.services.clientpolicy.condition.ClientUpdaterSourceRolesConditionFactory -org.keycloak.services.clientpolicy.condition.AnyClientConditionFactory \ No newline at end of file +org.keycloak.services.clientpolicy.condition.AnyClientConditionFactory +org.keycloak.services.clientpolicy.condition.ClientIdsConditionFactory \ No newline at end of file diff --git a/services/src/main/resources/META-INF/services/org.keycloak.services.clientpolicy.executor.ClientPolicyExecutorProviderFactory b/services/src/main/resources/META-INF/services/org.keycloak.services.clientpolicy.executor.ClientPolicyExecutorProviderFactory index 4f0320c276b7..370e888c94b8 100644 --- a/services/src/main/resources/META-INF/services/org.keycloak.services.clientpolicy.executor.ClientPolicyExecutorProviderFactory +++ b/services/src/main/resources/META-INF/services/org.keycloak.services.clientpolicy.executor.ClientPolicyExecutorProviderFactory @@ -13,6 +13,7 @@ org.keycloak.services.clientpolicy.executor.FullScopeDisabledExecutorFactory org.keycloak.protocol.oidc.grants.ciba.clientpolicy.executor.SecureCibaSessionEnforceExecutorFactory org.keycloak.protocol.oidc.grants.ciba.clientpolicy.executor.SecureCibaSignedAuthenticationRequestExecutorFactory org.keycloak.protocol.oidc.grants.ciba.clientpolicy.executor.SecureCibaAuthenticationRequestSigningAlgorithmExecutorFactory +org.keycloak.services.clientpolicy.executor.RegexRedirectUriExecutorFactory org.keycloak.services.clientpolicy.executor.SecureLogoutExecutorFactory org.keycloak.services.clientpolicy.executor.RejectResourceOwnerPasswordCredentialsGrantExecutorFactory org.keycloak.services.clientpolicy.executor.ClientSecretRotationExecutorFactory diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/util/ClientPoliciesUtil.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/util/ClientPoliciesUtil.java index 1859e8a5f6ec..6b00fd2a7cc1 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/util/ClientPoliciesUtil.java +++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/util/ClientPoliciesUtil.java @@ -48,6 +48,7 @@ import org.keycloak.representations.idm.ClientRepresentation; import org.keycloak.services.clientpolicy.ClientPolicyEvent; import org.keycloak.services.clientpolicy.condition.ClientAccessTypeCondition; +import org.keycloak.services.clientpolicy.condition.ClientIdsCondition; import org.keycloak.services.clientpolicy.condition.ClientRolesCondition; import org.keycloak.services.clientpolicy.condition.ClientScopesCondition; import org.keycloak.services.clientpolicy.condition.ClientUpdaterContextCondition; @@ -61,6 +62,7 @@ import org.keycloak.services.clientpolicy.executor.IntentClientBindCheckExecutor; import org.keycloak.services.clientpolicy.executor.PKCEEnforcerExecutor; import org.keycloak.services.clientpolicy.executor.RejectResourceOwnerPasswordCredentialsGrantExecutor; +import org.keycloak.services.clientpolicy.executor.RegexRedirectUriExecutor; import org.keycloak.services.clientpolicy.executor.RejectImplicitGrantExecutor; import org.keycloak.services.clientpolicy.executor.SecureClientAuthenticatorExecutor; import org.keycloak.services.clientpolicy.executor.SecureRedirectUrisEnforcerExecutor; @@ -241,6 +243,12 @@ public static SecureSigningAlgorithmExecutor.Configuration createSecureSigningAl return config; } + public static RegexRedirectUriExecutor.Configuration createRegexRedirectUriExecutorConfig(List regexPatterns){ + RegexRedirectUriExecutor.Configuration config = new RegexRedirectUriExecutor.Configuration(); + config.setRedirectUriRegexPatterns(regexPatterns); + return config; + } + public static SecureCibaAuthenticationRequestSigningAlgorithmExecutor.Configuration createSecureCibaAuthenticationRequestSigningAlgorithmExecutorConfig(String defaultAlgorithm) { SecureCibaAuthenticationRequestSigningAlgorithmExecutor.Configuration config = new SecureCibaAuthenticationRequestSigningAlgorithmExecutor.Configuration(); config.setDefaultAlgorithm(defaultAlgorithm); @@ -501,4 +509,10 @@ private static SignatureSignerContext createSignatureSignerContext(KeyWrapper ke throw new IllegalArgumentException("No signer provider for key algorithm type " + keyWrapper.getType()); } } + + public static ClientIdsCondition.Configuration createClientIdsConditionConfig(List clientIds){ + ClientIdsCondition.Configuration config = new ClientIdsCondition.Configuration(); + config.setClientIds(clientIds); + return config; + } } From f15aaffde786f4a0f461720d14a767db9075af24 Mon Sep 17 00:00:00 2001 From: Patrick Weiner Date: Mon, 29 Aug 2022 11:52:42 +0200 Subject: [PATCH 137/158] Allow ignoring of the additional request params size check and add "hash" to the ignore list. ignore parameter length for parameter 'dtbs' [squashed with update 24.0.5] --- .../request/AuthzEndpointRequestParser.java | 16 +++-- .../AuthzEndpointRequestParserTest.java | 59 +++++++++++++++++++ 2 files changed, 70 insertions(+), 5 deletions(-) create mode 100644 services/src/test/java/org/keycloak/protocol/oidc/endpoints/request/AuthzEndpointRequestParserTest.java diff --git a/services/src/main/java/org/keycloak/protocol/oidc/endpoints/request/AuthzEndpointRequestParser.java b/services/src/main/java/org/keycloak/protocol/oidc/endpoints/request/AuthzEndpointRequestParser.java index 246d40978383..01a174417f3c 100644 --- a/services/src/main/java/org/keycloak/protocol/oidc/endpoints/request/AuthzEndpointRequestParser.java +++ b/services/src/main/java/org/keycloak/protocol/oidc/endpoints/request/AuthzEndpointRequestParser.java @@ -51,6 +51,7 @@ public abstract class AuthzEndpointRequestParser { /** Set of known protocol GET params not to be stored into additionalReqParams} */ public static final Set KNOWN_REQ_PARAMS = new HashSet<>(); + public static final Set ADDITIONAL_REQ_PARAMS_MAX_SIZE_IGNORE = new HashSet<>(); static { KNOWN_REQ_PARAMS.add(OIDCLoginProtocol.CLIENT_ID_PARAM); KNOWN_REQ_PARAMS.add(OIDCLoginProtocol.RESPONSE_TYPE_PARAM); @@ -74,12 +75,17 @@ public abstract class AuthzEndpointRequestParser { KNOWN_REQ_PARAMS.add(OIDCLoginProtocol.CODE_CHALLENGE_PARAM); KNOWN_REQ_PARAMS.add(OIDCLoginProtocol.CODE_CHALLENGE_METHOD_PARAM); - // Those are not OAuth/OIDC parameters, but they should never be added to the additionalRequestParameters - KNOWN_REQ_PARAMS.add(OAuth2Constants.CLIENT_ASSERTION_TYPE); - KNOWN_REQ_PARAMS.add(OAuth2Constants.CLIENT_ASSERTION); - KNOWN_REQ_PARAMS.add(OAuth2Constants.CLIENT_SECRET); + // Ignore "hash" parameter for param size check, because more than 4 hashes get filtered by this check. + ADDITIONAL_REQ_PARAMS_MAX_SIZE_IGNORE.add("hash"); + ADDITIONAL_REQ_PARAMS_MAX_SIZE_IGNORE.add("dtbs"); + + // Those are not OAuth/OIDC parameters, but they should never be added to the additionalRequestParameters + KNOWN_REQ_PARAMS.add(OAuth2Constants.CLIENT_ASSERTION_TYPE); + KNOWN_REQ_PARAMS.add(OAuth2Constants.CLIENT_ASSERTION); + KNOWN_REQ_PARAMS.add(OAuth2Constants.CLIENT_SECRET); } + public void parseRequest(AuthorizationEndpointRequest request) { String clientId = getParameter(OIDCLoginProtocol.CLIENT_ID_PARAM); if (clientId != null && request.clientId != null && !request.clientId.equals(clientId)) { @@ -131,7 +137,7 @@ protected void extractAdditionalReqParams(Map additionalReqParam if (value != null && value.trim().isEmpty()) { value = null; } - if (value != null && value.length() <= ADDITIONAL_REQ_PARAMS_MAX_SIZE) { + if (value != null && ((value.length() <= ADDITIONAL_REQ_PARAMS_MAX_SIZE)) || ADDITIONAL_REQ_PARAMS_MAX_SIZE_IGNORE.contains(paramName)) { if (additionalReqParams.size() >= ADDITIONAL_REQ_PARAMS_MAX_MUMBER) { logger.debug("Maximal number of additional OIDC params (" + ADDITIONAL_REQ_PARAMS_MAX_MUMBER + ") exceeded, ignoring rest of them!"); break; diff --git a/services/src/test/java/org/keycloak/protocol/oidc/endpoints/request/AuthzEndpointRequestParserTest.java b/services/src/test/java/org/keycloak/protocol/oidc/endpoints/request/AuthzEndpointRequestParserTest.java new file mode 100644 index 000000000000..a383d82c931a --- /dev/null +++ b/services/src/test/java/org/keycloak/protocol/oidc/endpoints/request/AuthzEndpointRequestParserTest.java @@ -0,0 +1,59 @@ +package org.keycloak.protocol.oidc.endpoints.request; + +import org.junit.Test; + +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; + +public class AuthzEndpointRequestParserTest { + + + @Test + public void testExtractAdditionalReqParams() { + + TestAuthzEndpointRequestParser test = new TestAuthzEndpointRequestParser(); + + Map additionalReqParams = new HashMap<>(); + + test.extractAdditionalReqParams(additionalReqParams); + + assertNotNull(additionalReqParams); + assertEquals(1, additionalReqParams.size()); + assertTrue(additionalReqParams.containsKey("hash")); + + } + + + private class TestAuthzEndpointRequestParser extends AuthzEndpointRequestParser { + + @Override + protected String getParameter(String paramName) { + return "VER2CFYJFMliVLAFlYUUE9Vj4FkbIhHq1Ufe_VnWcsY%2C7P2xRG3WGTXogxuhXOfIrZsfL8lrnCE-G7E95chcwBw%2C9ba29mAU6sK4O9cBk22sGe9bK0Y5ilNycVVx0yij5SM%2Cw6HfN_o5gO7s39azUBWTyCBQiEbbihAGddwr9eUteFA%2CjYYPTQT4nz79AYMNOM67TcnDABQXOMWK0yJ_91EXwaM%2CVER2CFYJFMliVLAFlYUUE9Vj4FkbIhHq1Ufe_VnWcsY%2C7P2xRG3WGTXogxuhXOfIrZsfL8lrnCE-G7E95chcwBw%2C9ba29mAU6sK4O9cBk22sGe9bK0Y5ilNycVVx0yij5SM%2Cw6HfN_o5gO7s39azUBWTyCBQiEbbihAGddwr9eUteFA%2CjYYPTQT4nz79AYMNOM67TcnDABQXOMWK0yJ_91EXwaMVER2CFYJFMliVLAFlYUUE9Vj4FkbIhHq1Ufe_VnWcsY%2C7P2xRG3WGTXogxuhXOfIrZsfL8lrnCE-G7E95chcwBw%2C9ba29mAU6sK4O9cBk22sGe9bK0Y5ilNycVVx0yij5SM%2Cw6HfN_o5gO7s39azUBWTyCBQiEbbihAGddwr9eUteFA%2CjYYPTQT4nz79AYMNOM67TcnDABQXOMWK0yJ_91EXwaM%2CVER2CFYJFMliVLAFlYUUE9Vj4FkbIhHq1Ufe_VnWcsY%2C7P2xRG3WGTXogxuhXOfIrZsfL8lrnCE-G7E95chcwBw%2C9ba29mAU6sK4O9cBk22sGe9bK0Y5ilNycVVx0yij5SM%2Cw6HfN_o5gO7s39azUBWTyCBQiEbbihAGddwr9eUteFA%2CjYYPTQT4nz79AYMNOM67TcnDABQXOMWK0yJ_91EXwaMVER2CFYJFMliVLAFlYUUE9Vj4FkbIhHq1Ufe_VnWcsY%2C7P2xRG3WGTXogxuhXOfIrZsfL8lrnCE-G7E95chcwBw%2C9ba29mAU6sK4O9cBk22sGe9bK0Y5ilNycVVx0yij5SM%2Cw6HfN_o5gO7s39azUBWTyCBQiEbbihAGddwr9eUteFA%2CjYYPTQT4nz79AYMNOM67TcnDABQXOMWK0yJ_91EXwaM%2CVER2CFYJFMliVLAFlYUUE9Vj4FkbIhHq1Ufe_VnWcsY%2C7P2xRG3WGTXogxuhXOfIrZsfL8lrnCE-G7E95chcwBw%2C9ba29mAU6sK4O9cBk22sGe9bK0Y5ilNycVVx0yij5SM%2Cw6HfN_o5gO7s39azUBWTyCBQiEbbihAGddwr9eUteFA%2CjYYPTQT4nz79AYMNOM67TcnDABQXOMWK0yJ_91EXwaMVER2CFYJFMliVLAFlYUUE9Vj4FkbIhHq1Ufe_VnWcsY%2C7P2xRG3WGTXogxuhXOfIrZsfL8lrnCE-G7E95chcwBw%2C9ba29mAU6sK4O9cBk22sGe9bK0Y5ilNycVVx0yij5SM%2Cw6HfN_o5gO7s39azUBWTyCBQiEbbihAGddwr9eUteFA%2CjYYPTQT4nz79AYMNOM67TcnDABQXOMWK0yJ_91EXwaM%2CVER2CFYJFMliVLAFlYUUE9Vj4FkbIhHq1Ufe_VnWcsY%2C7P2xRG3WGTXogxuhXOfIrZsfL8lrnCE-G7E95chcwBw%2C9ba29mAU6sK4O9cBk22sGe9bK0Y5ilNycVVx0yij5SM%2Cw6HfN_o5gO7s39azUBWTyCBQiEbbihAGddwr9eUteFA%2CjYYPTQT4nz79AYMNOM67TcnDABQXOMWK0yJ_91EXwaMVER2CFYJFMliVLAFlYUUE9Vj4FkbIhHq1Ufe_VnWcsY%2C7P2xRG3WGTXogxuhXOfIrZsfL8lrnCE-G7E95chcwBw%2C9ba29mAU6sK4O9cBk22sGe9bK0Y5ilNycVVx0yij5SM%2Cw6HfN_o5gO7s39azUBWTyCBQiEbbihAGddwr9eUteFA%2CjYYPTQT4nz79AYMNOM67TcnDABQXOMWK0yJ_91EXwaM%2CVER2CFYJFMliVLAFlYUUE9Vj4FkbIhHq1Ufe_VnWcsY%2C7P2xRG3WGTXogxuhXOfIrZsfL8lrnCE-G7E95chcwBw%2C9ba29mAU6sK4O9cBk22sGe9bK0Y5ilNycVVx0yij5SM%2Cw6HfN_o5gO7s39azUBWTyCBQiEbbihAGddwr9eUteFA%2CjYYPTQT4nz79AYMNOM67TcnDABQXOMWK0yJ_91EXwaMVER2CFYJFMliVLAFlYUUE9Vj4FkbIhHq1Ufe_VnWcsY%2C7P2xRG3WGTXogxuhXOfIrZsfL8lrnCE-G7E95chcwBw%2C9ba29mAU6sK4O9cBk22sGe9bK0Y5ilNycVVx0yij5SM%2Cw6HfN_o5gO7s39azUBWTyCBQiEbbihAGddwr9eUteFA%2CjYYPTQT4nz79AYMNOM67TcnDABQXOMWK0yJ_91EXwaM%2CVER2CFYJFMliVLAFlYUUE9Vj4FkbIhHq1Ufe_VnWcsY%2C7P2xRG3WGTXogxuhXOfIrZsfL8lrnCE-G7E95chcwBw%2C9ba29mAU6sK4O9cBk22sGe9bK0Y5ilNycVVx0yij5SM%2Cw6HfN_o5gO7s39azUBWTyCBQiEbbihAGddwr9eUteFA%2CjYYPTQT4nz79AYMNOM67TcnDABQXOMWK0yJ_91EXwaM"; + } + + @Override + protected Integer getIntParameter(String paramName) { + return 10; + } + + @Override + protected Set keySet() { + + Set parameterSet = new HashSet<>(); + parameterSet.add("hash"); + parameterSet.add("customParameter1"); + parameterSet.add("customParameter2"); + parameterSet.add("client_id"); + + return parameterSet; + } + } + + +} From 317772cb3fa2a38f26e381a7f9001fe723c66696 Mon Sep 17 00:00:00 2001 From: szoescher Date: Mon, 3 Oct 2022 09:18:08 +0200 Subject: [PATCH 138/158] KEYCLOAK-224: Adds error that is not brute-force-protection relevant; --- .../keycloak/authentication/AuthenticationFlowError.java | 4 +++- .../authentication/DefaultAuthenticationFlow.java | 9 ++++++++- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/server-spi-private/src/main/java/org/keycloak/authentication/AuthenticationFlowError.java b/server-spi-private/src/main/java/org/keycloak/authentication/AuthenticationFlowError.java index 612b859e563a..5f47a33f8832 100755 --- a/server-spi-private/src/main/java/org/keycloak/authentication/AuthenticationFlowError.java +++ b/server-spi-private/src/main/java/org/keycloak/authentication/AuthenticationFlowError.java @@ -47,5 +47,7 @@ public enum AuthenticationFlowError { DISPLAY_NOT_SUPPORTED, ACCESS_DENIED, - GENERIC_AUTHENTICATION_ERROR + GENERIC_AUTHENTICATION_ERROR, + + NOT_AUTHENTICATION_RELEVANT_ERROR } diff --git a/services/src/main/java/org/keycloak/authentication/DefaultAuthenticationFlow.java b/services/src/main/java/org/keycloak/authentication/DefaultAuthenticationFlow.java index 31db8a72a153..696b95a8b58a 100755 --- a/services/src/main/java/org/keycloak/authentication/DefaultAuthenticationFlow.java +++ b/services/src/main/java/org/keycloak/authentication/DefaultAuthenticationFlow.java @@ -486,7 +486,14 @@ public Response processResult(AuthenticationProcessor.Result result, boolean isA return null; case FAILED: logger.debugv("authenticator FAILED: {0}", execution.getAuthenticator()); - processor.logFailure(); + + + // ignore brute force protection for non-authentication relevant errors. + if (result.error != null + && !result.error.equals(AuthenticationFlowError.NOT_AUTHENTICATION_RELEVANT_ERROR)) { + processor.logFailure(); + } + setExecutionStatus(execution, AuthenticationSessionModel.ExecutionStatus.FAILED); if (result.getChallenge() != null) { return sendChallenge(result, execution); From 444037594287ddfbf18951c3aa4cc7124a7e15fd Mon Sep 17 00:00:00 2001 From: Otto Touzil Date: Tue, 2 Apr 2024 13:26:03 +0200 Subject: [PATCH 139/158] [Admin-Theme]: Rename admin-theme. --- .../admin/messages/messages_ca.properties | 0 .../admin/messages/messages_de.properties | 0 .../admin/messages/messages_es.properties | 0 .../admin/messages/messages_fr.properties | 0 .../admin/messages/messages_ja.properties | 0 .../admin/messages/messages_lt.properties | 0 .../admin/messages/messages_no.properties | 0 .../admin/messages/messages_pl.properties | 0 .../admin/messages/messages_pt_BR.properties | 0 .../admin/messages/messages_ru.properties | 0 .../admin/messages/messages_zh_CN.properties | 0 .../META-INF/keycloak-themes.json | 2 +- .../admin/messages/messages_en.properties | 0 .../admin/theme.properties | 0 js/apps/admin-ui/pom.xml | 54 +++++++++---------- .../context/server-info/__tests__/mock.json | 6 +-- .../keycloak/theme/ThemeSelectorProvider.java | 3 +- .../admin/AdminConsoleLandingPageTest.java | 2 +- .../testsuite/admin/ServerInfoTest.java | 2 +- .../theme/ThemeResourceProviderTest.java | 2 +- .../resources/META-INF/keycloak-themes.json | 5 +- 21 files changed, 38 insertions(+), 38 deletions(-) rename js/apps/admin-ui/maven-resources-community/theme/{keycloak.v2 => primesign.v2}/admin/messages/messages_ca.properties (100%) rename js/apps/admin-ui/maven-resources-community/theme/{keycloak.v2 => primesign.v2}/admin/messages/messages_de.properties (100%) rename js/apps/admin-ui/maven-resources-community/theme/{keycloak.v2 => primesign.v2}/admin/messages/messages_es.properties (100%) rename js/apps/admin-ui/maven-resources-community/theme/{keycloak.v2 => primesign.v2}/admin/messages/messages_fr.properties (100%) rename js/apps/admin-ui/maven-resources-community/theme/{keycloak.v2 => primesign.v2}/admin/messages/messages_ja.properties (100%) rename js/apps/admin-ui/maven-resources-community/theme/{keycloak.v2 => primesign.v2}/admin/messages/messages_lt.properties (100%) rename js/apps/admin-ui/maven-resources-community/theme/{keycloak.v2 => primesign.v2}/admin/messages/messages_no.properties (100%) rename js/apps/admin-ui/maven-resources-community/theme/{keycloak.v2 => primesign.v2}/admin/messages/messages_pl.properties (100%) rename js/apps/admin-ui/maven-resources-community/theme/{keycloak.v2 => primesign.v2}/admin/messages/messages_pt_BR.properties (100%) rename js/apps/admin-ui/maven-resources-community/theme/{keycloak.v2 => primesign.v2}/admin/messages/messages_ru.properties (100%) rename js/apps/admin-ui/maven-resources-community/theme/{keycloak.v2 => primesign.v2}/admin/messages/messages_zh_CN.properties (100%) rename js/apps/admin-ui/maven-resources/theme/{keycloak.v2 => primesign.v2}/admin/messages/messages_en.properties (100%) rename js/apps/admin-ui/maven-resources/theme/{keycloak.v2 => primesign.v2}/admin/theme.properties (100%) diff --git a/js/apps/admin-ui/maven-resources-community/theme/keycloak.v2/admin/messages/messages_ca.properties b/js/apps/admin-ui/maven-resources-community/theme/primesign.v2/admin/messages/messages_ca.properties similarity index 100% rename from js/apps/admin-ui/maven-resources-community/theme/keycloak.v2/admin/messages/messages_ca.properties rename to js/apps/admin-ui/maven-resources-community/theme/primesign.v2/admin/messages/messages_ca.properties diff --git a/js/apps/admin-ui/maven-resources-community/theme/keycloak.v2/admin/messages/messages_de.properties b/js/apps/admin-ui/maven-resources-community/theme/primesign.v2/admin/messages/messages_de.properties similarity index 100% rename from js/apps/admin-ui/maven-resources-community/theme/keycloak.v2/admin/messages/messages_de.properties rename to js/apps/admin-ui/maven-resources-community/theme/primesign.v2/admin/messages/messages_de.properties diff --git a/js/apps/admin-ui/maven-resources-community/theme/keycloak.v2/admin/messages/messages_es.properties b/js/apps/admin-ui/maven-resources-community/theme/primesign.v2/admin/messages/messages_es.properties similarity index 100% rename from js/apps/admin-ui/maven-resources-community/theme/keycloak.v2/admin/messages/messages_es.properties rename to js/apps/admin-ui/maven-resources-community/theme/primesign.v2/admin/messages/messages_es.properties diff --git a/js/apps/admin-ui/maven-resources-community/theme/keycloak.v2/admin/messages/messages_fr.properties b/js/apps/admin-ui/maven-resources-community/theme/primesign.v2/admin/messages/messages_fr.properties similarity index 100% rename from js/apps/admin-ui/maven-resources-community/theme/keycloak.v2/admin/messages/messages_fr.properties rename to js/apps/admin-ui/maven-resources-community/theme/primesign.v2/admin/messages/messages_fr.properties diff --git a/js/apps/admin-ui/maven-resources-community/theme/keycloak.v2/admin/messages/messages_ja.properties b/js/apps/admin-ui/maven-resources-community/theme/primesign.v2/admin/messages/messages_ja.properties similarity index 100% rename from js/apps/admin-ui/maven-resources-community/theme/keycloak.v2/admin/messages/messages_ja.properties rename to js/apps/admin-ui/maven-resources-community/theme/primesign.v2/admin/messages/messages_ja.properties diff --git a/js/apps/admin-ui/maven-resources-community/theme/keycloak.v2/admin/messages/messages_lt.properties b/js/apps/admin-ui/maven-resources-community/theme/primesign.v2/admin/messages/messages_lt.properties similarity index 100% rename from js/apps/admin-ui/maven-resources-community/theme/keycloak.v2/admin/messages/messages_lt.properties rename to js/apps/admin-ui/maven-resources-community/theme/primesign.v2/admin/messages/messages_lt.properties diff --git a/js/apps/admin-ui/maven-resources-community/theme/keycloak.v2/admin/messages/messages_no.properties b/js/apps/admin-ui/maven-resources-community/theme/primesign.v2/admin/messages/messages_no.properties similarity index 100% rename from js/apps/admin-ui/maven-resources-community/theme/keycloak.v2/admin/messages/messages_no.properties rename to js/apps/admin-ui/maven-resources-community/theme/primesign.v2/admin/messages/messages_no.properties diff --git a/js/apps/admin-ui/maven-resources-community/theme/keycloak.v2/admin/messages/messages_pl.properties b/js/apps/admin-ui/maven-resources-community/theme/primesign.v2/admin/messages/messages_pl.properties similarity index 100% rename from js/apps/admin-ui/maven-resources-community/theme/keycloak.v2/admin/messages/messages_pl.properties rename to js/apps/admin-ui/maven-resources-community/theme/primesign.v2/admin/messages/messages_pl.properties diff --git a/js/apps/admin-ui/maven-resources-community/theme/keycloak.v2/admin/messages/messages_pt_BR.properties b/js/apps/admin-ui/maven-resources-community/theme/primesign.v2/admin/messages/messages_pt_BR.properties similarity index 100% rename from js/apps/admin-ui/maven-resources-community/theme/keycloak.v2/admin/messages/messages_pt_BR.properties rename to js/apps/admin-ui/maven-resources-community/theme/primesign.v2/admin/messages/messages_pt_BR.properties diff --git a/js/apps/admin-ui/maven-resources-community/theme/keycloak.v2/admin/messages/messages_ru.properties b/js/apps/admin-ui/maven-resources-community/theme/primesign.v2/admin/messages/messages_ru.properties similarity index 100% rename from js/apps/admin-ui/maven-resources-community/theme/keycloak.v2/admin/messages/messages_ru.properties rename to js/apps/admin-ui/maven-resources-community/theme/primesign.v2/admin/messages/messages_ru.properties diff --git a/js/apps/admin-ui/maven-resources-community/theme/keycloak.v2/admin/messages/messages_zh_CN.properties b/js/apps/admin-ui/maven-resources-community/theme/primesign.v2/admin/messages/messages_zh_CN.properties similarity index 100% rename from js/apps/admin-ui/maven-resources-community/theme/keycloak.v2/admin/messages/messages_zh_CN.properties rename to js/apps/admin-ui/maven-resources-community/theme/primesign.v2/admin/messages/messages_zh_CN.properties diff --git a/js/apps/admin-ui/maven-resources/META-INF/keycloak-themes.json b/js/apps/admin-ui/maven-resources/META-INF/keycloak-themes.json index 4bd655870214..4d95deafedd8 100644 --- a/js/apps/admin-ui/maven-resources/META-INF/keycloak-themes.json +++ b/js/apps/admin-ui/maven-resources/META-INF/keycloak-themes.json @@ -1,7 +1,7 @@ { "themes": [ { - "name": "keycloak.v2", + "name": "primesign.v2", "types": [ "admin" ] diff --git a/js/apps/admin-ui/maven-resources/theme/keycloak.v2/admin/messages/messages_en.properties b/js/apps/admin-ui/maven-resources/theme/primesign.v2/admin/messages/messages_en.properties similarity index 100% rename from js/apps/admin-ui/maven-resources/theme/keycloak.v2/admin/messages/messages_en.properties rename to js/apps/admin-ui/maven-resources/theme/primesign.v2/admin/messages/messages_en.properties diff --git a/js/apps/admin-ui/maven-resources/theme/keycloak.v2/admin/theme.properties b/js/apps/admin-ui/maven-resources/theme/primesign.v2/admin/theme.properties similarity index 100% rename from js/apps/admin-ui/maven-resources/theme/keycloak.v2/admin/theme.properties rename to js/apps/admin-ui/maven-resources/theme/primesign.v2/admin/theme.properties diff --git a/js/apps/admin-ui/pom.xml b/js/apps/admin-ui/pom.xml index 2216f49637ef..cce4ce5e476f 100644 --- a/js/apps/admin-ui/pom.xml +++ b/js/apps/admin-ui/pom.xml @@ -14,7 +14,7 @@ keycloak-admin-ui Keycloak Admin UI - The user inferface to administrate the Keycloak server. + The user interface to administrate the Keycloak server. @@ -25,28 +25,28 @@ - - - maven-resources-plugin - - - copy-resources - generate-resources - - copy-resources - - - ${project.build.outputDirectory} - - - maven-resources-community - - - - - - - + + + maven-resources-plugin + + + copy-resources + generate-resources + + copy-resources + + + ${project.build.outputDirectory} + + + maven-resources-community + + + + + + + @@ -58,7 +58,7 @@ dist - theme/keycloak.v2/admin/resources + theme/primesign.v2/admin/resources index.html locales/** @@ -109,7 +109,7 @@ dist/index.html - target/classes/theme/keycloak.v2/admin/index.ftl + target/classes/theme/primesign.v2/admin/index.ftl false @@ -124,10 +124,6 @@ ]]> ]]> - - ]]> - ]]> - Keycloak Administration UI]]> diff --git a/js/apps/admin-ui/src/context/server-info/__tests__/mock.json b/js/apps/admin-ui/src/context/server-info/__tests__/mock.json index fb68699134f0..0f3f161d2ceb 100644 --- a/js/apps/admin-ui/src/context/server-info/__tests__/mock.json +++ b/js/apps/admin-ui/src/context/server-info/__tests__/mock.json @@ -96,7 +96,7 @@ "zh-CN" ] }, - { "name": "keycloak.v2" } + { "name": "primesign.v2" } ], "login": [ { @@ -203,7 +203,7 @@ ] }, { - "name": "keycloak.v2", + "name": "primesign.v2", "locales": [ "ca", "cs", @@ -7145,4 +7145,4 @@ "USER_SESSION" ] } -} +} \ No newline at end of file diff --git a/server-spi/src/main/java/org/keycloak/theme/ThemeSelectorProvider.java b/server-spi/src/main/java/org/keycloak/theme/ThemeSelectorProvider.java index 38f492a29436..482cb0536cf8 100755 --- a/server-spi/src/main/java/org/keycloak/theme/ThemeSelectorProvider.java +++ b/server-spi/src/main/java/org/keycloak/theme/ThemeSelectorProvider.java @@ -29,6 +29,7 @@ public interface ThemeSelectorProvider extends Provider { String DEFAULT = "keycloak"; String DEFAULT_V2 = "keycloak.v2"; String DEFAULT_V3 = "keycloak.v3"; + String PRIMESIGN_V2 = "primesign.v2"; /** * Return the theme name to use for the specified type @@ -53,7 +54,7 @@ default String getDefaultThemeName(Theme.Type type) { } if ((type == Theme.Type.ADMIN) && Profile.isFeatureEnabled(Profile.Feature.ADMIN2)) { - return DEFAULT_V2; + return PRIMESIGN_V2; } if ((type == Theme.Type.LOGIN) && Profile.isFeatureEnabled(Profile.Feature.LOGIN2)) { diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/AdminConsoleLandingPageTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/AdminConsoleLandingPageTest.java index bf3fe5c6df71..37eb5c6e94e6 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/AdminConsoleLandingPageTest.java +++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/AdminConsoleLandingPageTest.java @@ -48,7 +48,7 @@ public void landingPage() throws IOException { Assert.assertEquals(suiteContext.getAuthServerInfo().getContextRoot() + "/auth", authUrl); String resourceUrl = config.get("resourceUrl"); - Assert.assertTrue(resourceUrl.matches("/auth/resources/[^/]*/admin/keycloak.v2")); + Assert.assertTrue(resourceUrl.matches("/auth/resources/[^/]*/admin/primesign.v2")); String consoleBaseUrl = config.get("consoleBaseUrl"); Assert.assertEquals(consoleBaseUrl, "/auth/admin/master/console/"); diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/ServerInfoTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/ServerInfoTest.java index 57de87ba0de6..446cf01d225b 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/ServerInfoTest.java +++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/ServerInfoTest.java @@ -57,7 +57,7 @@ public void testServerInfo() { assertNotNull(info.getThemes()); assertNotNull(info.getThemes().get("account")); Assert.assertNames(info.getThemes().get("account"), "base", "keycloak.v3", "custom-account-provider"); - Assert.assertNames(info.getThemes().get("admin"), "base", "keycloak.v2"); + Assert.assertNames(info.getThemes().get("admin"), "base", "primesign.v2"); Assert.assertNames(info.getThemes().get("email"), "base", "keycloak"); Assert.assertNames(info.getThemes().get("login"), "address", "base", "environment-agnostic", "keycloak"); Assert.assertNames(info.getThemes().get("welcome"), "keycloak"); diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/theme/ThemeResourceProviderTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/theme/ThemeResourceProviderTest.java index edfd7a8087b7..961c17d8d7d9 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/theme/ThemeResourceProviderTest.java +++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/theme/ThemeResourceProviderTest.java @@ -52,7 +52,7 @@ public void testThemeFallback() { // Fallback to default theme when requested theme don't exists Theme theme = session.theme().getTheme("address", Theme.Type.ADMIN); Assert.assertNotNull(theme); - Assert.assertEquals("keycloak.v2", theme.getName()); + Assert.assertEquals("primesign.v2", theme.getName()); } catch (IOException e) { Assert.fail(e.getMessage()); } diff --git a/themes/src/main/resources/META-INF/keycloak-themes.json b/themes/src/main/resources/META-INF/keycloak-themes.json index f3599a2fbe7d..8383cb610fb1 100755 --- a/themes/src/main/resources/META-INF/keycloak-themes.json +++ b/themes/src/main/resources/META-INF/keycloak-themes.json @@ -7,6 +7,9 @@ "types": [ "login", "common", "email", "welcome" ] }, { "name" : "keycloak.v2", - "types": [ "account", "admin", "login" ] + "types": [ "account", "login" ] + }, { + "name" : "primesign.v2", + "types": [ "admin" ] }] } From a5992365efcdd02ea665f6b5061086dbab7f5414 Mon Sep 17 00:00:00 2001 From: Patrick Weiner Date: Thu, 1 Jun 2023 15:57:32 +0200 Subject: [PATCH 140/158] [Admin-Theme]: Allow setting of account id in clients. changed [24.0.2] --- .../theme/primesign.v2/admin/messages/messages_en.properties | 5 ++++- js/apps/admin-ui/src/clients/ClientDescription.tsx | 5 +++++ js/apps/admin-ui/src/clients/ClientDetails.tsx | 3 +++ 3 files changed, 12 insertions(+), 1 deletion(-) diff --git a/js/apps/admin-ui/maven-resources/theme/primesign.v2/admin/messages/messages_en.properties b/js/apps/admin-ui/maven-resources/theme/primesign.v2/admin/messages/messages_en.properties index ec93b61e83c8..4a1fa2f0050c 100644 --- a/js/apps/admin-ui/maven-resources/theme/primesign.v2/admin/messages/messages_en.properties +++ b/js/apps/admin-ui/maven-resources/theme/primesign.v2/admin/messages/messages_en.properties @@ -3077,4 +3077,7 @@ sendIdTokenOnLogoutHelp=If the 'id_token_hint' parameter should be sent in logou sendClientIdOnLogout=Send 'client_id' in logout requests sendClientIdOnLogoutHelp=If the 'client_id' parameter should be sent in logout requests. searchClientRegistration=Search for policy -importFileHelp=File to import a key \ No newline at end of file +importFileHelp=File to import a key + +accountIdLabel=Account ID +accountIdHelpText=ID of the client, which is stored at the metering service. \ No newline at end of file diff --git a/js/apps/admin-ui/src/clients/ClientDescription.tsx b/js/apps/admin-ui/src/clients/ClientDescription.tsx index 4d08ac0a22a6..7277fa47c39e 100644 --- a/js/apps/admin-ui/src/clients/ClientDescription.tsx +++ b/js/apps/admin-ui/src/clients/ClientDescription.tsx @@ -37,6 +37,11 @@ export const ClientDescription = ({ }, }} /> + Date: Fri, 2 Jun 2023 09:36:52 +0200 Subject: [PATCH 141/158] [Admin-Theme]: Add sms gateway configuration to authentication policies. [changed 24.0.2] Policies.tsx & SmsPolicy.tsx lint fixes --- .../admin/messages/messages_en.properties | 58 +- .../src/authentication/policies/Policies.tsx | 8 + .../src/authentication/policies/SmsPolicy.tsx | 877 ++++++++++++++++++ 3 files changed, 942 insertions(+), 1 deletion(-) create mode 100644 js/apps/admin-ui/src/authentication/policies/SmsPolicy.tsx diff --git a/js/apps/admin-ui/maven-resources/theme/primesign.v2/admin/messages/messages_en.properties b/js/apps/admin-ui/maven-resources/theme/primesign.v2/admin/messages/messages_en.properties index 4a1fa2f0050c..0589fdfcdfaf 100644 --- a/js/apps/admin-ui/maven-resources/theme/primesign.v2/admin/messages/messages_en.properties +++ b/js/apps/admin-ui/maven-resources/theme/primesign.v2/admin/messages/messages_en.properties @@ -3080,4 +3080,60 @@ searchClientRegistration=Search for policy importFileHelp=File to import a key accountIdLabel=Account ID -accountIdHelpText=ID of the client, which is stored at the metering service. \ No newline at end of file +accountIdHelpText=ID of the client, which is stored at the metering service. + +smsTitle=SMS Policy +smsDefaultGateway=Default gateway +smsFallbackGateway=Fallback gateway +smsTimeout=Timeout +smsTanLength=TAN length +smsRefLength=Reference value length +smsResendDelay=Resend TAN delay +smsResendMaxRetries=Resend TAN maximum retries +smsUpdateSuccess=SMS policy successfully updated +smsUpdateError=Could not update SMS policy: {{error}} +smsatTitle=SMS.AT +smsatProviderUrl=Override Provider URL +smsatSenderAddress=Sender address +smsatSenderAddressTypeTitle=Sender address type +smsatSenderAddressTypeNational=National +smsatSenderAddressTypeInternational=International +smsatSenderAddressTypeAlphanumeric=Alphanumeric +smsatSenderAddressTypeShortcode=Shortcode +smsatAuthMethodTitle=Authentication method +smsatAuthMethodBasic=Username / Password +smsatAuthMethodToken=API Token +smsatUsername=Username +smsatPassword=Password +smsatApiToken=API token +smsatConnectTimeout=Connect timeout +smsatReadTimeout=Read timeout +nexmoTitle=NEXMO +nexmoProviderUrl=Override Provider URL +nexmoSenderId=Sender ID +nexmoApiKey=API Key +nexmoApiSecret=API Secret +nexmoConnectTimeout=Connect timeout +nexmoReadTimeout=Read timeout +smsDefaultGatewayHelpText=The default gateway is used to send the first SMS. +smsFallbackGatewayHelpText=The fallback gateway is used to resend an SMS. +smsTimeoutHelpText=The lifetime of a SMS-TAN until it expires and is no longer valid. In case of resend TAN the lifetime is resetted, meaning every SMS-TAN is valid for 5 minutes. +smsTanLengthHelpText=The length of the SMS-TAN. +smsRefLengthHelpText=The length of the reference value (displayed as the transaction-ID in the SMS-TAN). +smsResendDelayHelpText=The delay a user has to wait until he can request a new SMS. +smsResendMaxRetriesHelpText=The maximum number of SMS-TAN send retries a user can request. +smsatProviderUrlHelpText=Overrides the default URL of the SMS.AT gateway provider., +smsatSenderAddressHelpText=Address of the sender (assigned to the account) from which the message is sent, e.g. PrimeSign., +smsatSenderAddressTypeHelpText=The sender address type. The following address types are supported: national, international, alphanumeric oshortcode). We currently use alphanumeric. +smsatAuthMethodHelpText=How to authenticate at the REST API. +smsatUsernameHelpText=Authenticate with this username. +smsatPasswordHelpText=Authenticate with this password. +smsatApiTokenHelpText=The API Token of SMS-AT. +smsatConnectTimeoutHelpText=How many seconds to wait for the server connection. +smsatReadTimeoutHelpText=How many seconds to wait for a response. +nexmoProviderUrlHelpText=Overrides the default URL for the NEXMO gateway provider. +nexmoSenderIdHelpText=Name or phone number of the sender. +nexmoApiKeyHelpText=Authenticate with this API key. +nexmoApiSecretHelpText=Authenticate with this API secret. +nexmoConnectTimeoutHelpText=How many seconds to wait for the server connection. +nexmoReadTimeoutHelpText=How many seconds to wait for a response. \ No newline at end of file diff --git a/js/apps/admin-ui/src/authentication/policies/Policies.tsx b/js/apps/admin-ui/src/authentication/policies/Policies.tsx index bdb1bf384b45..7b5436cbbc61 100644 --- a/js/apps/admin-ui/src/authentication/policies/Policies.tsx +++ b/js/apps/admin-ui/src/authentication/policies/Policies.tsx @@ -11,6 +11,7 @@ import { CibaPolicy } from "./CibaPolicy"; import { OtpPolicy } from "./OtpPolicy"; import { PasswordPolicy } from "./PasswordPolicy"; import { WebauthnPolicy } from "./WebauthnPolicy"; +import { SmsPolicy } from "./SmsPolicy"; export const Policies = () => { const { t } = useTranslation(); @@ -78,6 +79,13 @@ export const Policies = () => { > + {t("smsTitle")}} + > + + ); }; diff --git a/js/apps/admin-ui/src/authentication/policies/SmsPolicy.tsx b/js/apps/admin-ui/src/authentication/policies/SmsPolicy.tsx new file mode 100644 index 000000000000..265b38d30af2 --- /dev/null +++ b/js/apps/admin-ui/src/authentication/policies/SmsPolicy.tsx @@ -0,0 +1,877 @@ +import type RealmRepresentation from "@keycloak/keycloak-admin-client/lib/defs/realmRepresentation"; +import { useTranslation } from "react-i18next"; +import { useEffect, useState } from "react"; +import { + Accordion, + AccordionContent, + AccordionItem, + AccordionToggle, + ActionGroup, + AlertVariant, + Button, + ButtonVariant, + FormGroup, + NumberInput, + PageSection, + Select, + SelectOption, + SelectVariant, + ValidatedOptions, +} from "@patternfly/react-core"; +import { FormAccess } from "../../components/form/FormAccess"; +import { Controller, useForm } from "react-hook-form"; +import useToggle from "../../utils/useToggle"; +import { adminClient } from "../../admin-client"; +import { useRealm } from "../../context/realm-context/RealmContext"; +import { useAlerts } from "../../components/alert/Alerts"; +import { KeycloakTextInput } from "../../components/keycloak-text-input/KeycloakTextInput"; +import { HelpItem } from "ui-shared"; + +type SmsPolicyProps = { + realm: RealmRepresentation; + realmUpdated: (realm: RealmRepresentation) => void; +}; + +export const SmsPolicy = ({ realm, realmUpdated }: SmsPolicyProps) => { + const { t } = useTranslation(); + const { + control, + register, + reset, + handleSubmit, + formState: { isDirty }, + } = useForm({ mode: "onChange" }); + + const { realm: realmName } = useRealm(); + const { addAlert, addError } = useAlerts(); + const [openDefaultGateway, toggleDefaultGateway] = useToggle(); + const [openFallbackGateway, toggleFallbackGateway] = useToggle(); + const [openSenderAddressType, toggleSenderAddressType] = useToggle(); + const [openAuthMethod, toggleAuthMethod] = useToggle(); + + const [expanded, setExpanded] = useState([""]); + const [smsatAuthMethod, setSmsAuthMethod] = useState("basic"); + + const toggleAccordion = (id: any) => { + const index = expanded.indexOf(id); + const newExpanded: string[] = + index < 0 + ? [...expanded, id] + : [ + ...expanded.slice(0, index), + ...expanded.slice(index + 1, expanded.length), + ]; + setExpanded(newExpanded); + }; + + const SMS_GATEWAYS = [ + { + key: "smsat", + title: t("smsatTitle"), + }, + { + key: "nexmo", + title: t("nexmoTitle"), + }, + ] as const; + + const SENDER_ADDRESS_TYPES = [ + { + key: "national", + title: t("smsatSenderAddressTypeNational"), + }, + { + key: "international", + title: t("smsatSenderAddressTypeInternational"), + }, + { + key: "alphanumeric", + title: t("smsatSenderAddressTypeAlphanumeric"), + }, + { + key: "shortcode", + title: t("smsatSenderAddressTypeShortcode"), + }, + ] as const; + + const AUTH_METHOD = [ + { + key: "basic", + title: t("smsatAuthMethodBasic"), + }, + { + key: "token", + title: t("smsatAuthMethodToken"), + }, + ] as const; + + const setupForm = (realm: RealmRepresentation) => { + reset({ + ...realm, + }); + setSmsAuthMethod( + realm.attributes?.["smsatAuthMethod"] + ? realm.attributes["smsatAuthMethod"] + : "basic", + ); + }; + + useEffect(() => setupForm(realm), []); + + const prepareRealmAttributes = async ( + updatedAttributes: RealmRepresentation, + ) => { + const currentRealm = await adminClient.realms.findOne({ + realm: realmName, + }); + if (currentRealm) { + realm.attributes = { + ...currentRealm.attributes, + ...updatedAttributes.attributes, + }; + return realm; + } else { + throw "Realm not found."; + } + }; + + const save = async (input: RealmRepresentation) => { + try { + const merge = await prepareRealmAttributes(input); + await adminClient.realms.update({ realm: realmName }, merge); + const updatedRealm = await adminClient.realms.findOne({ + realm: realmName, + }); + realmUpdated(updatedRealm!); + setupForm(updatedRealm!); + addAlert(t("smsUpdateSuccess"), AlertVariant.success); + } catch (error) { + addError("smsUpdateError", error); + } + }; + + return ( + + + + } + fieldId="attributes.smsDefaultGateway" + > + ( + + )} + /> + + + } + fieldId="attributes.smsFallbackGateway" + > + ( + + )} + /> + + + } + fieldId="attributes.smsTimeout" + > + { + const MIN_VALUE = 0; + const setValue = (newValue: number) => + field.onChange(Math.max(newValue, MIN_VALUE)); + + return ( + setValue(parseInt(field.value) + 1)} + onMinus={() => setValue(parseInt(field.value) - 1)} + onChange={(event) => { + const newValue = Number(event.currentTarget.value); + setValue(!isNaN(newValue) ? newValue : 5); + }} + /> + ); + }} + /> + + + } + fieldId="attributes.smsTanLength" + > + { + const MIN_VALUE = 1; + const setValue = (newValue: number) => + field.onChange(Math.max(newValue, MIN_VALUE)); + + return ( + setValue(parseInt(field.value) + 1)} + onMinus={() => setValue(parseInt(field.value) - 1)} + onChange={(event) => { + const newValue = Number(event.currentTarget.value); + setValue(!isNaN(newValue) ? newValue : 6); + }} + /> + ); + }} + /> + + + } + fieldId="attributes.smsRefLength" + > + { + const MIN_VALUE = 1; + const setValue = (newValue: number) => + field.onChange(Math.max(newValue, MIN_VALUE)); + + return ( + setValue(parseInt(field.value) + 1)} + onMinus={() => setValue(parseInt(field.value) - 1)} + onChange={(event) => { + const newValue = Number(event.currentTarget.value); + setValue(!isNaN(newValue) ? newValue : 6); + }} + /> + ); + }} + /> + + + } + fieldId="attributes.smsResendDelay" + > + { + const MIN_VALUE = 0; + const setValue = (newValue: number) => + field.onChange(Math.max(newValue, MIN_VALUE)); + + return ( + setValue(parseInt(field.value) + 1)} + onMinus={() => setValue(parseInt(field.value) - 1)} + onChange={(event) => { + const newValue = Number(event.currentTarget.value); + setValue(!isNaN(newValue) ? newValue : 30); + }} + /> + ); + }} + /> + + + } + fieldId="attributes.smsResendMaxRetries" + > + { + const MIN_VALUE = 0; + const setValue = (newValue: number) => + field.onChange(Math.max(newValue, MIN_VALUE)); + + return ( + setValue(parseInt(field.value) + 1)} + onMinus={() => setValue(parseInt(field.value) - 1)} + onChange={(event) => { + const newValue = Number(event.currentTarget.value); + setValue(!isNaN(newValue) ? newValue : 3); + }} + /> + ); + }} + /> + + + + + toggleAccordion("smsat-toggle")} + isExpanded={expanded.includes("smsat-toggle")} + id="smsat-toggle" + > + {t("smsatTitle")} + + + + } + label={t("smsatProviderUrl")} + fieldId="attributes.smsatProviderUrl" + validated={ValidatedOptions.default} + > + + + + } + label={t("smsatSenderAddress")} + fieldId="attributes.smsatSenderAddress" + validated={ValidatedOptions.default} + > + + + + } + fieldId="attributes.smsatSenderAddressType" + > + ( + + )} + /> + + + } + fieldId="attributes.smsatAuthMethod" + > + ( + + )} + /> + +

    + + + } + fieldId="attributes.smsatConnectTimeout" + > + { + const MIN_VALUE = 0; + const setValue = (newValue: number) => + field.onChange(Math.max(newValue, MIN_VALUE)); + + return ( + setValue(parseInt(field.value) + 1)} + onMinus={() => setValue(parseInt(field.value) - 1)} + onChange={(event) => { + const newValue = Number(event.currentTarget.value); + setValue(!isNaN(newValue) ? newValue : 0); + }} + /> + ); + }} + /> + + + } + fieldId="attributes.smsatReadTimeout" + > + { + const MIN_VALUE = 0; + const setValue = (newValue: number) => + field.onChange(Math.max(newValue, MIN_VALUE)); + + return ( + setValue(parseInt(field.value) + 1)} + onMinus={() => setValue(parseInt(field.value) - 1)} + onChange={(event) => { + const newValue = Number(event.currentTarget.value); + setValue(!isNaN(newValue) ? newValue : 0); + }} + /> + ); + }} + /> + + + + + toggleAccordion("nexmo-toggle")} + isExpanded={expanded.includes("nexmo-toggle")} + id="nexmo-toggle" + > + {t("nexmoTitle")} + + + + } + label={t("nexmoProviderUrl")} + fieldId="attributes.nexmoProviderUrl" + validated={ValidatedOptions.default} + > + + + + } + label={t("nexmoSenderId")} + fieldId="attributes.nexmoSenderId" + validated={ValidatedOptions.default} + > + + + + } + label={t("nexmoApiKey")} + fieldId="attributes.nexmoApiKey" + validated={ValidatedOptions.default} + > + + + + } + label={t("nexmoApiSecret")} + fieldId="attributes.nexmoApiSecret" + validated={ValidatedOptions.default} + > + + + + } + fieldId="attributes.nexmoConnectTimeout" + > + { + const MIN_VALUE = 0; + const setValue = (newValue: number) => + field.onChange(Math.max(newValue, MIN_VALUE)); + + return ( + setValue(parseInt(field.value) + 1)} + onMinus={() => setValue(parseInt(field.value) - 1)} + onChange={(event) => { + const newValue = Number(event.currentTarget.value); + setValue(!isNaN(newValue) ? newValue : 0); + }} + /> + ); + }} + /> + + + } + fieldId="attributes.nexmoReadTimeout" + > + { + const MIN_VALUE = 0; + const setValue = (newValue: number) => + field.onChange(Math.max(newValue, MIN_VALUE)); + + return ( + setValue(parseInt(field.value) + 1)} + onMinus={() => setValue(parseInt(field.value) - 1)} + onChange={(event) => { + const newValue = Number(event.currentTarget.value); + setValue(!isNaN(newValue) ? newValue : 0); + }} + /> + ); + }} + /> + + + + + + + + + + + + ); +}; From 787a318d094260fe6a1aed4a05bd39a15250afdd Mon Sep 17 00:00:00 2001 From: Patrick Weiner Date: Fri, 2 Jun 2023 10:22:41 +0200 Subject: [PATCH 142/158] [Admin-Theme]: Allow setting of allowed identity providers per client. --- js/apps/admin-ui/src/clients/ClientDetails.tsx | 6 ++++++ .../admin-ui/src/clients/add/LoginSettings.tsx | 18 +++++++++++++++++- 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/js/apps/admin-ui/src/clients/ClientDetails.tsx b/js/apps/admin-ui/src/clients/ClientDetails.tsx index 88b3ae62f923..2ade1220b119 100644 --- a/js/apps/admin-ui/src/clients/ClientDetails.tsx +++ b/js/apps/admin-ui/src/clients/ClientDetails.tsx @@ -305,6 +305,12 @@ export default function ClientDetails() { if (client.attributes?.["accountId"]) { form.setValue("attributes.accountId", client.attributes["accountId"]); } + if (client.attributes?.["identityProviders"]) { + form.setValue( + "attributes.identityProviders", + client.attributes["identityProviders"], + ); + } }; useFetch( diff --git a/js/apps/admin-ui/src/clients/add/LoginSettings.tsx b/js/apps/admin-ui/src/clients/add/LoginSettings.tsx index 518646f8d19e..13a6d3ccd810 100644 --- a/js/apps/admin-ui/src/clients/add/LoginSettings.tsx +++ b/js/apps/admin-ui/src/clients/add/LoginSettings.tsx @@ -30,6 +30,22 @@ export const LoginSettings = ({ const standardFlowEnabled = watch("standardFlowEnabled"); return ( <> + + } + > + + ); -}; +}; \ No newline at end of file From 617a8313f4123b0eb74f73697046db1684df525f Mon Sep 17 00:00:00 2001 From: Patrick Weiner Date: Mon, 5 Jun 2023 15:53:39 +0200 Subject: [PATCH 143/158] Add gitlab-ci to build and push packages to artifactory. --- .gitlab-ci.yml | 39 +++++++++++++++++++++++++++++ .m2/settings.xml | 61 ++++++++++++++++++++++++++++++++++++++++++++++ boms/pom.xml | 15 +++++++++++- maven-settings.xml | 2 +- pom.xml | 16 +++++++----- 5 files changed, 125 insertions(+), 8 deletions(-) create mode 100644 .gitlab-ci.yml create mode 100644 .m2/settings.xml diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml new file mode 100644 index 000000000000..1c692c8fbccc --- /dev/null +++ b/.gitlab-ci.yml @@ -0,0 +1,39 @@ +--- +stages: + - build + +image: maven:3.6.3-openjdk-17 + + +variables: + MAVEN_OPTS: "-Dmaven.repo.local=${CI_PROJECT_DIR}/.m2/repository -Dhttps.protocols=TLSv1.2 -Dorg.slf4j.simpleLogger.log.org.apache.maven.cli.transfer.Slf4jMavenTransferListener=WARN -Dorg.slf4j.simpleLogger.showDateTime=true" + MAVEN_CLI_OPTS: "-f ${CI_PROJECT_DIR}/pom.xml -s ${CI_PROJECT_DIR}/.m2/settings.xml --batch-mode --errors --show-version -DskipTests -DskipExamples -DskipTestsuite -DinstallAtEnd=false -DdeployAtEnd=false" + +cache: + paths: + - .m2/repository + +build: + stage: build + only: + refs: + - main + except: + variables: + - $CI_COMMIT_MESSAGE =~ /\[maven-release-plugin\] prepare release/ + script: + - mvn $MAVEN_CLI_OPTS $MAVEN_PROJECT_OPTS deploy + +build-fb: + stage: build + only: + refs: + # feature branch + - /^fb-.*$/ + # bugfix branch + - /^fix-.*$/ + except: + variables: + - $CI_COMMIT_MESSAGE =~ /\[maven-release-plugin\] prepare release/ + script: + - mvn $MAVEN_CLI_OPTS $MAVEN_PROJECT_OPTS clean deploy diff --git a/.m2/settings.xml b/.m2/settings.xml new file mode 100644 index 000000000000..692d898c04b6 --- /dev/null +++ b/.m2/settings.xml @@ -0,0 +1,61 @@ + + + + + releases + ${env.MAVEN_REPO_USER} + ${env.MAVEN_REPO_PASS} + + + snapshots + ${env.MAVEN_REPO_USER} + ${env.MAVEN_REPO_PASS} + + + + + + + + false + + central + libs-releases + https://artifactory.intra.prime-sign.com/artifactory/libs-releases + + + + snapshots + libs-snapshots + https://artifactory.intra.prime-sign.com/artifactory/libs-snapshots + + + Redhat Repo + libs-snapshots + https://maven.repository.redhat.com/ga/ + + + + + + false + + central + plugins-releases + https://artifactory.intra.prime-sign.com/artifactory/plugins-releases + + + + snapshots + plugins-snapshots + https://artifactory.intra.prime-sign.com/artifactory/plugins-snapshots + + + artifactory + + + + artifactory + + \ No newline at end of file diff --git a/boms/pom.xml b/boms/pom.xml index 520b43a00937..00d48c91fa0c 100644 --- a/boms/pom.xml +++ b/boms/pom.xml @@ -57,7 +57,20 @@ spi misc - + + + + releases + libs-releases-local + ${env.MAVEN_REPO_URL}/libs-releases-local + + + snapshots + libs-snapshots-local + ${env.MAVEN_REPO_URL}/libs-snapshots-local + + + diff --git a/maven-settings.xml b/maven-settings.xml index 950b2dba5333..a855e38dd133 100644 --- a/maven-settings.xml +++ b/maven-settings.xml @@ -49,4 +49,4 @@ jboss-public-repository - + \ No newline at end of file diff --git a/pom.xml b/pom.xml index c0853d6026c4..bac95bb3113a 100644 --- a/pom.xml +++ b/pom.xml @@ -256,17 +256,21 @@ - scm:git:git://github.com/keycloak/keycloak.git - scm:git:git@github.com:keycloak/keycloak.git - https://github.com/keycloak/keycloak/tree/master/ + scm:git:git@gitlab.intra.prime-sign.com:tc-dev/keycloak.git + HEAD - ${jboss.releases.repo.id} - JBoss Releases Repository - ${jboss.releases.repo.url} + releases + libs-releases-local + ${env.MAVEN_REPO_URL}/libs-releases-local + + snapshots + libs-snapshots-local + ${env.MAVEN_REPO_URL}/libs-snapshots-local + From ae7be3986898182f4c30422c9798b73429f5b6b7 Mon Sep 17 00:00:00 2001 From: Otto Touzil Date: Mon, 26 Jun 2023 10:38:40 +0200 Subject: [PATCH 144/158] german eid / idg identityprovider [squashed 24.0.2] rename idg-eid-idenityprovider to german-eid ui changes for IDG provider [changed 24.0.2] idg lint fixes squash! german eid / idg identityprovider --- .../admin/messages/messages_en.properties | 5 +- .../src/clients/add/LoginSettings.tsx | 6 +- .../identity-providers/add/AddIdgConnect.tsx | 96 +++++++++++++++++++ .../identity-providers/add/DetailSettings.tsx | 7 +- .../add/IdgGeneralSettings.tsx | 52 ++++++++++ .../admin-ui/src/identity-providers/routes.ts | 2 + .../routes/IdentityProviderIDG.tsx | 23 +++++ 7 files changed, 186 insertions(+), 5 deletions(-) create mode 100644 js/apps/admin-ui/src/identity-providers/add/AddIdgConnect.tsx create mode 100644 js/apps/admin-ui/src/identity-providers/add/IdgGeneralSettings.tsx create mode 100644 js/apps/admin-ui/src/identity-providers/routes/IdentityProviderIDG.tsx diff --git a/js/apps/admin-ui/maven-resources/theme/primesign.v2/admin/messages/messages_en.properties b/js/apps/admin-ui/maven-resources/theme/primesign.v2/admin/messages/messages_en.properties index 0589fdfcdfaf..1db665f69e9b 100644 --- a/js/apps/admin-ui/maven-resources/theme/primesign.v2/admin/messages/messages_en.properties +++ b/js/apps/admin-ui/maven-resources/theme/primesign.v2/admin/messages/messages_en.properties @@ -3136,4 +3136,7 @@ nexmoSenderIdHelpText=Name or phone number of the sender. nexmoApiKeyHelpText=Authenticate with this API key. nexmoApiSecretHelpText=Authenticate with this API secret. nexmoConnectTimeoutHelpText=How many seconds to wait for the server connection. -nexmoReadTimeoutHelpText=How many seconds to wait for a response. \ No newline at end of file +nexmoReadTimeoutHelpText=How many seconds to wait for a response. + +addIdgProvider=Add IDG Provider +identityProvidersHelp=The aliases of the allowed identity providers, seperated by comma. \ No newline at end of file diff --git a/js/apps/admin-ui/src/clients/add/LoginSettings.tsx b/js/apps/admin-ui/src/clients/add/LoginSettings.tsx index 13a6d3ccd810..81ab3f041a5c 100644 --- a/js/apps/admin-ui/src/clients/add/LoginSettings.tsx +++ b/js/apps/admin-ui/src/clients/add/LoginSettings.tsx @@ -31,11 +31,11 @@ export const LoginSettings = ({ return ( <> } @@ -201,4 +201,4 @@ export const LoginSettings = ({ )} ); -}; \ No newline at end of file +}; diff --git a/js/apps/admin-ui/src/identity-providers/add/AddIdgConnect.tsx b/js/apps/admin-ui/src/identity-providers/add/AddIdgConnect.tsx new file mode 100644 index 000000000000..03f91e9ca5f1 --- /dev/null +++ b/js/apps/admin-ui/src/identity-providers/add/AddIdgConnect.tsx @@ -0,0 +1,96 @@ +import type IdentityProviderRepresentation from "@keycloak/keycloak-admin-client/lib/defs/identityProviderRepresentation"; +import { + ActionGroup, + AlertVariant, + Button, + PageSection, +} from "@patternfly/react-core"; +import { FormProvider, useForm } from "react-hook-form"; +import { useTranslation } from "react-i18next"; +import { Link, useNavigate } from "react-router-dom"; + +import { adminClient } from "../../admin-client"; +import { useAlerts } from "../../components/alert/Alerts"; +import { FormAccess } from "../../components/form/FormAccess"; +import { ViewHeader } from "../../components/view-header/ViewHeader"; +import { useRealm } from "../../context/realm-context/RealmContext"; +import { toIdentityProvider } from "../routes/IdentityProvider"; +import { toIdentityProviders } from "../routes/IdentityProviders"; +import { IdgGeneralSettings } from "./IdgGeneralSettings"; + +type DiscoveryIdentityProvider = IdentityProviderRepresentation & { + discoveryEndpoint?: string; +}; + +export default function AddIdgConnect() { + const { t } = useTranslation(); + const navigate = useNavigate(); + const id = "german-eid"; + + const form = useForm({ + defaultValues: { alias: id, config: { allowCreate: "true" } }, + }); + const { + handleSubmit, + formState: { isDirty }, + } = form; + const { addAlert, addError } = useAlerts(); + const { realm } = useRealm(); + + const onSubmit = async (provider: DiscoveryIdentityProvider) => { + delete provider.discoveryEndpoint; + try { + await adminClient.identityProviders.create({ + ...provider, + providerId: id, + }); + addAlert(t("createIdentityProviderSuccess"), AlertVariant.success); + navigate( + toIdentityProvider({ + realm, + providerId: id, + alias: provider.alias!, + tab: "settings", + }), + ); + } catch (error) { + addError("createIdentityProviderError", error); + } + }; + + return ( + <> + + + + + + + + + + + + + + ); +} diff --git a/js/apps/admin-ui/src/identity-providers/add/DetailSettings.tsx b/js/apps/admin-ui/src/identity-providers/add/DetailSettings.tsx index 0f660cf7184e..9b3e037b32f7 100644 --- a/js/apps/admin-ui/src/identity-providers/add/DetailSettings.tsx +++ b/js/apps/admin-ui/src/identity-providers/add/DetailSettings.tsx @@ -66,6 +66,7 @@ import { OIDCAuthentication } from "./OIDCAuthentication"; import { OIDCGeneralSettings } from "./OIDCGeneralSettings"; import { ReqAuthnConstraints } from "./ReqAuthnConstraintsSettings"; import { SamlGeneralSettings } from "./SamlGeneralSettings"; +import { IdgGeneralSettings } from "./IdgGeneralSettings"; type HeaderProps = { onChange: (value: boolean) => void; @@ -403,6 +404,7 @@ export default function DetailSettings() { const isOIDC = provider.providerId!.includes("oidc"); const isSAML = provider.providerId!.includes("saml"); + const isIDG = provider.providerId!.includes("german-eid"); const loader = async () => { const [loaderMappers, loaderMapperTypes] = await Promise.all([ @@ -438,9 +440,12 @@ export default function DetailSettings() { isHorizontal onSubmit={handleSubmit(save)} > - {!isOIDC && !isSAML && } + {!isOIDC && !isSAML && !isIDG && ( + + )} {isOIDC && } {isSAML && } + {isIDG && } {providerInfo && ( )} diff --git a/js/apps/admin-ui/src/identity-providers/add/IdgGeneralSettings.tsx b/js/apps/admin-ui/src/identity-providers/add/IdgGeneralSettings.tsx new file mode 100644 index 000000000000..011ad60e9053 --- /dev/null +++ b/js/apps/admin-ui/src/identity-providers/add/IdgGeneralSettings.tsx @@ -0,0 +1,52 @@ +import { FormGroup, ValidatedOptions } from "@patternfly/react-core"; +import { useFormContext } from "react-hook-form"; +import { useTranslation } from "react-i18next"; +import { useParams } from "react-router-dom"; + +import { HelpItem } from "ui-shared"; +import { KeycloakTextInput } from "../../components/keycloak-text-input/KeycloakTextInput"; +import { DisplayOrder } from "../component/DisplayOrder"; +import { RedirectUrl } from "../component/RedirectUrl"; +import { TextField } from "../component/TextField"; +import type { IdentityProviderParams } from "../routes/IdentityProvider"; + +export const IdgGeneralSettings = ({ id }: { id: string }) => { + const { t } = useTranslation(); + const { tab } = useParams(); + + const { + register, + formState: { errors }, + } = useFormContext(); + + return ( + <> + + + } + fieldId="alias" + isRequired + validated={ + errors.alias ? ValidatedOptions.error : ValidatedOptions.default + } + helperTextInvalid={t("required")} + > + + + + + + + ); +}; diff --git a/js/apps/admin-ui/src/identity-providers/routes.ts b/js/apps/admin-ui/src/identity-providers/routes.ts index 6ff67d1483d2..e16628990272 100644 --- a/js/apps/admin-ui/src/identity-providers/routes.ts +++ b/js/apps/admin-ui/src/identity-providers/routes.ts @@ -7,6 +7,7 @@ import { IdentityProvidersRoute } from "./routes/IdentityProviders"; import { IdentityProviderAddMapperRoute } from "./routes/AddMapper"; import { IdentityProviderEditMapperRoute } from "./routes/EditMapper"; import { IdentityProviderCreateRoute } from "./routes/IdentityProviderCreate"; +import { IdentityProviderIdgRoute } from "./routes/IdentityProviderIDG"; const routes: AppRouteObject[] = [ IdentityProviderAddMapperRoute, @@ -14,6 +15,7 @@ const routes: AppRouteObject[] = [ IdentityProvidersRoute, IdentityProviderOidcRoute, IdentityProviderSamlRoute, + IdentityProviderIdgRoute, IdentityProviderKeycloakOidcRoute, IdentityProviderCreateRoute, IdentityProviderRoute, diff --git a/js/apps/admin-ui/src/identity-providers/routes/IdentityProviderIDG.tsx b/js/apps/admin-ui/src/identity-providers/routes/IdentityProviderIDG.tsx new file mode 100644 index 000000000000..470b86f5416c --- /dev/null +++ b/js/apps/admin-ui/src/identity-providers/routes/IdentityProviderIDG.tsx @@ -0,0 +1,23 @@ +import { lazy } from "react"; +import type { Path } from "react-router-dom"; +import { generatePath } from "react-router-dom"; +import type { AppRouteObject } from "../../routes"; + +export type IdentityProviderIdgParams = { realm: string }; + +const AddIdgConnect = lazy(() => import("../add/AddIdgConnect")); + +export const IdentityProviderIdgRoute: AppRouteObject = { + path: "/:realm/identity-providers/german-eid/add", + element: , + breadcrumb: (t) => t("addIdgProvider"), + handle: { + access: "manage-identity-providers", + }, +}; + +export const toIdentityProviderIdg = ( + params: IdentityProviderIdgParams, +): Partial => ({ + pathname: generatePath(IdentityProviderIdgRoute.path, params), +}); From f05798e6f69a1782e4e29733152f3dfffa699ee8 Mon Sep 17 00:00:00 2001 From: Patrick Weiner Date: Tue, 4 Jul 2023 15:00:40 +0200 Subject: [PATCH 145/158] Make EventBuilder storeImmediately configurable via property. --- .../src/main/java/org/keycloak/events/EventBuilder.java | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/server-spi-private/src/main/java/org/keycloak/events/EventBuilder.java b/server-spi-private/src/main/java/org/keycloak/events/EventBuilder.java index aa682b5ad634..57124ef5101c 100755 --- a/server-spi-private/src/main/java/org/keycloak/events/EventBuilder.java +++ b/server-spi-private/src/main/java/org/keycloak/events/EventBuilder.java @@ -68,6 +68,13 @@ public EventBuilder(RealmModel realm, KeycloakSession session) { this.listeners = getEventListeners(session, realm); realm(realm); + + String storeImmediatelyProperty = System.getenv("KC_EVENT_STOREIMMEDIATELY"); + + if (storeImmediatelyProperty != null) { + this.storeImmediately = Boolean.parseBoolean(storeImmediatelyProperty); + } + } private static EventStoreProvider getEventStoreProvider(KeycloakSession session) { @@ -270,4 +277,4 @@ private void sendNow(EventStoreProvider targetStore, Set eventTypes, Lis } } -} +} \ No newline at end of file From a7d83d568795e9e4be24ddddd70460faa756ef04 Mon Sep 17 00:00:00 2001 From: Patrick Weiner Date: Mon, 28 Aug 2023 10:15:36 +0200 Subject: [PATCH 146/158] Extract storing of devicecode and usercode in own method. --- .../BackchannelAuthenticationEndpoint.java | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/services/src/main/java/org/keycloak/protocol/oidc/grants/ciba/endpoints/BackchannelAuthenticationEndpoint.java b/services/src/main/java/org/keycloak/protocol/oidc/grants/ciba/endpoints/BackchannelAuthenticationEndpoint.java index a2b80c520b17..cc07ba26fa70 100644 --- a/services/src/main/java/org/keycloak/protocol/oidc/grants/ciba/endpoints/BackchannelAuthenticationEndpoint.java +++ b/services/src/main/java/org/keycloak/protocol/oidc/grants/ciba/endpoints/BackchannelAuthenticationEndpoint.java @@ -99,12 +99,12 @@ public Response processGrantRequest() { UserModel user = request.getUser(); String infoUsedByAuthentication = resolver.getInfoUsedByAuthentication(user); + CibaConfig cibaPolicy = realm.getCibaPolicy(); + int poolingInterval = cibaPolicy.getPoolingInterval(); - if (provider.requestAuthentication(request, infoUsedByAuthentication)) { - CibaConfig cibaPolicy = realm.getCibaPolicy(); - int poolingInterval = cibaPolicy.getPoolingInterval(); + storeAuthenticationRequest(request, cibaPolicy, authReqId); - storeAuthenticationRequest(request, cibaPolicy, authReqId); + if (provider.requestAuthentication(request, infoUsedByAuthentication)) { ObjectNode response = JsonSerialization.createObjectNode(); @@ -151,12 +151,19 @@ private void storeAuthenticationRequest(CIBAAuthenticationRequest request, CibaC // To inform "expired_token" to the client, the lifespan of the cache provider is longer than device code int lifespanSeconds = expiresIn + poolingInterval + 10; + + this.store(userCode, deviceCode, lifespanSeconds); - SingleUseObjectProvider singleUseStore = session.singleUseObjects(); + } + + + protected void store(OAuth2DeviceUserCodeModel userCode, OAuth2DeviceCodeModel deviceCode, int lifespanSeconds) { + + SingleUseObjectProvider singleUseStore = session.getProvider(SingleUseObjectProvider.class); singleUseStore.put(deviceCode.serializeKey(), lifespanSeconds, deviceCode.toMap()); singleUseStore.put(userCode.serializeKey(), lifespanSeconds, userCode.serializeValue()); - } + } private CIBAAuthenticationRequest authorizeClient(MultivaluedMap params) { ClientModel client = null; From 0851ac5340ef1fa3afd2507c5c6b409638fe22ec Mon Sep 17 00:00:00 2001 From: Patrick Weiner Date: Mon, 28 Aug 2023 10:42:29 +0200 Subject: [PATCH 147/158] Add overridable methods for device code approval. --- ...channelAuthenticationCallbackEndpoint.java | 41 ++++++++++++++----- 1 file changed, 30 insertions(+), 11 deletions(-) diff --git a/services/src/main/java/org/keycloak/protocol/oidc/grants/ciba/endpoints/BackchannelAuthenticationCallbackEndpoint.java b/services/src/main/java/org/keycloak/protocol/oidc/grants/ciba/endpoints/BackchannelAuthenticationCallbackEndpoint.java index 069052dc1472..17402b19bba5 100644 --- a/services/src/main/java/org/keycloak/protocol/oidc/grants/ciba/endpoints/BackchannelAuthenticationCallbackEndpoint.java +++ b/services/src/main/java/org/keycloak/protocol/oidc/grants/ciba/endpoints/BackchannelAuthenticationCallbackEndpoint.java @@ -56,6 +56,7 @@ public class BackchannelAuthenticationCallbackEndpoint extends AbstractCibaEndpo private static final Logger logger = Logger.getLogger(BackchannelAuthenticationCallbackEndpoint.class); private final HttpRequest httpRequest; + protected AuthenticationChannelResponse authenticationChannelResponse; public BackchannelAuthenticationCallbackEndpoint(KeycloakSession session, EventBuilder event) { super(session, event); @@ -69,7 +70,8 @@ public BackchannelAuthenticationCallbackEndpoint(KeycloakSession session, EventB @Produces(MediaType.APPLICATION_JSON) public Response processAuthenticationChannelResult(AuthenticationChannelResponse response) { event.event(EventType.LOGIN); - BackchannelAuthCallbackContext ctx = verifyAuthenticationRequest(httpRequest.getHttpHeaders()); + this.authenticationChannelResponse = response; + BackchannelAuthCallbackContext ctx = verifyAuthenticationRequest(getRawBearerToken()); AccessToken bearerToken = ctx.bearerToken; OAuth2DeviceCodeModel deviceModel = ctx.deviceModel; @@ -81,15 +83,21 @@ public Response processAuthenticationChannelResult(AuthenticationChannelResponse Response.Status.BAD_REQUEST); } + status = preApprove(response); + switch (status) { case SUCCEED: - approveRequest(bearerToken, response.getAdditionalParams()); + approveRequest(bearerToken.getId(), response.getAdditionalParams()); break; case CANCELLED: case UNAUTHORIZED: - denyRequest(bearerToken, status); + denyRequest(bearerToken.getId(), status); break; } + + if (!postApprove(response)) { + denyRequest(bearerToken.getId(), status); + } // Call the notification endpoint ClientModel client = session.getContext().getClient(); @@ -101,8 +109,7 @@ public Response processAuthenticationChannelResult(AuthenticationChannelResponse return Response.ok(MediaType.APPLICATION_JSON_TYPE).build(); } - private BackchannelAuthCallbackContext verifyAuthenticationRequest(HttpHeaders headers) { - String rawBearerToken = AppAuthManager.extractAuthorizationHeaderTokenOrReturnNull(headers); + protected BackchannelAuthCallbackContext verifyAuthenticationRequest(String rawBearerToken) { if (rawBearerToken == null) { throw new ErrorResponseException(OAuthErrorException.INVALID_TOKEN, "Invalid token", Response.Status.UNAUTHORIZED); @@ -152,24 +159,36 @@ private BackchannelAuthCallbackContext verifyAuthenticationRequest(HttpHeaders h return new BackchannelAuthCallbackContext(bearerToken, deviceCode); } - private void cancelRequest(String authResultId) { + protected void cancelRequest(String authResultId) { OAuth2DeviceCodeModel userCode = DeviceEndpoint.getDeviceByUserCode(session, realm, authResultId); DeviceGrantType.removeDeviceByDeviceCode(session, userCode.getDeviceCode()); DeviceGrantType.removeDeviceByUserCode(session, realm, authResultId); } + + protected Status preApprove(AuthenticationChannelResponse response) { + return response.getStatus(); + } - private void approveRequest(AccessToken authReqId, Map additionalParams) { - DeviceGrantType.approveUserCode(session, realm, authReqId.getId(), "fake", additionalParams); + protected void approveRequest(String authReqId, Map additionalParams) { + DeviceGrantType.approveUserCode(session, realm, authReqId, "fake", additionalParams); + } + + protected boolean postApprove(AuthenticationChannelResponse response) { + return true; } - private void denyRequest(AccessToken authReqId, Status status) { + protected void denyRequest(String authReqId, Status status) { if (CANCELLED.equals(status)) { event.error(Errors.NOT_ALLOWED); } else { event.error(Errors.CONSENT_DENIED); } - DeviceGrantType.denyUserCode(session, realm, authReqId.getId()); + DeviceGrantType.denyUserCode(session, realm, authReqId); + } + + protected String getRawBearerToken() { + return AppAuthManager.extractAuthorizationHeaderTokenOrReturnNull(httpRequest.getHttpHeaders()); } protected void sendClientNotificationRequest(ClientModel client, CibaConfig cibaConfig, OAuth2DeviceCodeModel deviceModel) { @@ -208,7 +227,7 @@ protected void sendClientNotificationRequest(ClientModel client, CibaConfig ciba } } - private class BackchannelAuthCallbackContext { + protected static class BackchannelAuthCallbackContext { private final AccessToken bearerToken; private final OAuth2DeviceCodeModel deviceModel; From fb0ff5423a17a1f6b82e8dbe2db3b738a5c50a81 Mon Sep 17 00:00:00 2001 From: Manuel Schallar Date: Thu, 14 Sep 2023 11:23:37 +0200 Subject: [PATCH 148/158] Make tokenverifier overridable --- ...channelAuthenticationCallbackEndpoint.java | 31 +++++++++++++++---- 1 file changed, 25 insertions(+), 6 deletions(-) diff --git a/services/src/main/java/org/keycloak/protocol/oidc/grants/ciba/endpoints/BackchannelAuthenticationCallbackEndpoint.java b/services/src/main/java/org/keycloak/protocol/oidc/grants/ciba/endpoints/BackchannelAuthenticationCallbackEndpoint.java index 17402b19bba5..31100afed75f 100644 --- a/services/src/main/java/org/keycloak/protocol/oidc/grants/ciba/endpoints/BackchannelAuthenticationCallbackEndpoint.java +++ b/services/src/main/java/org/keycloak/protocol/oidc/grants/ciba/endpoints/BackchannelAuthenticationCallbackEndpoint.java @@ -118,12 +118,7 @@ protected BackchannelAuthCallbackContext verifyAuthenticationRequest(String rawB AccessToken bearerToken; try { - bearerToken = TokenVerifier.createWithoutSignature(session.tokens().decode(rawBearerToken, AccessToken.class)) - .withDefaultChecks() - .realmUrl(Urls.realmIssuer(session.getContext().getUri().getBaseUri(), realm.getName())) - .checkActive(true) - .audience(Urls.realmIssuer(session.getContext().getUri().getBaseUri(), realm.getName())) - .verify().getToken(); + bearerToken = getTokenVerifier(rawBearerToken).verify().getToken(); } catch (Exception e) { event.error(Errors.INVALID_TOKEN); // authentication channel id format is invalid or it has already been used @@ -191,6 +186,30 @@ protected String getRawBearerToken() { return AppAuthManager.extractAuthorizationHeaderTokenOrReturnNull(httpRequest.getHttpHeaders()); } + /** + * Returns a tokenverifier for {@link AccessToken}s with + * {@link TokenVerifier#withDefaultChecks()} with configured realmurl, isactive + * check an audience. + * + * @param rawBearerToken The raw bearer token. (required; must not be + * {@code null}) + * + * @return The token verifier for {@link AccessToken}s. (never {@code null}) + * + * @implNote Note that the token will only verified once + * {@link TokenVerifier#verify()} is called. + */ + protected TokenVerifier getTokenVerifier(String rawBearerToken) { + + return TokenVerifier + .createWithoutSignature(session.tokens().decode(rawBearerToken, AccessToken.class)) + .withDefaultChecks() + .realmUrl(Urls.realmIssuer(session.getContext().getUri().getBaseUri(), realm.getName())) + .checkActive(true) + .audience(Urls.realmIssuer(session.getContext().getUri().getBaseUri(), realm.getName())); + + } + protected void sendClientNotificationRequest(ClientModel client, CibaConfig cibaConfig, OAuth2DeviceCodeModel deviceModel) { String clientNotificationEndpoint = cibaConfig.getBackchannelClientNotificationEndpoint(client); if (clientNotificationEndpoint == null) { From 7ee6766c864b26f23ba56f95c490a136a0e6268b Mon Sep 17 00:00:00 2001 From: Otto Touzil Date: Mon, 18 Sep 2023 11:15:38 +0200 Subject: [PATCH 149/158] getAuthRequestedUserHint as protected method --- .../ciba/endpoints/BackchannelAuthenticationEndpoint.java | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/services/src/main/java/org/keycloak/protocol/oidc/grants/ciba/endpoints/BackchannelAuthenticationEndpoint.java b/services/src/main/java/org/keycloak/protocol/oidc/grants/ciba/endpoints/BackchannelAuthenticationEndpoint.java index cc07ba26fa70..799c528a0f53 100644 --- a/services/src/main/java/org/keycloak/protocol/oidc/grants/ciba/endpoints/BackchannelAuthenticationEndpoint.java +++ b/services/src/main/java/org/keycloak/protocol/oidc/grants/ciba/endpoints/BackchannelAuthenticationEndpoint.java @@ -174,7 +174,7 @@ private CIBAAuthenticationRequest authorizeClient(MultivaluedMap throw new ErrorResponseException(errorRep.getError(), errorRep.getErrorDescription(), Response.Status.UNAUTHORIZED); } BackchannelAuthenticationEndpointRequest endpointRequest = BackchannelAuthenticationEndpointRequestParserProcessor.parseRequest(event, session, client, params, realm.getCibaPolicy()); - UserModel user = resolveUser(endpointRequest, realm.getCibaPolicy().getAuthRequestedUserHint()); + UserModel user = resolveUser(endpointRequest, getAuthRequestedUserHint()); CIBAAuthenticationRequest request = new CIBAAuthenticationRequest(session, user, client); @@ -245,6 +245,10 @@ private CIBAAuthenticationRequest authorizeClient(MultivaluedMap return request; } + protected String getAuthRequestedUserHint() { + return realm.getCibaPolicy().getAuthRequestedUserHint(); + } + protected void extractAdditionalParams(BackchannelAuthenticationEndpointRequest endpointRequest, CIBAAuthenticationRequest request) { for (String paramName : endpointRequest.getAdditionalReqParams().keySet()) { request.setOtherClaims(paramName, endpointRequest.getAdditionalReqParams().get(paramName)); From 2c0489752ee950427eb8bf209032169e1ea10d5a Mon Sep 17 00:00:00 2001 From: Otto Touzil Date: Mon, 16 Oct 2023 09:30:40 +0200 Subject: [PATCH 150/158] increase ADDITIONAL_REQ_PARAMS_MAX_SIZE --- .../oidc/endpoints/request/AuthzEndpointRequestParser.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/services/src/main/java/org/keycloak/protocol/oidc/endpoints/request/AuthzEndpointRequestParser.java b/services/src/main/java/org/keycloak/protocol/oidc/endpoints/request/AuthzEndpointRequestParser.java index 01a174417f3c..2739d3828f2a 100644 --- a/services/src/main/java/org/keycloak/protocol/oidc/endpoints/request/AuthzEndpointRequestParser.java +++ b/services/src/main/java/org/keycloak/protocol/oidc/endpoints/request/AuthzEndpointRequestParser.java @@ -38,7 +38,7 @@ public abstract class AuthzEndpointRequestParser { * Max number of additional req params copied into client session note to prevent DoS attacks * */ - public static final int ADDITIONAL_REQ_PARAMS_MAX_MUMBER = 5; + public static final int ADDITIONAL_REQ_PARAMS_MAX_MUMBER = 10; /** * Max size of additional req param value copied into client session note to prevent DoS attacks - params with longer value are ignored From c6220db24152a787f24419235df26cb715eab9e0 Mon Sep 17 00:00:00 2001 From: Patrick Weiner Date: Thu, 16 Nov 2023 12:54:23 +0100 Subject: [PATCH 151/158] Add loginSmsTan and onboardingSmsTan to LoginFormsPages. --- .../java/org/keycloak/forms/login/LoginFormsPages.java | 3 ++- .../java/org/keycloak/forms/login/LoginFormsProvider.java | 8 ++++++++ .../org/keycloak/forms/login/freemarker/Templates.java | 4 ++++ 3 files changed, 14 insertions(+), 1 deletion(-) diff --git a/server-spi-private/src/main/java/org/keycloak/forms/login/LoginFormsPages.java b/server-spi-private/src/main/java/org/keycloak/forms/login/LoginFormsPages.java index 2e6bb11983da..b30d4275818b 100755 --- a/server-spi-private/src/main/java/org/keycloak/forms/login/LoginFormsPages.java +++ b/server-spi-private/src/main/java/org/keycloak/forms/login/LoginFormsPages.java @@ -28,6 +28,7 @@ public enum LoginFormsPages { LOGIN_PAGE_EXPIRED, CODE, X509_CONFIRM, SAML_POST_FORM, LOGIN_OAUTH2_DEVICE_VERIFY_USER_CODE, IDP_REVIEW_USER_PROFILE, LOGIN_RECOVERY_AUTHN_CODES_INPUT, LOGIN_RECOVERY_AUTHN_CODES_CONFIG, - FRONTCHANNEL_LOGOUT, LOGOUT_CONFIRM, UPDATE_EMAIL, LOGIN_RESET_OTP; + FRONTCHANNEL_LOGOUT, LOGOUT_CONFIRM, UPDATE_EMAIL, LOGIN_RESET_OTP, + LOGIN_SMS_TAN, ONBOARDING_SMS_TAN; } diff --git a/server-spi-private/src/main/java/org/keycloak/forms/login/LoginFormsProvider.java b/server-spi-private/src/main/java/org/keycloak/forms/login/LoginFormsProvider.java index 5f9138b6b760..7e5f81cffcac 100755 --- a/server-spi-private/src/main/java/org/keycloak/forms/login/LoginFormsProvider.java +++ b/server-spi-private/src/main/java/org/keycloak/forms/login/LoginFormsProvider.java @@ -56,6 +56,14 @@ public interface LoginFormsProvider extends Provider { String getMessage(String message); + default Response createLoginSmsTan() { + throw new UnsupportedOperationException("The 'createLoginSmsTan' shouldn't be called."); + } + + default Response createOnboardingSmsTan() { + throw new UnsupportedOperationException("The 'createOnboardingSmsTan' shouldn't be called."); + } + Response createLoginUsernamePassword(); Response createLoginUsername(); diff --git a/services/src/main/java/org/keycloak/forms/login/freemarker/Templates.java b/services/src/main/java/org/keycloak/forms/login/freemarker/Templates.java index 6f20125c9ff2..f200c76ed13c 100755 --- a/services/src/main/java/org/keycloak/forms/login/freemarker/Templates.java +++ b/services/src/main/java/org/keycloak/forms/login/freemarker/Templates.java @@ -86,6 +86,10 @@ public static String getTemplate(LoginFormsPages page) { return "frontchannel-logout.ftl"; case LOGOUT_CONFIRM: return "logout-confirm.ftl"; + case LOGIN_SMS_TAN: + return "sms-validation.ftl"; + case ONBOARDING_SMS_TAN: + return "onboarding-sms-validation.ftl"; default: throw new IllegalArgumentException(); } From 8ab660f00d6d8948b8bcb9094e994574cf8e4a7a Mon Sep 17 00:00:00 2001 From: Manuel Schallar Date: Mon, 12 Feb 2024 10:28:54 +0100 Subject: [PATCH 152/158] Add Generic-SMS Tan to LoginFormsPages --- .../main/java/org/keycloak/forms/login/LoginFormsPages.java | 2 +- .../java/org/keycloak/forms/login/LoginFormsProvider.java | 4 ++++ .../java/org/keycloak/forms/login/freemarker/Templates.java | 2 ++ 3 files changed, 7 insertions(+), 1 deletion(-) diff --git a/server-spi-private/src/main/java/org/keycloak/forms/login/LoginFormsPages.java b/server-spi-private/src/main/java/org/keycloak/forms/login/LoginFormsPages.java index b30d4275818b..1456372bf0bc 100755 --- a/server-spi-private/src/main/java/org/keycloak/forms/login/LoginFormsPages.java +++ b/server-spi-private/src/main/java/org/keycloak/forms/login/LoginFormsPages.java @@ -29,6 +29,6 @@ public enum LoginFormsPages { LOGIN_OAUTH2_DEVICE_VERIFY_USER_CODE, IDP_REVIEW_USER_PROFILE, LOGIN_RECOVERY_AUTHN_CODES_INPUT, LOGIN_RECOVERY_AUTHN_CODES_CONFIG, FRONTCHANNEL_LOGOUT, LOGOUT_CONFIRM, UPDATE_EMAIL, LOGIN_RESET_OTP, - LOGIN_SMS_TAN, ONBOARDING_SMS_TAN; + LOGIN_SMS_TAN, ONBOARDING_SMS_TAN, GENERIC_SMS_TAN; } diff --git a/server-spi-private/src/main/java/org/keycloak/forms/login/LoginFormsProvider.java b/server-spi-private/src/main/java/org/keycloak/forms/login/LoginFormsProvider.java index 7e5f81cffcac..c659cd016d75 100755 --- a/server-spi-private/src/main/java/org/keycloak/forms/login/LoginFormsProvider.java +++ b/server-spi-private/src/main/java/org/keycloak/forms/login/LoginFormsProvider.java @@ -64,6 +64,10 @@ default Response createOnboardingSmsTan() { throw new UnsupportedOperationException("The 'createOnboardingSmsTan' shouldn't be called."); } + default Response createGenericSmsTanPage() { + throw new UnsupportedOperationException("The 'createGenericSmsTanPage' shouldn't be called."); + } + Response createLoginUsernamePassword(); Response createLoginUsername(); diff --git a/services/src/main/java/org/keycloak/forms/login/freemarker/Templates.java b/services/src/main/java/org/keycloak/forms/login/freemarker/Templates.java index f200c76ed13c..a1cf03f45e02 100755 --- a/services/src/main/java/org/keycloak/forms/login/freemarker/Templates.java +++ b/services/src/main/java/org/keycloak/forms/login/freemarker/Templates.java @@ -90,6 +90,8 @@ public static String getTemplate(LoginFormsPages page) { return "sms-validation.ftl"; case ONBOARDING_SMS_TAN: return "onboarding-sms-validation.ftl"; + case GENERIC_SMS_TAN: + return "generic-sms-validation.ftl"; default: throw new IllegalArgumentException(); } From 7c2a2bafe9c5d50fef15c50052bd78ff74924bff Mon Sep 17 00:00:00 2001 From: Otto Touzil Date: Fri, 14 Jun 2024 22:30:20 +0200 Subject: [PATCH 153/158] align CIBA request parameter limits with browser flow limit --- ...channelAuthenticationEndpointRequestParser.java | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/services/src/main/java/org/keycloak/protocol/oidc/grants/ciba/endpoints/request/BackchannelAuthenticationEndpointRequestParser.java b/services/src/main/java/org/keycloak/protocol/oidc/grants/ciba/endpoints/request/BackchannelAuthenticationEndpointRequestParser.java index 6d734d2f8069..77743f08cd61 100644 --- a/services/src/main/java/org/keycloak/protocol/oidc/grants/ciba/endpoints/request/BackchannelAuthenticationEndpointRequestParser.java +++ b/services/src/main/java/org/keycloak/protocol/oidc/grants/ciba/endpoints/request/BackchannelAuthenticationEndpointRequestParser.java @@ -21,6 +21,7 @@ import org.jboss.logging.Logger; import org.keycloak.OAuth2Constants; import org.keycloak.protocol.oidc.OIDCLoginProtocol; +import org.keycloak.protocol.oidc.endpoints.request.AuthzEndpointRequestParser; import org.keycloak.protocol.oidc.grants.ciba.CibaGrantType; import java.util.HashSet; @@ -38,13 +39,13 @@ public abstract class BackchannelAuthenticationEndpointRequestParser { * Max number of additional req params copied into client session note to prevent DoS attacks * */ - public static final int ADDITIONAL_REQ_PARAMS_MAX_MUMBER = 5; + public static final int ADDITIONAL_REQ_PARAMS_MAX_MUMBER = AuthzEndpointRequestParser.ADDITIONAL_REQ_PARAMS_MAX_MUMBER; /** * Max size of additional req param value copied into client session note to prevent DoS attacks - params with longer value are ignored * */ - public static final int ADDITIONAL_REQ_PARAMS_MAX_SIZE = 200; + public static final int ADDITIONAL_REQ_PARAMS_MAX_SIZE = AuthzEndpointRequestParser.ADDITIONAL_REQ_PARAMS_MAX_SIZE; public static final String CIBA_SIGNED_AUTHENTICATION_REQUEST = "ParsedSignedAuthenticationRequest"; @@ -77,6 +78,15 @@ public abstract class BackchannelAuthenticationEndpointRequestParser { // if these are included in Backchannel Authentication Request's body part for "client_secret_post" client authentication KNOWN_REQ_PARAMS.add(OAuth2Constants.CLIENT_ID); KNOWN_REQ_PARAMS.add(OAuth2Constants.CLIENT_SECRET); + + /* Ignore JTI as additional parameter. + * + * if not ignored it leads to a duplicate entry in the AUTH_REQ_ID JWE token + * (see BackchannelAuthenticationEndpoint:processGrantRequest) and due to + * deserialization to a wrong id in CibaGrantType:process + * which leads to an HTTP 400 response in the ciba token call. + */ + KNOWN_REQ_PARAMS.add("jti"); } public void parseRequest(BackchannelAuthenticationEndpointRequest request) { From 926d05eb3a3cf76d3ff4504f4471cac2ea2d20b6 Mon Sep 17 00:00:00 2001 From: Otto Touzil Date: Thu, 13 Jun 2024 10:45:40 +0200 Subject: [PATCH 154/158] set version to 24.0.5-PS-1-SNAPSHOT --- .github/workflows/js-ci.yml | 12 ++++++------ adapters/oidc/adapter-core/pom.xml | 2 +- adapters/oidc/installed/pom.xml | 2 +- adapters/oidc/jakarta-servlet-filter/pom.xml | 2 +- adapters/oidc/jaxrs-oauth-client/pom.xml | 2 +- adapters/oidc/jetty/jetty-core/pom.xml | 2 +- adapters/oidc/jetty/jetty9.4/pom.xml | 2 +- adapters/oidc/jetty/pom.xml | 2 +- adapters/oidc/js/pom.xml | 2 +- adapters/oidc/pom.xml | 2 +- adapters/oidc/servlet-filter/pom.xml | 2 +- adapters/oidc/spring-boot-adapter-core/pom.xml | 2 +- adapters/oidc/spring-boot-container-bundle/pom.xml | 2 +- adapters/oidc/spring-boot2/pom.xml | 2 +- adapters/oidc/spring-security/pom.xml | 2 +- adapters/oidc/tomcat/pom.xml | 2 +- adapters/oidc/tomcat/tomcat-core/pom.xml | 2 +- adapters/oidc/tomcat/tomcat/pom.xml | 2 +- adapters/oidc/undertow/pom.xml | 2 +- adapters/oidc/wildfly-elytron/pom.xml | 2 +- adapters/oidc/wildfly/pom.xml | 2 +- adapters/oidc/wildfly/wildfly-subsystem/pom.xml | 2 +- adapters/pom.xml | 2 +- adapters/saml/core-jakarta/pom.xml | 2 +- adapters/saml/core-public/pom.xml | 2 +- adapters/saml/core/pom.xml | 2 +- adapters/saml/jakarta-servlet-filter/pom.xml | 2 +- adapters/saml/jetty/jetty-core/pom.xml | 2 +- adapters/saml/jetty/jetty9.4/pom.xml | 2 +- adapters/saml/jetty/pom.xml | 2 +- adapters/saml/pom.xml | 2 +- adapters/saml/servlet-filter/pom.xml | 2 +- adapters/saml/tomcat/pom.xml | 2 +- adapters/saml/tomcat/tomcat-core/pom.xml | 2 +- adapters/saml/tomcat/tomcat/pom.xml | 2 +- adapters/saml/undertow/pom.xml | 2 +- adapters/saml/wildfly-elytron-jakarta/pom.xml | 2 +- adapters/saml/wildfly-elytron/pom.xml | 2 +- adapters/saml/wildfly/pom.xml | 2 +- .../saml/wildfly/wildfly-jakarta-subsystem/pom.xml | 2 +- adapters/saml/wildfly/wildfly-subsystem/pom.xml | 2 +- adapters/spi/adapter-spi/pom.xml | 2 +- adapters/spi/jakarta-servlet-adapter-spi/pom.xml | 2 +- adapters/spi/jboss-adapter-core/pom.xml | 2 +- adapters/spi/jetty-adapter-spi/pom.xml | 2 +- adapters/spi/pom.xml | 2 +- adapters/spi/servlet-adapter-spi/pom.xml | 2 +- adapters/spi/tomcat-adapter-spi/pom.xml | 2 +- adapters/spi/undertow-adapter-spi/pom.xml | 2 +- authz/client/pom.xml | 2 +- authz/policy-enforcer/pom.xml | 2 +- authz/policy/common/pom.xml | 2 +- authz/policy/pom.xml | 2 +- authz/pom.xml | 2 +- boms/adapter/pom.xml | 2 +- boms/misc/pom.xml | 2 +- boms/pom.xml | 2 +- boms/spi/pom.xml | 2 +- common/pom.xml | 2 +- core/pom.xml | 2 +- crypto/default/pom.xml | 2 +- crypto/elytron/pom.xml | 2 +- crypto/fips1402/pom.xml | 2 +- crypto/pom.xml | 2 +- dependencies/pom.xml | 2 +- dependencies/server-all/pom.xml | 2 +- dependencies/server-min/pom.xml | 2 +- distribution/adapters/pom.xml | 2 +- distribution/adapters/tomcat-adapter-zip/pom.xml | 2 +- distribution/adapters/wildfly-adapter/pom.xml | 2 +- distribution/api-docs-dist/pom.xml | 2 +- distribution/downloads/pom.xml | 2 +- .../feature-packs/adapter-feature-pack/pom.xml | 2 +- distribution/feature-packs/pom.xml | 2 +- distribution/galleon-feature-packs/pom.xml | 2 +- .../pom.xml | 2 +- .../saml-adapter-galleon-pack/pom.xml | 2 +- distribution/licenses-common/pom.xml | 2 +- .../maven-plugins/licenses-processor/pom.xml | 2 +- distribution/maven-plugins/pom.xml | 2 +- distribution/pom.xml | 2 +- distribution/saml-adapters/pom.xml | 2 +- .../saml-adapters/tomcat-adapter-zip/pom.xml | 2 +- distribution/saml-adapters/wildfly-adapter/pom.xml | 2 +- .../wildfly-adapter-jakarta-zip/pom.xml | 2 +- .../wildfly-adapter/wildfly-adapter-zip/pom.xml | 2 +- .../wildfly-adapter/wildfly-jakarta-modules/pom.xml | 2 +- .../wildfly-adapter/wildfly-modules/pom.xml | 2 +- docs/documentation/aggregation/pom.xml | 2 +- docs/documentation/api_documentation/pom.xml | 2 +- docs/documentation/authorization_services/pom.xml | 2 +- docs/documentation/dist/pom.xml | 2 +- docs/documentation/header-maven-plugin/pom.xml | 4 ++-- docs/documentation/pom.xml | 4 ++-- docs/documentation/release_notes/pom.xml | 2 +- docs/documentation/securing_apps/pom.xml | 2 +- docs/documentation/server_admin/pom.xml | 2 +- docs/documentation/server_development/pom.xml | 2 +- docs/documentation/tests/pom.xml | 2 +- .../topics/templates/document-attributes.adoc | 8 ++++---- docs/documentation/upgrading/pom.xml | 2 +- docs/guides/pom.xml | 2 +- docs/maven-plugin/pom.xml | 2 +- docs/pom.xml | 2 +- examples/admin-client/pom.xml | 2 +- examples/js-console/pom.xml | 2 +- examples/kerberos/pom.xml | 2 +- examples/ldap/pom.xml | 2 +- examples/pom.xml | 2 +- examples/providers/authenticator/pom.xml | 2 +- examples/providers/pom.xml | 2 +- examples/providers/rest/pom.xml | 2 +- examples/saml/pom.xml | 2 +- examples/saml/servlet-filter/pom.xml | 2 +- examples/themes/pom.xml | 2 +- federation/kerberos/pom.xml | 2 +- federation/ldap/pom.xml | 2 +- federation/pom.xml | 2 +- federation/sssd/pom.xml | 2 +- integration/admin-client-jee/pom.xml | 2 +- integration/admin-client/pom.xml | 2 +- integration/client-cli/admin-cli/pom.xml | 2 +- integration/client-cli/client-cli-dist/pom.xml | 2 +- .../client-cli/client-registration-cli/pom.xml | 2 +- integration/client-cli/pom.xml | 2 +- integration/client-registration/pom.xml | 2 +- integration/pom.xml | 2 +- js/apps/account-ui/pom.xml | 2 +- js/apps/admin-ui/pom.xml | 2 +- js/libs/keycloak-admin-client/package.json | 2 +- js/libs/keycloak-admin-client/pom.xml | 2 +- js/libs/keycloak-js/package.json | 2 +- js/libs/keycloak-js/pom.xml | 2 +- js/pom.xml | 2 +- misc/keycloak-test-helper/pom.xml | 2 +- misc/pom.xml | 2 +- .../keycloak-spring-boot-starter/pom.xml | 2 +- misc/spring-boot-starter/pom.xml | 2 +- model/infinispan/pom.xml | 2 +- model/jpa/pom.xml | 2 +- model/legacy/pom.xml | 2 +- model/pom.xml | 2 +- model/storage-private/pom.xml | 2 +- model/storage-services/pom.xml | 2 +- model/storage/pom.xml | 2 +- operator/pom.xml | 2 +- pom.xml | 4 ++-- quarkus/config-api/pom.xml | 2 +- quarkus/container/Dockerfile | 2 +- quarkus/deployment/pom.xml | 2 +- quarkus/dist/pom.xml | 2 +- quarkus/pom.xml | 2 +- quarkus/runtime/pom.xml | 2 +- quarkus/server/pom.xml | 2 +- quarkus/tests/integration/pom.xml | 2 +- quarkus/tests/junit5/pom.xml | 2 +- quarkus/tests/pom.xml | 2 +- release-details | 6 +++--- rest/admin-ui-ext/pom.xml | 2 +- rest/pom.xml | 2 +- saml-core-api/pom.xml | 2 +- saml-core/pom.xml | 2 +- server-spi-private/pom.xml | 2 +- server-spi/pom.xml | 2 +- services/pom.xml | 2 +- testsuite/db-allocator-plugin/pom.xml | 2 +- testsuite/integration-arquillian/pom.xml | 2 +- .../servers/adapter-spi/pom.xml | 2 +- .../adapter-spi/undertow-adapter-jakarta/pom.xml | 2 +- .../undertow-adapter-saml-jakarta/pom.xml | 2 +- .../adapter-spi/undertow-adapter-spi-jakarta/pom.xml | 2 +- .../servers/app-server/app-server-spi/pom.xml | 2 +- .../servers/app-server/jboss/eap/pom.xml | 2 +- .../servers/app-server/jboss/eap6/pom.xml | 2 +- .../servers/app-server/jboss/galleon/pom.xml | 2 +- .../servers/app-server/jboss/pom.xml | 2 +- .../servers/app-server/jboss/wildfly/pom.xml | 2 +- .../servers/app-server/jetty/94/pom.xml | 2 +- .../servers/app-server/jetty/common/pom.xml | 2 +- .../servers/app-server/jetty/pom.xml | 2 +- .../servers/app-server/karaf/fuse63/pom.xml | 2 +- .../servers/app-server/karaf/fuse7x/pom.xml | 2 +- .../servers/app-server/karaf/pom.xml | 2 +- .../servers/app-server/pom.xml | 2 +- .../servers/app-server/tomcat/common/pom.xml | 2 +- .../servers/app-server/tomcat/pom.xml | 2 +- .../servers/app-server/tomcat/tomcat8/pom.xml | 2 +- .../servers/app-server/tomcat/tomcat9/pom.xml | 2 +- .../servers/app-server/undertow/pom.xml | 2 +- .../servers/auth-server/pom.xml | 2 +- .../servers/auth-server/quarkus/pom.xml | 2 +- .../servers/auth-server/services/pom.xml | 2 +- .../services/testsuite-providers-deployment/pom.xml | 2 +- .../auth-server/services/testsuite-providers/pom.xml | 2 +- .../servers/auth-server/undertow/pom.xml | 2 +- .../servers/cache-server/infinispan/datagrid/pom.xml | 2 +- .../cache-server/infinispan/infinispan/pom.xml | 2 +- .../servers/cache-server/infinispan/pom.xml | 2 +- .../servers/cache-server/legacy/datagrid/pom.xml | 2 +- .../servers/cache-server/legacy/infinispan/pom.xml | 2 +- .../servers/cache-server/legacy/pom.xml | 2 +- .../servers/cache-server/pom.xml | 2 +- .../integration-arquillian/servers/migration/pom.xml | 2 +- testsuite/integration-arquillian/servers/pom.xml | 2 +- .../test-apps/app-profile-jee/pom.xml | 2 +- .../test-apps/cors/angular-product/pom.xml | 2 +- .../test-apps/cors/database-service/pom.xml | 2 +- .../integration-arquillian/test-apps/cors/pom.xml | 2 +- .../test-apps/fuse/camel-fuse7-undertow/pom.xml | 2 +- .../test-apps/fuse/camel/pom.xml | 2 +- .../test-apps/fuse/customer-app-fuse/pom.xml | 2 +- .../test-apps/fuse/cxf-jaxrs-fuse7-undertow/pom.xml | 2 +- .../test-apps/fuse/cxf-jaxrs/pom.xml | 2 +- .../test-apps/fuse/cxf-jaxws-fuse7-undertow/pom.xml | 2 +- .../test-apps/fuse/cxf-jaxws/pom.xml | 2 +- .../test-apps/fuse/external-config/pom.xml | 2 +- .../test-apps/fuse/features/pom.xml | 2 +- .../integration-arquillian/test-apps/fuse/pom.xml | 2 +- .../test-apps/fuse/product-app-fuse/pom.xml | 2 +- .../fuse/product-app-fuse7-undertow/pom.xml | 2 +- .../test-apps/hello-world-authz-service/pom.xml | 2 +- testsuite/integration-arquillian/test-apps/pom.xml | 2 +- .../test-apps/servlet-authz/pom.xml | 2 +- .../test-apps/servlet-policy-enforcer/pom.xml | 2 +- .../test-apps/servlets-jakarta/pom.xml | 2 +- .../test-apps/servlets/pom.xml | 2 +- .../test-apps/spring-boot-adapter-app/pom.xml | 2 +- .../test-apps/test-apps-dist/pom.xml | 2 +- testsuite/integration-arquillian/tests/base/pom.xml | 2 +- .../tests/other/adapters/jboss/pom.xml | 2 +- .../tests/other/adapters/karaf/fuse61/pom.xml | 2 +- .../tests/other/adapters/karaf/fuse62/pom.xml | 2 +- .../tests/other/adapters/karaf/karaf3/pom.xml | 2 +- .../tests/other/adapters/karaf/pom.xml | 2 +- .../tests/other/adapters/pom.xml | 2 +- .../tests/other/adapters/was/pom.xml | 2 +- .../tests/other/adapters/was/was8/pom.xml | 2 +- .../tests/other/adapters/wls/pom.xml | 2 +- .../tests/other/adapters/wls/wls12/pom.xml | 2 +- .../tests/other/base-ui/pom.xml | 2 +- .../tests/other/jpa-performance/pom.xml | 2 +- .../tests/other/mod_auth_mellon/pom.xml | 2 +- testsuite/integration-arquillian/tests/other/pom.xml | 2 +- .../tests/other/springboot-tests/pom.xml | 2 +- .../integration-arquillian/tests/other/sssd/pom.xml | 2 +- .../tests/other/webauthn/pom.xml | 2 +- testsuite/integration-arquillian/tests/pom.xml | 2 +- testsuite/integration-arquillian/util/pom.xml | 2 +- testsuite/model/pom.xml | 2 +- testsuite/pom.xml | 2 +- testsuite/utils/pom.xml | 2 +- themes/pom.xml | 2 +- util/embedded-ldap/pom.xml | 2 +- util/pom.xml | 2 +- 254 files changed, 267 insertions(+), 267 deletions(-) diff --git a/.github/workflows/js-ci.yml b/.github/workflows/js-ci.yml index 38ae8315db57..bccca7ac81b1 100644 --- a/.github/workflows/js-ci.yml +++ b/.github/workflows/js-ci.yml @@ -54,13 +54,13 @@ jobs: - name: Build Keycloak run: | ./mvnw clean install --errors -DskipTests -DskipTestsuite -DskipExamples -Pdistribution - mv ./quarkus/dist/target/keycloak-999.0.0-SNAPSHOT.tar.gz ./keycloak-999.0.0-SNAPSHOT.tar.gz + mv ./quarkus/dist/target/keycloak-24.0.5-PS-1-SNAPSHOT.tar.gz ./keycloak-24.0.5-PS-1-SNAPSHOT.tar.gz - name: Upload Keycloak dist uses: actions/upload-artifact@v3 with: name: keycloak - path: keycloak-999.0.0-SNAPSHOT.tar.gz + path: keycloak-24.0.5-PS-1-SNAPSHOT.tar.gz admin-client: name: Admin Client @@ -214,8 +214,8 @@ jobs: - 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=transient-users &> ~/server.log & + tar xfvz keycloak-24.0.5-PS-1-SNAPSHOT.tar.gz + keycloak-24.0.5-PS-1-SNAPSHOT/bin/kc.sh start-dev --features=transient-users &> ~/server.log & env: KEYCLOAK_ADMIN: admin KEYCLOAK_ADMIN_PASSWORD: admin @@ -297,8 +297,8 @@ jobs: - 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,transient-users &> ~/server.log & + tar xfvz keycloak-24.0.5-PS-1-SNAPSHOT.tar.gz + keycloak-24.0.5-PS-1-SNAPSHOT/bin/kc.sh start-dev --features=admin-fine-grained-authz,transient-users &> ~/server.log & env: KEYCLOAK_ADMIN: admin KEYCLOAK_ADMIN_PASSWORD: admin diff --git a/adapters/oidc/adapter-core/pom.xml b/adapters/oidc/adapter-core/pom.xml index 98c8f1103eeb..89a1259510de 100755 --- a/adapters/oidc/adapter-core/pom.xml +++ b/adapters/oidc/adapter-core/pom.xml @@ -21,7 +21,7 @@ keycloak-parent org.keycloak - 24.0.5 + 24.0.5-PS-1-SNAPSHOT ../../../pom.xml 4.0.0 diff --git a/adapters/oidc/installed/pom.xml b/adapters/oidc/installed/pom.xml index 663ce2ab7c00..7f9a4489466f 100755 --- a/adapters/oidc/installed/pom.xml +++ b/adapters/oidc/installed/pom.xml @@ -21,7 +21,7 @@ keycloak-parent org.keycloak - 24.0.5 + 24.0.5-PS-1-SNAPSHOT ../../../pom.xml 4.0.0 diff --git a/adapters/oidc/jakarta-servlet-filter/pom.xml b/adapters/oidc/jakarta-servlet-filter/pom.xml index 2927ea3dc583..2a8af0f47d28 100755 --- a/adapters/oidc/jakarta-servlet-filter/pom.xml +++ b/adapters/oidc/jakarta-servlet-filter/pom.xml @@ -21,7 +21,7 @@ keycloak-parent org.keycloak - 24.0.5 + 24.0.5-PS-1-SNAPSHOT ../../../pom.xml 4.0.0 diff --git a/adapters/oidc/jaxrs-oauth-client/pom.xml b/adapters/oidc/jaxrs-oauth-client/pom.xml index 803dc6d86a25..abc1d20b77b4 100755 --- a/adapters/oidc/jaxrs-oauth-client/pom.xml +++ b/adapters/oidc/jaxrs-oauth-client/pom.xml @@ -21,7 +21,7 @@ keycloak-parent org.keycloak - 24.0.5 + 24.0.5-PS-1-SNAPSHOT ../../../pom.xml 4.0.0 diff --git a/adapters/oidc/jetty/jetty-core/pom.xml b/adapters/oidc/jetty/jetty-core/pom.xml index 265589ab9184..c4bbd5857417 100755 --- a/adapters/oidc/jetty/jetty-core/pom.xml +++ b/adapters/oidc/jetty/jetty-core/pom.xml @@ -21,7 +21,7 @@ keycloak-parent org.keycloak - 24.0.5 + 24.0.5-PS-1-SNAPSHOT ../../../../pom.xml 4.0.0 diff --git a/adapters/oidc/jetty/jetty9.4/pom.xml b/adapters/oidc/jetty/jetty9.4/pom.xml index 32107f3ae6e3..ec410b28d3aa 100644 --- a/adapters/oidc/jetty/jetty9.4/pom.xml +++ b/adapters/oidc/jetty/jetty9.4/pom.xml @@ -21,7 +21,7 @@ keycloak-parent org.keycloak - 24.0.5 + 24.0.5-PS-1-SNAPSHOT ../../../../pom.xml 4.0.0 diff --git a/adapters/oidc/jetty/pom.xml b/adapters/oidc/jetty/pom.xml index f7db7183aeba..ed84cd7bf4bc 100755 --- a/adapters/oidc/jetty/pom.xml +++ b/adapters/oidc/jetty/pom.xml @@ -20,7 +20,7 @@ keycloak-parent org.keycloak - 24.0.5 + 24.0.5-PS-1-SNAPSHOT ../../../pom.xml Keycloak Jetty Integration diff --git a/adapters/oidc/js/pom.xml b/adapters/oidc/js/pom.xml index 239530971ff9..fd3f64843841 100644 --- a/adapters/oidc/js/pom.xml +++ b/adapters/oidc/js/pom.xml @@ -5,7 +5,7 @@ keycloak-parent org.keycloak - 24.0.5 + 24.0.5-PS-1-SNAPSHOT ../../../pom.xml diff --git a/adapters/oidc/pom.xml b/adapters/oidc/pom.xml index 9a9bf517edae..dd5e34267cf3 100755 --- a/adapters/oidc/pom.xml +++ b/adapters/oidc/pom.xml @@ -20,7 +20,7 @@ keycloak-parent org.keycloak - 24.0.5 + 24.0.5-PS-1-SNAPSHOT ../../pom.xml Keycloak OIDC Client Adapter Modules diff --git a/adapters/oidc/servlet-filter/pom.xml b/adapters/oidc/servlet-filter/pom.xml index 78eff3edfff7..ea4055505fa1 100755 --- a/adapters/oidc/servlet-filter/pom.xml +++ b/adapters/oidc/servlet-filter/pom.xml @@ -21,7 +21,7 @@ keycloak-parent org.keycloak - 24.0.5 + 24.0.5-PS-1-SNAPSHOT ../../../pom.xml 4.0.0 diff --git a/adapters/oidc/spring-boot-adapter-core/pom.xml b/adapters/oidc/spring-boot-adapter-core/pom.xml index 77b6884e0742..169a3afb9278 100755 --- a/adapters/oidc/spring-boot-adapter-core/pom.xml +++ b/adapters/oidc/spring-boot-adapter-core/pom.xml @@ -21,7 +21,7 @@ keycloak-parent org.keycloak - 24.0.5 + 24.0.5-PS-1-SNAPSHOT ../../../pom.xml 4.0.0 diff --git a/adapters/oidc/spring-boot-container-bundle/pom.xml b/adapters/oidc/spring-boot-container-bundle/pom.xml index 8b1675dbf164..3fc5bca0cf7e 100644 --- a/adapters/oidc/spring-boot-container-bundle/pom.xml +++ b/adapters/oidc/spring-boot-container-bundle/pom.xml @@ -4,7 +4,7 @@ keycloak-parent org.keycloak - 24.0.5 + 24.0.5-PS-1-SNAPSHOT ../../../pom.xml spring-boot-container-bundle diff --git a/adapters/oidc/spring-boot2/pom.xml b/adapters/oidc/spring-boot2/pom.xml index 5d9f1d495e30..431e9349fd0a 100755 --- a/adapters/oidc/spring-boot2/pom.xml +++ b/adapters/oidc/spring-boot2/pom.xml @@ -21,7 +21,7 @@ keycloak-parent org.keycloak - 24.0.5 + 24.0.5-PS-1-SNAPSHOT ../../../pom.xml 4.0.0 diff --git a/adapters/oidc/spring-security/pom.xml b/adapters/oidc/spring-security/pom.xml index 9ccd5ad9584d..f5b3d7306b55 100644 --- a/adapters/oidc/spring-security/pom.xml +++ b/adapters/oidc/spring-security/pom.xml @@ -21,7 +21,7 @@ keycloak-parent org.keycloak - 24.0.5 + 24.0.5-PS-1-SNAPSHOT ../../../pom.xml 4.0.0 diff --git a/adapters/oidc/tomcat/pom.xml b/adapters/oidc/tomcat/pom.xml index 111f9ac4268d..94dab66fbae3 100755 --- a/adapters/oidc/tomcat/pom.xml +++ b/adapters/oidc/tomcat/pom.xml @@ -20,7 +20,7 @@ keycloak-parent org.keycloak - 24.0.5 + 24.0.5-PS-1-SNAPSHOT ../../../pom.xml Keycloak Tomcat Integration diff --git a/adapters/oidc/tomcat/tomcat-core/pom.xml b/adapters/oidc/tomcat/tomcat-core/pom.xml index 44efbeae2879..093cea04c170 100755 --- a/adapters/oidc/tomcat/tomcat-core/pom.xml +++ b/adapters/oidc/tomcat/tomcat-core/pom.xml @@ -21,7 +21,7 @@ keycloak-tomcat-integration-pom org.keycloak - 24.0.5 + 24.0.5-PS-1-SNAPSHOT ../pom.xml 4.0.0 diff --git a/adapters/oidc/tomcat/tomcat/pom.xml b/adapters/oidc/tomcat/tomcat/pom.xml index ab5440f4f80e..6bf5b31c54bd 100755 --- a/adapters/oidc/tomcat/tomcat/pom.xml +++ b/adapters/oidc/tomcat/tomcat/pom.xml @@ -21,7 +21,7 @@ keycloak-tomcat-integration-pom org.keycloak - 24.0.5 + 24.0.5-PS-1-SNAPSHOT ../pom.xml 4.0.0 diff --git a/adapters/oidc/undertow/pom.xml b/adapters/oidc/undertow/pom.xml index e5c78bd41c9b..b2b131d2fd70 100755 --- a/adapters/oidc/undertow/pom.xml +++ b/adapters/oidc/undertow/pom.xml @@ -21,7 +21,7 @@ keycloak-parent org.keycloak - 24.0.5 + 24.0.5-PS-1-SNAPSHOT ../../../pom.xml 4.0.0 diff --git a/adapters/oidc/wildfly-elytron/pom.xml b/adapters/oidc/wildfly-elytron/pom.xml index 67e1b508db5e..75623c12dd22 100755 --- a/adapters/oidc/wildfly-elytron/pom.xml +++ b/adapters/oidc/wildfly-elytron/pom.xml @@ -22,7 +22,7 @@ keycloak-parent org.keycloak - 24.0.5 + 24.0.5-PS-1-SNAPSHOT ../../../pom.xml 4.0.0 diff --git a/adapters/oidc/wildfly/pom.xml b/adapters/oidc/wildfly/pom.xml index f97c86702747..3e39e19b9583 100755 --- a/adapters/oidc/wildfly/pom.xml +++ b/adapters/oidc/wildfly/pom.xml @@ -20,7 +20,7 @@ keycloak-parent org.keycloak - 24.0.5 + 24.0.5-PS-1-SNAPSHOT ../../../pom.xml Keycloak WildFly Integration diff --git a/adapters/oidc/wildfly/wildfly-subsystem/pom.xml b/adapters/oidc/wildfly/wildfly-subsystem/pom.xml index 374a99212a92..aca4d48e4022 100755 --- a/adapters/oidc/wildfly/wildfly-subsystem/pom.xml +++ b/adapters/oidc/wildfly/wildfly-subsystem/pom.xml @@ -21,7 +21,7 @@ org.keycloak keycloak-parent - 24.0.5 + 24.0.5-PS-1-SNAPSHOT ../../../../pom.xml diff --git a/adapters/pom.xml b/adapters/pom.xml index b3be935dc570..b6ec5c8bd15a 100755 --- a/adapters/pom.xml +++ b/adapters/pom.xml @@ -20,7 +20,7 @@ keycloak-parent org.keycloak - 24.0.5 + 24.0.5-PS-1-SNAPSHOT ../pom.xml Keycloak Adapters diff --git a/adapters/saml/core-jakarta/pom.xml b/adapters/saml/core-jakarta/pom.xml index 7e84cd087a7f..7cde93d24501 100644 --- a/adapters/saml/core-jakarta/pom.xml +++ b/adapters/saml/core-jakarta/pom.xml @@ -6,7 +6,7 @@ keycloak-parent org.keycloak - 24.0.5 + 24.0.5-PS-1-SNAPSHOT ../../../pom.xml diff --git a/adapters/saml/core-public/pom.xml b/adapters/saml/core-public/pom.xml index 4a5dc064e031..23727e3ca9d8 100755 --- a/adapters/saml/core-public/pom.xml +++ b/adapters/saml/core-public/pom.xml @@ -21,7 +21,7 @@ keycloak-parent org.keycloak - 24.0.5 + 24.0.5-PS-1-SNAPSHOT ../../../pom.xml 4.0.0 diff --git a/adapters/saml/core/pom.xml b/adapters/saml/core/pom.xml index 1d2e67fc39e7..fa5a8c8e7427 100755 --- a/adapters/saml/core/pom.xml +++ b/adapters/saml/core/pom.xml @@ -21,7 +21,7 @@ keycloak-parent org.keycloak - 24.0.5 + 24.0.5-PS-1-SNAPSHOT ../../../pom.xml 4.0.0 diff --git a/adapters/saml/jakarta-servlet-filter/pom.xml b/adapters/saml/jakarta-servlet-filter/pom.xml index 1deea262f7ff..2b4a40a9624c 100755 --- a/adapters/saml/jakarta-servlet-filter/pom.xml +++ b/adapters/saml/jakarta-servlet-filter/pom.xml @@ -21,7 +21,7 @@ keycloak-parent org.keycloak - 24.0.5 + 24.0.5-PS-1-SNAPSHOT ../../../pom.xml 4.0.0 diff --git a/adapters/saml/jetty/jetty-core/pom.xml b/adapters/saml/jetty/jetty-core/pom.xml index a849b825f709..81e6f9173965 100755 --- a/adapters/saml/jetty/jetty-core/pom.xml +++ b/adapters/saml/jetty/jetty-core/pom.xml @@ -21,7 +21,7 @@ keycloak-parent org.keycloak - 24.0.5 + 24.0.5-PS-1-SNAPSHOT ../../../../pom.xml 4.0.0 diff --git a/adapters/saml/jetty/jetty9.4/pom.xml b/adapters/saml/jetty/jetty9.4/pom.xml index be927cb1009a..49dc9af6cf17 100644 --- a/adapters/saml/jetty/jetty9.4/pom.xml +++ b/adapters/saml/jetty/jetty9.4/pom.xml @@ -21,7 +21,7 @@ keycloak-parent org.keycloak - 24.0.5 + 24.0.5-PS-1-SNAPSHOT ../../../../pom.xml 4.0.0 diff --git a/adapters/saml/jetty/pom.xml b/adapters/saml/jetty/pom.xml index d02d240b4981..6e7ab90d8d08 100755 --- a/adapters/saml/jetty/pom.xml +++ b/adapters/saml/jetty/pom.xml @@ -20,7 +20,7 @@ keycloak-parent org.keycloak - 24.0.5 + 24.0.5-PS-1-SNAPSHOT ../../../pom.xml Keycloak SAML Jetty Integration diff --git a/adapters/saml/pom.xml b/adapters/saml/pom.xml index 22ffb0c623a1..74b17ed0c9a4 100755 --- a/adapters/saml/pom.xml +++ b/adapters/saml/pom.xml @@ -20,7 +20,7 @@ keycloak-parent org.keycloak - 24.0.5 + 24.0.5-PS-1-SNAPSHOT ../../pom.xml Keycloak SAML Client Adapter Modules diff --git a/adapters/saml/servlet-filter/pom.xml b/adapters/saml/servlet-filter/pom.xml index 385d9ab3b589..d8948d331b6a 100755 --- a/adapters/saml/servlet-filter/pom.xml +++ b/adapters/saml/servlet-filter/pom.xml @@ -21,7 +21,7 @@ keycloak-parent org.keycloak - 24.0.5 + 24.0.5-PS-1-SNAPSHOT ../../../pom.xml 4.0.0 diff --git a/adapters/saml/tomcat/pom.xml b/adapters/saml/tomcat/pom.xml index ad79e20cb08b..787827a5891c 100755 --- a/adapters/saml/tomcat/pom.xml +++ b/adapters/saml/tomcat/pom.xml @@ -20,7 +20,7 @@ keycloak-parent org.keycloak - 24.0.5 + 24.0.5-PS-1-SNAPSHOT ../../../pom.xml Keycloak SAML Tomcat Integration diff --git a/adapters/saml/tomcat/tomcat-core/pom.xml b/adapters/saml/tomcat/tomcat-core/pom.xml index 505f93971efe..a04e22e6f3ff 100755 --- a/adapters/saml/tomcat/tomcat-core/pom.xml +++ b/adapters/saml/tomcat/tomcat-core/pom.xml @@ -21,7 +21,7 @@ keycloak-saml-tomcat-integration-pom org.keycloak - 24.0.5 + 24.0.5-PS-1-SNAPSHOT ../pom.xml 4.0.0 diff --git a/adapters/saml/tomcat/tomcat/pom.xml b/adapters/saml/tomcat/tomcat/pom.xml index db02a09eeb61..64e0729ecd46 100755 --- a/adapters/saml/tomcat/tomcat/pom.xml +++ b/adapters/saml/tomcat/tomcat/pom.xml @@ -21,7 +21,7 @@ keycloak-saml-tomcat-integration-pom org.keycloak - 24.0.5 + 24.0.5-PS-1-SNAPSHOT ../pom.xml 4.0.0 diff --git a/adapters/saml/undertow/pom.xml b/adapters/saml/undertow/pom.xml index 79776568f129..d6b47cdac671 100755 --- a/adapters/saml/undertow/pom.xml +++ b/adapters/saml/undertow/pom.xml @@ -21,7 +21,7 @@ keycloak-parent org.keycloak - 24.0.5 + 24.0.5-PS-1-SNAPSHOT ../../../pom.xml 4.0.0 diff --git a/adapters/saml/wildfly-elytron-jakarta/pom.xml b/adapters/saml/wildfly-elytron-jakarta/pom.xml index 46a73c4d71d2..e527b1a2ace5 100755 --- a/adapters/saml/wildfly-elytron-jakarta/pom.xml +++ b/adapters/saml/wildfly-elytron-jakarta/pom.xml @@ -21,7 +21,7 @@ keycloak-parent org.keycloak - 24.0.5 + 24.0.5-PS-1-SNAPSHOT ../../../pom.xml 4.0.0 diff --git a/adapters/saml/wildfly-elytron/pom.xml b/adapters/saml/wildfly-elytron/pom.xml index 647588752094..0d446a5e5404 100755 --- a/adapters/saml/wildfly-elytron/pom.xml +++ b/adapters/saml/wildfly-elytron/pom.xml @@ -21,7 +21,7 @@ keycloak-parent org.keycloak - 24.0.5 + 24.0.5-PS-1-SNAPSHOT ../../../pom.xml 4.0.0 diff --git a/adapters/saml/wildfly/pom.xml b/adapters/saml/wildfly/pom.xml index 54ea545ebd51..23af66ce9d54 100755 --- a/adapters/saml/wildfly/pom.xml +++ b/adapters/saml/wildfly/pom.xml @@ -20,7 +20,7 @@ keycloak-parent org.keycloak - 24.0.5 + 24.0.5-PS-1-SNAPSHOT ../../../pom.xml Keycloak SAML Wildfly Integration diff --git a/adapters/saml/wildfly/wildfly-jakarta-subsystem/pom.xml b/adapters/saml/wildfly/wildfly-jakarta-subsystem/pom.xml index c19ad9284602..1201be853a1c 100755 --- a/adapters/saml/wildfly/wildfly-jakarta-subsystem/pom.xml +++ b/adapters/saml/wildfly/wildfly-jakarta-subsystem/pom.xml @@ -21,7 +21,7 @@ org.keycloak keycloak-parent - 24.0.5 + 24.0.5-PS-1-SNAPSHOT ../../../../pom.xml diff --git a/adapters/saml/wildfly/wildfly-subsystem/pom.xml b/adapters/saml/wildfly/wildfly-subsystem/pom.xml index 66a5593fec9c..4ae82e5d9aad 100755 --- a/adapters/saml/wildfly/wildfly-subsystem/pom.xml +++ b/adapters/saml/wildfly/wildfly-subsystem/pom.xml @@ -21,7 +21,7 @@ org.keycloak keycloak-parent - 24.0.5 + 24.0.5-PS-1-SNAPSHOT ../../../../pom.xml diff --git a/adapters/spi/adapter-spi/pom.xml b/adapters/spi/adapter-spi/pom.xml index cf3996c05e85..429f06b92f2f 100755 --- a/adapters/spi/adapter-spi/pom.xml +++ b/adapters/spi/adapter-spi/pom.xml @@ -21,7 +21,7 @@ keycloak-parent org.keycloak - 24.0.5 + 24.0.5-PS-1-SNAPSHOT ../../../pom.xml 4.0.0 diff --git a/adapters/spi/jakarta-servlet-adapter-spi/pom.xml b/adapters/spi/jakarta-servlet-adapter-spi/pom.xml index 24caa4ed7578..de784d65e61f 100755 --- a/adapters/spi/jakarta-servlet-adapter-spi/pom.xml +++ b/adapters/spi/jakarta-servlet-adapter-spi/pom.xml @@ -21,7 +21,7 @@ keycloak-parent org.keycloak - 24.0.5 + 24.0.5-PS-1-SNAPSHOT ../../../pom.xml 4.0.0 diff --git a/adapters/spi/jboss-adapter-core/pom.xml b/adapters/spi/jboss-adapter-core/pom.xml index 87dd226c548b..8ed8428b29bb 100755 --- a/adapters/spi/jboss-adapter-core/pom.xml +++ b/adapters/spi/jboss-adapter-core/pom.xml @@ -21,7 +21,7 @@ keycloak-parent org.keycloak - 24.0.5 + 24.0.5-PS-1-SNAPSHOT ../../../pom.xml 4.0.0 diff --git a/adapters/spi/jetty-adapter-spi/pom.xml b/adapters/spi/jetty-adapter-spi/pom.xml index 128079d12f0b..429d325016d9 100755 --- a/adapters/spi/jetty-adapter-spi/pom.xml +++ b/adapters/spi/jetty-adapter-spi/pom.xml @@ -21,7 +21,7 @@ keycloak-parent org.keycloak - 24.0.5 + 24.0.5-PS-1-SNAPSHOT ../../../pom.xml 4.0.0 diff --git a/adapters/spi/pom.xml b/adapters/spi/pom.xml index 83426f598757..b3ed887f0a87 100755 --- a/adapters/spi/pom.xml +++ b/adapters/spi/pom.xml @@ -20,7 +20,7 @@ keycloak-parent org.keycloak - 24.0.5 + 24.0.5-PS-1-SNAPSHOT ../../pom.xml Keycloak Client Adapter SPI Modules diff --git a/adapters/spi/servlet-adapter-spi/pom.xml b/adapters/spi/servlet-adapter-spi/pom.xml index f463a3b92f31..cd0c2a26d531 100755 --- a/adapters/spi/servlet-adapter-spi/pom.xml +++ b/adapters/spi/servlet-adapter-spi/pom.xml @@ -21,7 +21,7 @@ keycloak-parent org.keycloak - 24.0.5 + 24.0.5-PS-1-SNAPSHOT ../../../pom.xml 4.0.0 diff --git a/adapters/spi/tomcat-adapter-spi/pom.xml b/adapters/spi/tomcat-adapter-spi/pom.xml index 904fc2053870..662bed713c66 100755 --- a/adapters/spi/tomcat-adapter-spi/pom.xml +++ b/adapters/spi/tomcat-adapter-spi/pom.xml @@ -21,7 +21,7 @@ keycloak-parent org.keycloak - 24.0.5 + 24.0.5-PS-1-SNAPSHOT ../../../pom.xml 4.0.0 diff --git a/adapters/spi/undertow-adapter-spi/pom.xml b/adapters/spi/undertow-adapter-spi/pom.xml index a4a91e338505..1855a2e41439 100755 --- a/adapters/spi/undertow-adapter-spi/pom.xml +++ b/adapters/spi/undertow-adapter-spi/pom.xml @@ -21,7 +21,7 @@ keycloak-parent org.keycloak - 24.0.5 + 24.0.5-PS-1-SNAPSHOT ../../../pom.xml 4.0.0 diff --git a/authz/client/pom.xml b/authz/client/pom.xml index f8b16c6b7fce..6ac014cb57a9 100644 --- a/authz/client/pom.xml +++ b/authz/client/pom.xml @@ -7,7 +7,7 @@ org.keycloak keycloak-authz-parent - 24.0.5 + 24.0.5-PS-1-SNAPSHOT ../pom.xml diff --git a/authz/policy-enforcer/pom.xml b/authz/policy-enforcer/pom.xml index 63d64e2beea4..2e6f1bde220b 100755 --- a/authz/policy-enforcer/pom.xml +++ b/authz/policy-enforcer/pom.xml @@ -21,7 +21,7 @@ org.keycloak keycloak-authz-parent - 24.0.5 + 24.0.5-PS-1-SNAPSHOT ../pom.xml 4.0.0 diff --git a/authz/policy/common/pom.xml b/authz/policy/common/pom.xml index 8bd9a5022abb..3959d9936531 100644 --- a/authz/policy/common/pom.xml +++ b/authz/policy/common/pom.xml @@ -25,7 +25,7 @@ org.keycloak keycloak-authz-provider-parent - 24.0.5 + 24.0.5-PS-1-SNAPSHOT ../pom.xml diff --git a/authz/policy/pom.xml b/authz/policy/pom.xml index cae95a5a23e2..2ac7eaf76a31 100644 --- a/authz/policy/pom.xml +++ b/authz/policy/pom.xml @@ -7,7 +7,7 @@ org.keycloak keycloak-authz-parent - 24.0.5 + 24.0.5-PS-1-SNAPSHOT ../pom.xml diff --git a/authz/pom.xml b/authz/pom.xml index 477c28ecd3ff..cb19c3618caa 100644 --- a/authz/pom.xml +++ b/authz/pom.xml @@ -7,7 +7,7 @@ org.keycloak keycloak-parent - 24.0.5 + 24.0.5-PS-1-SNAPSHOT ../pom.xml diff --git a/boms/adapter/pom.xml b/boms/adapter/pom.xml index f1fac6ff25a6..0e8f4cecc805 100644 --- a/boms/adapter/pom.xml +++ b/boms/adapter/pom.xml @@ -22,7 +22,7 @@ org.keycloak.bom keycloak-bom-parent - 24.0.5 + 24.0.5-PS-1-SNAPSHOT org.keycloak.bom diff --git a/boms/misc/pom.xml b/boms/misc/pom.xml index 47b917d59b73..dda6203e52d5 100644 --- a/boms/misc/pom.xml +++ b/boms/misc/pom.xml @@ -22,7 +22,7 @@ org.keycloak.bom keycloak-bom-parent - 24.0.5 + 24.0.5-PS-1-SNAPSHOT org.keycloak.bom diff --git a/boms/pom.xml b/boms/pom.xml index 00d48c91fa0c..2128b3500f46 100644 --- a/boms/pom.xml +++ b/boms/pom.xml @@ -27,7 +27,7 @@ org.keycloak.bom keycloak-bom-parent - 24.0.5 + 24.0.5-PS-1-SNAPSHOT pom diff --git a/boms/spi/pom.xml b/boms/spi/pom.xml index e118390570c0..92f79f7ce9e7 100644 --- a/boms/spi/pom.xml +++ b/boms/spi/pom.xml @@ -23,7 +23,7 @@ org.keycloak.bom keycloak-bom-parent - 24.0.5 + 24.0.5-PS-1-SNAPSHOT org.keycloak.bom diff --git a/common/pom.xml b/common/pom.xml index a9c4cb09e558..c1c1cef5e11a 100755 --- a/common/pom.xml +++ b/common/pom.xml @@ -21,7 +21,7 @@ keycloak-parent org.keycloak - 24.0.5 + 24.0.5-PS-1-SNAPSHOT ../pom.xml 4.0.0 diff --git a/core/pom.xml b/core/pom.xml index b63dcee53711..dad4966f1552 100755 --- a/core/pom.xml +++ b/core/pom.xml @@ -21,7 +21,7 @@ keycloak-parent org.keycloak - 24.0.5 + 24.0.5-PS-1-SNAPSHOT ../pom.xml 4.0.0 diff --git a/crypto/default/pom.xml b/crypto/default/pom.xml index 9d60cf92b7a9..ede21f625137 100644 --- a/crypto/default/pom.xml +++ b/crypto/default/pom.xml @@ -21,7 +21,7 @@ keycloak-crypto-parent org.keycloak - 24.0.5 + 24.0.5-PS-1-SNAPSHOT ../pom.xml 4.0.0 diff --git a/crypto/elytron/pom.xml b/crypto/elytron/pom.xml index 499551674b64..193c6aba6939 100644 --- a/crypto/elytron/pom.xml +++ b/crypto/elytron/pom.xml @@ -21,7 +21,7 @@ keycloak-crypto-parent org.keycloak - 24.0.5 + 24.0.5-PS-1-SNAPSHOT ../pom.xml 4.0.0 diff --git a/crypto/fips1402/pom.xml b/crypto/fips1402/pom.xml index 9a53632f0e22..4df6a949a666 100644 --- a/crypto/fips1402/pom.xml +++ b/crypto/fips1402/pom.xml @@ -21,7 +21,7 @@ keycloak-crypto-parent org.keycloak - 24.0.5 + 24.0.5-PS-1-SNAPSHOT ../pom.xml 4.0.0 diff --git a/crypto/pom.xml b/crypto/pom.xml index 2b0ed4d2bba9..a74e5609ea81 100644 --- a/crypto/pom.xml +++ b/crypto/pom.xml @@ -20,7 +20,7 @@ keycloak-parent org.keycloak - 24.0.5 + 24.0.5-PS-1-SNAPSHOT ../pom.xml Keycloak Crypto Parent diff --git a/dependencies/pom.xml b/dependencies/pom.xml index 66f60000ad34..db69d83ceba9 100755 --- a/dependencies/pom.xml +++ b/dependencies/pom.xml @@ -21,7 +21,7 @@ keycloak-parent org.keycloak - 24.0.5 + 24.0.5-PS-1-SNAPSHOT 4.0.0 diff --git a/dependencies/server-all/pom.xml b/dependencies/server-all/pom.xml index 5d711bb37a17..88136b3a9cc1 100755 --- a/dependencies/server-all/pom.xml +++ b/dependencies/server-all/pom.xml @@ -21,7 +21,7 @@ keycloak-dependencies-parent org.keycloak - 24.0.5 + 24.0.5-PS-1-SNAPSHOT 4.0.0 diff --git a/dependencies/server-min/pom.xml b/dependencies/server-min/pom.xml index 4ab8a7db3c15..2094ba58cea8 100755 --- a/dependencies/server-min/pom.xml +++ b/dependencies/server-min/pom.xml @@ -21,7 +21,7 @@ keycloak-dependencies-parent org.keycloak - 24.0.5 + 24.0.5-PS-1-SNAPSHOT 4.0.0 diff --git a/distribution/adapters/pom.xml b/distribution/adapters/pom.xml index 729fb2edf1fb..f8b49affbb9d 100755 --- a/distribution/adapters/pom.xml +++ b/distribution/adapters/pom.xml @@ -20,7 +20,7 @@ keycloak-distribution-parent org.keycloak - 24.0.5 + 24.0.5-PS-1-SNAPSHOT Adapters Distribution Parent diff --git a/distribution/adapters/tomcat-adapter-zip/pom.xml b/distribution/adapters/tomcat-adapter-zip/pom.xml index 9a47ce58ba59..c3e413a2b55c 100755 --- a/distribution/adapters/tomcat-adapter-zip/pom.xml +++ b/distribution/adapters/tomcat-adapter-zip/pom.xml @@ -21,7 +21,7 @@ keycloak-parent org.keycloak - 24.0.5 + 24.0.5-PS-1-SNAPSHOT ../../../pom.xml diff --git a/distribution/adapters/wildfly-adapter/pom.xml b/distribution/adapters/wildfly-adapter/pom.xml index 0bd83e1d89bb..07821961f606 100644 --- a/distribution/adapters/wildfly-adapter/pom.xml +++ b/distribution/adapters/wildfly-adapter/pom.xml @@ -21,7 +21,7 @@ keycloak-adapters-distribution-parent org.keycloak - 24.0.5 + 24.0.5-PS-1-SNAPSHOT diff --git a/distribution/api-docs-dist/pom.xml b/distribution/api-docs-dist/pom.xml index 1170c0555a7a..2e7c8295c7d5 100755 --- a/distribution/api-docs-dist/pom.xml +++ b/distribution/api-docs-dist/pom.xml @@ -21,7 +21,7 @@ keycloak-distribution-parent org.keycloak - 24.0.5 + 24.0.5-PS-1-SNAPSHOT keycloak-api-docs-dist diff --git a/distribution/downloads/pom.xml b/distribution/downloads/pom.xml index e676e1792512..73d8c4fa12fb 100755 --- a/distribution/downloads/pom.xml +++ b/distribution/downloads/pom.xml @@ -21,7 +21,7 @@ keycloak-distribution-parent org.keycloak - 24.0.5 + 24.0.5-PS-1-SNAPSHOT keycloak-dist-downloads diff --git a/distribution/feature-packs/adapter-feature-pack/pom.xml b/distribution/feature-packs/adapter-feature-pack/pom.xml index 67d8050c9e86..3eb9078d0c95 100755 --- a/distribution/feature-packs/adapter-feature-pack/pom.xml +++ b/distribution/feature-packs/adapter-feature-pack/pom.xml @@ -19,7 +19,7 @@ org.keycloak feature-packs-parent - 24.0.5 + 24.0.5-PS-1-SNAPSHOT diff --git a/distribution/feature-packs/pom.xml b/distribution/feature-packs/pom.xml index 70fcce625843..d002f024fd9e 100644 --- a/distribution/feature-packs/pom.xml +++ b/distribution/feature-packs/pom.xml @@ -20,7 +20,7 @@ keycloak-distribution-parent org.keycloak - 24.0.5 + 24.0.5-PS-1-SNAPSHOT Feature Pack Builds diff --git a/distribution/galleon-feature-packs/pom.xml b/distribution/galleon-feature-packs/pom.xml index 411a7d9035f2..d3745ee5a088 100644 --- a/distribution/galleon-feature-packs/pom.xml +++ b/distribution/galleon-feature-packs/pom.xml @@ -20,7 +20,7 @@ keycloak-distribution-parent org.keycloak - 24.0.5 + 24.0.5-PS-1-SNAPSHOT Galleon Feature Pack Builds diff --git a/distribution/galleon-feature-packs/saml-adapter-galleon-pack-layer-metadata-tests/pom.xml b/distribution/galleon-feature-packs/saml-adapter-galleon-pack-layer-metadata-tests/pom.xml index 6bcff79a6c84..f1b2798e1bfa 100644 --- a/distribution/galleon-feature-packs/saml-adapter-galleon-pack-layer-metadata-tests/pom.xml +++ b/distribution/galleon-feature-packs/saml-adapter-galleon-pack-layer-metadata-tests/pom.xml @@ -19,7 +19,7 @@ org.keycloak galleon-feature-packs-parent - 24.0.5 + 24.0.5-PS-1-SNAPSHOT 4.0.0 diff --git a/distribution/galleon-feature-packs/saml-adapter-galleon-pack/pom.xml b/distribution/galleon-feature-packs/saml-adapter-galleon-pack/pom.xml index a0bf0e24bd28..7fbc3269e431 100644 --- a/distribution/galleon-feature-packs/saml-adapter-galleon-pack/pom.xml +++ b/distribution/galleon-feature-packs/saml-adapter-galleon-pack/pom.xml @@ -19,7 +19,7 @@ org.keycloak galleon-feature-packs-parent - 24.0.5 + 24.0.5-PS-1-SNAPSHOT 4.0.0 diff --git a/distribution/licenses-common/pom.xml b/distribution/licenses-common/pom.xml index f42352601d99..9d4aeb89cbf9 100644 --- a/distribution/licenses-common/pom.xml +++ b/distribution/licenses-common/pom.xml @@ -20,7 +20,7 @@ keycloak-distribution-parent org.keycloak - 24.0.5 + 24.0.5-PS-1-SNAPSHOT keycloak-distribution-licenses-common diff --git a/distribution/maven-plugins/licenses-processor/pom.xml b/distribution/maven-plugins/licenses-processor/pom.xml index 198f2952a96d..af2b0ae20aca 100644 --- a/distribution/maven-plugins/licenses-processor/pom.xml +++ b/distribution/maven-plugins/licenses-processor/pom.xml @@ -20,7 +20,7 @@ keycloak-distribution-maven-plugins-parent org.keycloak - 24.0.5 + 24.0.5-PS-1-SNAPSHOT keycloak-distribution-licenses-maven-plugin diff --git a/distribution/maven-plugins/pom.xml b/distribution/maven-plugins/pom.xml index 277bdd07691e..20c227310458 100644 --- a/distribution/maven-plugins/pom.xml +++ b/distribution/maven-plugins/pom.xml @@ -20,7 +20,7 @@ keycloak-distribution-parent org.keycloak - 24.0.5 + 24.0.5-PS-1-SNAPSHOT keycloak-distribution-maven-plugins-parent diff --git a/distribution/pom.xml b/distribution/pom.xml index 8f87bc041018..1651604ceb10 100755 --- a/distribution/pom.xml +++ b/distribution/pom.xml @@ -20,7 +20,7 @@ keycloak-parent org.keycloak - 24.0.5 + 24.0.5-PS-1-SNAPSHOT ../pom.xml diff --git a/distribution/saml-adapters/pom.xml b/distribution/saml-adapters/pom.xml index 622619c6818a..17f30ea574f9 100755 --- a/distribution/saml-adapters/pom.xml +++ b/distribution/saml-adapters/pom.xml @@ -20,7 +20,7 @@ keycloak-distribution-parent org.keycloak - 24.0.5 + 24.0.5-PS-1-SNAPSHOT SAML Adapters Distribution Parent diff --git a/distribution/saml-adapters/tomcat-adapter-zip/pom.xml b/distribution/saml-adapters/tomcat-adapter-zip/pom.xml index cb5cfb562b57..cf33794ba432 100755 --- a/distribution/saml-adapters/tomcat-adapter-zip/pom.xml +++ b/distribution/saml-adapters/tomcat-adapter-zip/pom.xml @@ -21,7 +21,7 @@ keycloak-parent org.keycloak - 24.0.5 + 24.0.5-PS-1-SNAPSHOT ../../../pom.xml diff --git a/distribution/saml-adapters/wildfly-adapter/pom.xml b/distribution/saml-adapters/wildfly-adapter/pom.xml index 5b646b4ca8fc..6aea93d46033 100755 --- a/distribution/saml-adapters/wildfly-adapter/pom.xml +++ b/distribution/saml-adapters/wildfly-adapter/pom.xml @@ -20,7 +20,7 @@ keycloak-parent org.keycloak - 24.0.5 + 24.0.5-PS-1-SNAPSHOT ../../../pom.xml Keycloak Wildfly SAML Adapter diff --git a/distribution/saml-adapters/wildfly-adapter/wildfly-adapter-jakarta-zip/pom.xml b/distribution/saml-adapters/wildfly-adapter/wildfly-adapter-jakarta-zip/pom.xml index 07bd7858593d..fe8896ae743c 100755 --- a/distribution/saml-adapters/wildfly-adapter/wildfly-adapter-jakarta-zip/pom.xml +++ b/distribution/saml-adapters/wildfly-adapter/wildfly-adapter-jakarta-zip/pom.xml @@ -21,7 +21,7 @@ keycloak-parent org.keycloak - 24.0.5 + 24.0.5-PS-1-SNAPSHOT ../../../../pom.xml diff --git a/distribution/saml-adapters/wildfly-adapter/wildfly-adapter-zip/pom.xml b/distribution/saml-adapters/wildfly-adapter/wildfly-adapter-zip/pom.xml index c7d41f401274..dcb6f9fc0d39 100755 --- a/distribution/saml-adapters/wildfly-adapter/wildfly-adapter-zip/pom.xml +++ b/distribution/saml-adapters/wildfly-adapter/wildfly-adapter-zip/pom.xml @@ -21,7 +21,7 @@ keycloak-parent org.keycloak - 24.0.5 + 24.0.5-PS-1-SNAPSHOT ../../../../pom.xml diff --git a/distribution/saml-adapters/wildfly-adapter/wildfly-jakarta-modules/pom.xml b/distribution/saml-adapters/wildfly-adapter/wildfly-jakarta-modules/pom.xml index bd65d8951d88..abf66b9ef7b0 100755 --- a/distribution/saml-adapters/wildfly-adapter/wildfly-jakarta-modules/pom.xml +++ b/distribution/saml-adapters/wildfly-adapter/wildfly-jakarta-modules/pom.xml @@ -25,7 +25,7 @@ keycloak-parent org.keycloak - 24.0.5 + 24.0.5-PS-1-SNAPSHOT ../../../../pom.xml diff --git a/distribution/saml-adapters/wildfly-adapter/wildfly-modules/pom.xml b/distribution/saml-adapters/wildfly-adapter/wildfly-modules/pom.xml index beba9d3516f2..c6dd8f130b7e 100755 --- a/distribution/saml-adapters/wildfly-adapter/wildfly-modules/pom.xml +++ b/distribution/saml-adapters/wildfly-adapter/wildfly-modules/pom.xml @@ -25,7 +25,7 @@ keycloak-parent org.keycloak - 24.0.5 + 24.0.5-PS-1-SNAPSHOT ../../../../pom.xml diff --git a/docs/documentation/aggregation/pom.xml b/docs/documentation/aggregation/pom.xml index fb100caeab43..530bd719f3ed 100644 --- a/docs/documentation/aggregation/pom.xml +++ b/docs/documentation/aggregation/pom.xml @@ -5,7 +5,7 @@ org.keycloak.documentation documentation-parent - 24.0.5 + 24.0.5-PS-1-SNAPSHOT ../pom.xml diff --git a/docs/documentation/api_documentation/pom.xml b/docs/documentation/api_documentation/pom.xml index 02cab5851dfb..e7e6d2e21a8f 100644 --- a/docs/documentation/api_documentation/pom.xml +++ b/docs/documentation/api_documentation/pom.xml @@ -5,7 +5,7 @@ org.keycloak.documentation documentation-parent - 24.0.5 + 24.0.5-PS-1-SNAPSHOT ../pom.xml diff --git a/docs/documentation/authorization_services/pom.xml b/docs/documentation/authorization_services/pom.xml index 0e78b6d44cc2..e053361b631f 100644 --- a/docs/documentation/authorization_services/pom.xml +++ b/docs/documentation/authorization_services/pom.xml @@ -5,7 +5,7 @@ org.keycloak.documentation documentation-parent - 24.0.5 + 24.0.5-PS-1-SNAPSHOT ../pom.xml diff --git a/docs/documentation/dist/pom.xml b/docs/documentation/dist/pom.xml index c4fb6f92fff7..99dcc9e56c5e 100644 --- a/docs/documentation/dist/pom.xml +++ b/docs/documentation/dist/pom.xml @@ -5,7 +5,7 @@ org.keycloak.documentation documentation-parent - 24.0.5 + 24.0.5-PS-1-SNAPSHOT ../pom.xml diff --git a/docs/documentation/header-maven-plugin/pom.xml b/docs/documentation/header-maven-plugin/pom.xml index cd84d508d0a1..37e7a6f87422 100644 --- a/docs/documentation/header-maven-plugin/pom.xml +++ b/docs/documentation/header-maven-plugin/pom.xml @@ -5,12 +5,12 @@ documentation-parent org.keycloak.documentation - 24.0.5 + 24.0.5-PS-1-SNAPSHOT org.keycloak.documentation header-maven-plugin - 24.0.5 + 24.0.5-PS-1-SNAPSHOT maven-plugin github-maven-plugin diff --git a/docs/documentation/pom.xml b/docs/documentation/pom.xml index d1d956635565..379e95e6f48e 100644 --- a/docs/documentation/pom.xml +++ b/docs/documentation/pom.xml @@ -5,14 +5,14 @@ keycloak-docs-parent org.keycloak - 24.0.5 + 24.0.5-PS-1-SNAPSHOT ../pom.xml Keycloak Documentation Parent org.keycloak.documentation documentation-parent - 24.0.5 + 24.0.5-PS-1-SNAPSHOT pom diff --git a/docs/documentation/release_notes/pom.xml b/docs/documentation/release_notes/pom.xml index 0d8affabaf21..e8275e5d62b3 100644 --- a/docs/documentation/release_notes/pom.xml +++ b/docs/documentation/release_notes/pom.xml @@ -5,7 +5,7 @@ org.keycloak.documentation documentation-parent - 24.0.5 + 24.0.5-PS-1-SNAPSHOT ../pom.xml diff --git a/docs/documentation/securing_apps/pom.xml b/docs/documentation/securing_apps/pom.xml index 98643276daa3..fed06340f64d 100644 --- a/docs/documentation/securing_apps/pom.xml +++ b/docs/documentation/securing_apps/pom.xml @@ -5,7 +5,7 @@ org.keycloak.documentation documentation-parent - 24.0.5 + 24.0.5-PS-1-SNAPSHOT ../pom.xml diff --git a/docs/documentation/server_admin/pom.xml b/docs/documentation/server_admin/pom.xml index 4bce6539b2ad..f399fb8a3da5 100644 --- a/docs/documentation/server_admin/pom.xml +++ b/docs/documentation/server_admin/pom.xml @@ -5,7 +5,7 @@ org.keycloak.documentation documentation-parent - 24.0.5 + 24.0.5-PS-1-SNAPSHOT ../pom.xml diff --git a/docs/documentation/server_development/pom.xml b/docs/documentation/server_development/pom.xml index ed375e1fb643..8c2ffbde8cf0 100644 --- a/docs/documentation/server_development/pom.xml +++ b/docs/documentation/server_development/pom.xml @@ -5,7 +5,7 @@ org.keycloak.documentation documentation-parent - 24.0.5 + 24.0.5-PS-1-SNAPSHOT ../pom.xml diff --git a/docs/documentation/tests/pom.xml b/docs/documentation/tests/pom.xml index d43dc8130b4e..fb80372dc5b3 100644 --- a/docs/documentation/tests/pom.xml +++ b/docs/documentation/tests/pom.xml @@ -60,7 +60,7 @@ org.keycloak.documentation documentation-parent - 24.0.5 + 24.0.5-PS-1-SNAPSHOT ../pom.xml diff --git a/docs/documentation/topics/templates/document-attributes.adoc b/docs/documentation/topics/templates/document-attributes.adoc index 1d0955088b43..8623db5487ce 100644 --- a/docs/documentation/topics/templates/document-attributes.adoc +++ b/docs/documentation/topics/templates/document-attributes.adoc @@ -2,10 +2,10 @@ :project_name_full: Keycloak :project_community: true :project_product: false -:project_version: 24.0.5 -:project_versionMvn: 24.0.5 -:project_versionNpm: 24.0.5 -:project_versionDoc: 24.0.5 +:project_version: 24.0.5-PS-1-SNAPSHOT +:project_versionMvn: 24.0.5-PS-1-SNAPSHOT +:project_versionNpm: 24.0.5-PS-1-SNAPSHOT +:project_versionDoc: 24.0.5-PS-1-SNAPSHOT :archivebasename: keycloak :archivedownloadurl: https://github.com/keycloak/keycloak/releases/download/{project_version}/keycloak-{project_version}.zip diff --git a/docs/documentation/upgrading/pom.xml b/docs/documentation/upgrading/pom.xml index 8fa348237047..a80695b519dd 100644 --- a/docs/documentation/upgrading/pom.xml +++ b/docs/documentation/upgrading/pom.xml @@ -5,7 +5,7 @@ org.keycloak.documentation documentation-parent - 24.0.5 + 24.0.5-PS-1-SNAPSHOT ../pom.xml diff --git a/docs/guides/pom.xml b/docs/guides/pom.xml index 3eb19330d37f..0a68ee5d3538 100644 --- a/docs/guides/pom.xml +++ b/docs/guides/pom.xml @@ -19,7 +19,7 @@ keycloak-docs-parent org.keycloak - 24.0.5 + 24.0.5-PS-1-SNAPSHOT ../pom.xml diff --git a/docs/maven-plugin/pom.xml b/docs/maven-plugin/pom.xml index 1fcde54db620..fa36588185e9 100644 --- a/docs/maven-plugin/pom.xml +++ b/docs/maven-plugin/pom.xml @@ -20,7 +20,7 @@ keycloak-docs-parent org.keycloak - 24.0.5 + 24.0.5-PS-1-SNAPSHOT ../pom.xml diff --git a/docs/pom.xml b/docs/pom.xml index 84223a01b3c9..f659fd2f3530 100755 --- a/docs/pom.xml +++ b/docs/pom.xml @@ -19,7 +19,7 @@ keycloak-parent org.keycloak - 24.0.5 + 24.0.5-PS-1-SNAPSHOT ../pom.xml Keycloak Docs Parent diff --git a/examples/admin-client/pom.xml b/examples/admin-client/pom.xml index 68e4454bcaf8..4f41a87a0288 100755 --- a/examples/admin-client/pom.xml +++ b/examples/admin-client/pom.xml @@ -22,7 +22,7 @@ keycloak-examples-parent org.keycloak - 24.0.5 + 24.0.5-PS-1-SNAPSHOT Keycloak Examples - Admin Client diff --git a/examples/js-console/pom.xml b/examples/js-console/pom.xml index ecfccacbb942..e7379561af75 100755 --- a/examples/js-console/pom.xml +++ b/examples/js-console/pom.xml @@ -21,7 +21,7 @@ keycloak-examples-parent org.keycloak - 24.0.5 + 24.0.5-PS-1-SNAPSHOT 4.0.0 diff --git a/examples/kerberos/pom.xml b/examples/kerberos/pom.xml index d53dec85d1f9..e0cff999468a 100755 --- a/examples/kerberos/pom.xml +++ b/examples/kerberos/pom.xml @@ -22,7 +22,7 @@ keycloak-examples-parent org.keycloak - 24.0.5 + 24.0.5-PS-1-SNAPSHOT Keycloak Examples - Kerberos Credential Delegation diff --git a/examples/ldap/pom.xml b/examples/ldap/pom.xml index 56d809229983..5a46ecebdf2a 100644 --- a/examples/ldap/pom.xml +++ b/examples/ldap/pom.xml @@ -22,7 +22,7 @@ keycloak-examples-parent org.keycloak - 24.0.5 + 24.0.5-PS-1-SNAPSHOT 4.0.0 diff --git a/examples/pom.xml b/examples/pom.xml index b37575352346..b85513313c7c 100755 --- a/examples/pom.xml +++ b/examples/pom.xml @@ -20,7 +20,7 @@ keycloak-parent org.keycloak - 24.0.5 + 24.0.5-PS-1-SNAPSHOT Keycloak Examples diff --git a/examples/providers/authenticator/pom.xml b/examples/providers/authenticator/pom.xml index 40d5cafb4327..c0e90b7ea80d 100755 --- a/examples/providers/authenticator/pom.xml +++ b/examples/providers/authenticator/pom.xml @@ -20,7 +20,7 @@ keycloak-examples-providers-parent org.keycloak - 24.0.5 + 24.0.5-PS-1-SNAPSHOT Authenticator Example diff --git a/examples/providers/pom.xml b/examples/providers/pom.xml index aca94c6d0c58..189e99767e54 100755 --- a/examples/providers/pom.xml +++ b/examples/providers/pom.xml @@ -20,7 +20,7 @@ keycloak-examples-parent org.keycloak - 24.0.5 + 24.0.5-PS-1-SNAPSHOT Provider Examples diff --git a/examples/providers/rest/pom.xml b/examples/providers/rest/pom.xml index 7c632a0638b8..621de1ac7bf0 100755 --- a/examples/providers/rest/pom.xml +++ b/examples/providers/rest/pom.xml @@ -20,7 +20,7 @@ keycloak-examples-providers-parent org.keycloak - 24.0.5 + 24.0.5-PS-1-SNAPSHOT REST Example diff --git a/examples/saml/pom.xml b/examples/saml/pom.xml index 88dd9df0955a..aa0b324184d1 100755 --- a/examples/saml/pom.xml +++ b/examples/saml/pom.xml @@ -20,7 +20,7 @@ keycloak-examples-parent org.keycloak - 24.0.5 + 24.0.5-PS-1-SNAPSHOT SAML Examples diff --git a/examples/saml/servlet-filter/pom.xml b/examples/saml/servlet-filter/pom.xml index bf92416a29fe..1cf167d5782a 100755 --- a/examples/saml/servlet-filter/pom.xml +++ b/examples/saml/servlet-filter/pom.xml @@ -22,7 +22,7 @@ keycloak-examples-saml-parent org.keycloak - 24.0.5 + 24.0.5-PS-1-SNAPSHOT saml-servlet-filter diff --git a/examples/themes/pom.xml b/examples/themes/pom.xml index 7a539c2ec805..fda0201c6d45 100755 --- a/examples/themes/pom.xml +++ b/examples/themes/pom.xml @@ -20,7 +20,7 @@ keycloak-examples-parent org.keycloak - 24.0.5 + 24.0.5-PS-1-SNAPSHOT Themes Examples diff --git a/federation/kerberos/pom.xml b/federation/kerberos/pom.xml index 8c53c532ce20..9761832d85f8 100755 --- a/federation/kerberos/pom.xml +++ b/federation/kerberos/pom.xml @@ -20,7 +20,7 @@ keycloak-parent org.keycloak - 24.0.5 + 24.0.5-PS-1-SNAPSHOT ../../pom.xml 4.0.0 diff --git a/federation/ldap/pom.xml b/federation/ldap/pom.xml index 71b38dbd7db6..4d3014da7ae6 100755 --- a/federation/ldap/pom.xml +++ b/federation/ldap/pom.xml @@ -20,7 +20,7 @@ keycloak-parent org.keycloak - 24.0.5 + 24.0.5-PS-1-SNAPSHOT ../../pom.xml 4.0.0 diff --git a/federation/pom.xml b/federation/pom.xml index 78cd2251dfc7..0e1aad4c9778 100755 --- a/federation/pom.xml +++ b/federation/pom.xml @@ -22,7 +22,7 @@ keycloak-parent org.keycloak - 24.0.5 + 24.0.5-PS-1-SNAPSHOT ../pom.xml 4.0.0 diff --git a/federation/sssd/pom.xml b/federation/sssd/pom.xml index 3b56d8f9ecc0..192249612f5e 100644 --- a/federation/sssd/pom.xml +++ b/federation/sssd/pom.xml @@ -4,7 +4,7 @@ keycloak-parent org.keycloak - 24.0.5 + 24.0.5-PS-1-SNAPSHOT ../../pom.xml 4.0.0 diff --git a/integration/admin-client-jee/pom.xml b/integration/admin-client-jee/pom.xml index 5bfa5abc959d..2d691cdbaca9 100755 --- a/integration/admin-client-jee/pom.xml +++ b/integration/admin-client-jee/pom.xml @@ -22,7 +22,7 @@ keycloak-integration-parent org.keycloak - 24.0.5 + 24.0.5-PS-1-SNAPSHOT 4.0.0 diff --git a/integration/admin-client/pom.xml b/integration/admin-client/pom.xml index 1b52402084d0..821f76e9b3af 100755 --- a/integration/admin-client/pom.xml +++ b/integration/admin-client/pom.xml @@ -22,7 +22,7 @@ keycloak-integration-parent org.keycloak - 24.0.5 + 24.0.5-PS-1-SNAPSHOT 4.0.0 diff --git a/integration/client-cli/admin-cli/pom.xml b/integration/client-cli/admin-cli/pom.xml index 030e418412e6..51cd182b2cb3 100755 --- a/integration/client-cli/admin-cli/pom.xml +++ b/integration/client-cli/admin-cli/pom.xml @@ -21,7 +21,7 @@ keycloak-client-cli-parent org.keycloak - 24.0.5 + 24.0.5-PS-1-SNAPSHOT 4.0.0 diff --git a/integration/client-cli/client-cli-dist/pom.xml b/integration/client-cli/client-cli-dist/pom.xml index 33b8f2807ae2..3c998b9ca2ae 100755 --- a/integration/client-cli/client-cli-dist/pom.xml +++ b/integration/client-cli/client-cli-dist/pom.xml @@ -21,7 +21,7 @@ keycloak-client-cli-parent org.keycloak - 24.0.5 + 24.0.5-PS-1-SNAPSHOT keycloak-client-cli-dist diff --git a/integration/client-cli/client-registration-cli/pom.xml b/integration/client-cli/client-registration-cli/pom.xml index 623936e0e6ef..a8d25aa73783 100755 --- a/integration/client-cli/client-registration-cli/pom.xml +++ b/integration/client-cli/client-registration-cli/pom.xml @@ -21,7 +21,7 @@ keycloak-client-cli-parent org.keycloak - 24.0.5 + 24.0.5-PS-1-SNAPSHOT 4.0.0 diff --git a/integration/client-cli/pom.xml b/integration/client-cli/pom.xml index 12687dc1d642..65ce7e7b9255 100644 --- a/integration/client-cli/pom.xml +++ b/integration/client-cli/pom.xml @@ -20,7 +20,7 @@ keycloak-integration-parent org.keycloak - 24.0.5 + 24.0.5-PS-1-SNAPSHOT Keycloak Client CLI diff --git a/integration/client-registration/pom.xml b/integration/client-registration/pom.xml index 74931ab581f7..8324cc19aca2 100755 --- a/integration/client-registration/pom.xml +++ b/integration/client-registration/pom.xml @@ -21,7 +21,7 @@ keycloak-integration-parent org.keycloak - 24.0.5 + 24.0.5-PS-1-SNAPSHOT 4.0.0 diff --git a/integration/pom.xml b/integration/pom.xml index ef82922c4e68..d9cfebbc9978 100755 --- a/integration/pom.xml +++ b/integration/pom.xml @@ -20,7 +20,7 @@ keycloak-parent org.keycloak - 24.0.5 + 24.0.5-PS-1-SNAPSHOT ../pom.xml Keycloak Integration diff --git a/js/apps/account-ui/pom.xml b/js/apps/account-ui/pom.xml index 7e4d5f0390a3..cf5953d8e4fa 100644 --- a/js/apps/account-ui/pom.xml +++ b/js/apps/account-ui/pom.xml @@ -7,7 +7,7 @@ keycloak-js-parent org.keycloak - 24.0.5 + 24.0.5-PS-1-SNAPSHOT ../../pom.xml diff --git a/js/apps/admin-ui/pom.xml b/js/apps/admin-ui/pom.xml index cce4ce5e476f..34cf79d755b9 100644 --- a/js/apps/admin-ui/pom.xml +++ b/js/apps/admin-ui/pom.xml @@ -7,7 +7,7 @@ keycloak-js-parent org.keycloak - 24.0.5 + 24.0.5-PS-1-SNAPSHOT ../../pom.xml diff --git a/js/libs/keycloak-admin-client/package.json b/js/libs/keycloak-admin-client/package.json index 2e493971a030..85b0f472651b 100644 --- a/js/libs/keycloak-admin-client/package.json +++ b/js/libs/keycloak-admin-client/package.json @@ -1,6 +1,6 @@ { "name": "@keycloak/keycloak-admin-client", - "version": "24.0.5", + "version": "24.0.5-PS-1-SNAPSHOT", "description": "A client to interact with Keycloak's Administration API", "type": "module", "main": "lib/index.js", diff --git a/js/libs/keycloak-admin-client/pom.xml b/js/libs/keycloak-admin-client/pom.xml index 2b7b892ef549..02a332045caf 100644 --- a/js/libs/keycloak-admin-client/pom.xml +++ b/js/libs/keycloak-admin-client/pom.xml @@ -5,7 +5,7 @@ keycloak-js-parent org.keycloak - 24.0.5 + 24.0.5-PS-1-SNAPSHOT ../../pom.xml diff --git a/js/libs/keycloak-js/package.json b/js/libs/keycloak-js/package.json index b15ba8e52462..649823810df7 100644 --- a/js/libs/keycloak-js/package.json +++ b/js/libs/keycloak-js/package.json @@ -1,6 +1,6 @@ { "name": "keycloak-js", - "version": "24.0.5", + "version": "24.0.5-PS-1-SNAPSHOT", "description": "A client-side JavaScript OpenID Connect library that can be used to secure web applications", "main": "./dist/keycloak.js", "module": "./dist/keycloak.mjs", diff --git a/js/libs/keycloak-js/pom.xml b/js/libs/keycloak-js/pom.xml index 7cf3af46394f..4b236b8b1025 100644 --- a/js/libs/keycloak-js/pom.xml +++ b/js/libs/keycloak-js/pom.xml @@ -5,7 +5,7 @@ keycloak-js-parent org.keycloak - 24.0.5 + 24.0.5-PS-1-SNAPSHOT ../../pom.xml diff --git a/js/pom.xml b/js/pom.xml index 42ed38c2482d..3a1964b1367a 100644 --- a/js/pom.xml +++ b/js/pom.xml @@ -5,7 +5,7 @@ keycloak-parent org.keycloak - 24.0.5 + 24.0.5-PS-1-SNAPSHOT ../pom.xml diff --git a/misc/keycloak-test-helper/pom.xml b/misc/keycloak-test-helper/pom.xml index 26787621dc7b..1b5cdc057801 100644 --- a/misc/keycloak-test-helper/pom.xml +++ b/misc/keycloak-test-helper/pom.xml @@ -6,7 +6,7 @@ keycloak-misc-parent org.keycloak - 24.0.5 + 24.0.5-PS-1-SNAPSHOT org.keycloak keycloak-test-helper diff --git a/misc/pom.xml b/misc/pom.xml index f3c60abbe3a0..d4b66f314a86 100644 --- a/misc/pom.xml +++ b/misc/pom.xml @@ -3,7 +3,7 @@ keycloak-parent org.keycloak - 24.0.5 + 24.0.5-PS-1-SNAPSHOT Keycloak Misc diff --git a/misc/spring-boot-starter/keycloak-spring-boot-starter/pom.xml b/misc/spring-boot-starter/keycloak-spring-boot-starter/pom.xml index 4417d8670d34..9b64faf16d80 100644 --- a/misc/spring-boot-starter/keycloak-spring-boot-starter/pom.xml +++ b/misc/spring-boot-starter/keycloak-spring-boot-starter/pom.xml @@ -4,7 +4,7 @@ org.keycloak keycloak-spring-boot-starter-parent - 24.0.5 + 24.0.5-PS-1-SNAPSHOT keycloak-spring-boot-starter Keycloak :: Spring :: Boot :: Default :: Starter diff --git a/misc/spring-boot-starter/pom.xml b/misc/spring-boot-starter/pom.xml index 26ae9be8adc7..317e46e72948 100644 --- a/misc/spring-boot-starter/pom.xml +++ b/misc/spring-boot-starter/pom.xml @@ -5,7 +5,7 @@ keycloak-misc-parent org.keycloak - 24.0.5 + 24.0.5-PS-1-SNAPSHOT org.keycloak keycloak-spring-boot-starter-parent diff --git a/model/infinispan/pom.xml b/model/infinispan/pom.xml index df45b120a833..7855e072fe6f 100755 --- a/model/infinispan/pom.xml +++ b/model/infinispan/pom.xml @@ -21,7 +21,7 @@ keycloak-model-pom org.keycloak - 24.0.5 + 24.0.5-PS-1-SNAPSHOT 17 diff --git a/model/jpa/pom.xml b/model/jpa/pom.xml index 546110a3acc4..0b06077f7148 100755 --- a/model/jpa/pom.xml +++ b/model/jpa/pom.xml @@ -21,7 +21,7 @@ keycloak-model-pom org.keycloak - 24.0.5 + 24.0.5-PS-1-SNAPSHOT 4.0.0 diff --git a/model/legacy/pom.xml b/model/legacy/pom.xml index 86d6b0fece0e..dcf8414572e4 100644 --- a/model/legacy/pom.xml +++ b/model/legacy/pom.xml @@ -3,7 +3,7 @@ keycloak-model-pom org.keycloak - 24.0.5 + 24.0.5-PS-1-SNAPSHOT 4.0.0 diff --git a/model/pom.xml b/model/pom.xml index c344be30a444..70556ff0de85 100755 --- a/model/pom.xml +++ b/model/pom.xml @@ -20,7 +20,7 @@ keycloak-parent org.keycloak - 24.0.5 + 24.0.5-PS-1-SNAPSHOT ../pom.xml Keycloak Model Parent diff --git a/model/storage-private/pom.xml b/model/storage-private/pom.xml index 62dc91188a0d..46725b1d3ba2 100644 --- a/model/storage-private/pom.xml +++ b/model/storage-private/pom.xml @@ -3,7 +3,7 @@ keycloak-model-pom org.keycloak - 24.0.5 + 24.0.5-PS-1-SNAPSHOT 4.0.0 diff --git a/model/storage-services/pom.xml b/model/storage-services/pom.xml index dc35300af08c..cd244c3b0b98 100644 --- a/model/storage-services/pom.xml +++ b/model/storage-services/pom.xml @@ -3,7 +3,7 @@ keycloak-model-pom org.keycloak - 24.0.5 + 24.0.5-PS-1-SNAPSHOT 4.0.0 diff --git a/model/storage/pom.xml b/model/storage/pom.xml index ca6a53e57761..e8de4bbd29f8 100644 --- a/model/storage/pom.xml +++ b/model/storage/pom.xml @@ -3,7 +3,7 @@ keycloak-model-pom org.keycloak - 24.0.5 + 24.0.5-PS-1-SNAPSHOT 4.0.0 diff --git a/operator/pom.xml b/operator/pom.xml index 987d95e803c4..359a7e94cf25 100644 --- a/operator/pom.xml +++ b/operator/pom.xml @@ -7,7 +7,7 @@ keycloak-parent org.keycloak - 24.0.5 + 24.0.5-PS-1-SNAPSHOT ../pom.xml diff --git a/pom.xml b/pom.xml index bac95bb3113a..0edfeda49a0e 100644 --- a/pom.xml +++ b/pom.xml @@ -31,11 +31,11 @@ org.keycloak keycloak-parent - 24.0.5 + 24.0.5-PS-1-SNAPSHOT pom - 24.0.5 + 24.0.5-PS-1-SNAPSHOT 1.5.8 diff --git a/quarkus/config-api/pom.xml b/quarkus/config-api/pom.xml index 31d2c6bc0fd1..947ddb13c73c 100755 --- a/quarkus/config-api/pom.xml +++ b/quarkus/config-api/pom.xml @@ -21,7 +21,7 @@ keycloak-quarkus-parent org.keycloak - 24.0.5 + 24.0.5-PS-1-SNAPSHOT ../pom.xml 4.0.0 diff --git a/quarkus/container/Dockerfile b/quarkus/container/Dockerfile index 1ebc911dd0eb..68692ee71f05 100644 --- a/quarkus/container/Dockerfile +++ b/quarkus/container/Dockerfile @@ -1,6 +1,6 @@ FROM registry.access.redhat.com/ubi9 AS ubi-micro-build -ENV KEYCLOAK_VERSION 24.0.5 +ENV KEYCLOAK_VERSION 24.0.5-PS-1-SNAPSHOT ARG KEYCLOAK_DIST=https://github.com/keycloak/keycloak/releases/download/$KEYCLOAK_VERSION/keycloak-$KEYCLOAK_VERSION.tar.gz RUN dnf install -y tar gzip diff --git a/quarkus/deployment/pom.xml b/quarkus/deployment/pom.xml index ba87acfb491f..147d92a8c700 100644 --- a/quarkus/deployment/pom.xml +++ b/quarkus/deployment/pom.xml @@ -5,7 +5,7 @@ keycloak-quarkus-parent org.keycloak - 24.0.5 + 24.0.5-PS-1-SNAPSHOT ../pom.xml 4.0.0 diff --git a/quarkus/dist/pom.xml b/quarkus/dist/pom.xml index 266cc97d855a..a2a40ca56e9f 100755 --- a/quarkus/dist/pom.xml +++ b/quarkus/dist/pom.xml @@ -21,7 +21,7 @@ keycloak-quarkus-parent org.keycloak - 24.0.5 + 24.0.5-PS-1-SNAPSHOT keycloak-quarkus-dist diff --git a/quarkus/pom.xml b/quarkus/pom.xml index 613e600270a2..cd1db6c4ff5a 100644 --- a/quarkus/pom.xml +++ b/quarkus/pom.xml @@ -20,7 +20,7 @@ keycloak-parent org.keycloak - 24.0.5 + 24.0.5-PS-1-SNAPSHOT ../pom.xml Keycloak Quarkus Parent diff --git a/quarkus/runtime/pom.xml b/quarkus/runtime/pom.xml index a55ababdf71a..e019c8edd12d 100644 --- a/quarkus/runtime/pom.xml +++ b/quarkus/runtime/pom.xml @@ -5,7 +5,7 @@ keycloak-quarkus-parent org.keycloak - 24.0.5 + 24.0.5-PS-1-SNAPSHOT ../pom.xml 4.0.0 diff --git a/quarkus/server/pom.xml b/quarkus/server/pom.xml index 61e4f56d941d..0ddf25c2bcee 100644 --- a/quarkus/server/pom.xml +++ b/quarkus/server/pom.xml @@ -7,7 +7,7 @@ keycloak-quarkus-parent org.keycloak - 24.0.5 + 24.0.5-PS-1-SNAPSHOT ../pom.xml diff --git a/quarkus/tests/integration/pom.xml b/quarkus/tests/integration/pom.xml index 59b2aa85e502..47a4d4568053 100644 --- a/quarkus/tests/integration/pom.xml +++ b/quarkus/tests/integration/pom.xml @@ -24,7 +24,7 @@ keycloak-quarkus-test-parent org.keycloak - 24.0.5 + 24.0.5-PS-1-SNAPSHOT ../pom.xml diff --git a/quarkus/tests/junit5/pom.xml b/quarkus/tests/junit5/pom.xml index de3537d77694..f94d57b26cb5 100644 --- a/quarkus/tests/junit5/pom.xml +++ b/quarkus/tests/junit5/pom.xml @@ -24,7 +24,7 @@ keycloak-quarkus-test-parent org.keycloak - 24.0.5 + 24.0.5-PS-1-SNAPSHOT ../pom.xml diff --git a/quarkus/tests/pom.xml b/quarkus/tests/pom.xml index ffb29da694f7..bbbe604ee837 100644 --- a/quarkus/tests/pom.xml +++ b/quarkus/tests/pom.xml @@ -24,7 +24,7 @@ keycloak-quarkus-parent org.keycloak - 24.0.5 + 24.0.5-PS-1-SNAPSHOT ../pom.xml diff --git a/release-details b/release-details index f18a6e339320..2000f66b8ae3 100644 --- a/release-details +++ b/release-details @@ -1,3 +1,3 @@ -VERSION=21.1.1 -SHORT_VERSION=21.1.1 -NPM_VERSION=21.1.1 \ No newline at end of file +VERSION=24.0.5-PS-1-SNAPSHOT +SHORT_VERSION=24.0.5-PS-1-SNAPSHOT +NPM_VERSION=24.0.5-PS-1-SNAPSHOT diff --git a/rest/admin-ui-ext/pom.xml b/rest/admin-ui-ext/pom.xml index eec2d94ed45c..945f09034c16 100644 --- a/rest/admin-ui-ext/pom.xml +++ b/rest/admin-ui-ext/pom.xml @@ -22,7 +22,7 @@ org.keycloak keycloak-rest-parent - 24.0.5 + 24.0.5-PS-1-SNAPSHOT keycloak-rest-admin-ui-ext diff --git a/rest/pom.xml b/rest/pom.xml index 43a268003667..14036985ac3e 100644 --- a/rest/pom.xml +++ b/rest/pom.xml @@ -22,7 +22,7 @@ keycloak-parent org.keycloak - 24.0.5 + 24.0.5-PS-1-SNAPSHOT Keycloak Administration UI diff --git a/saml-core-api/pom.xml b/saml-core-api/pom.xml index c5e4ca9e7a82..71e5539862cf 100755 --- a/saml-core-api/pom.xml +++ b/saml-core-api/pom.xml @@ -21,7 +21,7 @@ keycloak-parent org.keycloak - 24.0.5 + 24.0.5-PS-1-SNAPSHOT ../pom.xml 4.0.0 diff --git a/saml-core/pom.xml b/saml-core/pom.xml index c8a0b22180ee..e444d2c6fd9e 100755 --- a/saml-core/pom.xml +++ b/saml-core/pom.xml @@ -21,7 +21,7 @@ keycloak-parent org.keycloak - 24.0.5 + 24.0.5-PS-1-SNAPSHOT ../pom.xml 4.0.0 diff --git a/server-spi-private/pom.xml b/server-spi-private/pom.xml index 4f5df05d5ce3..ce06bc820c73 100755 --- a/server-spi-private/pom.xml +++ b/server-spi-private/pom.xml @@ -21,7 +21,7 @@ keycloak-parent org.keycloak - 24.0.5 + 24.0.5-PS-1-SNAPSHOT ../pom.xml 4.0.0 diff --git a/server-spi/pom.xml b/server-spi/pom.xml index a98f89fd5ff1..ad68a0a6fe58 100755 --- a/server-spi/pom.xml +++ b/server-spi/pom.xml @@ -21,7 +21,7 @@ keycloak-parent org.keycloak - 24.0.5 + 24.0.5-PS-1-SNAPSHOT ../pom.xml 4.0.0 diff --git a/services/pom.xml b/services/pom.xml index be841425ad74..88e6bccfcd2a 100755 --- a/services/pom.xml +++ b/services/pom.xml @@ -21,7 +21,7 @@ keycloak-parent org.keycloak - 24.0.5 + 24.0.5-PS-1-SNAPSHOT ../pom.xml 4.0.0 diff --git a/testsuite/db-allocator-plugin/pom.xml b/testsuite/db-allocator-plugin/pom.xml index 5002dca33b78..6f75f4e5f244 100644 --- a/testsuite/db-allocator-plugin/pom.xml +++ b/testsuite/db-allocator-plugin/pom.xml @@ -22,7 +22,7 @@ keycloak-testsuite-pom org.keycloak - 24.0.5 + 24.0.5-PS-1-SNAPSHOT 4.0.0 diff --git a/testsuite/integration-arquillian/pom.xml b/testsuite/integration-arquillian/pom.xml index 765dca22ffda..9e68dcebf3cc 100644 --- a/testsuite/integration-arquillian/pom.xml +++ b/testsuite/integration-arquillian/pom.xml @@ -22,7 +22,7 @@ org.keycloak keycloak-testsuite-pom - 24.0.5 + 24.0.5-PS-1-SNAPSHOT ../pom.xml diff --git a/testsuite/integration-arquillian/servers/adapter-spi/pom.xml b/testsuite/integration-arquillian/servers/adapter-spi/pom.xml index 998ff58c05f5..0c87d20be817 100644 --- a/testsuite/integration-arquillian/servers/adapter-spi/pom.xml +++ b/testsuite/integration-arquillian/servers/adapter-spi/pom.xml @@ -5,7 +5,7 @@ integration-arquillian-servers org.keycloak.testsuite - 24.0.5 + 24.0.5-PS-1-SNAPSHOT 4.0.0 pom diff --git a/testsuite/integration-arquillian/servers/adapter-spi/undertow-adapter-jakarta/pom.xml b/testsuite/integration-arquillian/servers/adapter-spi/undertow-adapter-jakarta/pom.xml index eecae41d07a4..6f3d15d80a50 100644 --- a/testsuite/integration-arquillian/servers/adapter-spi/undertow-adapter-jakarta/pom.xml +++ b/testsuite/integration-arquillian/servers/adapter-spi/undertow-adapter-jakarta/pom.xml @@ -5,7 +5,7 @@ integration-arquillian-servers-adapter-spi org.keycloak.testsuite - 24.0.5 + 24.0.5-PS-1-SNAPSHOT ../pom.xml 4.0.0 diff --git a/testsuite/integration-arquillian/servers/adapter-spi/undertow-adapter-saml-jakarta/pom.xml b/testsuite/integration-arquillian/servers/adapter-spi/undertow-adapter-saml-jakarta/pom.xml index 8aa9289d60a9..4390895bc956 100644 --- a/testsuite/integration-arquillian/servers/adapter-spi/undertow-adapter-saml-jakarta/pom.xml +++ b/testsuite/integration-arquillian/servers/adapter-spi/undertow-adapter-saml-jakarta/pom.xml @@ -5,7 +5,7 @@ integration-arquillian-servers-adapter-spi org.keycloak.testsuite - 24.0.5 + 24.0.5-PS-1-SNAPSHOT ../pom.xml 4.0.0 diff --git a/testsuite/integration-arquillian/servers/adapter-spi/undertow-adapter-spi-jakarta/pom.xml b/testsuite/integration-arquillian/servers/adapter-spi/undertow-adapter-spi-jakarta/pom.xml index 9dc178a35d90..96e29d5d74f8 100644 --- a/testsuite/integration-arquillian/servers/adapter-spi/undertow-adapter-spi-jakarta/pom.xml +++ b/testsuite/integration-arquillian/servers/adapter-spi/undertow-adapter-spi-jakarta/pom.xml @@ -5,7 +5,7 @@ integration-arquillian-servers-adapter-spi org.keycloak.testsuite - 24.0.5 + 24.0.5-PS-1-SNAPSHOT ../pom.xml 4.0.0 diff --git a/testsuite/integration-arquillian/servers/app-server/app-server-spi/pom.xml b/testsuite/integration-arquillian/servers/app-server/app-server-spi/pom.xml index 214d7b1620fe..a56533c19166 100644 --- a/testsuite/integration-arquillian/servers/app-server/app-server-spi/pom.xml +++ b/testsuite/integration-arquillian/servers/app-server/app-server-spi/pom.xml @@ -21,7 +21,7 @@ org.keycloak.testsuite integration-arquillian-servers-app-server - 24.0.5 + 24.0.5-PS-1-SNAPSHOT 4.0.0 diff --git a/testsuite/integration-arquillian/servers/app-server/jboss/eap/pom.xml b/testsuite/integration-arquillian/servers/app-server/jboss/eap/pom.xml index c0d82ff28af8..d49fcd0fa831 100644 --- a/testsuite/integration-arquillian/servers/app-server/jboss/eap/pom.xml +++ b/testsuite/integration-arquillian/servers/app-server/jboss/eap/pom.xml @@ -21,7 +21,7 @@ org.keycloak.testsuite integration-arquillian-servers-app-server-jboss - 24.0.5 + 24.0.5-PS-1-SNAPSHOT 4.0.0 diff --git a/testsuite/integration-arquillian/servers/app-server/jboss/eap6/pom.xml b/testsuite/integration-arquillian/servers/app-server/jboss/eap6/pom.xml index c8cacfdb58f7..05425d3d467a 100644 --- a/testsuite/integration-arquillian/servers/app-server/jboss/eap6/pom.xml +++ b/testsuite/integration-arquillian/servers/app-server/jboss/eap6/pom.xml @@ -21,7 +21,7 @@ org.keycloak.testsuite integration-arquillian-servers-app-server-jboss - 24.0.5 + 24.0.5-PS-1-SNAPSHOT 4.0.0 diff --git a/testsuite/integration-arquillian/servers/app-server/jboss/galleon/pom.xml b/testsuite/integration-arquillian/servers/app-server/jboss/galleon/pom.xml index 5d4f5b3cbbcb..e5a71f6f950d 100644 --- a/testsuite/integration-arquillian/servers/app-server/jboss/galleon/pom.xml +++ b/testsuite/integration-arquillian/servers/app-server/jboss/galleon/pom.xml @@ -5,7 +5,7 @@ org.keycloak.testsuite integration-arquillian-servers-app-server-jboss - 24.0.5 + 24.0.5-PS-1-SNAPSHOT 4.0.0 diff --git a/testsuite/integration-arquillian/servers/app-server/jboss/pom.xml b/testsuite/integration-arquillian/servers/app-server/jboss/pom.xml index 92fed290cf22..a861a9597f70 100644 --- a/testsuite/integration-arquillian/servers/app-server/jboss/pom.xml +++ b/testsuite/integration-arquillian/servers/app-server/jboss/pom.xml @@ -22,7 +22,7 @@ org.keycloak.testsuite integration-arquillian-servers-app-server - 24.0.5 + 24.0.5-PS-1-SNAPSHOT 4.0.0 diff --git a/testsuite/integration-arquillian/servers/app-server/jboss/wildfly/pom.xml b/testsuite/integration-arquillian/servers/app-server/jboss/wildfly/pom.xml index a23527015fa8..5a6c4de882ed 100644 --- a/testsuite/integration-arquillian/servers/app-server/jboss/wildfly/pom.xml +++ b/testsuite/integration-arquillian/servers/app-server/jboss/wildfly/pom.xml @@ -21,7 +21,7 @@ org.keycloak.testsuite integration-arquillian-servers-app-server-jboss - 24.0.5 + 24.0.5-PS-1-SNAPSHOT 4.0.0 diff --git a/testsuite/integration-arquillian/servers/app-server/jetty/94/pom.xml b/testsuite/integration-arquillian/servers/app-server/jetty/94/pom.xml index b6075b0a6e17..ea8cf2b21e43 100644 --- a/testsuite/integration-arquillian/servers/app-server/jetty/94/pom.xml +++ b/testsuite/integration-arquillian/servers/app-server/jetty/94/pom.xml @@ -18,7 +18,7 @@ org.keycloak.testsuite integration-arquillian-servers-app-server-jetty - 24.0.5 + 24.0.5-PS-1-SNAPSHOT 4.0.0 diff --git a/testsuite/integration-arquillian/servers/app-server/jetty/common/pom.xml b/testsuite/integration-arquillian/servers/app-server/jetty/common/pom.xml index d73a59f8d127..254d76a3e6d8 100644 --- a/testsuite/integration-arquillian/servers/app-server/jetty/common/pom.xml +++ b/testsuite/integration-arquillian/servers/app-server/jetty/common/pom.xml @@ -18,7 +18,7 @@ org.keycloak.testsuite integration-arquillian-servers-app-server-jetty - 24.0.5 + 24.0.5-PS-1-SNAPSHOT 4.0.0 diff --git a/testsuite/integration-arquillian/servers/app-server/jetty/pom.xml b/testsuite/integration-arquillian/servers/app-server/jetty/pom.xml index aeacdeb5a1ab..9ce02171af75 100644 --- a/testsuite/integration-arquillian/servers/app-server/jetty/pom.xml +++ b/testsuite/integration-arquillian/servers/app-server/jetty/pom.xml @@ -18,7 +18,7 @@ org.keycloak.testsuite integration-arquillian-servers-app-server - 24.0.5 + 24.0.5-PS-1-SNAPSHOT 4.0.0 diff --git a/testsuite/integration-arquillian/servers/app-server/karaf/fuse63/pom.xml b/testsuite/integration-arquillian/servers/app-server/karaf/fuse63/pom.xml index 364850609576..101a17fc07b0 100644 --- a/testsuite/integration-arquillian/servers/app-server/karaf/fuse63/pom.xml +++ b/testsuite/integration-arquillian/servers/app-server/karaf/fuse63/pom.xml @@ -21,7 +21,7 @@ org.keycloak.testsuite integration-arquillian-servers-app-server-karaf - 24.0.5 + 24.0.5-PS-1-SNAPSHOT 4.0.0 diff --git a/testsuite/integration-arquillian/servers/app-server/karaf/fuse7x/pom.xml b/testsuite/integration-arquillian/servers/app-server/karaf/fuse7x/pom.xml index 674f1ff2ae7e..49c86e628568 100644 --- a/testsuite/integration-arquillian/servers/app-server/karaf/fuse7x/pom.xml +++ b/testsuite/integration-arquillian/servers/app-server/karaf/fuse7x/pom.xml @@ -21,7 +21,7 @@ org.keycloak.testsuite integration-arquillian-servers-app-server-karaf - 24.0.5 + 24.0.5-PS-1-SNAPSHOT 4.0.0 diff --git a/testsuite/integration-arquillian/servers/app-server/karaf/pom.xml b/testsuite/integration-arquillian/servers/app-server/karaf/pom.xml index 5a20450a2c56..b8a18ade7ec5 100644 --- a/testsuite/integration-arquillian/servers/app-server/karaf/pom.xml +++ b/testsuite/integration-arquillian/servers/app-server/karaf/pom.xml @@ -21,7 +21,7 @@ org.keycloak.testsuite integration-arquillian-servers-app-server - 24.0.5 + 24.0.5-PS-1-SNAPSHOT 4.0.0 diff --git a/testsuite/integration-arquillian/servers/app-server/pom.xml b/testsuite/integration-arquillian/servers/app-server/pom.xml index 027d43473702..30b907a8dbaa 100644 --- a/testsuite/integration-arquillian/servers/app-server/pom.xml +++ b/testsuite/integration-arquillian/servers/app-server/pom.xml @@ -21,7 +21,7 @@ org.keycloak.testsuite integration-arquillian-servers - 24.0.5 + 24.0.5-PS-1-SNAPSHOT 4.0.0 diff --git a/testsuite/integration-arquillian/servers/app-server/tomcat/common/pom.xml b/testsuite/integration-arquillian/servers/app-server/tomcat/common/pom.xml index 7110f5a8d3c7..5b8dbda98d3d 100644 --- a/testsuite/integration-arquillian/servers/app-server/tomcat/common/pom.xml +++ b/testsuite/integration-arquillian/servers/app-server/tomcat/common/pom.xml @@ -5,7 +5,7 @@ integration-arquillian-servers-app-server-tomcat org.keycloak.testsuite - 24.0.5 + 24.0.5-PS-1-SNAPSHOT 4.0.0 diff --git a/testsuite/integration-arquillian/servers/app-server/tomcat/pom.xml b/testsuite/integration-arquillian/servers/app-server/tomcat/pom.xml index d5b0a763568d..cf3694d65e34 100644 --- a/testsuite/integration-arquillian/servers/app-server/tomcat/pom.xml +++ b/testsuite/integration-arquillian/servers/app-server/tomcat/pom.xml @@ -21,7 +21,7 @@ org.keycloak.testsuite integration-arquillian-servers-app-server - 24.0.5 + 24.0.5-PS-1-SNAPSHOT 4.0.0 diff --git a/testsuite/integration-arquillian/servers/app-server/tomcat/tomcat8/pom.xml b/testsuite/integration-arquillian/servers/app-server/tomcat/tomcat8/pom.xml index 5b93904b3f72..424212072958 100644 --- a/testsuite/integration-arquillian/servers/app-server/tomcat/tomcat8/pom.xml +++ b/testsuite/integration-arquillian/servers/app-server/tomcat/tomcat8/pom.xml @@ -21,7 +21,7 @@ org.keycloak.testsuite integration-arquillian-servers-app-server-tomcat - 24.0.5 + 24.0.5-PS-1-SNAPSHOT 4.0.0 diff --git a/testsuite/integration-arquillian/servers/app-server/tomcat/tomcat9/pom.xml b/testsuite/integration-arquillian/servers/app-server/tomcat/tomcat9/pom.xml index 93c38fb3eb09..992ce60f6bd4 100644 --- a/testsuite/integration-arquillian/servers/app-server/tomcat/tomcat9/pom.xml +++ b/testsuite/integration-arquillian/servers/app-server/tomcat/tomcat9/pom.xml @@ -21,7 +21,7 @@ org.keycloak.testsuite integration-arquillian-servers-app-server-tomcat - 24.0.5 + 24.0.5-PS-1-SNAPSHOT 4.0.0 diff --git a/testsuite/integration-arquillian/servers/app-server/undertow/pom.xml b/testsuite/integration-arquillian/servers/app-server/undertow/pom.xml index 02f57c947f59..9d880fd8baaf 100644 --- a/testsuite/integration-arquillian/servers/app-server/undertow/pom.xml +++ b/testsuite/integration-arquillian/servers/app-server/undertow/pom.xml @@ -18,7 +18,7 @@ org.keycloak.testsuite integration-arquillian-servers-app-server - 24.0.5 + 24.0.5-PS-1-SNAPSHOT 4.0.0 diff --git a/testsuite/integration-arquillian/servers/auth-server/pom.xml b/testsuite/integration-arquillian/servers/auth-server/pom.xml index 7fa63212b9a7..aba8a1b77dbe 100644 --- a/testsuite/integration-arquillian/servers/auth-server/pom.xml +++ b/testsuite/integration-arquillian/servers/auth-server/pom.xml @@ -21,7 +21,7 @@ org.keycloak.testsuite integration-arquillian-servers - 24.0.5 + 24.0.5-PS-1-SNAPSHOT 4.0.0 diff --git a/testsuite/integration-arquillian/servers/auth-server/quarkus/pom.xml b/testsuite/integration-arquillian/servers/auth-server/quarkus/pom.xml index 7c41b59971e9..9ea5f3a6fd34 100644 --- a/testsuite/integration-arquillian/servers/auth-server/quarkus/pom.xml +++ b/testsuite/integration-arquillian/servers/auth-server/quarkus/pom.xml @@ -5,7 +5,7 @@ integration-arquillian-servers-auth-server org.keycloak.testsuite - 24.0.5 + 24.0.5-PS-1-SNAPSHOT 4.0.0 diff --git a/testsuite/integration-arquillian/servers/auth-server/services/pom.xml b/testsuite/integration-arquillian/servers/auth-server/services/pom.xml index 7b4ed55b1bc5..1fe47179c9c6 100644 --- a/testsuite/integration-arquillian/servers/auth-server/services/pom.xml +++ b/testsuite/integration-arquillian/servers/auth-server/services/pom.xml @@ -21,7 +21,7 @@ org.keycloak.testsuite integration-arquillian-servers-auth-server - 24.0.5 + 24.0.5-PS-1-SNAPSHOT 4.0.0 diff --git a/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers-deployment/pom.xml b/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers-deployment/pom.xml index 3d20f4aa2e9d..d72046f81bff 100644 --- a/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers-deployment/pom.xml +++ b/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers-deployment/pom.xml @@ -24,7 +24,7 @@ org.keycloak.testsuite integration-arquillian-servers-auth-server-services - 24.0.5 + 24.0.5-PS-1-SNAPSHOT integration-arquillian-testsuite-providers-deployment diff --git a/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/pom.xml b/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/pom.xml index 48f02cb6365d..bae89a35fe7c 100644 --- a/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/pom.xml +++ b/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/pom.xml @@ -24,7 +24,7 @@ org.keycloak.testsuite integration-arquillian-servers-auth-server-services - 24.0.5 + 24.0.5-PS-1-SNAPSHOT integration-arquillian-testsuite-providers diff --git a/testsuite/integration-arquillian/servers/auth-server/undertow/pom.xml b/testsuite/integration-arquillian/servers/auth-server/undertow/pom.xml index 21ab6ad782f3..8a50292ea293 100644 --- a/testsuite/integration-arquillian/servers/auth-server/undertow/pom.xml +++ b/testsuite/integration-arquillian/servers/auth-server/undertow/pom.xml @@ -21,7 +21,7 @@ org.keycloak.testsuite integration-arquillian-servers-auth-server - 24.0.5 + 24.0.5-PS-1-SNAPSHOT 4.0.0 diff --git a/testsuite/integration-arquillian/servers/cache-server/infinispan/datagrid/pom.xml b/testsuite/integration-arquillian/servers/cache-server/infinispan/datagrid/pom.xml index a32d5af3bf0e..078241dc9adc 100644 --- a/testsuite/integration-arquillian/servers/cache-server/infinispan/datagrid/pom.xml +++ b/testsuite/integration-arquillian/servers/cache-server/infinispan/datagrid/pom.xml @@ -21,7 +21,7 @@ org.keycloak.testsuite integration-arquillian-servers-cache-server-infinispan - 24.0.5 + 24.0.5-PS-1-SNAPSHOT 4.0.0 diff --git a/testsuite/integration-arquillian/servers/cache-server/infinispan/infinispan/pom.xml b/testsuite/integration-arquillian/servers/cache-server/infinispan/infinispan/pom.xml index 5aae62989faa..4d1a5f5c2597 100644 --- a/testsuite/integration-arquillian/servers/cache-server/infinispan/infinispan/pom.xml +++ b/testsuite/integration-arquillian/servers/cache-server/infinispan/infinispan/pom.xml @@ -21,7 +21,7 @@ org.keycloak.testsuite integration-arquillian-servers-cache-server-infinispan - 24.0.5 + 24.0.5-PS-1-SNAPSHOT 4.0.0 diff --git a/testsuite/integration-arquillian/servers/cache-server/infinispan/pom.xml b/testsuite/integration-arquillian/servers/cache-server/infinispan/pom.xml index e5b7956ed593..bfd50590f132 100644 --- a/testsuite/integration-arquillian/servers/cache-server/infinispan/pom.xml +++ b/testsuite/integration-arquillian/servers/cache-server/infinispan/pom.xml @@ -24,7 +24,7 @@ org.keycloak.testsuite integration-arquillian-servers-cache-server - 24.0.5 + 24.0.5-PS-1-SNAPSHOT pom diff --git a/testsuite/integration-arquillian/servers/cache-server/legacy/datagrid/pom.xml b/testsuite/integration-arquillian/servers/cache-server/legacy/datagrid/pom.xml index 60bcb47d1852..b633e3647eca 100644 --- a/testsuite/integration-arquillian/servers/cache-server/legacy/datagrid/pom.xml +++ b/testsuite/integration-arquillian/servers/cache-server/legacy/datagrid/pom.xml @@ -21,7 +21,7 @@ org.keycloak.testsuite integration-arquillian-servers-cache-server-legacy - 24.0.5 + 24.0.5-PS-1-SNAPSHOT 4.0.0 diff --git a/testsuite/integration-arquillian/servers/cache-server/legacy/infinispan/pom.xml b/testsuite/integration-arquillian/servers/cache-server/legacy/infinispan/pom.xml index fc50819cee70..8805e726adf7 100644 --- a/testsuite/integration-arquillian/servers/cache-server/legacy/infinispan/pom.xml +++ b/testsuite/integration-arquillian/servers/cache-server/legacy/infinispan/pom.xml @@ -21,7 +21,7 @@ org.keycloak.testsuite integration-arquillian-servers-cache-server-legacy - 24.0.5 + 24.0.5-PS-1-SNAPSHOT 4.0.0 diff --git a/testsuite/integration-arquillian/servers/cache-server/legacy/pom.xml b/testsuite/integration-arquillian/servers/cache-server/legacy/pom.xml index c93a7554227f..135a842ee9e1 100644 --- a/testsuite/integration-arquillian/servers/cache-server/legacy/pom.xml +++ b/testsuite/integration-arquillian/servers/cache-server/legacy/pom.xml @@ -21,7 +21,7 @@ org.keycloak.testsuite integration-arquillian-servers-cache-server - 24.0.5 + 24.0.5-PS-1-SNAPSHOT 4.0.0 diff --git a/testsuite/integration-arquillian/servers/cache-server/pom.xml b/testsuite/integration-arquillian/servers/cache-server/pom.xml index 6e83be7253ff..56cb86a4da74 100644 --- a/testsuite/integration-arquillian/servers/cache-server/pom.xml +++ b/testsuite/integration-arquillian/servers/cache-server/pom.xml @@ -21,7 +21,7 @@ org.keycloak.testsuite integration-arquillian-servers - 24.0.5 + 24.0.5-PS-1-SNAPSHOT 4.0.0 diff --git a/testsuite/integration-arquillian/servers/migration/pom.xml b/testsuite/integration-arquillian/servers/migration/pom.xml index 7b6d115c0138..4b7feac820d1 100644 --- a/testsuite/integration-arquillian/servers/migration/pom.xml +++ b/testsuite/integration-arquillian/servers/migration/pom.xml @@ -21,7 +21,7 @@ org.keycloak.testsuite integration-arquillian-servers - 24.0.5 + 24.0.5-PS-1-SNAPSHOT 4.0.0 diff --git a/testsuite/integration-arquillian/servers/pom.xml b/testsuite/integration-arquillian/servers/pom.xml index d2d055ab5ecc..e95e70b41c45 100644 --- a/testsuite/integration-arquillian/servers/pom.xml +++ b/testsuite/integration-arquillian/servers/pom.xml @@ -21,7 +21,7 @@ org.keycloak.testsuite integration-arquillian - 24.0.5 + 24.0.5-PS-1-SNAPSHOT 4.0.0 diff --git a/testsuite/integration-arquillian/test-apps/app-profile-jee/pom.xml b/testsuite/integration-arquillian/test-apps/app-profile-jee/pom.xml index beaaee718206..a7c685b753b4 100644 --- a/testsuite/integration-arquillian/test-apps/app-profile-jee/pom.xml +++ b/testsuite/integration-arquillian/test-apps/app-profile-jee/pom.xml @@ -5,7 +5,7 @@ org.keycloak.testsuite integration-arquillian-test-apps - 24.0.5 + 24.0.5-PS-1-SNAPSHOT keycloak-test-app-profile-jee diff --git a/testsuite/integration-arquillian/test-apps/cors/angular-product/pom.xml b/testsuite/integration-arquillian/test-apps/cors/angular-product/pom.xml index f893f0700b63..638cfb7f1f52 100755 --- a/testsuite/integration-arquillian/test-apps/cors/angular-product/pom.xml +++ b/testsuite/integration-arquillian/test-apps/cors/angular-product/pom.xml @@ -21,7 +21,7 @@ org.keycloak.testsuite integration-arquillian-test-apps-cors-parent - 24.0.5 + 24.0.5-PS-1-SNAPSHOT 4.0.0 diff --git a/testsuite/integration-arquillian/test-apps/cors/database-service/pom.xml b/testsuite/integration-arquillian/test-apps/cors/database-service/pom.xml index c8f62d845d3e..9ab84b0f2096 100755 --- a/testsuite/integration-arquillian/test-apps/cors/database-service/pom.xml +++ b/testsuite/integration-arquillian/test-apps/cors/database-service/pom.xml @@ -21,7 +21,7 @@ org.keycloak.testsuite integration-arquillian-test-apps-cors-parent - 24.0.5 + 24.0.5-PS-1-SNAPSHOT 4.0.0 diff --git a/testsuite/integration-arquillian/test-apps/cors/pom.xml b/testsuite/integration-arquillian/test-apps/cors/pom.xml index 165e09043e4f..6848a876ca79 100644 --- a/testsuite/integration-arquillian/test-apps/cors/pom.xml +++ b/testsuite/integration-arquillian/test-apps/cors/pom.xml @@ -5,7 +5,7 @@ integration-arquillian-test-apps org.keycloak.testsuite - 24.0.5 + 24.0.5-PS-1-SNAPSHOT 4.0.0 diff --git a/testsuite/integration-arquillian/test-apps/fuse/camel-fuse7-undertow/pom.xml b/testsuite/integration-arquillian/test-apps/fuse/camel-fuse7-undertow/pom.xml index ea10944e6e4e..04ddab08bd3d 100755 --- a/testsuite/integration-arquillian/test-apps/fuse/camel-fuse7-undertow/pom.xml +++ b/testsuite/integration-arquillian/test-apps/fuse/camel-fuse7-undertow/pom.xml @@ -21,7 +21,7 @@ integration-arquillian-test-apps-fuse-parent org.keycloak.testsuite - 24.0.5 + 24.0.5-PS-1-SNAPSHOT 4.0.0 diff --git a/testsuite/integration-arquillian/test-apps/fuse/camel/pom.xml b/testsuite/integration-arquillian/test-apps/fuse/camel/pom.xml index 9f8cbf318269..cab75a348903 100755 --- a/testsuite/integration-arquillian/test-apps/fuse/camel/pom.xml +++ b/testsuite/integration-arquillian/test-apps/fuse/camel/pom.xml @@ -21,7 +21,7 @@ integration-arquillian-test-apps-fuse-parent org.keycloak.testsuite - 24.0.5 + 24.0.5-PS-1-SNAPSHOT 4.0.0 diff --git a/testsuite/integration-arquillian/test-apps/fuse/customer-app-fuse/pom.xml b/testsuite/integration-arquillian/test-apps/fuse/customer-app-fuse/pom.xml index da9c147c3123..9753eb0a770d 100755 --- a/testsuite/integration-arquillian/test-apps/fuse/customer-app-fuse/pom.xml +++ b/testsuite/integration-arquillian/test-apps/fuse/customer-app-fuse/pom.xml @@ -21,7 +21,7 @@ integration-arquillian-test-apps-fuse-parent org.keycloak.testsuite - 24.0.5 + 24.0.5-PS-1-SNAPSHOT 4.0.0 diff --git a/testsuite/integration-arquillian/test-apps/fuse/cxf-jaxrs-fuse7-undertow/pom.xml b/testsuite/integration-arquillian/test-apps/fuse/cxf-jaxrs-fuse7-undertow/pom.xml index b1066bb91e57..9ba7666a87a1 100755 --- a/testsuite/integration-arquillian/test-apps/fuse/cxf-jaxrs-fuse7-undertow/pom.xml +++ b/testsuite/integration-arquillian/test-apps/fuse/cxf-jaxrs-fuse7-undertow/pom.xml @@ -21,7 +21,7 @@ integration-arquillian-test-apps-fuse-parent org.keycloak.testsuite - 24.0.5 + 24.0.5-PS-1-SNAPSHOT 4.0.0 diff --git a/testsuite/integration-arquillian/test-apps/fuse/cxf-jaxrs/pom.xml b/testsuite/integration-arquillian/test-apps/fuse/cxf-jaxrs/pom.xml index 19f3b50b4015..5fe2de5e0222 100755 --- a/testsuite/integration-arquillian/test-apps/fuse/cxf-jaxrs/pom.xml +++ b/testsuite/integration-arquillian/test-apps/fuse/cxf-jaxrs/pom.xml @@ -21,7 +21,7 @@ integration-arquillian-test-apps-fuse-parent org.keycloak.testsuite - 24.0.5 + 24.0.5-PS-1-SNAPSHOT 4.0.0 diff --git a/testsuite/integration-arquillian/test-apps/fuse/cxf-jaxws-fuse7-undertow/pom.xml b/testsuite/integration-arquillian/test-apps/fuse/cxf-jaxws-fuse7-undertow/pom.xml index e9e57c5c7f03..4b4b930a3d27 100755 --- a/testsuite/integration-arquillian/test-apps/fuse/cxf-jaxws-fuse7-undertow/pom.xml +++ b/testsuite/integration-arquillian/test-apps/fuse/cxf-jaxws-fuse7-undertow/pom.xml @@ -21,7 +21,7 @@ integration-arquillian-test-apps-fuse-parent org.keycloak.testsuite - 24.0.5 + 24.0.5-PS-1-SNAPSHOT 4.0.0 diff --git a/testsuite/integration-arquillian/test-apps/fuse/cxf-jaxws/pom.xml b/testsuite/integration-arquillian/test-apps/fuse/cxf-jaxws/pom.xml index b2694b3e81bb..10e5203e2c5f 100755 --- a/testsuite/integration-arquillian/test-apps/fuse/cxf-jaxws/pom.xml +++ b/testsuite/integration-arquillian/test-apps/fuse/cxf-jaxws/pom.xml @@ -21,7 +21,7 @@ integration-arquillian-test-apps-fuse-parent org.keycloak.testsuite - 24.0.5 + 24.0.5-PS-1-SNAPSHOT 4.0.0 diff --git a/testsuite/integration-arquillian/test-apps/fuse/external-config/pom.xml b/testsuite/integration-arquillian/test-apps/fuse/external-config/pom.xml index 5b15bcb8bdc1..98bb828d816b 100755 --- a/testsuite/integration-arquillian/test-apps/fuse/external-config/pom.xml +++ b/testsuite/integration-arquillian/test-apps/fuse/external-config/pom.xml @@ -21,7 +21,7 @@ integration-arquillian-test-apps-fuse-parent org.keycloak.testsuite - 24.0.5 + 24.0.5-PS-1-SNAPSHOT Keycloak Examples - External Config diff --git a/testsuite/integration-arquillian/test-apps/fuse/features/pom.xml b/testsuite/integration-arquillian/test-apps/fuse/features/pom.xml index 0f24cfed7803..bde0adee3a62 100755 --- a/testsuite/integration-arquillian/test-apps/fuse/features/pom.xml +++ b/testsuite/integration-arquillian/test-apps/fuse/features/pom.xml @@ -21,7 +21,7 @@ integration-arquillian-test-apps-fuse-parent org.keycloak.testsuite - 24.0.5 + 24.0.5-PS-1-SNAPSHOT 4.0.0 diff --git a/testsuite/integration-arquillian/test-apps/fuse/pom.xml b/testsuite/integration-arquillian/test-apps/fuse/pom.xml index 23027122d141..6a4747d0bd23 100755 --- a/testsuite/integration-arquillian/test-apps/fuse/pom.xml +++ b/testsuite/integration-arquillian/test-apps/fuse/pom.xml @@ -20,7 +20,7 @@ integration-arquillian-test-apps org.keycloak.testsuite - 24.0.5 + 24.0.5-PS-1-SNAPSHOT Fuse Test Applications diff --git a/testsuite/integration-arquillian/test-apps/fuse/product-app-fuse/pom.xml b/testsuite/integration-arquillian/test-apps/fuse/product-app-fuse/pom.xml index 8e7daa1982a3..df21d1906033 100755 --- a/testsuite/integration-arquillian/test-apps/fuse/product-app-fuse/pom.xml +++ b/testsuite/integration-arquillian/test-apps/fuse/product-app-fuse/pom.xml @@ -21,7 +21,7 @@ integration-arquillian-test-apps-fuse-parent org.keycloak.testsuite - 24.0.5 + 24.0.5-PS-1-SNAPSHOT 4.0.0 diff --git a/testsuite/integration-arquillian/test-apps/fuse/product-app-fuse7-undertow/pom.xml b/testsuite/integration-arquillian/test-apps/fuse/product-app-fuse7-undertow/pom.xml index 5bc3825303b5..8e3f3bde3e74 100755 --- a/testsuite/integration-arquillian/test-apps/fuse/product-app-fuse7-undertow/pom.xml +++ b/testsuite/integration-arquillian/test-apps/fuse/product-app-fuse7-undertow/pom.xml @@ -21,7 +21,7 @@ integration-arquillian-test-apps-fuse-parent org.keycloak.testsuite - 24.0.5 + 24.0.5-PS-1-SNAPSHOT 4.0.0 diff --git a/testsuite/integration-arquillian/test-apps/hello-world-authz-service/pom.xml b/testsuite/integration-arquillian/test-apps/hello-world-authz-service/pom.xml index e725617663ec..17b425670e7f 100755 --- a/testsuite/integration-arquillian/test-apps/hello-world-authz-service/pom.xml +++ b/testsuite/integration-arquillian/test-apps/hello-world-authz-service/pom.xml @@ -24,7 +24,7 @@ org.keycloak.testsuite integration-arquillian-test-apps - 24.0.5 + 24.0.5-PS-1-SNAPSHOT hello-world-authz-service diff --git a/testsuite/integration-arquillian/test-apps/pom.xml b/testsuite/integration-arquillian/test-apps/pom.xml index 028a362cdb25..65aebe20bcd4 100644 --- a/testsuite/integration-arquillian/test-apps/pom.xml +++ b/testsuite/integration-arquillian/test-apps/pom.xml @@ -5,7 +5,7 @@ integration-arquillian org.keycloak.testsuite - 24.0.5 + 24.0.5-PS-1-SNAPSHOT 4.0.0 diff --git a/testsuite/integration-arquillian/test-apps/servlet-authz/pom.xml b/testsuite/integration-arquillian/test-apps/servlet-authz/pom.xml index 8ed2df40dade..96b332fdb52c 100755 --- a/testsuite/integration-arquillian/test-apps/servlet-authz/pom.xml +++ b/testsuite/integration-arquillian/test-apps/servlet-authz/pom.xml @@ -6,7 +6,7 @@ org.keycloak.testsuite integration-arquillian-test-apps - 24.0.5 + 24.0.5-PS-1-SNAPSHOT servlet-authz-app diff --git a/testsuite/integration-arquillian/test-apps/servlet-policy-enforcer/pom.xml b/testsuite/integration-arquillian/test-apps/servlet-policy-enforcer/pom.xml index abb954b67099..a33c5218dbe6 100755 --- a/testsuite/integration-arquillian/test-apps/servlet-policy-enforcer/pom.xml +++ b/testsuite/integration-arquillian/test-apps/servlet-policy-enforcer/pom.xml @@ -24,7 +24,7 @@ org.keycloak.testsuite integration-arquillian-test-apps - 24.0.5 + 24.0.5-PS-1-SNAPSHOT servlet-policy-enforcer diff --git a/testsuite/integration-arquillian/test-apps/servlets-jakarta/pom.xml b/testsuite/integration-arquillian/test-apps/servlets-jakarta/pom.xml index a566d88fe4ca..40902fd4fe72 100644 --- a/testsuite/integration-arquillian/test-apps/servlets-jakarta/pom.xml +++ b/testsuite/integration-arquillian/test-apps/servlets-jakarta/pom.xml @@ -6,7 +6,7 @@ org.keycloak.testsuite integration-arquillian-test-apps - 24.0.5 + 24.0.5-PS-1-SNAPSHOT integration-arquillian-test-apps-servlets-jakarta diff --git a/testsuite/integration-arquillian/test-apps/servlets/pom.xml b/testsuite/integration-arquillian/test-apps/servlets/pom.xml index 9d93fc53efcb..fc35964f0ce5 100644 --- a/testsuite/integration-arquillian/test-apps/servlets/pom.xml +++ b/testsuite/integration-arquillian/test-apps/servlets/pom.xml @@ -5,7 +5,7 @@ integration-arquillian-test-apps org.keycloak.testsuite - 24.0.5 + 24.0.5-PS-1-SNAPSHOT 4.0.0 diff --git a/testsuite/integration-arquillian/test-apps/spring-boot-adapter-app/pom.xml b/testsuite/integration-arquillian/test-apps/spring-boot-adapter-app/pom.xml index 020f21f90ad3..a32fa5c47899 100644 --- a/testsuite/integration-arquillian/test-apps/spring-boot-adapter-app/pom.xml +++ b/testsuite/integration-arquillian/test-apps/spring-boot-adapter-app/pom.xml @@ -5,7 +5,7 @@ org.keycloak.testsuite integration-arquillian-test-apps - 24.0.5 + 24.0.5-PS-1-SNAPSHOT 4.0.0 diff --git a/testsuite/integration-arquillian/test-apps/test-apps-dist/pom.xml b/testsuite/integration-arquillian/test-apps/test-apps-dist/pom.xml index 961e777bbf2a..159d6106d774 100644 --- a/testsuite/integration-arquillian/test-apps/test-apps-dist/pom.xml +++ b/testsuite/integration-arquillian/test-apps/test-apps-dist/pom.xml @@ -5,7 +5,7 @@ integration-arquillian-test-apps org.keycloak.testsuite - 24.0.5 + 24.0.5-PS-1-SNAPSHOT 4.0.0 diff --git a/testsuite/integration-arquillian/tests/base/pom.xml b/testsuite/integration-arquillian/tests/base/pom.xml index 46a19ebf6ab6..a6c8f0e20de1 100644 --- a/testsuite/integration-arquillian/tests/base/pom.xml +++ b/testsuite/integration-arquillian/tests/base/pom.xml @@ -21,7 +21,7 @@ org.keycloak.testsuite integration-arquillian-tests - 24.0.5 + 24.0.5-PS-1-SNAPSHOT 4.0.0 diff --git a/testsuite/integration-arquillian/tests/other/adapters/jboss/pom.xml b/testsuite/integration-arquillian/tests/other/adapters/jboss/pom.xml index 0f2c45e68b97..39b10fde50bb 100644 --- a/testsuite/integration-arquillian/tests/other/adapters/jboss/pom.xml +++ b/testsuite/integration-arquillian/tests/other/adapters/jboss/pom.xml @@ -24,7 +24,7 @@ org.keycloak.testsuite integration-arquillian-tests-adapters - 24.0.5 + 24.0.5-PS-1-SNAPSHOT integration-arquillian-tests-adapters-jboss diff --git a/testsuite/integration-arquillian/tests/other/adapters/karaf/fuse61/pom.xml b/testsuite/integration-arquillian/tests/other/adapters/karaf/fuse61/pom.xml index 6dcee61df8c0..37e99766105f 100644 --- a/testsuite/integration-arquillian/tests/other/adapters/karaf/fuse61/pom.xml +++ b/testsuite/integration-arquillian/tests/other/adapters/karaf/fuse61/pom.xml @@ -24,7 +24,7 @@ org.keycloak.testsuite integration-arquillian-tests-adapters-karaf - 24.0.5 + 24.0.5-PS-1-SNAPSHOT integration-arquillian-tests-adapters-fuse61 diff --git a/testsuite/integration-arquillian/tests/other/adapters/karaf/fuse62/pom.xml b/testsuite/integration-arquillian/tests/other/adapters/karaf/fuse62/pom.xml index c66732beabfc..85a5af68f075 100644 --- a/testsuite/integration-arquillian/tests/other/adapters/karaf/fuse62/pom.xml +++ b/testsuite/integration-arquillian/tests/other/adapters/karaf/fuse62/pom.xml @@ -24,7 +24,7 @@ org.keycloak.testsuite integration-arquillian-tests-adapters-karaf - 24.0.5 + 24.0.5-PS-1-SNAPSHOT integration-arquillian-tests-adapters-fuse62 diff --git a/testsuite/integration-arquillian/tests/other/adapters/karaf/karaf3/pom.xml b/testsuite/integration-arquillian/tests/other/adapters/karaf/karaf3/pom.xml index dbe6e0ed2c56..b8535d4075fc 100644 --- a/testsuite/integration-arquillian/tests/other/adapters/karaf/karaf3/pom.xml +++ b/testsuite/integration-arquillian/tests/other/adapters/karaf/karaf3/pom.xml @@ -24,7 +24,7 @@ org.keycloak.testsuite integration-arquillian-tests-adapters-karaf - 24.0.5 + 24.0.5-PS-1-SNAPSHOT integration-arquillian-tests-adapters-karaf3 diff --git a/testsuite/integration-arquillian/tests/other/adapters/karaf/pom.xml b/testsuite/integration-arquillian/tests/other/adapters/karaf/pom.xml index 2c0ee171876c..a0cfc133cd8e 100644 --- a/testsuite/integration-arquillian/tests/other/adapters/karaf/pom.xml +++ b/testsuite/integration-arquillian/tests/other/adapters/karaf/pom.xml @@ -24,7 +24,7 @@ org.keycloak.testsuite integration-arquillian-tests-adapters - 24.0.5 + 24.0.5-PS-1-SNAPSHOT integration-arquillian-tests-adapters-karaf diff --git a/testsuite/integration-arquillian/tests/other/adapters/pom.xml b/testsuite/integration-arquillian/tests/other/adapters/pom.xml index f23388b512e5..9cf8e969d64e 100644 --- a/testsuite/integration-arquillian/tests/other/adapters/pom.xml +++ b/testsuite/integration-arquillian/tests/other/adapters/pom.xml @@ -24,7 +24,7 @@ org.keycloak.testsuite integration-arquillian-tests-other - 24.0.5 + 24.0.5-PS-1-SNAPSHOT integration-arquillian-tests-adapters diff --git a/testsuite/integration-arquillian/tests/other/adapters/was/pom.xml b/testsuite/integration-arquillian/tests/other/adapters/was/pom.xml index 31d5fffca1ac..47f517981707 100644 --- a/testsuite/integration-arquillian/tests/other/adapters/was/pom.xml +++ b/testsuite/integration-arquillian/tests/other/adapters/was/pom.xml @@ -24,7 +24,7 @@ org.keycloak.testsuite integration-arquillian-tests-adapters - 24.0.5 + 24.0.5-PS-1-SNAPSHOT integration-arquillian-tests-adapters-was diff --git a/testsuite/integration-arquillian/tests/other/adapters/was/was8/pom.xml b/testsuite/integration-arquillian/tests/other/adapters/was/was8/pom.xml index 2966a93ae6c6..8fa1fa6bd7e5 100644 --- a/testsuite/integration-arquillian/tests/other/adapters/was/was8/pom.xml +++ b/testsuite/integration-arquillian/tests/other/adapters/was/was8/pom.xml @@ -24,7 +24,7 @@ org.keycloak.testsuite integration-arquillian-tests-adapters-was - 24.0.5 + 24.0.5-PS-1-SNAPSHOT integration-arquillian-tests-adapters-was8 diff --git a/testsuite/integration-arquillian/tests/other/adapters/wls/pom.xml b/testsuite/integration-arquillian/tests/other/adapters/wls/pom.xml index 3fae907682bb..00a5f48e610e 100644 --- a/testsuite/integration-arquillian/tests/other/adapters/wls/pom.xml +++ b/testsuite/integration-arquillian/tests/other/adapters/wls/pom.xml @@ -24,7 +24,7 @@ org.keycloak.testsuite integration-arquillian-tests-adapters - 24.0.5 + 24.0.5-PS-1-SNAPSHOT integration-arquillian-tests-adapters-wls diff --git a/testsuite/integration-arquillian/tests/other/adapters/wls/wls12/pom.xml b/testsuite/integration-arquillian/tests/other/adapters/wls/wls12/pom.xml index 10d9979d0d15..1dc653512288 100644 --- a/testsuite/integration-arquillian/tests/other/adapters/wls/wls12/pom.xml +++ b/testsuite/integration-arquillian/tests/other/adapters/wls/wls12/pom.xml @@ -24,7 +24,7 @@ org.keycloak.testsuite integration-arquillian-tests-adapters-wls - 24.0.5 + 24.0.5-PS-1-SNAPSHOT integration-arquillian-tests-adapters-wls12 diff --git a/testsuite/integration-arquillian/tests/other/base-ui/pom.xml b/testsuite/integration-arquillian/tests/other/base-ui/pom.xml index e85f842bb01f..9a655c658a2b 100644 --- a/testsuite/integration-arquillian/tests/other/base-ui/pom.xml +++ b/testsuite/integration-arquillian/tests/other/base-ui/pom.xml @@ -22,7 +22,7 @@ integration-arquillian-tests-other org.keycloak.testsuite - 24.0.5 + 24.0.5-PS-1-SNAPSHOT 4.0.0 diff --git a/testsuite/integration-arquillian/tests/other/jpa-performance/pom.xml b/testsuite/integration-arquillian/tests/other/jpa-performance/pom.xml index f84cbd857bbf..af94476f12c9 100644 --- a/testsuite/integration-arquillian/tests/other/jpa-performance/pom.xml +++ b/testsuite/integration-arquillian/tests/other/jpa-performance/pom.xml @@ -24,7 +24,7 @@ org.keycloak.testsuite integration-arquillian-tests-other - 24.0.5 + 24.0.5-PS-1-SNAPSHOT integration-arquillian-tests-jpa-performance diff --git a/testsuite/integration-arquillian/tests/other/mod_auth_mellon/pom.xml b/testsuite/integration-arquillian/tests/other/mod_auth_mellon/pom.xml index 2105c3cfab01..a878ed7804f2 100644 --- a/testsuite/integration-arquillian/tests/other/mod_auth_mellon/pom.xml +++ b/testsuite/integration-arquillian/tests/other/mod_auth_mellon/pom.xml @@ -24,7 +24,7 @@ org.keycloak.testsuite integration-arquillian-tests-other - 24.0.5 + 24.0.5-PS-1-SNAPSHOT integration-arquillian-tests-other-mod_auth_mellon diff --git a/testsuite/integration-arquillian/tests/other/pom.xml b/testsuite/integration-arquillian/tests/other/pom.xml index de45b6402f57..acedec213239 100644 --- a/testsuite/integration-arquillian/tests/other/pom.xml +++ b/testsuite/integration-arquillian/tests/other/pom.xml @@ -24,7 +24,7 @@ org.keycloak.testsuite integration-arquillian-tests - 24.0.5 + 24.0.5-PS-1-SNAPSHOT integration-arquillian-tests-other diff --git a/testsuite/integration-arquillian/tests/other/springboot-tests/pom.xml b/testsuite/integration-arquillian/tests/other/springboot-tests/pom.xml index 358a6ec4e086..fba69e990c78 100644 --- a/testsuite/integration-arquillian/tests/other/springboot-tests/pom.xml +++ b/testsuite/integration-arquillian/tests/other/springboot-tests/pom.xml @@ -5,7 +5,7 @@ integration-arquillian-tests-other org.keycloak.testsuite - 24.0.5 + 24.0.5-PS-1-SNAPSHOT 4.0.0 diff --git a/testsuite/integration-arquillian/tests/other/sssd/pom.xml b/testsuite/integration-arquillian/tests/other/sssd/pom.xml index 42c4f84a8cc6..4b4582987dcf 100644 --- a/testsuite/integration-arquillian/tests/other/sssd/pom.xml +++ b/testsuite/integration-arquillian/tests/other/sssd/pom.xml @@ -5,7 +5,7 @@ integration-arquillian-tests-other org.keycloak.testsuite - 24.0.5 + 24.0.5-PS-1-SNAPSHOT 4.0.0 diff --git a/testsuite/integration-arquillian/tests/other/webauthn/pom.xml b/testsuite/integration-arquillian/tests/other/webauthn/pom.xml index a8e70f4f283c..2512a291c6a3 100644 --- a/testsuite/integration-arquillian/tests/other/webauthn/pom.xml +++ b/testsuite/integration-arquillian/tests/other/webauthn/pom.xml @@ -5,7 +5,7 @@ org.keycloak.testsuite integration-arquillian-tests-other - 24.0.5 + 24.0.5-PS-1-SNAPSHOT 4.0.0 jar diff --git a/testsuite/integration-arquillian/tests/pom.xml b/testsuite/integration-arquillian/tests/pom.xml index e2dbba1f5cb4..e27ad54c0b27 100644 --- a/testsuite/integration-arquillian/tests/pom.xml +++ b/testsuite/integration-arquillian/tests/pom.xml @@ -24,7 +24,7 @@ org.keycloak.testsuite integration-arquillian - 24.0.5 + 24.0.5-PS-1-SNAPSHOT pom diff --git a/testsuite/integration-arquillian/util/pom.xml b/testsuite/integration-arquillian/util/pom.xml index 3126226b047d..7cccc90b6492 100644 --- a/testsuite/integration-arquillian/util/pom.xml +++ b/testsuite/integration-arquillian/util/pom.xml @@ -21,7 +21,7 @@ org.keycloak.testsuite integration-arquillian - 24.0.5 + 24.0.5-PS-1-SNAPSHOT 4.0.0 diff --git a/testsuite/model/pom.xml b/testsuite/model/pom.xml index 722dea9b6970..c98dfefaa072 100644 --- a/testsuite/model/pom.xml +++ b/testsuite/model/pom.xml @@ -4,7 +4,7 @@ org.keycloak keycloak-testsuite-pom - 24.0.5 + 24.0.5-PS-1-SNAPSHOT ../pom.xml diff --git a/testsuite/pom.xml b/testsuite/pom.xml index d5358ffbc51b..8eb6041370af 100755 --- a/testsuite/pom.xml +++ b/testsuite/pom.xml @@ -21,7 +21,7 @@ keycloak-parent org.keycloak - 24.0.5 + 24.0.5-PS-1-SNAPSHOT ../pom.xml 4.0.0 diff --git a/testsuite/utils/pom.xml b/testsuite/utils/pom.xml index 1121b82b0230..249c69f12f2e 100755 --- a/testsuite/utils/pom.xml +++ b/testsuite/utils/pom.xml @@ -21,7 +21,7 @@ keycloak-testsuite-pom org.keycloak - 24.0.5 + 24.0.5-PS-1-SNAPSHOT 4.0.0 diff --git a/themes/pom.xml b/themes/pom.xml index d5c0c67f87c8..ccd3249d5862 100755 --- a/themes/pom.xml +++ b/themes/pom.xml @@ -3,7 +3,7 @@ keycloak-parent org.keycloak - 24.0.5 + 24.0.5-PS-1-SNAPSHOT 4.0.0 diff --git a/util/embedded-ldap/pom.xml b/util/embedded-ldap/pom.xml index ed5e8ade7f91..252a9e781c7b 100644 --- a/util/embedded-ldap/pom.xml +++ b/util/embedded-ldap/pom.xml @@ -21,7 +21,7 @@ keycloak-parent org.keycloak - 24.0.5 + 24.0.5-PS-1-SNAPSHOT ../../pom.xml 4.0.0 diff --git a/util/pom.xml b/util/pom.xml index 8aaa239ac4aa..5f6928364b21 100644 --- a/util/pom.xml +++ b/util/pom.xml @@ -20,7 +20,7 @@ keycloak-parent org.keycloak - 24.0.5 + 24.0.5-PS-1-SNAPSHOT ../pom.xml From 5c657728852c4db92b6468475cb2d8e8c3eb21d4 Mon Sep 17 00:00:00 2001 From: Patrick Weiner Date: Thu, 20 Jun 2024 08:35:00 +0200 Subject: [PATCH 155/158] Set version to 24.0.5-PS-1 --- .github/workflows/js-ci.yml | 12 ++++++------ adapters/oidc/adapter-core/pom.xml | 2 +- adapters/oidc/installed/pom.xml | 2 +- adapters/oidc/jakarta-servlet-filter/pom.xml | 2 +- adapters/oidc/jaxrs-oauth-client/pom.xml | 2 +- adapters/oidc/jetty/jetty-core/pom.xml | 2 +- adapters/oidc/jetty/jetty9.4/pom.xml | 2 +- adapters/oidc/jetty/pom.xml | 2 +- adapters/oidc/js/pom.xml | 2 +- adapters/oidc/pom.xml | 2 +- adapters/oidc/servlet-filter/pom.xml | 2 +- adapters/oidc/spring-boot-adapter-core/pom.xml | 2 +- adapters/oidc/spring-boot-container-bundle/pom.xml | 2 +- adapters/oidc/spring-boot2/pom.xml | 2 +- adapters/oidc/spring-security/pom.xml | 2 +- adapters/oidc/tomcat/pom.xml | 2 +- adapters/oidc/tomcat/tomcat-core/pom.xml | 2 +- adapters/oidc/tomcat/tomcat/pom.xml | 2 +- adapters/oidc/undertow/pom.xml | 2 +- adapters/oidc/wildfly-elytron/pom.xml | 2 +- adapters/oidc/wildfly/pom.xml | 2 +- adapters/oidc/wildfly/wildfly-subsystem/pom.xml | 2 +- adapters/pom.xml | 2 +- adapters/saml/core-jakarta/pom.xml | 2 +- adapters/saml/core-public/pom.xml | 2 +- adapters/saml/core/pom.xml | 2 +- adapters/saml/jakarta-servlet-filter/pom.xml | 2 +- adapters/saml/jetty/jetty-core/pom.xml | 2 +- adapters/saml/jetty/jetty9.4/pom.xml | 2 +- adapters/saml/jetty/pom.xml | 2 +- adapters/saml/pom.xml | 2 +- adapters/saml/servlet-filter/pom.xml | 2 +- adapters/saml/tomcat/pom.xml | 2 +- adapters/saml/tomcat/tomcat-core/pom.xml | 2 +- adapters/saml/tomcat/tomcat/pom.xml | 2 +- adapters/saml/undertow/pom.xml | 2 +- adapters/saml/wildfly-elytron-jakarta/pom.xml | 2 +- adapters/saml/wildfly-elytron/pom.xml | 2 +- adapters/saml/wildfly/pom.xml | 2 +- .../saml/wildfly/wildfly-jakarta-subsystem/pom.xml | 2 +- adapters/saml/wildfly/wildfly-subsystem/pom.xml | 2 +- adapters/spi/adapter-spi/pom.xml | 2 +- adapters/spi/jakarta-servlet-adapter-spi/pom.xml | 2 +- adapters/spi/jboss-adapter-core/pom.xml | 2 +- adapters/spi/jetty-adapter-spi/pom.xml | 2 +- adapters/spi/pom.xml | 2 +- adapters/spi/servlet-adapter-spi/pom.xml | 2 +- adapters/spi/tomcat-adapter-spi/pom.xml | 2 +- adapters/spi/undertow-adapter-spi/pom.xml | 2 +- authz/client/pom.xml | 2 +- authz/policy-enforcer/pom.xml | 2 +- authz/policy/common/pom.xml | 2 +- authz/policy/pom.xml | 2 +- authz/pom.xml | 2 +- boms/adapter/pom.xml | 2 +- boms/misc/pom.xml | 2 +- boms/pom.xml | 2 +- boms/spi/pom.xml | 2 +- common/pom.xml | 2 +- core/pom.xml | 2 +- crypto/default/pom.xml | 2 +- crypto/elytron/pom.xml | 2 +- crypto/fips1402/pom.xml | 2 +- crypto/pom.xml | 2 +- dependencies/pom.xml | 2 +- dependencies/server-all/pom.xml | 2 +- dependencies/server-min/pom.xml | 2 +- distribution/adapters/pom.xml | 2 +- distribution/adapters/tomcat-adapter-zip/pom.xml | 2 +- distribution/adapters/wildfly-adapter/pom.xml | 2 +- distribution/api-docs-dist/pom.xml | 2 +- distribution/downloads/pom.xml | 2 +- .../feature-packs/adapter-feature-pack/pom.xml | 2 +- distribution/feature-packs/pom.xml | 2 +- distribution/galleon-feature-packs/pom.xml | 2 +- .../pom.xml | 2 +- .../saml-adapter-galleon-pack/pom.xml | 2 +- distribution/licenses-common/pom.xml | 2 +- .../maven-plugins/licenses-processor/pom.xml | 2 +- distribution/maven-plugins/pom.xml | 2 +- distribution/pom.xml | 2 +- distribution/saml-adapters/pom.xml | 2 +- .../saml-adapters/tomcat-adapter-zip/pom.xml | 2 +- distribution/saml-adapters/wildfly-adapter/pom.xml | 2 +- .../wildfly-adapter-jakarta-zip/pom.xml | 2 +- .../wildfly-adapter/wildfly-adapter-zip/pom.xml | 2 +- .../wildfly-adapter/wildfly-jakarta-modules/pom.xml | 2 +- .../wildfly-adapter/wildfly-modules/pom.xml | 2 +- docs/documentation/aggregation/pom.xml | 2 +- docs/documentation/api_documentation/pom.xml | 2 +- docs/documentation/authorization_services/pom.xml | 2 +- docs/documentation/dist/pom.xml | 2 +- docs/documentation/header-maven-plugin/pom.xml | 4 ++-- docs/documentation/pom.xml | 4 ++-- docs/documentation/release_notes/pom.xml | 2 +- docs/documentation/securing_apps/pom.xml | 2 +- docs/documentation/server_admin/pom.xml | 2 +- docs/documentation/server_development/pom.xml | 2 +- docs/documentation/tests/pom.xml | 2 +- .../topics/templates/document-attributes.adoc | 8 ++++---- docs/documentation/upgrading/pom.xml | 2 +- docs/guides/pom.xml | 2 +- docs/maven-plugin/pom.xml | 2 +- docs/pom.xml | 2 +- examples/admin-client/pom.xml | 2 +- examples/js-console/pom.xml | 2 +- examples/kerberos/pom.xml | 2 +- examples/ldap/pom.xml | 2 +- examples/pom.xml | 2 +- examples/providers/authenticator/pom.xml | 2 +- examples/providers/pom.xml | 2 +- examples/providers/rest/pom.xml | 2 +- examples/saml/pom.xml | 2 +- examples/saml/servlet-filter/pom.xml | 2 +- examples/themes/pom.xml | 2 +- federation/kerberos/pom.xml | 2 +- federation/ldap/pom.xml | 2 +- federation/pom.xml | 2 +- federation/sssd/pom.xml | 2 +- integration/admin-client-jee/pom.xml | 2 +- integration/admin-client/pom.xml | 2 +- integration/client-cli/admin-cli/pom.xml | 2 +- integration/client-cli/client-cli-dist/pom.xml | 2 +- .../client-cli/client-registration-cli/pom.xml | 2 +- integration/client-cli/pom.xml | 2 +- integration/client-registration/pom.xml | 2 +- integration/pom.xml | 2 +- js/apps/account-ui/pom.xml | 2 +- js/apps/admin-ui/pom.xml | 2 +- js/libs/keycloak-admin-client/package.json | 2 +- js/libs/keycloak-admin-client/pom.xml | 2 +- js/libs/keycloak-js/package.json | 2 +- js/libs/keycloak-js/pom.xml | 2 +- js/pom.xml | 2 +- misc/keycloak-test-helper/pom.xml | 2 +- misc/pom.xml | 2 +- .../keycloak-spring-boot-starter/pom.xml | 2 +- misc/spring-boot-starter/pom.xml | 2 +- model/infinispan/pom.xml | 2 +- model/jpa/pom.xml | 2 +- model/legacy/pom.xml | 2 +- model/pom.xml | 2 +- model/storage-private/pom.xml | 2 +- model/storage-services/pom.xml | 2 +- model/storage/pom.xml | 2 +- operator/pom.xml | 2 +- pom.xml | 4 ++-- quarkus/config-api/pom.xml | 2 +- quarkus/container/Dockerfile | 2 +- quarkus/deployment/pom.xml | 2 +- quarkus/dist/pom.xml | 2 +- quarkus/pom.xml | 2 +- quarkus/runtime/pom.xml | 2 +- quarkus/server/pom.xml | 2 +- quarkus/tests/integration/pom.xml | 2 +- quarkus/tests/junit5/pom.xml | 2 +- quarkus/tests/pom.xml | 2 +- release-details | 6 +++--- rest/admin-ui-ext/pom.xml | 2 +- rest/pom.xml | 2 +- saml-core-api/pom.xml | 2 +- saml-core/pom.xml | 2 +- server-spi-private/pom.xml | 2 +- server-spi/pom.xml | 2 +- services/pom.xml | 2 +- testsuite/db-allocator-plugin/pom.xml | 2 +- testsuite/integration-arquillian/pom.xml | 2 +- .../servers/adapter-spi/pom.xml | 2 +- .../adapter-spi/undertow-adapter-jakarta/pom.xml | 2 +- .../undertow-adapter-saml-jakarta/pom.xml | 2 +- .../adapter-spi/undertow-adapter-spi-jakarta/pom.xml | 2 +- .../servers/app-server/app-server-spi/pom.xml | 2 +- .../servers/app-server/jboss/eap/pom.xml | 2 +- .../servers/app-server/jboss/eap6/pom.xml | 2 +- .../servers/app-server/jboss/galleon/pom.xml | 2 +- .../servers/app-server/jboss/pom.xml | 2 +- .../servers/app-server/jboss/wildfly/pom.xml | 2 +- .../servers/app-server/jetty/94/pom.xml | 2 +- .../servers/app-server/jetty/common/pom.xml | 2 +- .../servers/app-server/jetty/pom.xml | 2 +- .../servers/app-server/karaf/fuse63/pom.xml | 2 +- .../servers/app-server/karaf/fuse7x/pom.xml | 2 +- .../servers/app-server/karaf/pom.xml | 2 +- .../servers/app-server/pom.xml | 2 +- .../servers/app-server/tomcat/common/pom.xml | 2 +- .../servers/app-server/tomcat/pom.xml | 2 +- .../servers/app-server/tomcat/tomcat8/pom.xml | 2 +- .../servers/app-server/tomcat/tomcat9/pom.xml | 2 +- .../servers/app-server/undertow/pom.xml | 2 +- .../servers/auth-server/pom.xml | 2 +- .../servers/auth-server/quarkus/pom.xml | 2 +- .../servers/auth-server/services/pom.xml | 2 +- .../services/testsuite-providers-deployment/pom.xml | 2 +- .../auth-server/services/testsuite-providers/pom.xml | 2 +- .../servers/auth-server/undertow/pom.xml | 2 +- .../servers/cache-server/infinispan/datagrid/pom.xml | 2 +- .../cache-server/infinispan/infinispan/pom.xml | 2 +- .../servers/cache-server/infinispan/pom.xml | 2 +- .../servers/cache-server/legacy/datagrid/pom.xml | 2 +- .../servers/cache-server/legacy/infinispan/pom.xml | 2 +- .../servers/cache-server/legacy/pom.xml | 2 +- .../servers/cache-server/pom.xml | 2 +- .../integration-arquillian/servers/migration/pom.xml | 2 +- testsuite/integration-arquillian/servers/pom.xml | 2 +- .../test-apps/app-profile-jee/pom.xml | 2 +- .../test-apps/cors/angular-product/pom.xml | 2 +- .../test-apps/cors/database-service/pom.xml | 2 +- .../integration-arquillian/test-apps/cors/pom.xml | 2 +- .../test-apps/fuse/camel-fuse7-undertow/pom.xml | 2 +- .../test-apps/fuse/camel/pom.xml | 2 +- .../test-apps/fuse/customer-app-fuse/pom.xml | 2 +- .../test-apps/fuse/cxf-jaxrs-fuse7-undertow/pom.xml | 2 +- .../test-apps/fuse/cxf-jaxrs/pom.xml | 2 +- .../test-apps/fuse/cxf-jaxws-fuse7-undertow/pom.xml | 2 +- .../test-apps/fuse/cxf-jaxws/pom.xml | 2 +- .../test-apps/fuse/external-config/pom.xml | 2 +- .../test-apps/fuse/features/pom.xml | 2 +- .../integration-arquillian/test-apps/fuse/pom.xml | 2 +- .../test-apps/fuse/product-app-fuse/pom.xml | 2 +- .../fuse/product-app-fuse7-undertow/pom.xml | 2 +- .../test-apps/hello-world-authz-service/pom.xml | 2 +- testsuite/integration-arquillian/test-apps/pom.xml | 2 +- .../test-apps/servlet-authz/pom.xml | 2 +- .../test-apps/servlet-policy-enforcer/pom.xml | 2 +- .../test-apps/servlets-jakarta/pom.xml | 2 +- .../test-apps/servlets/pom.xml | 2 +- .../test-apps/spring-boot-adapter-app/pom.xml | 2 +- .../test-apps/test-apps-dist/pom.xml | 2 +- testsuite/integration-arquillian/tests/base/pom.xml | 2 +- .../tests/other/adapters/jboss/pom.xml | 2 +- .../tests/other/adapters/karaf/fuse61/pom.xml | 2 +- .../tests/other/adapters/karaf/fuse62/pom.xml | 2 +- .../tests/other/adapters/karaf/karaf3/pom.xml | 2 +- .../tests/other/adapters/karaf/pom.xml | 2 +- .../tests/other/adapters/pom.xml | 2 +- .../tests/other/adapters/was/pom.xml | 2 +- .../tests/other/adapters/was/was8/pom.xml | 2 +- .../tests/other/adapters/wls/pom.xml | 2 +- .../tests/other/adapters/wls/wls12/pom.xml | 2 +- .../tests/other/base-ui/pom.xml | 2 +- .../tests/other/jpa-performance/pom.xml | 2 +- .../tests/other/mod_auth_mellon/pom.xml | 2 +- testsuite/integration-arquillian/tests/other/pom.xml | 2 +- .../tests/other/springboot-tests/pom.xml | 2 +- .../integration-arquillian/tests/other/sssd/pom.xml | 2 +- .../tests/other/webauthn/pom.xml | 2 +- testsuite/integration-arquillian/tests/pom.xml | 2 +- testsuite/integration-arquillian/util/pom.xml | 2 +- testsuite/model/pom.xml | 2 +- testsuite/pom.xml | 2 +- testsuite/utils/pom.xml | 2 +- themes/pom.xml | 2 +- util/embedded-ldap/pom.xml | 2 +- util/pom.xml | 2 +- 254 files changed, 267 insertions(+), 267 deletions(-) diff --git a/.github/workflows/js-ci.yml b/.github/workflows/js-ci.yml index bccca7ac81b1..5d828cbbd5e8 100644 --- a/.github/workflows/js-ci.yml +++ b/.github/workflows/js-ci.yml @@ -54,13 +54,13 @@ jobs: - name: Build Keycloak run: | ./mvnw clean install --errors -DskipTests -DskipTestsuite -DskipExamples -Pdistribution - mv ./quarkus/dist/target/keycloak-24.0.5-PS-1-SNAPSHOT.tar.gz ./keycloak-24.0.5-PS-1-SNAPSHOT.tar.gz + mv ./quarkus/dist/target/keycloak-24.0.5-PS-1.tar.gz ./keycloak-24.0.5-PS-1.tar.gz - name: Upload Keycloak dist uses: actions/upload-artifact@v3 with: name: keycloak - path: keycloak-24.0.5-PS-1-SNAPSHOT.tar.gz + path: keycloak-24.0.5-PS-1.tar.gz admin-client: name: Admin Client @@ -214,8 +214,8 @@ jobs: - name: Start Keycloak server run: | - tar xfvz keycloak-24.0.5-PS-1-SNAPSHOT.tar.gz - keycloak-24.0.5-PS-1-SNAPSHOT/bin/kc.sh start-dev --features=transient-users &> ~/server.log & + tar xfvz keycloak-24.0.5-PS-1.tar.gz + keycloak-24.0.5-PS-1/bin/kc.sh start-dev --features=transient-users &> ~/server.log & env: KEYCLOAK_ADMIN: admin KEYCLOAK_ADMIN_PASSWORD: admin @@ -297,8 +297,8 @@ jobs: - name: Start Keycloak server run: | - tar xfvz keycloak-24.0.5-PS-1-SNAPSHOT.tar.gz - keycloak-24.0.5-PS-1-SNAPSHOT/bin/kc.sh start-dev --features=admin-fine-grained-authz,transient-users &> ~/server.log & + tar xfvz keycloak-24.0.5-PS-1.tar.gz + keycloak-24.0.5-PS-1/bin/kc.sh start-dev --features=admin-fine-grained-authz,transient-users &> ~/server.log & env: KEYCLOAK_ADMIN: admin KEYCLOAK_ADMIN_PASSWORD: admin diff --git a/adapters/oidc/adapter-core/pom.xml b/adapters/oidc/adapter-core/pom.xml index 89a1259510de..7a9cc3caca41 100755 --- a/adapters/oidc/adapter-core/pom.xml +++ b/adapters/oidc/adapter-core/pom.xml @@ -21,7 +21,7 @@ keycloak-parent org.keycloak - 24.0.5-PS-1-SNAPSHOT + 24.0.5-PS-1 ../../../pom.xml 4.0.0 diff --git a/adapters/oidc/installed/pom.xml b/adapters/oidc/installed/pom.xml index 7f9a4489466f..85a08037159c 100755 --- a/adapters/oidc/installed/pom.xml +++ b/adapters/oidc/installed/pom.xml @@ -21,7 +21,7 @@ keycloak-parent org.keycloak - 24.0.5-PS-1-SNAPSHOT + 24.0.5-PS-1 ../../../pom.xml 4.0.0 diff --git a/adapters/oidc/jakarta-servlet-filter/pom.xml b/adapters/oidc/jakarta-servlet-filter/pom.xml index 2a8af0f47d28..07d76d27f169 100755 --- a/adapters/oidc/jakarta-servlet-filter/pom.xml +++ b/adapters/oidc/jakarta-servlet-filter/pom.xml @@ -21,7 +21,7 @@ keycloak-parent org.keycloak - 24.0.5-PS-1-SNAPSHOT + 24.0.5-PS-1 ../../../pom.xml 4.0.0 diff --git a/adapters/oidc/jaxrs-oauth-client/pom.xml b/adapters/oidc/jaxrs-oauth-client/pom.xml index abc1d20b77b4..9c856618c90e 100755 --- a/adapters/oidc/jaxrs-oauth-client/pom.xml +++ b/adapters/oidc/jaxrs-oauth-client/pom.xml @@ -21,7 +21,7 @@ keycloak-parent org.keycloak - 24.0.5-PS-1-SNAPSHOT + 24.0.5-PS-1 ../../../pom.xml 4.0.0 diff --git a/adapters/oidc/jetty/jetty-core/pom.xml b/adapters/oidc/jetty/jetty-core/pom.xml index c4bbd5857417..c0d6acf769bd 100755 --- a/adapters/oidc/jetty/jetty-core/pom.xml +++ b/adapters/oidc/jetty/jetty-core/pom.xml @@ -21,7 +21,7 @@ keycloak-parent org.keycloak - 24.0.5-PS-1-SNAPSHOT + 24.0.5-PS-1 ../../../../pom.xml 4.0.0 diff --git a/adapters/oidc/jetty/jetty9.4/pom.xml b/adapters/oidc/jetty/jetty9.4/pom.xml index ec410b28d3aa..9c02d709e8a1 100644 --- a/adapters/oidc/jetty/jetty9.4/pom.xml +++ b/adapters/oidc/jetty/jetty9.4/pom.xml @@ -21,7 +21,7 @@ keycloak-parent org.keycloak - 24.0.5-PS-1-SNAPSHOT + 24.0.5-PS-1 ../../../../pom.xml 4.0.0 diff --git a/adapters/oidc/jetty/pom.xml b/adapters/oidc/jetty/pom.xml index ed84cd7bf4bc..cba7925012a5 100755 --- a/adapters/oidc/jetty/pom.xml +++ b/adapters/oidc/jetty/pom.xml @@ -20,7 +20,7 @@ keycloak-parent org.keycloak - 24.0.5-PS-1-SNAPSHOT + 24.0.5-PS-1 ../../../pom.xml Keycloak Jetty Integration diff --git a/adapters/oidc/js/pom.xml b/adapters/oidc/js/pom.xml index fd3f64843841..a2b84676d729 100644 --- a/adapters/oidc/js/pom.xml +++ b/adapters/oidc/js/pom.xml @@ -5,7 +5,7 @@ keycloak-parent org.keycloak - 24.0.5-PS-1-SNAPSHOT + 24.0.5-PS-1 ../../../pom.xml diff --git a/adapters/oidc/pom.xml b/adapters/oidc/pom.xml index dd5e34267cf3..44481bade880 100755 --- a/adapters/oidc/pom.xml +++ b/adapters/oidc/pom.xml @@ -20,7 +20,7 @@ keycloak-parent org.keycloak - 24.0.5-PS-1-SNAPSHOT + 24.0.5-PS-1 ../../pom.xml Keycloak OIDC Client Adapter Modules diff --git a/adapters/oidc/servlet-filter/pom.xml b/adapters/oidc/servlet-filter/pom.xml index ea4055505fa1..82a8ae29d524 100755 --- a/adapters/oidc/servlet-filter/pom.xml +++ b/adapters/oidc/servlet-filter/pom.xml @@ -21,7 +21,7 @@ keycloak-parent org.keycloak - 24.0.5-PS-1-SNAPSHOT + 24.0.5-PS-1 ../../../pom.xml 4.0.0 diff --git a/adapters/oidc/spring-boot-adapter-core/pom.xml b/adapters/oidc/spring-boot-adapter-core/pom.xml index 169a3afb9278..8c7d7c15e978 100755 --- a/adapters/oidc/spring-boot-adapter-core/pom.xml +++ b/adapters/oidc/spring-boot-adapter-core/pom.xml @@ -21,7 +21,7 @@ keycloak-parent org.keycloak - 24.0.5-PS-1-SNAPSHOT + 24.0.5-PS-1 ../../../pom.xml 4.0.0 diff --git a/adapters/oidc/spring-boot-container-bundle/pom.xml b/adapters/oidc/spring-boot-container-bundle/pom.xml index 3fc5bca0cf7e..8e8ed307fecc 100644 --- a/adapters/oidc/spring-boot-container-bundle/pom.xml +++ b/adapters/oidc/spring-boot-container-bundle/pom.xml @@ -4,7 +4,7 @@ keycloak-parent org.keycloak - 24.0.5-PS-1-SNAPSHOT + 24.0.5-PS-1 ../../../pom.xml spring-boot-container-bundle diff --git a/adapters/oidc/spring-boot2/pom.xml b/adapters/oidc/spring-boot2/pom.xml index 431e9349fd0a..d4f80533218c 100755 --- a/adapters/oidc/spring-boot2/pom.xml +++ b/adapters/oidc/spring-boot2/pom.xml @@ -21,7 +21,7 @@ keycloak-parent org.keycloak - 24.0.5-PS-1-SNAPSHOT + 24.0.5-PS-1 ../../../pom.xml 4.0.0 diff --git a/adapters/oidc/spring-security/pom.xml b/adapters/oidc/spring-security/pom.xml index f5b3d7306b55..37565ef1b7af 100644 --- a/adapters/oidc/spring-security/pom.xml +++ b/adapters/oidc/spring-security/pom.xml @@ -21,7 +21,7 @@ keycloak-parent org.keycloak - 24.0.5-PS-1-SNAPSHOT + 24.0.5-PS-1 ../../../pom.xml 4.0.0 diff --git a/adapters/oidc/tomcat/pom.xml b/adapters/oidc/tomcat/pom.xml index 94dab66fbae3..174f5c6b7951 100755 --- a/adapters/oidc/tomcat/pom.xml +++ b/adapters/oidc/tomcat/pom.xml @@ -20,7 +20,7 @@ keycloak-parent org.keycloak - 24.0.5-PS-1-SNAPSHOT + 24.0.5-PS-1 ../../../pom.xml Keycloak Tomcat Integration diff --git a/adapters/oidc/tomcat/tomcat-core/pom.xml b/adapters/oidc/tomcat/tomcat-core/pom.xml index 093cea04c170..d0db3e19d801 100755 --- a/adapters/oidc/tomcat/tomcat-core/pom.xml +++ b/adapters/oidc/tomcat/tomcat-core/pom.xml @@ -21,7 +21,7 @@ keycloak-tomcat-integration-pom org.keycloak - 24.0.5-PS-1-SNAPSHOT + 24.0.5-PS-1 ../pom.xml 4.0.0 diff --git a/adapters/oidc/tomcat/tomcat/pom.xml b/adapters/oidc/tomcat/tomcat/pom.xml index 6bf5b31c54bd..464c086fb05b 100755 --- a/adapters/oidc/tomcat/tomcat/pom.xml +++ b/adapters/oidc/tomcat/tomcat/pom.xml @@ -21,7 +21,7 @@ keycloak-tomcat-integration-pom org.keycloak - 24.0.5-PS-1-SNAPSHOT + 24.0.5-PS-1 ../pom.xml 4.0.0 diff --git a/adapters/oidc/undertow/pom.xml b/adapters/oidc/undertow/pom.xml index b2b131d2fd70..bd8830f43d25 100755 --- a/adapters/oidc/undertow/pom.xml +++ b/adapters/oidc/undertow/pom.xml @@ -21,7 +21,7 @@ keycloak-parent org.keycloak - 24.0.5-PS-1-SNAPSHOT + 24.0.5-PS-1 ../../../pom.xml 4.0.0 diff --git a/adapters/oidc/wildfly-elytron/pom.xml b/adapters/oidc/wildfly-elytron/pom.xml index 75623c12dd22..9445991abf64 100755 --- a/adapters/oidc/wildfly-elytron/pom.xml +++ b/adapters/oidc/wildfly-elytron/pom.xml @@ -22,7 +22,7 @@ keycloak-parent org.keycloak - 24.0.5-PS-1-SNAPSHOT + 24.0.5-PS-1 ../../../pom.xml 4.0.0 diff --git a/adapters/oidc/wildfly/pom.xml b/adapters/oidc/wildfly/pom.xml index 3e39e19b9583..148bc50332ad 100755 --- a/adapters/oidc/wildfly/pom.xml +++ b/adapters/oidc/wildfly/pom.xml @@ -20,7 +20,7 @@ keycloak-parent org.keycloak - 24.0.5-PS-1-SNAPSHOT + 24.0.5-PS-1 ../../../pom.xml Keycloak WildFly Integration diff --git a/adapters/oidc/wildfly/wildfly-subsystem/pom.xml b/adapters/oidc/wildfly/wildfly-subsystem/pom.xml index aca4d48e4022..d90aef502e70 100755 --- a/adapters/oidc/wildfly/wildfly-subsystem/pom.xml +++ b/adapters/oidc/wildfly/wildfly-subsystem/pom.xml @@ -21,7 +21,7 @@ org.keycloak keycloak-parent - 24.0.5-PS-1-SNAPSHOT + 24.0.5-PS-1 ../../../../pom.xml diff --git a/adapters/pom.xml b/adapters/pom.xml index b6ec5c8bd15a..fda5c6972c2e 100755 --- a/adapters/pom.xml +++ b/adapters/pom.xml @@ -20,7 +20,7 @@ keycloak-parent org.keycloak - 24.0.5-PS-1-SNAPSHOT + 24.0.5-PS-1 ../pom.xml Keycloak Adapters diff --git a/adapters/saml/core-jakarta/pom.xml b/adapters/saml/core-jakarta/pom.xml index 7cde93d24501..d35167a1526a 100644 --- a/adapters/saml/core-jakarta/pom.xml +++ b/adapters/saml/core-jakarta/pom.xml @@ -6,7 +6,7 @@ keycloak-parent org.keycloak - 24.0.5-PS-1-SNAPSHOT + 24.0.5-PS-1 ../../../pom.xml diff --git a/adapters/saml/core-public/pom.xml b/adapters/saml/core-public/pom.xml index 23727e3ca9d8..fdc1fbb494fb 100755 --- a/adapters/saml/core-public/pom.xml +++ b/adapters/saml/core-public/pom.xml @@ -21,7 +21,7 @@ keycloak-parent org.keycloak - 24.0.5-PS-1-SNAPSHOT + 24.0.5-PS-1 ../../../pom.xml 4.0.0 diff --git a/adapters/saml/core/pom.xml b/adapters/saml/core/pom.xml index fa5a8c8e7427..f48c905a97b2 100755 --- a/adapters/saml/core/pom.xml +++ b/adapters/saml/core/pom.xml @@ -21,7 +21,7 @@ keycloak-parent org.keycloak - 24.0.5-PS-1-SNAPSHOT + 24.0.5-PS-1 ../../../pom.xml 4.0.0 diff --git a/adapters/saml/jakarta-servlet-filter/pom.xml b/adapters/saml/jakarta-servlet-filter/pom.xml index 2b4a40a9624c..f4f06301064f 100755 --- a/adapters/saml/jakarta-servlet-filter/pom.xml +++ b/adapters/saml/jakarta-servlet-filter/pom.xml @@ -21,7 +21,7 @@ keycloak-parent org.keycloak - 24.0.5-PS-1-SNAPSHOT + 24.0.5-PS-1 ../../../pom.xml 4.0.0 diff --git a/adapters/saml/jetty/jetty-core/pom.xml b/adapters/saml/jetty/jetty-core/pom.xml index 81e6f9173965..719edd0d3468 100755 --- a/adapters/saml/jetty/jetty-core/pom.xml +++ b/adapters/saml/jetty/jetty-core/pom.xml @@ -21,7 +21,7 @@ keycloak-parent org.keycloak - 24.0.5-PS-1-SNAPSHOT + 24.0.5-PS-1 ../../../../pom.xml 4.0.0 diff --git a/adapters/saml/jetty/jetty9.4/pom.xml b/adapters/saml/jetty/jetty9.4/pom.xml index 49dc9af6cf17..9e44862955f7 100644 --- a/adapters/saml/jetty/jetty9.4/pom.xml +++ b/adapters/saml/jetty/jetty9.4/pom.xml @@ -21,7 +21,7 @@ keycloak-parent org.keycloak - 24.0.5-PS-1-SNAPSHOT + 24.0.5-PS-1 ../../../../pom.xml 4.0.0 diff --git a/adapters/saml/jetty/pom.xml b/adapters/saml/jetty/pom.xml index 6e7ab90d8d08..7c3b937d657d 100755 --- a/adapters/saml/jetty/pom.xml +++ b/adapters/saml/jetty/pom.xml @@ -20,7 +20,7 @@ keycloak-parent org.keycloak - 24.0.5-PS-1-SNAPSHOT + 24.0.5-PS-1 ../../../pom.xml Keycloak SAML Jetty Integration diff --git a/adapters/saml/pom.xml b/adapters/saml/pom.xml index 74b17ed0c9a4..8f4fe7ec9f3e 100755 --- a/adapters/saml/pom.xml +++ b/adapters/saml/pom.xml @@ -20,7 +20,7 @@ keycloak-parent org.keycloak - 24.0.5-PS-1-SNAPSHOT + 24.0.5-PS-1 ../../pom.xml Keycloak SAML Client Adapter Modules diff --git a/adapters/saml/servlet-filter/pom.xml b/adapters/saml/servlet-filter/pom.xml index d8948d331b6a..c1a45f1198b5 100755 --- a/adapters/saml/servlet-filter/pom.xml +++ b/adapters/saml/servlet-filter/pom.xml @@ -21,7 +21,7 @@ keycloak-parent org.keycloak - 24.0.5-PS-1-SNAPSHOT + 24.0.5-PS-1 ../../../pom.xml 4.0.0 diff --git a/adapters/saml/tomcat/pom.xml b/adapters/saml/tomcat/pom.xml index 787827a5891c..4281d19d6d15 100755 --- a/adapters/saml/tomcat/pom.xml +++ b/adapters/saml/tomcat/pom.xml @@ -20,7 +20,7 @@ keycloak-parent org.keycloak - 24.0.5-PS-1-SNAPSHOT + 24.0.5-PS-1 ../../../pom.xml Keycloak SAML Tomcat Integration diff --git a/adapters/saml/tomcat/tomcat-core/pom.xml b/adapters/saml/tomcat/tomcat-core/pom.xml index a04e22e6f3ff..3040f31e7545 100755 --- a/adapters/saml/tomcat/tomcat-core/pom.xml +++ b/adapters/saml/tomcat/tomcat-core/pom.xml @@ -21,7 +21,7 @@ keycloak-saml-tomcat-integration-pom org.keycloak - 24.0.5-PS-1-SNAPSHOT + 24.0.5-PS-1 ../pom.xml 4.0.0 diff --git a/adapters/saml/tomcat/tomcat/pom.xml b/adapters/saml/tomcat/tomcat/pom.xml index 64e0729ecd46..8b55eafecc19 100755 --- a/adapters/saml/tomcat/tomcat/pom.xml +++ b/adapters/saml/tomcat/tomcat/pom.xml @@ -21,7 +21,7 @@ keycloak-saml-tomcat-integration-pom org.keycloak - 24.0.5-PS-1-SNAPSHOT + 24.0.5-PS-1 ../pom.xml 4.0.0 diff --git a/adapters/saml/undertow/pom.xml b/adapters/saml/undertow/pom.xml index d6b47cdac671..b736c9b4edf2 100755 --- a/adapters/saml/undertow/pom.xml +++ b/adapters/saml/undertow/pom.xml @@ -21,7 +21,7 @@ keycloak-parent org.keycloak - 24.0.5-PS-1-SNAPSHOT + 24.0.5-PS-1 ../../../pom.xml 4.0.0 diff --git a/adapters/saml/wildfly-elytron-jakarta/pom.xml b/adapters/saml/wildfly-elytron-jakarta/pom.xml index e527b1a2ace5..0ee6372b631b 100755 --- a/adapters/saml/wildfly-elytron-jakarta/pom.xml +++ b/adapters/saml/wildfly-elytron-jakarta/pom.xml @@ -21,7 +21,7 @@ keycloak-parent org.keycloak - 24.0.5-PS-1-SNAPSHOT + 24.0.5-PS-1 ../../../pom.xml 4.0.0 diff --git a/adapters/saml/wildfly-elytron/pom.xml b/adapters/saml/wildfly-elytron/pom.xml index 0d446a5e5404..9f0ef66a74c0 100755 --- a/adapters/saml/wildfly-elytron/pom.xml +++ b/adapters/saml/wildfly-elytron/pom.xml @@ -21,7 +21,7 @@ keycloak-parent org.keycloak - 24.0.5-PS-1-SNAPSHOT + 24.0.5-PS-1 ../../../pom.xml 4.0.0 diff --git a/adapters/saml/wildfly/pom.xml b/adapters/saml/wildfly/pom.xml index 23af66ce9d54..a03f66306a27 100755 --- a/adapters/saml/wildfly/pom.xml +++ b/adapters/saml/wildfly/pom.xml @@ -20,7 +20,7 @@ keycloak-parent org.keycloak - 24.0.5-PS-1-SNAPSHOT + 24.0.5-PS-1 ../../../pom.xml Keycloak SAML Wildfly Integration diff --git a/adapters/saml/wildfly/wildfly-jakarta-subsystem/pom.xml b/adapters/saml/wildfly/wildfly-jakarta-subsystem/pom.xml index 1201be853a1c..088097749888 100755 --- a/adapters/saml/wildfly/wildfly-jakarta-subsystem/pom.xml +++ b/adapters/saml/wildfly/wildfly-jakarta-subsystem/pom.xml @@ -21,7 +21,7 @@ org.keycloak keycloak-parent - 24.0.5-PS-1-SNAPSHOT + 24.0.5-PS-1 ../../../../pom.xml diff --git a/adapters/saml/wildfly/wildfly-subsystem/pom.xml b/adapters/saml/wildfly/wildfly-subsystem/pom.xml index 4ae82e5d9aad..45dd888d5464 100755 --- a/adapters/saml/wildfly/wildfly-subsystem/pom.xml +++ b/adapters/saml/wildfly/wildfly-subsystem/pom.xml @@ -21,7 +21,7 @@ org.keycloak keycloak-parent - 24.0.5-PS-1-SNAPSHOT + 24.0.5-PS-1 ../../../../pom.xml diff --git a/adapters/spi/adapter-spi/pom.xml b/adapters/spi/adapter-spi/pom.xml index 429f06b92f2f..8ee72d3542e3 100755 --- a/adapters/spi/adapter-spi/pom.xml +++ b/adapters/spi/adapter-spi/pom.xml @@ -21,7 +21,7 @@ keycloak-parent org.keycloak - 24.0.5-PS-1-SNAPSHOT + 24.0.5-PS-1 ../../../pom.xml 4.0.0 diff --git a/adapters/spi/jakarta-servlet-adapter-spi/pom.xml b/adapters/spi/jakarta-servlet-adapter-spi/pom.xml index de784d65e61f..c9ef28722e3b 100755 --- a/adapters/spi/jakarta-servlet-adapter-spi/pom.xml +++ b/adapters/spi/jakarta-servlet-adapter-spi/pom.xml @@ -21,7 +21,7 @@ keycloak-parent org.keycloak - 24.0.5-PS-1-SNAPSHOT + 24.0.5-PS-1 ../../../pom.xml 4.0.0 diff --git a/adapters/spi/jboss-adapter-core/pom.xml b/adapters/spi/jboss-adapter-core/pom.xml index 8ed8428b29bb..98b9af9826f1 100755 --- a/adapters/spi/jboss-adapter-core/pom.xml +++ b/adapters/spi/jboss-adapter-core/pom.xml @@ -21,7 +21,7 @@ keycloak-parent org.keycloak - 24.0.5-PS-1-SNAPSHOT + 24.0.5-PS-1 ../../../pom.xml 4.0.0 diff --git a/adapters/spi/jetty-adapter-spi/pom.xml b/adapters/spi/jetty-adapter-spi/pom.xml index 429d325016d9..e220e24b3567 100755 --- a/adapters/spi/jetty-adapter-spi/pom.xml +++ b/adapters/spi/jetty-adapter-spi/pom.xml @@ -21,7 +21,7 @@ keycloak-parent org.keycloak - 24.0.5-PS-1-SNAPSHOT + 24.0.5-PS-1 ../../../pom.xml 4.0.0 diff --git a/adapters/spi/pom.xml b/adapters/spi/pom.xml index b3ed887f0a87..a43bac320e72 100755 --- a/adapters/spi/pom.xml +++ b/adapters/spi/pom.xml @@ -20,7 +20,7 @@ keycloak-parent org.keycloak - 24.0.5-PS-1-SNAPSHOT + 24.0.5-PS-1 ../../pom.xml Keycloak Client Adapter SPI Modules diff --git a/adapters/spi/servlet-adapter-spi/pom.xml b/adapters/spi/servlet-adapter-spi/pom.xml index cd0c2a26d531..5655d1316dae 100755 --- a/adapters/spi/servlet-adapter-spi/pom.xml +++ b/adapters/spi/servlet-adapter-spi/pom.xml @@ -21,7 +21,7 @@ keycloak-parent org.keycloak - 24.0.5-PS-1-SNAPSHOT + 24.0.5-PS-1 ../../../pom.xml 4.0.0 diff --git a/adapters/spi/tomcat-adapter-spi/pom.xml b/adapters/spi/tomcat-adapter-spi/pom.xml index 662bed713c66..ceebacd40686 100755 --- a/adapters/spi/tomcat-adapter-spi/pom.xml +++ b/adapters/spi/tomcat-adapter-spi/pom.xml @@ -21,7 +21,7 @@ keycloak-parent org.keycloak - 24.0.5-PS-1-SNAPSHOT + 24.0.5-PS-1 ../../../pom.xml 4.0.0 diff --git a/adapters/spi/undertow-adapter-spi/pom.xml b/adapters/spi/undertow-adapter-spi/pom.xml index 1855a2e41439..17ac7a51f23f 100755 --- a/adapters/spi/undertow-adapter-spi/pom.xml +++ b/adapters/spi/undertow-adapter-spi/pom.xml @@ -21,7 +21,7 @@ keycloak-parent org.keycloak - 24.0.5-PS-1-SNAPSHOT + 24.0.5-PS-1 ../../../pom.xml 4.0.0 diff --git a/authz/client/pom.xml b/authz/client/pom.xml index 6ac014cb57a9..0b8dc45415b6 100644 --- a/authz/client/pom.xml +++ b/authz/client/pom.xml @@ -7,7 +7,7 @@ org.keycloak keycloak-authz-parent - 24.0.5-PS-1-SNAPSHOT + 24.0.5-PS-1 ../pom.xml diff --git a/authz/policy-enforcer/pom.xml b/authz/policy-enforcer/pom.xml index 2e6f1bde220b..3f90f40ee955 100755 --- a/authz/policy-enforcer/pom.xml +++ b/authz/policy-enforcer/pom.xml @@ -21,7 +21,7 @@ org.keycloak keycloak-authz-parent - 24.0.5-PS-1-SNAPSHOT + 24.0.5-PS-1 ../pom.xml 4.0.0 diff --git a/authz/policy/common/pom.xml b/authz/policy/common/pom.xml index 3959d9936531..eb16b8fe8f1c 100644 --- a/authz/policy/common/pom.xml +++ b/authz/policy/common/pom.xml @@ -25,7 +25,7 @@ org.keycloak keycloak-authz-provider-parent - 24.0.5-PS-1-SNAPSHOT + 24.0.5-PS-1 ../pom.xml diff --git a/authz/policy/pom.xml b/authz/policy/pom.xml index 2ac7eaf76a31..9848a2d25578 100644 --- a/authz/policy/pom.xml +++ b/authz/policy/pom.xml @@ -7,7 +7,7 @@ org.keycloak keycloak-authz-parent - 24.0.5-PS-1-SNAPSHOT + 24.0.5-PS-1 ../pom.xml diff --git a/authz/pom.xml b/authz/pom.xml index cb19c3618caa..0fe1b84d7e0d 100644 --- a/authz/pom.xml +++ b/authz/pom.xml @@ -7,7 +7,7 @@ org.keycloak keycloak-parent - 24.0.5-PS-1-SNAPSHOT + 24.0.5-PS-1 ../pom.xml diff --git a/boms/adapter/pom.xml b/boms/adapter/pom.xml index 0e8f4cecc805..2248976cfb36 100644 --- a/boms/adapter/pom.xml +++ b/boms/adapter/pom.xml @@ -22,7 +22,7 @@ org.keycloak.bom keycloak-bom-parent - 24.0.5-PS-1-SNAPSHOT + 24.0.5-PS-1 org.keycloak.bom diff --git a/boms/misc/pom.xml b/boms/misc/pom.xml index dda6203e52d5..c92767a37c97 100644 --- a/boms/misc/pom.xml +++ b/boms/misc/pom.xml @@ -22,7 +22,7 @@ org.keycloak.bom keycloak-bom-parent - 24.0.5-PS-1-SNAPSHOT + 24.0.5-PS-1 org.keycloak.bom diff --git a/boms/pom.xml b/boms/pom.xml index 2128b3500f46..c097ce9bf34a 100644 --- a/boms/pom.xml +++ b/boms/pom.xml @@ -27,7 +27,7 @@ org.keycloak.bom keycloak-bom-parent - 24.0.5-PS-1-SNAPSHOT + 24.0.5-PS-1 pom diff --git a/boms/spi/pom.xml b/boms/spi/pom.xml index 92f79f7ce9e7..a7834f7cfdb1 100644 --- a/boms/spi/pom.xml +++ b/boms/spi/pom.xml @@ -23,7 +23,7 @@ org.keycloak.bom keycloak-bom-parent - 24.0.5-PS-1-SNAPSHOT + 24.0.5-PS-1 org.keycloak.bom diff --git a/common/pom.xml b/common/pom.xml index c1c1cef5e11a..7133ed24b2d1 100755 --- a/common/pom.xml +++ b/common/pom.xml @@ -21,7 +21,7 @@ keycloak-parent org.keycloak - 24.0.5-PS-1-SNAPSHOT + 24.0.5-PS-1 ../pom.xml 4.0.0 diff --git a/core/pom.xml b/core/pom.xml index dad4966f1552..ccb19a1ee26f 100755 --- a/core/pom.xml +++ b/core/pom.xml @@ -21,7 +21,7 @@ keycloak-parent org.keycloak - 24.0.5-PS-1-SNAPSHOT + 24.0.5-PS-1 ../pom.xml 4.0.0 diff --git a/crypto/default/pom.xml b/crypto/default/pom.xml index ede21f625137..6968bc319cca 100644 --- a/crypto/default/pom.xml +++ b/crypto/default/pom.xml @@ -21,7 +21,7 @@ keycloak-crypto-parent org.keycloak - 24.0.5-PS-1-SNAPSHOT + 24.0.5-PS-1 ../pom.xml 4.0.0 diff --git a/crypto/elytron/pom.xml b/crypto/elytron/pom.xml index 193c6aba6939..ed17f80b5478 100644 --- a/crypto/elytron/pom.xml +++ b/crypto/elytron/pom.xml @@ -21,7 +21,7 @@ keycloak-crypto-parent org.keycloak - 24.0.5-PS-1-SNAPSHOT + 24.0.5-PS-1 ../pom.xml 4.0.0 diff --git a/crypto/fips1402/pom.xml b/crypto/fips1402/pom.xml index 4df6a949a666..0a7201922f27 100644 --- a/crypto/fips1402/pom.xml +++ b/crypto/fips1402/pom.xml @@ -21,7 +21,7 @@ keycloak-crypto-parent org.keycloak - 24.0.5-PS-1-SNAPSHOT + 24.0.5-PS-1 ../pom.xml 4.0.0 diff --git a/crypto/pom.xml b/crypto/pom.xml index a74e5609ea81..a9b305c970eb 100644 --- a/crypto/pom.xml +++ b/crypto/pom.xml @@ -20,7 +20,7 @@ keycloak-parent org.keycloak - 24.0.5-PS-1-SNAPSHOT + 24.0.5-PS-1 ../pom.xml Keycloak Crypto Parent diff --git a/dependencies/pom.xml b/dependencies/pom.xml index db69d83ceba9..46890379d70d 100755 --- a/dependencies/pom.xml +++ b/dependencies/pom.xml @@ -21,7 +21,7 @@ keycloak-parent org.keycloak - 24.0.5-PS-1-SNAPSHOT + 24.0.5-PS-1 4.0.0 diff --git a/dependencies/server-all/pom.xml b/dependencies/server-all/pom.xml index 88136b3a9cc1..a24901cd1a66 100755 --- a/dependencies/server-all/pom.xml +++ b/dependencies/server-all/pom.xml @@ -21,7 +21,7 @@ keycloak-dependencies-parent org.keycloak - 24.0.5-PS-1-SNAPSHOT + 24.0.5-PS-1 4.0.0 diff --git a/dependencies/server-min/pom.xml b/dependencies/server-min/pom.xml index 2094ba58cea8..179cd15b7c97 100755 --- a/dependencies/server-min/pom.xml +++ b/dependencies/server-min/pom.xml @@ -21,7 +21,7 @@ keycloak-dependencies-parent org.keycloak - 24.0.5-PS-1-SNAPSHOT + 24.0.5-PS-1 4.0.0 diff --git a/distribution/adapters/pom.xml b/distribution/adapters/pom.xml index f8b49affbb9d..0478417bc882 100755 --- a/distribution/adapters/pom.xml +++ b/distribution/adapters/pom.xml @@ -20,7 +20,7 @@ keycloak-distribution-parent org.keycloak - 24.0.5-PS-1-SNAPSHOT + 24.0.5-PS-1 Adapters Distribution Parent diff --git a/distribution/adapters/tomcat-adapter-zip/pom.xml b/distribution/adapters/tomcat-adapter-zip/pom.xml index c3e413a2b55c..6540bdf9f726 100755 --- a/distribution/adapters/tomcat-adapter-zip/pom.xml +++ b/distribution/adapters/tomcat-adapter-zip/pom.xml @@ -21,7 +21,7 @@ keycloak-parent org.keycloak - 24.0.5-PS-1-SNAPSHOT + 24.0.5-PS-1 ../../../pom.xml diff --git a/distribution/adapters/wildfly-adapter/pom.xml b/distribution/adapters/wildfly-adapter/pom.xml index 07821961f606..8fd3b64309c1 100644 --- a/distribution/adapters/wildfly-adapter/pom.xml +++ b/distribution/adapters/wildfly-adapter/pom.xml @@ -21,7 +21,7 @@ keycloak-adapters-distribution-parent org.keycloak - 24.0.5-PS-1-SNAPSHOT + 24.0.5-PS-1 diff --git a/distribution/api-docs-dist/pom.xml b/distribution/api-docs-dist/pom.xml index 2e7c8295c7d5..99f4410f7bf6 100755 --- a/distribution/api-docs-dist/pom.xml +++ b/distribution/api-docs-dist/pom.xml @@ -21,7 +21,7 @@ keycloak-distribution-parent org.keycloak - 24.0.5-PS-1-SNAPSHOT + 24.0.5-PS-1 keycloak-api-docs-dist diff --git a/distribution/downloads/pom.xml b/distribution/downloads/pom.xml index 73d8c4fa12fb..42412225fcfa 100755 --- a/distribution/downloads/pom.xml +++ b/distribution/downloads/pom.xml @@ -21,7 +21,7 @@ keycloak-distribution-parent org.keycloak - 24.0.5-PS-1-SNAPSHOT + 24.0.5-PS-1 keycloak-dist-downloads diff --git a/distribution/feature-packs/adapter-feature-pack/pom.xml b/distribution/feature-packs/adapter-feature-pack/pom.xml index 3eb9078d0c95..4def3d4e70a3 100755 --- a/distribution/feature-packs/adapter-feature-pack/pom.xml +++ b/distribution/feature-packs/adapter-feature-pack/pom.xml @@ -19,7 +19,7 @@ org.keycloak feature-packs-parent - 24.0.5-PS-1-SNAPSHOT + 24.0.5-PS-1 diff --git a/distribution/feature-packs/pom.xml b/distribution/feature-packs/pom.xml index d002f024fd9e..88c5187eac8e 100644 --- a/distribution/feature-packs/pom.xml +++ b/distribution/feature-packs/pom.xml @@ -20,7 +20,7 @@ keycloak-distribution-parent org.keycloak - 24.0.5-PS-1-SNAPSHOT + 24.0.5-PS-1 Feature Pack Builds diff --git a/distribution/galleon-feature-packs/pom.xml b/distribution/galleon-feature-packs/pom.xml index d3745ee5a088..e3508ee107c7 100644 --- a/distribution/galleon-feature-packs/pom.xml +++ b/distribution/galleon-feature-packs/pom.xml @@ -20,7 +20,7 @@ keycloak-distribution-parent org.keycloak - 24.0.5-PS-1-SNAPSHOT + 24.0.5-PS-1 Galleon Feature Pack Builds diff --git a/distribution/galleon-feature-packs/saml-adapter-galleon-pack-layer-metadata-tests/pom.xml b/distribution/galleon-feature-packs/saml-adapter-galleon-pack-layer-metadata-tests/pom.xml index f1b2798e1bfa..d699231c52d5 100644 --- a/distribution/galleon-feature-packs/saml-adapter-galleon-pack-layer-metadata-tests/pom.xml +++ b/distribution/galleon-feature-packs/saml-adapter-galleon-pack-layer-metadata-tests/pom.xml @@ -19,7 +19,7 @@ org.keycloak galleon-feature-packs-parent - 24.0.5-PS-1-SNAPSHOT + 24.0.5-PS-1 4.0.0 diff --git a/distribution/galleon-feature-packs/saml-adapter-galleon-pack/pom.xml b/distribution/galleon-feature-packs/saml-adapter-galleon-pack/pom.xml index 7fbc3269e431..b40985603a54 100644 --- a/distribution/galleon-feature-packs/saml-adapter-galleon-pack/pom.xml +++ b/distribution/galleon-feature-packs/saml-adapter-galleon-pack/pom.xml @@ -19,7 +19,7 @@ org.keycloak galleon-feature-packs-parent - 24.0.5-PS-1-SNAPSHOT + 24.0.5-PS-1 4.0.0 diff --git a/distribution/licenses-common/pom.xml b/distribution/licenses-common/pom.xml index 9d4aeb89cbf9..07ae3854a707 100644 --- a/distribution/licenses-common/pom.xml +++ b/distribution/licenses-common/pom.xml @@ -20,7 +20,7 @@ keycloak-distribution-parent org.keycloak - 24.0.5-PS-1-SNAPSHOT + 24.0.5-PS-1 keycloak-distribution-licenses-common diff --git a/distribution/maven-plugins/licenses-processor/pom.xml b/distribution/maven-plugins/licenses-processor/pom.xml index af2b0ae20aca..74bbb4014cb1 100644 --- a/distribution/maven-plugins/licenses-processor/pom.xml +++ b/distribution/maven-plugins/licenses-processor/pom.xml @@ -20,7 +20,7 @@ keycloak-distribution-maven-plugins-parent org.keycloak - 24.0.5-PS-1-SNAPSHOT + 24.0.5-PS-1 keycloak-distribution-licenses-maven-plugin diff --git a/distribution/maven-plugins/pom.xml b/distribution/maven-plugins/pom.xml index 20c227310458..28b8b9faf3cd 100644 --- a/distribution/maven-plugins/pom.xml +++ b/distribution/maven-plugins/pom.xml @@ -20,7 +20,7 @@ keycloak-distribution-parent org.keycloak - 24.0.5-PS-1-SNAPSHOT + 24.0.5-PS-1 keycloak-distribution-maven-plugins-parent diff --git a/distribution/pom.xml b/distribution/pom.xml index 1651604ceb10..7110cea423f3 100755 --- a/distribution/pom.xml +++ b/distribution/pom.xml @@ -20,7 +20,7 @@ keycloak-parent org.keycloak - 24.0.5-PS-1-SNAPSHOT + 24.0.5-PS-1 ../pom.xml diff --git a/distribution/saml-adapters/pom.xml b/distribution/saml-adapters/pom.xml index 17f30ea574f9..a6ed9a07ba05 100755 --- a/distribution/saml-adapters/pom.xml +++ b/distribution/saml-adapters/pom.xml @@ -20,7 +20,7 @@ keycloak-distribution-parent org.keycloak - 24.0.5-PS-1-SNAPSHOT + 24.0.5-PS-1 SAML Adapters Distribution Parent diff --git a/distribution/saml-adapters/tomcat-adapter-zip/pom.xml b/distribution/saml-adapters/tomcat-adapter-zip/pom.xml index cf33794ba432..1956cdf0477d 100755 --- a/distribution/saml-adapters/tomcat-adapter-zip/pom.xml +++ b/distribution/saml-adapters/tomcat-adapter-zip/pom.xml @@ -21,7 +21,7 @@ keycloak-parent org.keycloak - 24.0.5-PS-1-SNAPSHOT + 24.0.5-PS-1 ../../../pom.xml diff --git a/distribution/saml-adapters/wildfly-adapter/pom.xml b/distribution/saml-adapters/wildfly-adapter/pom.xml index 6aea93d46033..3a58608a6ded 100755 --- a/distribution/saml-adapters/wildfly-adapter/pom.xml +++ b/distribution/saml-adapters/wildfly-adapter/pom.xml @@ -20,7 +20,7 @@ keycloak-parent org.keycloak - 24.0.5-PS-1-SNAPSHOT + 24.0.5-PS-1 ../../../pom.xml Keycloak Wildfly SAML Adapter diff --git a/distribution/saml-adapters/wildfly-adapter/wildfly-adapter-jakarta-zip/pom.xml b/distribution/saml-adapters/wildfly-adapter/wildfly-adapter-jakarta-zip/pom.xml index fe8896ae743c..0a5fe0a35f97 100755 --- a/distribution/saml-adapters/wildfly-adapter/wildfly-adapter-jakarta-zip/pom.xml +++ b/distribution/saml-adapters/wildfly-adapter/wildfly-adapter-jakarta-zip/pom.xml @@ -21,7 +21,7 @@ keycloak-parent org.keycloak - 24.0.5-PS-1-SNAPSHOT + 24.0.5-PS-1 ../../../../pom.xml diff --git a/distribution/saml-adapters/wildfly-adapter/wildfly-adapter-zip/pom.xml b/distribution/saml-adapters/wildfly-adapter/wildfly-adapter-zip/pom.xml index dcb6f9fc0d39..10ca2a21347d 100755 --- a/distribution/saml-adapters/wildfly-adapter/wildfly-adapter-zip/pom.xml +++ b/distribution/saml-adapters/wildfly-adapter/wildfly-adapter-zip/pom.xml @@ -21,7 +21,7 @@ keycloak-parent org.keycloak - 24.0.5-PS-1-SNAPSHOT + 24.0.5-PS-1 ../../../../pom.xml diff --git a/distribution/saml-adapters/wildfly-adapter/wildfly-jakarta-modules/pom.xml b/distribution/saml-adapters/wildfly-adapter/wildfly-jakarta-modules/pom.xml index abf66b9ef7b0..eba195e1c782 100755 --- a/distribution/saml-adapters/wildfly-adapter/wildfly-jakarta-modules/pom.xml +++ b/distribution/saml-adapters/wildfly-adapter/wildfly-jakarta-modules/pom.xml @@ -25,7 +25,7 @@ keycloak-parent org.keycloak - 24.0.5-PS-1-SNAPSHOT + 24.0.5-PS-1 ../../../../pom.xml diff --git a/distribution/saml-adapters/wildfly-adapter/wildfly-modules/pom.xml b/distribution/saml-adapters/wildfly-adapter/wildfly-modules/pom.xml index c6dd8f130b7e..35a032aa3ba2 100755 --- a/distribution/saml-adapters/wildfly-adapter/wildfly-modules/pom.xml +++ b/distribution/saml-adapters/wildfly-adapter/wildfly-modules/pom.xml @@ -25,7 +25,7 @@ keycloak-parent org.keycloak - 24.0.5-PS-1-SNAPSHOT + 24.0.5-PS-1 ../../../../pom.xml diff --git a/docs/documentation/aggregation/pom.xml b/docs/documentation/aggregation/pom.xml index 530bd719f3ed..308c2df53d49 100644 --- a/docs/documentation/aggregation/pom.xml +++ b/docs/documentation/aggregation/pom.xml @@ -5,7 +5,7 @@ org.keycloak.documentation documentation-parent - 24.0.5-PS-1-SNAPSHOT + 24.0.5-PS-1 ../pom.xml diff --git a/docs/documentation/api_documentation/pom.xml b/docs/documentation/api_documentation/pom.xml index e7e6d2e21a8f..ab63ef07e71f 100644 --- a/docs/documentation/api_documentation/pom.xml +++ b/docs/documentation/api_documentation/pom.xml @@ -5,7 +5,7 @@ org.keycloak.documentation documentation-parent - 24.0.5-PS-1-SNAPSHOT + 24.0.5-PS-1 ../pom.xml diff --git a/docs/documentation/authorization_services/pom.xml b/docs/documentation/authorization_services/pom.xml index e053361b631f..65ae9868a8f4 100644 --- a/docs/documentation/authorization_services/pom.xml +++ b/docs/documentation/authorization_services/pom.xml @@ -5,7 +5,7 @@ org.keycloak.documentation documentation-parent - 24.0.5-PS-1-SNAPSHOT + 24.0.5-PS-1 ../pom.xml diff --git a/docs/documentation/dist/pom.xml b/docs/documentation/dist/pom.xml index 99dcc9e56c5e..9addaa55f70a 100644 --- a/docs/documentation/dist/pom.xml +++ b/docs/documentation/dist/pom.xml @@ -5,7 +5,7 @@ org.keycloak.documentation documentation-parent - 24.0.5-PS-1-SNAPSHOT + 24.0.5-PS-1 ../pom.xml diff --git a/docs/documentation/header-maven-plugin/pom.xml b/docs/documentation/header-maven-plugin/pom.xml index 37e7a6f87422..28346c40f99d 100644 --- a/docs/documentation/header-maven-plugin/pom.xml +++ b/docs/documentation/header-maven-plugin/pom.xml @@ -5,12 +5,12 @@ documentation-parent org.keycloak.documentation - 24.0.5-PS-1-SNAPSHOT + 24.0.5-PS-1 org.keycloak.documentation header-maven-plugin - 24.0.5-PS-1-SNAPSHOT + 24.0.5-PS-1 maven-plugin github-maven-plugin diff --git a/docs/documentation/pom.xml b/docs/documentation/pom.xml index 379e95e6f48e..60331f5d4e7f 100644 --- a/docs/documentation/pom.xml +++ b/docs/documentation/pom.xml @@ -5,14 +5,14 @@ keycloak-docs-parent org.keycloak - 24.0.5-PS-1-SNAPSHOT + 24.0.5-PS-1 ../pom.xml Keycloak Documentation Parent org.keycloak.documentation documentation-parent - 24.0.5-PS-1-SNAPSHOT + 24.0.5-PS-1 pom diff --git a/docs/documentation/release_notes/pom.xml b/docs/documentation/release_notes/pom.xml index e8275e5d62b3..0a14326c25fc 100644 --- a/docs/documentation/release_notes/pom.xml +++ b/docs/documentation/release_notes/pom.xml @@ -5,7 +5,7 @@ org.keycloak.documentation documentation-parent - 24.0.5-PS-1-SNAPSHOT + 24.0.5-PS-1 ../pom.xml diff --git a/docs/documentation/securing_apps/pom.xml b/docs/documentation/securing_apps/pom.xml index fed06340f64d..715ae2a5b620 100644 --- a/docs/documentation/securing_apps/pom.xml +++ b/docs/documentation/securing_apps/pom.xml @@ -5,7 +5,7 @@ org.keycloak.documentation documentation-parent - 24.0.5-PS-1-SNAPSHOT + 24.0.5-PS-1 ../pom.xml diff --git a/docs/documentation/server_admin/pom.xml b/docs/documentation/server_admin/pom.xml index f399fb8a3da5..2848c18f995f 100644 --- a/docs/documentation/server_admin/pom.xml +++ b/docs/documentation/server_admin/pom.xml @@ -5,7 +5,7 @@ org.keycloak.documentation documentation-parent - 24.0.5-PS-1-SNAPSHOT + 24.0.5-PS-1 ../pom.xml diff --git a/docs/documentation/server_development/pom.xml b/docs/documentation/server_development/pom.xml index 8c2ffbde8cf0..45e3e24f6012 100644 --- a/docs/documentation/server_development/pom.xml +++ b/docs/documentation/server_development/pom.xml @@ -5,7 +5,7 @@ org.keycloak.documentation documentation-parent - 24.0.5-PS-1-SNAPSHOT + 24.0.5-PS-1 ../pom.xml diff --git a/docs/documentation/tests/pom.xml b/docs/documentation/tests/pom.xml index fb80372dc5b3..acb884105859 100644 --- a/docs/documentation/tests/pom.xml +++ b/docs/documentation/tests/pom.xml @@ -60,7 +60,7 @@ org.keycloak.documentation documentation-parent - 24.0.5-PS-1-SNAPSHOT + 24.0.5-PS-1 ../pom.xml diff --git a/docs/documentation/topics/templates/document-attributes.adoc b/docs/documentation/topics/templates/document-attributes.adoc index 8623db5487ce..136e578fa061 100644 --- a/docs/documentation/topics/templates/document-attributes.adoc +++ b/docs/documentation/topics/templates/document-attributes.adoc @@ -2,10 +2,10 @@ :project_name_full: Keycloak :project_community: true :project_product: false -:project_version: 24.0.5-PS-1-SNAPSHOT -:project_versionMvn: 24.0.5-PS-1-SNAPSHOT -:project_versionNpm: 24.0.5-PS-1-SNAPSHOT -:project_versionDoc: 24.0.5-PS-1-SNAPSHOT +:project_version: 24.0.5-PS-1 +:project_versionMvn: 24.0.5-PS-1 +:project_versionNpm: 24.0.5-PS-1 +:project_versionDoc: 24.0.5-PS-1 :archivebasename: keycloak :archivedownloadurl: https://github.com/keycloak/keycloak/releases/download/{project_version}/keycloak-{project_version}.zip diff --git a/docs/documentation/upgrading/pom.xml b/docs/documentation/upgrading/pom.xml index a80695b519dd..3b6245e252c2 100644 --- a/docs/documentation/upgrading/pom.xml +++ b/docs/documentation/upgrading/pom.xml @@ -5,7 +5,7 @@ org.keycloak.documentation documentation-parent - 24.0.5-PS-1-SNAPSHOT + 24.0.5-PS-1 ../pom.xml diff --git a/docs/guides/pom.xml b/docs/guides/pom.xml index 0a68ee5d3538..67ae377e1012 100644 --- a/docs/guides/pom.xml +++ b/docs/guides/pom.xml @@ -19,7 +19,7 @@ keycloak-docs-parent org.keycloak - 24.0.5-PS-1-SNAPSHOT + 24.0.5-PS-1 ../pom.xml diff --git a/docs/maven-plugin/pom.xml b/docs/maven-plugin/pom.xml index fa36588185e9..743b87779e77 100644 --- a/docs/maven-plugin/pom.xml +++ b/docs/maven-plugin/pom.xml @@ -20,7 +20,7 @@ keycloak-docs-parent org.keycloak - 24.0.5-PS-1-SNAPSHOT + 24.0.5-PS-1 ../pom.xml diff --git a/docs/pom.xml b/docs/pom.xml index f659fd2f3530..529cc0b0aac7 100755 --- a/docs/pom.xml +++ b/docs/pom.xml @@ -19,7 +19,7 @@ keycloak-parent org.keycloak - 24.0.5-PS-1-SNAPSHOT + 24.0.5-PS-1 ../pom.xml Keycloak Docs Parent diff --git a/examples/admin-client/pom.xml b/examples/admin-client/pom.xml index 4f41a87a0288..0335d4d4ceeb 100755 --- a/examples/admin-client/pom.xml +++ b/examples/admin-client/pom.xml @@ -22,7 +22,7 @@ keycloak-examples-parent org.keycloak - 24.0.5-PS-1-SNAPSHOT + 24.0.5-PS-1 Keycloak Examples - Admin Client diff --git a/examples/js-console/pom.xml b/examples/js-console/pom.xml index e7379561af75..af88d14694d1 100755 --- a/examples/js-console/pom.xml +++ b/examples/js-console/pom.xml @@ -21,7 +21,7 @@ keycloak-examples-parent org.keycloak - 24.0.5-PS-1-SNAPSHOT + 24.0.5-PS-1 4.0.0 diff --git a/examples/kerberos/pom.xml b/examples/kerberos/pom.xml index e0cff999468a..7f7c8c88e226 100755 --- a/examples/kerberos/pom.xml +++ b/examples/kerberos/pom.xml @@ -22,7 +22,7 @@ keycloak-examples-parent org.keycloak - 24.0.5-PS-1-SNAPSHOT + 24.0.5-PS-1 Keycloak Examples - Kerberos Credential Delegation diff --git a/examples/ldap/pom.xml b/examples/ldap/pom.xml index 5a46ecebdf2a..964b9d146b9d 100644 --- a/examples/ldap/pom.xml +++ b/examples/ldap/pom.xml @@ -22,7 +22,7 @@ keycloak-examples-parent org.keycloak - 24.0.5-PS-1-SNAPSHOT + 24.0.5-PS-1 4.0.0 diff --git a/examples/pom.xml b/examples/pom.xml index b85513313c7c..34758219a0bc 100755 --- a/examples/pom.xml +++ b/examples/pom.xml @@ -20,7 +20,7 @@ keycloak-parent org.keycloak - 24.0.5-PS-1-SNAPSHOT + 24.0.5-PS-1 Keycloak Examples diff --git a/examples/providers/authenticator/pom.xml b/examples/providers/authenticator/pom.xml index c0e90b7ea80d..f56abcdcdbfa 100755 --- a/examples/providers/authenticator/pom.xml +++ b/examples/providers/authenticator/pom.xml @@ -20,7 +20,7 @@ keycloak-examples-providers-parent org.keycloak - 24.0.5-PS-1-SNAPSHOT + 24.0.5-PS-1 Authenticator Example diff --git a/examples/providers/pom.xml b/examples/providers/pom.xml index 189e99767e54..9178b53879a5 100755 --- a/examples/providers/pom.xml +++ b/examples/providers/pom.xml @@ -20,7 +20,7 @@ keycloak-examples-parent org.keycloak - 24.0.5-PS-1-SNAPSHOT + 24.0.5-PS-1 Provider Examples diff --git a/examples/providers/rest/pom.xml b/examples/providers/rest/pom.xml index 621de1ac7bf0..3c559d213392 100755 --- a/examples/providers/rest/pom.xml +++ b/examples/providers/rest/pom.xml @@ -20,7 +20,7 @@ keycloak-examples-providers-parent org.keycloak - 24.0.5-PS-1-SNAPSHOT + 24.0.5-PS-1 REST Example diff --git a/examples/saml/pom.xml b/examples/saml/pom.xml index aa0b324184d1..786d693b7892 100755 --- a/examples/saml/pom.xml +++ b/examples/saml/pom.xml @@ -20,7 +20,7 @@ keycloak-examples-parent org.keycloak - 24.0.5-PS-1-SNAPSHOT + 24.0.5-PS-1 SAML Examples diff --git a/examples/saml/servlet-filter/pom.xml b/examples/saml/servlet-filter/pom.xml index 1cf167d5782a..0cc06d709b1b 100755 --- a/examples/saml/servlet-filter/pom.xml +++ b/examples/saml/servlet-filter/pom.xml @@ -22,7 +22,7 @@ keycloak-examples-saml-parent org.keycloak - 24.0.5-PS-1-SNAPSHOT + 24.0.5-PS-1 saml-servlet-filter diff --git a/examples/themes/pom.xml b/examples/themes/pom.xml index fda0201c6d45..9d3a14fb3f0a 100755 --- a/examples/themes/pom.xml +++ b/examples/themes/pom.xml @@ -20,7 +20,7 @@ keycloak-examples-parent org.keycloak - 24.0.5-PS-1-SNAPSHOT + 24.0.5-PS-1 Themes Examples diff --git a/federation/kerberos/pom.xml b/federation/kerberos/pom.xml index 9761832d85f8..587e505c6e35 100755 --- a/federation/kerberos/pom.xml +++ b/federation/kerberos/pom.xml @@ -20,7 +20,7 @@ keycloak-parent org.keycloak - 24.0.5-PS-1-SNAPSHOT + 24.0.5-PS-1 ../../pom.xml 4.0.0 diff --git a/federation/ldap/pom.xml b/federation/ldap/pom.xml index 4d3014da7ae6..14de145e7f22 100755 --- a/federation/ldap/pom.xml +++ b/federation/ldap/pom.xml @@ -20,7 +20,7 @@ keycloak-parent org.keycloak - 24.0.5-PS-1-SNAPSHOT + 24.0.5-PS-1 ../../pom.xml 4.0.0 diff --git a/federation/pom.xml b/federation/pom.xml index 0e1aad4c9778..68f7f7311b11 100755 --- a/federation/pom.xml +++ b/federation/pom.xml @@ -22,7 +22,7 @@ keycloak-parent org.keycloak - 24.0.5-PS-1-SNAPSHOT + 24.0.5-PS-1 ../pom.xml 4.0.0 diff --git a/federation/sssd/pom.xml b/federation/sssd/pom.xml index 192249612f5e..3c273c3e7499 100644 --- a/federation/sssd/pom.xml +++ b/federation/sssd/pom.xml @@ -4,7 +4,7 @@ keycloak-parent org.keycloak - 24.0.5-PS-1-SNAPSHOT + 24.0.5-PS-1 ../../pom.xml 4.0.0 diff --git a/integration/admin-client-jee/pom.xml b/integration/admin-client-jee/pom.xml index 2d691cdbaca9..34465ce456ae 100755 --- a/integration/admin-client-jee/pom.xml +++ b/integration/admin-client-jee/pom.xml @@ -22,7 +22,7 @@ keycloak-integration-parent org.keycloak - 24.0.5-PS-1-SNAPSHOT + 24.0.5-PS-1 4.0.0 diff --git a/integration/admin-client/pom.xml b/integration/admin-client/pom.xml index 821f76e9b3af..729e81acb8bc 100755 --- a/integration/admin-client/pom.xml +++ b/integration/admin-client/pom.xml @@ -22,7 +22,7 @@ keycloak-integration-parent org.keycloak - 24.0.5-PS-1-SNAPSHOT + 24.0.5-PS-1 4.0.0 diff --git a/integration/client-cli/admin-cli/pom.xml b/integration/client-cli/admin-cli/pom.xml index 51cd182b2cb3..1248a5782fab 100755 --- a/integration/client-cli/admin-cli/pom.xml +++ b/integration/client-cli/admin-cli/pom.xml @@ -21,7 +21,7 @@ keycloak-client-cli-parent org.keycloak - 24.0.5-PS-1-SNAPSHOT + 24.0.5-PS-1 4.0.0 diff --git a/integration/client-cli/client-cli-dist/pom.xml b/integration/client-cli/client-cli-dist/pom.xml index 3c998b9ca2ae..7a3373db2c6a 100755 --- a/integration/client-cli/client-cli-dist/pom.xml +++ b/integration/client-cli/client-cli-dist/pom.xml @@ -21,7 +21,7 @@ keycloak-client-cli-parent org.keycloak - 24.0.5-PS-1-SNAPSHOT + 24.0.5-PS-1 keycloak-client-cli-dist diff --git a/integration/client-cli/client-registration-cli/pom.xml b/integration/client-cli/client-registration-cli/pom.xml index a8d25aa73783..a516ad6b3053 100755 --- a/integration/client-cli/client-registration-cli/pom.xml +++ b/integration/client-cli/client-registration-cli/pom.xml @@ -21,7 +21,7 @@ keycloak-client-cli-parent org.keycloak - 24.0.5-PS-1-SNAPSHOT + 24.0.5-PS-1 4.0.0 diff --git a/integration/client-cli/pom.xml b/integration/client-cli/pom.xml index 65ce7e7b9255..6984d5cb4840 100644 --- a/integration/client-cli/pom.xml +++ b/integration/client-cli/pom.xml @@ -20,7 +20,7 @@ keycloak-integration-parent org.keycloak - 24.0.5-PS-1-SNAPSHOT + 24.0.5-PS-1 Keycloak Client CLI diff --git a/integration/client-registration/pom.xml b/integration/client-registration/pom.xml index 8324cc19aca2..960d976c6e11 100755 --- a/integration/client-registration/pom.xml +++ b/integration/client-registration/pom.xml @@ -21,7 +21,7 @@ keycloak-integration-parent org.keycloak - 24.0.5-PS-1-SNAPSHOT + 24.0.5-PS-1 4.0.0 diff --git a/integration/pom.xml b/integration/pom.xml index d9cfebbc9978..1b1ab33dcfaa 100755 --- a/integration/pom.xml +++ b/integration/pom.xml @@ -20,7 +20,7 @@ keycloak-parent org.keycloak - 24.0.5-PS-1-SNAPSHOT + 24.0.5-PS-1 ../pom.xml Keycloak Integration diff --git a/js/apps/account-ui/pom.xml b/js/apps/account-ui/pom.xml index cf5953d8e4fa..1dfda3254f59 100644 --- a/js/apps/account-ui/pom.xml +++ b/js/apps/account-ui/pom.xml @@ -7,7 +7,7 @@ keycloak-js-parent org.keycloak - 24.0.5-PS-1-SNAPSHOT + 24.0.5-PS-1 ../../pom.xml diff --git a/js/apps/admin-ui/pom.xml b/js/apps/admin-ui/pom.xml index 34cf79d755b9..f663ed942666 100644 --- a/js/apps/admin-ui/pom.xml +++ b/js/apps/admin-ui/pom.xml @@ -7,7 +7,7 @@ keycloak-js-parent org.keycloak - 24.0.5-PS-1-SNAPSHOT + 24.0.5-PS-1 ../../pom.xml diff --git a/js/libs/keycloak-admin-client/package.json b/js/libs/keycloak-admin-client/package.json index 85b0f472651b..1e852457c478 100644 --- a/js/libs/keycloak-admin-client/package.json +++ b/js/libs/keycloak-admin-client/package.json @@ -1,6 +1,6 @@ { "name": "@keycloak/keycloak-admin-client", - "version": "24.0.5-PS-1-SNAPSHOT", + "version": "24.0.5-PS-1", "description": "A client to interact with Keycloak's Administration API", "type": "module", "main": "lib/index.js", diff --git a/js/libs/keycloak-admin-client/pom.xml b/js/libs/keycloak-admin-client/pom.xml index 02a332045caf..61c3bebea7c4 100644 --- a/js/libs/keycloak-admin-client/pom.xml +++ b/js/libs/keycloak-admin-client/pom.xml @@ -5,7 +5,7 @@ keycloak-js-parent org.keycloak - 24.0.5-PS-1-SNAPSHOT + 24.0.5-PS-1 ../../pom.xml diff --git a/js/libs/keycloak-js/package.json b/js/libs/keycloak-js/package.json index 649823810df7..fca0520d1bf9 100644 --- a/js/libs/keycloak-js/package.json +++ b/js/libs/keycloak-js/package.json @@ -1,6 +1,6 @@ { "name": "keycloak-js", - "version": "24.0.5-PS-1-SNAPSHOT", + "version": "24.0.5-PS-1", "description": "A client-side JavaScript OpenID Connect library that can be used to secure web applications", "main": "./dist/keycloak.js", "module": "./dist/keycloak.mjs", diff --git a/js/libs/keycloak-js/pom.xml b/js/libs/keycloak-js/pom.xml index 4b236b8b1025..962b90c6cf1f 100644 --- a/js/libs/keycloak-js/pom.xml +++ b/js/libs/keycloak-js/pom.xml @@ -5,7 +5,7 @@ keycloak-js-parent org.keycloak - 24.0.5-PS-1-SNAPSHOT + 24.0.5-PS-1 ../../pom.xml diff --git a/js/pom.xml b/js/pom.xml index 3a1964b1367a..5eb4975dffbc 100644 --- a/js/pom.xml +++ b/js/pom.xml @@ -5,7 +5,7 @@ keycloak-parent org.keycloak - 24.0.5-PS-1-SNAPSHOT + 24.0.5-PS-1 ../pom.xml diff --git a/misc/keycloak-test-helper/pom.xml b/misc/keycloak-test-helper/pom.xml index 1b5cdc057801..990a04770e1d 100644 --- a/misc/keycloak-test-helper/pom.xml +++ b/misc/keycloak-test-helper/pom.xml @@ -6,7 +6,7 @@ keycloak-misc-parent org.keycloak - 24.0.5-PS-1-SNAPSHOT + 24.0.5-PS-1 org.keycloak keycloak-test-helper diff --git a/misc/pom.xml b/misc/pom.xml index d4b66f314a86..5d140bacd276 100644 --- a/misc/pom.xml +++ b/misc/pom.xml @@ -3,7 +3,7 @@ keycloak-parent org.keycloak - 24.0.5-PS-1-SNAPSHOT + 24.0.5-PS-1 Keycloak Misc diff --git a/misc/spring-boot-starter/keycloak-spring-boot-starter/pom.xml b/misc/spring-boot-starter/keycloak-spring-boot-starter/pom.xml index 9b64faf16d80..49b0227bf69d 100644 --- a/misc/spring-boot-starter/keycloak-spring-boot-starter/pom.xml +++ b/misc/spring-boot-starter/keycloak-spring-boot-starter/pom.xml @@ -4,7 +4,7 @@ org.keycloak keycloak-spring-boot-starter-parent - 24.0.5-PS-1-SNAPSHOT + 24.0.5-PS-1 keycloak-spring-boot-starter Keycloak :: Spring :: Boot :: Default :: Starter diff --git a/misc/spring-boot-starter/pom.xml b/misc/spring-boot-starter/pom.xml index 317e46e72948..08a79a1ec753 100644 --- a/misc/spring-boot-starter/pom.xml +++ b/misc/spring-boot-starter/pom.xml @@ -5,7 +5,7 @@ keycloak-misc-parent org.keycloak - 24.0.5-PS-1-SNAPSHOT + 24.0.5-PS-1 org.keycloak keycloak-spring-boot-starter-parent diff --git a/model/infinispan/pom.xml b/model/infinispan/pom.xml index 7855e072fe6f..6a2384aaca65 100755 --- a/model/infinispan/pom.xml +++ b/model/infinispan/pom.xml @@ -21,7 +21,7 @@ keycloak-model-pom org.keycloak - 24.0.5-PS-1-SNAPSHOT + 24.0.5-PS-1 17 diff --git a/model/jpa/pom.xml b/model/jpa/pom.xml index 0b06077f7148..34eb9114e084 100755 --- a/model/jpa/pom.xml +++ b/model/jpa/pom.xml @@ -21,7 +21,7 @@ keycloak-model-pom org.keycloak - 24.0.5-PS-1-SNAPSHOT + 24.0.5-PS-1 4.0.0 diff --git a/model/legacy/pom.xml b/model/legacy/pom.xml index dcf8414572e4..aa2fc45d3592 100644 --- a/model/legacy/pom.xml +++ b/model/legacy/pom.xml @@ -3,7 +3,7 @@ keycloak-model-pom org.keycloak - 24.0.5-PS-1-SNAPSHOT + 24.0.5-PS-1 4.0.0 diff --git a/model/pom.xml b/model/pom.xml index 70556ff0de85..244b02a99878 100755 --- a/model/pom.xml +++ b/model/pom.xml @@ -20,7 +20,7 @@ keycloak-parent org.keycloak - 24.0.5-PS-1-SNAPSHOT + 24.0.5-PS-1 ../pom.xml Keycloak Model Parent diff --git a/model/storage-private/pom.xml b/model/storage-private/pom.xml index 46725b1d3ba2..300fa9bec341 100644 --- a/model/storage-private/pom.xml +++ b/model/storage-private/pom.xml @@ -3,7 +3,7 @@ keycloak-model-pom org.keycloak - 24.0.5-PS-1-SNAPSHOT + 24.0.5-PS-1 4.0.0 diff --git a/model/storage-services/pom.xml b/model/storage-services/pom.xml index cd244c3b0b98..164d317a361a 100644 --- a/model/storage-services/pom.xml +++ b/model/storage-services/pom.xml @@ -3,7 +3,7 @@ keycloak-model-pom org.keycloak - 24.0.5-PS-1-SNAPSHOT + 24.0.5-PS-1 4.0.0 diff --git a/model/storage/pom.xml b/model/storage/pom.xml index e8de4bbd29f8..839329b9d759 100644 --- a/model/storage/pom.xml +++ b/model/storage/pom.xml @@ -3,7 +3,7 @@ keycloak-model-pom org.keycloak - 24.0.5-PS-1-SNAPSHOT + 24.0.5-PS-1 4.0.0 diff --git a/operator/pom.xml b/operator/pom.xml index 359a7e94cf25..0821071fc7b3 100644 --- a/operator/pom.xml +++ b/operator/pom.xml @@ -7,7 +7,7 @@ keycloak-parent org.keycloak - 24.0.5-PS-1-SNAPSHOT + 24.0.5-PS-1 ../pom.xml diff --git a/pom.xml b/pom.xml index 0edfeda49a0e..cab56345ad54 100644 --- a/pom.xml +++ b/pom.xml @@ -31,11 +31,11 @@ org.keycloak keycloak-parent - 24.0.5-PS-1-SNAPSHOT + 24.0.5-PS-1 pom - 24.0.5-PS-1-SNAPSHOT + 24.0.5-PS-1 1.5.8 diff --git a/quarkus/config-api/pom.xml b/quarkus/config-api/pom.xml index 947ddb13c73c..d601986281a8 100755 --- a/quarkus/config-api/pom.xml +++ b/quarkus/config-api/pom.xml @@ -21,7 +21,7 @@ keycloak-quarkus-parent org.keycloak - 24.0.5-PS-1-SNAPSHOT + 24.0.5-PS-1 ../pom.xml 4.0.0 diff --git a/quarkus/container/Dockerfile b/quarkus/container/Dockerfile index 68692ee71f05..248b1b97c170 100644 --- a/quarkus/container/Dockerfile +++ b/quarkus/container/Dockerfile @@ -1,6 +1,6 @@ FROM registry.access.redhat.com/ubi9 AS ubi-micro-build -ENV KEYCLOAK_VERSION 24.0.5-PS-1-SNAPSHOT +ENV KEYCLOAK_VERSION 24.0.5-PS-1 ARG KEYCLOAK_DIST=https://github.com/keycloak/keycloak/releases/download/$KEYCLOAK_VERSION/keycloak-$KEYCLOAK_VERSION.tar.gz RUN dnf install -y tar gzip diff --git a/quarkus/deployment/pom.xml b/quarkus/deployment/pom.xml index 147d92a8c700..62e62b094b45 100644 --- a/quarkus/deployment/pom.xml +++ b/quarkus/deployment/pom.xml @@ -5,7 +5,7 @@ keycloak-quarkus-parent org.keycloak - 24.0.5-PS-1-SNAPSHOT + 24.0.5-PS-1 ../pom.xml 4.0.0 diff --git a/quarkus/dist/pom.xml b/quarkus/dist/pom.xml index a2a40ca56e9f..87c58449df62 100755 --- a/quarkus/dist/pom.xml +++ b/quarkus/dist/pom.xml @@ -21,7 +21,7 @@ keycloak-quarkus-parent org.keycloak - 24.0.5-PS-1-SNAPSHOT + 24.0.5-PS-1 keycloak-quarkus-dist diff --git a/quarkus/pom.xml b/quarkus/pom.xml index cd1db6c4ff5a..39334b1d0e68 100644 --- a/quarkus/pom.xml +++ b/quarkus/pom.xml @@ -20,7 +20,7 @@ keycloak-parent org.keycloak - 24.0.5-PS-1-SNAPSHOT + 24.0.5-PS-1 ../pom.xml Keycloak Quarkus Parent diff --git a/quarkus/runtime/pom.xml b/quarkus/runtime/pom.xml index e019c8edd12d..768ad817a4f4 100644 --- a/quarkus/runtime/pom.xml +++ b/quarkus/runtime/pom.xml @@ -5,7 +5,7 @@ keycloak-quarkus-parent org.keycloak - 24.0.5-PS-1-SNAPSHOT + 24.0.5-PS-1 ../pom.xml 4.0.0 diff --git a/quarkus/server/pom.xml b/quarkus/server/pom.xml index 0ddf25c2bcee..b9ed602771ad 100644 --- a/quarkus/server/pom.xml +++ b/quarkus/server/pom.xml @@ -7,7 +7,7 @@ keycloak-quarkus-parent org.keycloak - 24.0.5-PS-1-SNAPSHOT + 24.0.5-PS-1 ../pom.xml diff --git a/quarkus/tests/integration/pom.xml b/quarkus/tests/integration/pom.xml index 47a4d4568053..ce5864c24267 100644 --- a/quarkus/tests/integration/pom.xml +++ b/quarkus/tests/integration/pom.xml @@ -24,7 +24,7 @@ keycloak-quarkus-test-parent org.keycloak - 24.0.5-PS-1-SNAPSHOT + 24.0.5-PS-1 ../pom.xml diff --git a/quarkus/tests/junit5/pom.xml b/quarkus/tests/junit5/pom.xml index f94d57b26cb5..154a129dc7fd 100644 --- a/quarkus/tests/junit5/pom.xml +++ b/quarkus/tests/junit5/pom.xml @@ -24,7 +24,7 @@ keycloak-quarkus-test-parent org.keycloak - 24.0.5-PS-1-SNAPSHOT + 24.0.5-PS-1 ../pom.xml diff --git a/quarkus/tests/pom.xml b/quarkus/tests/pom.xml index bbbe604ee837..17c68758b94f 100644 --- a/quarkus/tests/pom.xml +++ b/quarkus/tests/pom.xml @@ -24,7 +24,7 @@ keycloak-quarkus-parent org.keycloak - 24.0.5-PS-1-SNAPSHOT + 24.0.5-PS-1 ../pom.xml diff --git a/release-details b/release-details index 2000f66b8ae3..e31b429559dd 100644 --- a/release-details +++ b/release-details @@ -1,3 +1,3 @@ -VERSION=24.0.5-PS-1-SNAPSHOT -SHORT_VERSION=24.0.5-PS-1-SNAPSHOT -NPM_VERSION=24.0.5-PS-1-SNAPSHOT +VERSION=24.0.5-PS-1 +SHORT_VERSION=24.0.5-PS-1 +NPM_VERSION=24.0.5-PS-1 diff --git a/rest/admin-ui-ext/pom.xml b/rest/admin-ui-ext/pom.xml index 945f09034c16..88e1a2fef197 100644 --- a/rest/admin-ui-ext/pom.xml +++ b/rest/admin-ui-ext/pom.xml @@ -22,7 +22,7 @@ org.keycloak keycloak-rest-parent - 24.0.5-PS-1-SNAPSHOT + 24.0.5-PS-1 keycloak-rest-admin-ui-ext diff --git a/rest/pom.xml b/rest/pom.xml index 14036985ac3e..dd23b44a453d 100644 --- a/rest/pom.xml +++ b/rest/pom.xml @@ -22,7 +22,7 @@ keycloak-parent org.keycloak - 24.0.5-PS-1-SNAPSHOT + 24.0.5-PS-1 Keycloak Administration UI diff --git a/saml-core-api/pom.xml b/saml-core-api/pom.xml index 71e5539862cf..d46e729c4957 100755 --- a/saml-core-api/pom.xml +++ b/saml-core-api/pom.xml @@ -21,7 +21,7 @@ keycloak-parent org.keycloak - 24.0.5-PS-1-SNAPSHOT + 24.0.5-PS-1 ../pom.xml 4.0.0 diff --git a/saml-core/pom.xml b/saml-core/pom.xml index e444d2c6fd9e..6c440f74a50b 100755 --- a/saml-core/pom.xml +++ b/saml-core/pom.xml @@ -21,7 +21,7 @@ keycloak-parent org.keycloak - 24.0.5-PS-1-SNAPSHOT + 24.0.5-PS-1 ../pom.xml 4.0.0 diff --git a/server-spi-private/pom.xml b/server-spi-private/pom.xml index ce06bc820c73..bbf2b5a8f661 100755 --- a/server-spi-private/pom.xml +++ b/server-spi-private/pom.xml @@ -21,7 +21,7 @@ keycloak-parent org.keycloak - 24.0.5-PS-1-SNAPSHOT + 24.0.5-PS-1 ../pom.xml 4.0.0 diff --git a/server-spi/pom.xml b/server-spi/pom.xml index ad68a0a6fe58..182a8ec35098 100755 --- a/server-spi/pom.xml +++ b/server-spi/pom.xml @@ -21,7 +21,7 @@ keycloak-parent org.keycloak - 24.0.5-PS-1-SNAPSHOT + 24.0.5-PS-1 ../pom.xml 4.0.0 diff --git a/services/pom.xml b/services/pom.xml index 88e6bccfcd2a..5996a3cc7d00 100755 --- a/services/pom.xml +++ b/services/pom.xml @@ -21,7 +21,7 @@ keycloak-parent org.keycloak - 24.0.5-PS-1-SNAPSHOT + 24.0.5-PS-1 ../pom.xml 4.0.0 diff --git a/testsuite/db-allocator-plugin/pom.xml b/testsuite/db-allocator-plugin/pom.xml index 6f75f4e5f244..2a119f102c9a 100644 --- a/testsuite/db-allocator-plugin/pom.xml +++ b/testsuite/db-allocator-plugin/pom.xml @@ -22,7 +22,7 @@ keycloak-testsuite-pom org.keycloak - 24.0.5-PS-1-SNAPSHOT + 24.0.5-PS-1 4.0.0 diff --git a/testsuite/integration-arquillian/pom.xml b/testsuite/integration-arquillian/pom.xml index 9e68dcebf3cc..e654d2bef401 100644 --- a/testsuite/integration-arquillian/pom.xml +++ b/testsuite/integration-arquillian/pom.xml @@ -22,7 +22,7 @@ org.keycloak keycloak-testsuite-pom - 24.0.5-PS-1-SNAPSHOT + 24.0.5-PS-1 ../pom.xml diff --git a/testsuite/integration-arquillian/servers/adapter-spi/pom.xml b/testsuite/integration-arquillian/servers/adapter-spi/pom.xml index 0c87d20be817..9192dfb9df3b 100644 --- a/testsuite/integration-arquillian/servers/adapter-spi/pom.xml +++ b/testsuite/integration-arquillian/servers/adapter-spi/pom.xml @@ -5,7 +5,7 @@ integration-arquillian-servers org.keycloak.testsuite - 24.0.5-PS-1-SNAPSHOT + 24.0.5-PS-1 4.0.0 pom diff --git a/testsuite/integration-arquillian/servers/adapter-spi/undertow-adapter-jakarta/pom.xml b/testsuite/integration-arquillian/servers/adapter-spi/undertow-adapter-jakarta/pom.xml index 6f3d15d80a50..892dea32ae96 100644 --- a/testsuite/integration-arquillian/servers/adapter-spi/undertow-adapter-jakarta/pom.xml +++ b/testsuite/integration-arquillian/servers/adapter-spi/undertow-adapter-jakarta/pom.xml @@ -5,7 +5,7 @@ integration-arquillian-servers-adapter-spi org.keycloak.testsuite - 24.0.5-PS-1-SNAPSHOT + 24.0.5-PS-1 ../pom.xml 4.0.0 diff --git a/testsuite/integration-arquillian/servers/adapter-spi/undertow-adapter-saml-jakarta/pom.xml b/testsuite/integration-arquillian/servers/adapter-spi/undertow-adapter-saml-jakarta/pom.xml index 4390895bc956..8c872b1fc92d 100644 --- a/testsuite/integration-arquillian/servers/adapter-spi/undertow-adapter-saml-jakarta/pom.xml +++ b/testsuite/integration-arquillian/servers/adapter-spi/undertow-adapter-saml-jakarta/pom.xml @@ -5,7 +5,7 @@ integration-arquillian-servers-adapter-spi org.keycloak.testsuite - 24.0.5-PS-1-SNAPSHOT + 24.0.5-PS-1 ../pom.xml 4.0.0 diff --git a/testsuite/integration-arquillian/servers/adapter-spi/undertow-adapter-spi-jakarta/pom.xml b/testsuite/integration-arquillian/servers/adapter-spi/undertow-adapter-spi-jakarta/pom.xml index 96e29d5d74f8..48d2b5c19c5a 100644 --- a/testsuite/integration-arquillian/servers/adapter-spi/undertow-adapter-spi-jakarta/pom.xml +++ b/testsuite/integration-arquillian/servers/adapter-spi/undertow-adapter-spi-jakarta/pom.xml @@ -5,7 +5,7 @@ integration-arquillian-servers-adapter-spi org.keycloak.testsuite - 24.0.5-PS-1-SNAPSHOT + 24.0.5-PS-1 ../pom.xml 4.0.0 diff --git a/testsuite/integration-arquillian/servers/app-server/app-server-spi/pom.xml b/testsuite/integration-arquillian/servers/app-server/app-server-spi/pom.xml index a56533c19166..923f7ddc5543 100644 --- a/testsuite/integration-arquillian/servers/app-server/app-server-spi/pom.xml +++ b/testsuite/integration-arquillian/servers/app-server/app-server-spi/pom.xml @@ -21,7 +21,7 @@ org.keycloak.testsuite integration-arquillian-servers-app-server - 24.0.5-PS-1-SNAPSHOT + 24.0.5-PS-1 4.0.0 diff --git a/testsuite/integration-arquillian/servers/app-server/jboss/eap/pom.xml b/testsuite/integration-arquillian/servers/app-server/jboss/eap/pom.xml index d49fcd0fa831..f23f8900207d 100644 --- a/testsuite/integration-arquillian/servers/app-server/jboss/eap/pom.xml +++ b/testsuite/integration-arquillian/servers/app-server/jboss/eap/pom.xml @@ -21,7 +21,7 @@ org.keycloak.testsuite integration-arquillian-servers-app-server-jboss - 24.0.5-PS-1-SNAPSHOT + 24.0.5-PS-1 4.0.0 diff --git a/testsuite/integration-arquillian/servers/app-server/jboss/eap6/pom.xml b/testsuite/integration-arquillian/servers/app-server/jboss/eap6/pom.xml index 05425d3d467a..5f5bf427b61d 100644 --- a/testsuite/integration-arquillian/servers/app-server/jboss/eap6/pom.xml +++ b/testsuite/integration-arquillian/servers/app-server/jboss/eap6/pom.xml @@ -21,7 +21,7 @@ org.keycloak.testsuite integration-arquillian-servers-app-server-jboss - 24.0.5-PS-1-SNAPSHOT + 24.0.5-PS-1 4.0.0 diff --git a/testsuite/integration-arquillian/servers/app-server/jboss/galleon/pom.xml b/testsuite/integration-arquillian/servers/app-server/jboss/galleon/pom.xml index e5a71f6f950d..7b95a8875fa5 100644 --- a/testsuite/integration-arquillian/servers/app-server/jboss/galleon/pom.xml +++ b/testsuite/integration-arquillian/servers/app-server/jboss/galleon/pom.xml @@ -5,7 +5,7 @@ org.keycloak.testsuite integration-arquillian-servers-app-server-jboss - 24.0.5-PS-1-SNAPSHOT + 24.0.5-PS-1 4.0.0 diff --git a/testsuite/integration-arquillian/servers/app-server/jboss/pom.xml b/testsuite/integration-arquillian/servers/app-server/jboss/pom.xml index a861a9597f70..d8a1adb10dd4 100644 --- a/testsuite/integration-arquillian/servers/app-server/jboss/pom.xml +++ b/testsuite/integration-arquillian/servers/app-server/jboss/pom.xml @@ -22,7 +22,7 @@ org.keycloak.testsuite integration-arquillian-servers-app-server - 24.0.5-PS-1-SNAPSHOT + 24.0.5-PS-1 4.0.0 diff --git a/testsuite/integration-arquillian/servers/app-server/jboss/wildfly/pom.xml b/testsuite/integration-arquillian/servers/app-server/jboss/wildfly/pom.xml index 5a6c4de882ed..3b1ea2d334de 100644 --- a/testsuite/integration-arquillian/servers/app-server/jboss/wildfly/pom.xml +++ b/testsuite/integration-arquillian/servers/app-server/jboss/wildfly/pom.xml @@ -21,7 +21,7 @@ org.keycloak.testsuite integration-arquillian-servers-app-server-jboss - 24.0.5-PS-1-SNAPSHOT + 24.0.5-PS-1 4.0.0 diff --git a/testsuite/integration-arquillian/servers/app-server/jetty/94/pom.xml b/testsuite/integration-arquillian/servers/app-server/jetty/94/pom.xml index ea8cf2b21e43..ceb342774467 100644 --- a/testsuite/integration-arquillian/servers/app-server/jetty/94/pom.xml +++ b/testsuite/integration-arquillian/servers/app-server/jetty/94/pom.xml @@ -18,7 +18,7 @@ org.keycloak.testsuite integration-arquillian-servers-app-server-jetty - 24.0.5-PS-1-SNAPSHOT + 24.0.5-PS-1 4.0.0 diff --git a/testsuite/integration-arquillian/servers/app-server/jetty/common/pom.xml b/testsuite/integration-arquillian/servers/app-server/jetty/common/pom.xml index 254d76a3e6d8..975ab9b43097 100644 --- a/testsuite/integration-arquillian/servers/app-server/jetty/common/pom.xml +++ b/testsuite/integration-arquillian/servers/app-server/jetty/common/pom.xml @@ -18,7 +18,7 @@ org.keycloak.testsuite integration-arquillian-servers-app-server-jetty - 24.0.5-PS-1-SNAPSHOT + 24.0.5-PS-1 4.0.0 diff --git a/testsuite/integration-arquillian/servers/app-server/jetty/pom.xml b/testsuite/integration-arquillian/servers/app-server/jetty/pom.xml index 9ce02171af75..4a48128e7820 100644 --- a/testsuite/integration-arquillian/servers/app-server/jetty/pom.xml +++ b/testsuite/integration-arquillian/servers/app-server/jetty/pom.xml @@ -18,7 +18,7 @@ org.keycloak.testsuite integration-arquillian-servers-app-server - 24.0.5-PS-1-SNAPSHOT + 24.0.5-PS-1 4.0.0 diff --git a/testsuite/integration-arquillian/servers/app-server/karaf/fuse63/pom.xml b/testsuite/integration-arquillian/servers/app-server/karaf/fuse63/pom.xml index 101a17fc07b0..8afff103c5f7 100644 --- a/testsuite/integration-arquillian/servers/app-server/karaf/fuse63/pom.xml +++ b/testsuite/integration-arquillian/servers/app-server/karaf/fuse63/pom.xml @@ -21,7 +21,7 @@ org.keycloak.testsuite integration-arquillian-servers-app-server-karaf - 24.0.5-PS-1-SNAPSHOT + 24.0.5-PS-1 4.0.0 diff --git a/testsuite/integration-arquillian/servers/app-server/karaf/fuse7x/pom.xml b/testsuite/integration-arquillian/servers/app-server/karaf/fuse7x/pom.xml index 49c86e628568..e180e84ae06f 100644 --- a/testsuite/integration-arquillian/servers/app-server/karaf/fuse7x/pom.xml +++ b/testsuite/integration-arquillian/servers/app-server/karaf/fuse7x/pom.xml @@ -21,7 +21,7 @@ org.keycloak.testsuite integration-arquillian-servers-app-server-karaf - 24.0.5-PS-1-SNAPSHOT + 24.0.5-PS-1 4.0.0 diff --git a/testsuite/integration-arquillian/servers/app-server/karaf/pom.xml b/testsuite/integration-arquillian/servers/app-server/karaf/pom.xml index b8a18ade7ec5..410632c521d7 100644 --- a/testsuite/integration-arquillian/servers/app-server/karaf/pom.xml +++ b/testsuite/integration-arquillian/servers/app-server/karaf/pom.xml @@ -21,7 +21,7 @@ org.keycloak.testsuite integration-arquillian-servers-app-server - 24.0.5-PS-1-SNAPSHOT + 24.0.5-PS-1 4.0.0 diff --git a/testsuite/integration-arquillian/servers/app-server/pom.xml b/testsuite/integration-arquillian/servers/app-server/pom.xml index 30b907a8dbaa..3ec76a11e299 100644 --- a/testsuite/integration-arquillian/servers/app-server/pom.xml +++ b/testsuite/integration-arquillian/servers/app-server/pom.xml @@ -21,7 +21,7 @@ org.keycloak.testsuite integration-arquillian-servers - 24.0.5-PS-1-SNAPSHOT + 24.0.5-PS-1 4.0.0 diff --git a/testsuite/integration-arquillian/servers/app-server/tomcat/common/pom.xml b/testsuite/integration-arquillian/servers/app-server/tomcat/common/pom.xml index 5b8dbda98d3d..380bb92cbaf9 100644 --- a/testsuite/integration-arquillian/servers/app-server/tomcat/common/pom.xml +++ b/testsuite/integration-arquillian/servers/app-server/tomcat/common/pom.xml @@ -5,7 +5,7 @@ integration-arquillian-servers-app-server-tomcat org.keycloak.testsuite - 24.0.5-PS-1-SNAPSHOT + 24.0.5-PS-1 4.0.0 diff --git a/testsuite/integration-arquillian/servers/app-server/tomcat/pom.xml b/testsuite/integration-arquillian/servers/app-server/tomcat/pom.xml index cf3694d65e34..5bdd15f7bd54 100644 --- a/testsuite/integration-arquillian/servers/app-server/tomcat/pom.xml +++ b/testsuite/integration-arquillian/servers/app-server/tomcat/pom.xml @@ -21,7 +21,7 @@ org.keycloak.testsuite integration-arquillian-servers-app-server - 24.0.5-PS-1-SNAPSHOT + 24.0.5-PS-1 4.0.0 diff --git a/testsuite/integration-arquillian/servers/app-server/tomcat/tomcat8/pom.xml b/testsuite/integration-arquillian/servers/app-server/tomcat/tomcat8/pom.xml index 424212072958..da73f6d7b327 100644 --- a/testsuite/integration-arquillian/servers/app-server/tomcat/tomcat8/pom.xml +++ b/testsuite/integration-arquillian/servers/app-server/tomcat/tomcat8/pom.xml @@ -21,7 +21,7 @@ org.keycloak.testsuite integration-arquillian-servers-app-server-tomcat - 24.0.5-PS-1-SNAPSHOT + 24.0.5-PS-1 4.0.0 diff --git a/testsuite/integration-arquillian/servers/app-server/tomcat/tomcat9/pom.xml b/testsuite/integration-arquillian/servers/app-server/tomcat/tomcat9/pom.xml index 992ce60f6bd4..098839d4ed61 100644 --- a/testsuite/integration-arquillian/servers/app-server/tomcat/tomcat9/pom.xml +++ b/testsuite/integration-arquillian/servers/app-server/tomcat/tomcat9/pom.xml @@ -21,7 +21,7 @@ org.keycloak.testsuite integration-arquillian-servers-app-server-tomcat - 24.0.5-PS-1-SNAPSHOT + 24.0.5-PS-1 4.0.0 diff --git a/testsuite/integration-arquillian/servers/app-server/undertow/pom.xml b/testsuite/integration-arquillian/servers/app-server/undertow/pom.xml index 9d880fd8baaf..211d83e769ac 100644 --- a/testsuite/integration-arquillian/servers/app-server/undertow/pom.xml +++ b/testsuite/integration-arquillian/servers/app-server/undertow/pom.xml @@ -18,7 +18,7 @@ org.keycloak.testsuite integration-arquillian-servers-app-server - 24.0.5-PS-1-SNAPSHOT + 24.0.5-PS-1 4.0.0 diff --git a/testsuite/integration-arquillian/servers/auth-server/pom.xml b/testsuite/integration-arquillian/servers/auth-server/pom.xml index aba8a1b77dbe..499c0f57793e 100644 --- a/testsuite/integration-arquillian/servers/auth-server/pom.xml +++ b/testsuite/integration-arquillian/servers/auth-server/pom.xml @@ -21,7 +21,7 @@ org.keycloak.testsuite integration-arquillian-servers - 24.0.5-PS-1-SNAPSHOT + 24.0.5-PS-1 4.0.0 diff --git a/testsuite/integration-arquillian/servers/auth-server/quarkus/pom.xml b/testsuite/integration-arquillian/servers/auth-server/quarkus/pom.xml index 9ea5f3a6fd34..604ecb2841e2 100644 --- a/testsuite/integration-arquillian/servers/auth-server/quarkus/pom.xml +++ b/testsuite/integration-arquillian/servers/auth-server/quarkus/pom.xml @@ -5,7 +5,7 @@ integration-arquillian-servers-auth-server org.keycloak.testsuite - 24.0.5-PS-1-SNAPSHOT + 24.0.5-PS-1 4.0.0 diff --git a/testsuite/integration-arquillian/servers/auth-server/services/pom.xml b/testsuite/integration-arquillian/servers/auth-server/services/pom.xml index 1fe47179c9c6..0ddbc2b020d3 100644 --- a/testsuite/integration-arquillian/servers/auth-server/services/pom.xml +++ b/testsuite/integration-arquillian/servers/auth-server/services/pom.xml @@ -21,7 +21,7 @@ org.keycloak.testsuite integration-arquillian-servers-auth-server - 24.0.5-PS-1-SNAPSHOT + 24.0.5-PS-1 4.0.0 diff --git a/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers-deployment/pom.xml b/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers-deployment/pom.xml index d72046f81bff..147a60f09042 100644 --- a/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers-deployment/pom.xml +++ b/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers-deployment/pom.xml @@ -24,7 +24,7 @@ org.keycloak.testsuite integration-arquillian-servers-auth-server-services - 24.0.5-PS-1-SNAPSHOT + 24.0.5-PS-1 integration-arquillian-testsuite-providers-deployment diff --git a/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/pom.xml b/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/pom.xml index bae89a35fe7c..62bf817f2ca6 100644 --- a/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/pom.xml +++ b/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/pom.xml @@ -24,7 +24,7 @@ org.keycloak.testsuite integration-arquillian-servers-auth-server-services - 24.0.5-PS-1-SNAPSHOT + 24.0.5-PS-1 integration-arquillian-testsuite-providers diff --git a/testsuite/integration-arquillian/servers/auth-server/undertow/pom.xml b/testsuite/integration-arquillian/servers/auth-server/undertow/pom.xml index 8a50292ea293..00b81e632bc9 100644 --- a/testsuite/integration-arquillian/servers/auth-server/undertow/pom.xml +++ b/testsuite/integration-arquillian/servers/auth-server/undertow/pom.xml @@ -21,7 +21,7 @@ org.keycloak.testsuite integration-arquillian-servers-auth-server - 24.0.5-PS-1-SNAPSHOT + 24.0.5-PS-1 4.0.0 diff --git a/testsuite/integration-arquillian/servers/cache-server/infinispan/datagrid/pom.xml b/testsuite/integration-arquillian/servers/cache-server/infinispan/datagrid/pom.xml index 078241dc9adc..e822c790586a 100644 --- a/testsuite/integration-arquillian/servers/cache-server/infinispan/datagrid/pom.xml +++ b/testsuite/integration-arquillian/servers/cache-server/infinispan/datagrid/pom.xml @@ -21,7 +21,7 @@ org.keycloak.testsuite integration-arquillian-servers-cache-server-infinispan - 24.0.5-PS-1-SNAPSHOT + 24.0.5-PS-1 4.0.0 diff --git a/testsuite/integration-arquillian/servers/cache-server/infinispan/infinispan/pom.xml b/testsuite/integration-arquillian/servers/cache-server/infinispan/infinispan/pom.xml index 4d1a5f5c2597..6ee8903007e6 100644 --- a/testsuite/integration-arquillian/servers/cache-server/infinispan/infinispan/pom.xml +++ b/testsuite/integration-arquillian/servers/cache-server/infinispan/infinispan/pom.xml @@ -21,7 +21,7 @@ org.keycloak.testsuite integration-arquillian-servers-cache-server-infinispan - 24.0.5-PS-1-SNAPSHOT + 24.0.5-PS-1 4.0.0 diff --git a/testsuite/integration-arquillian/servers/cache-server/infinispan/pom.xml b/testsuite/integration-arquillian/servers/cache-server/infinispan/pom.xml index bfd50590f132..2f59d194a0c2 100644 --- a/testsuite/integration-arquillian/servers/cache-server/infinispan/pom.xml +++ b/testsuite/integration-arquillian/servers/cache-server/infinispan/pom.xml @@ -24,7 +24,7 @@ org.keycloak.testsuite integration-arquillian-servers-cache-server - 24.0.5-PS-1-SNAPSHOT + 24.0.5-PS-1 pom diff --git a/testsuite/integration-arquillian/servers/cache-server/legacy/datagrid/pom.xml b/testsuite/integration-arquillian/servers/cache-server/legacy/datagrid/pom.xml index b633e3647eca..7f9072d423c1 100644 --- a/testsuite/integration-arquillian/servers/cache-server/legacy/datagrid/pom.xml +++ b/testsuite/integration-arquillian/servers/cache-server/legacy/datagrid/pom.xml @@ -21,7 +21,7 @@ org.keycloak.testsuite integration-arquillian-servers-cache-server-legacy - 24.0.5-PS-1-SNAPSHOT + 24.0.5-PS-1 4.0.0 diff --git a/testsuite/integration-arquillian/servers/cache-server/legacy/infinispan/pom.xml b/testsuite/integration-arquillian/servers/cache-server/legacy/infinispan/pom.xml index 8805e726adf7..7bf501712f8f 100644 --- a/testsuite/integration-arquillian/servers/cache-server/legacy/infinispan/pom.xml +++ b/testsuite/integration-arquillian/servers/cache-server/legacy/infinispan/pom.xml @@ -21,7 +21,7 @@ org.keycloak.testsuite integration-arquillian-servers-cache-server-legacy - 24.0.5-PS-1-SNAPSHOT + 24.0.5-PS-1 4.0.0 diff --git a/testsuite/integration-arquillian/servers/cache-server/legacy/pom.xml b/testsuite/integration-arquillian/servers/cache-server/legacy/pom.xml index 135a842ee9e1..2fd91a250c8a 100644 --- a/testsuite/integration-arquillian/servers/cache-server/legacy/pom.xml +++ b/testsuite/integration-arquillian/servers/cache-server/legacy/pom.xml @@ -21,7 +21,7 @@ org.keycloak.testsuite integration-arquillian-servers-cache-server - 24.0.5-PS-1-SNAPSHOT + 24.0.5-PS-1 4.0.0 diff --git a/testsuite/integration-arquillian/servers/cache-server/pom.xml b/testsuite/integration-arquillian/servers/cache-server/pom.xml index 56cb86a4da74..8a9006be6a5d 100644 --- a/testsuite/integration-arquillian/servers/cache-server/pom.xml +++ b/testsuite/integration-arquillian/servers/cache-server/pom.xml @@ -21,7 +21,7 @@ org.keycloak.testsuite integration-arquillian-servers - 24.0.5-PS-1-SNAPSHOT + 24.0.5-PS-1 4.0.0 diff --git a/testsuite/integration-arquillian/servers/migration/pom.xml b/testsuite/integration-arquillian/servers/migration/pom.xml index 4b7feac820d1..265a37441a5a 100644 --- a/testsuite/integration-arquillian/servers/migration/pom.xml +++ b/testsuite/integration-arquillian/servers/migration/pom.xml @@ -21,7 +21,7 @@ org.keycloak.testsuite integration-arquillian-servers - 24.0.5-PS-1-SNAPSHOT + 24.0.5-PS-1 4.0.0 diff --git a/testsuite/integration-arquillian/servers/pom.xml b/testsuite/integration-arquillian/servers/pom.xml index e95e70b41c45..dd162ed5d193 100644 --- a/testsuite/integration-arquillian/servers/pom.xml +++ b/testsuite/integration-arquillian/servers/pom.xml @@ -21,7 +21,7 @@ org.keycloak.testsuite integration-arquillian - 24.0.5-PS-1-SNAPSHOT + 24.0.5-PS-1 4.0.0 diff --git a/testsuite/integration-arquillian/test-apps/app-profile-jee/pom.xml b/testsuite/integration-arquillian/test-apps/app-profile-jee/pom.xml index a7c685b753b4..85927416956a 100644 --- a/testsuite/integration-arquillian/test-apps/app-profile-jee/pom.xml +++ b/testsuite/integration-arquillian/test-apps/app-profile-jee/pom.xml @@ -5,7 +5,7 @@ org.keycloak.testsuite integration-arquillian-test-apps - 24.0.5-PS-1-SNAPSHOT + 24.0.5-PS-1 keycloak-test-app-profile-jee diff --git a/testsuite/integration-arquillian/test-apps/cors/angular-product/pom.xml b/testsuite/integration-arquillian/test-apps/cors/angular-product/pom.xml index 638cfb7f1f52..91c4b7f3f48f 100755 --- a/testsuite/integration-arquillian/test-apps/cors/angular-product/pom.xml +++ b/testsuite/integration-arquillian/test-apps/cors/angular-product/pom.xml @@ -21,7 +21,7 @@ org.keycloak.testsuite integration-arquillian-test-apps-cors-parent - 24.0.5-PS-1-SNAPSHOT + 24.0.5-PS-1 4.0.0 diff --git a/testsuite/integration-arquillian/test-apps/cors/database-service/pom.xml b/testsuite/integration-arquillian/test-apps/cors/database-service/pom.xml index 9ab84b0f2096..dbb2a47e872b 100755 --- a/testsuite/integration-arquillian/test-apps/cors/database-service/pom.xml +++ b/testsuite/integration-arquillian/test-apps/cors/database-service/pom.xml @@ -21,7 +21,7 @@ org.keycloak.testsuite integration-arquillian-test-apps-cors-parent - 24.0.5-PS-1-SNAPSHOT + 24.0.5-PS-1 4.0.0 diff --git a/testsuite/integration-arquillian/test-apps/cors/pom.xml b/testsuite/integration-arquillian/test-apps/cors/pom.xml index 6848a876ca79..05f9aefce704 100644 --- a/testsuite/integration-arquillian/test-apps/cors/pom.xml +++ b/testsuite/integration-arquillian/test-apps/cors/pom.xml @@ -5,7 +5,7 @@ integration-arquillian-test-apps org.keycloak.testsuite - 24.0.5-PS-1-SNAPSHOT + 24.0.5-PS-1 4.0.0 diff --git a/testsuite/integration-arquillian/test-apps/fuse/camel-fuse7-undertow/pom.xml b/testsuite/integration-arquillian/test-apps/fuse/camel-fuse7-undertow/pom.xml index 04ddab08bd3d..0cecbbab6c76 100755 --- a/testsuite/integration-arquillian/test-apps/fuse/camel-fuse7-undertow/pom.xml +++ b/testsuite/integration-arquillian/test-apps/fuse/camel-fuse7-undertow/pom.xml @@ -21,7 +21,7 @@ integration-arquillian-test-apps-fuse-parent org.keycloak.testsuite - 24.0.5-PS-1-SNAPSHOT + 24.0.5-PS-1 4.0.0 diff --git a/testsuite/integration-arquillian/test-apps/fuse/camel/pom.xml b/testsuite/integration-arquillian/test-apps/fuse/camel/pom.xml index cab75a348903..f60a61293bb9 100755 --- a/testsuite/integration-arquillian/test-apps/fuse/camel/pom.xml +++ b/testsuite/integration-arquillian/test-apps/fuse/camel/pom.xml @@ -21,7 +21,7 @@ integration-arquillian-test-apps-fuse-parent org.keycloak.testsuite - 24.0.5-PS-1-SNAPSHOT + 24.0.5-PS-1 4.0.0 diff --git a/testsuite/integration-arquillian/test-apps/fuse/customer-app-fuse/pom.xml b/testsuite/integration-arquillian/test-apps/fuse/customer-app-fuse/pom.xml index 9753eb0a770d..0135b2303116 100755 --- a/testsuite/integration-arquillian/test-apps/fuse/customer-app-fuse/pom.xml +++ b/testsuite/integration-arquillian/test-apps/fuse/customer-app-fuse/pom.xml @@ -21,7 +21,7 @@ integration-arquillian-test-apps-fuse-parent org.keycloak.testsuite - 24.0.5-PS-1-SNAPSHOT + 24.0.5-PS-1 4.0.0 diff --git a/testsuite/integration-arquillian/test-apps/fuse/cxf-jaxrs-fuse7-undertow/pom.xml b/testsuite/integration-arquillian/test-apps/fuse/cxf-jaxrs-fuse7-undertow/pom.xml index 9ba7666a87a1..0ec99ef885f1 100755 --- a/testsuite/integration-arquillian/test-apps/fuse/cxf-jaxrs-fuse7-undertow/pom.xml +++ b/testsuite/integration-arquillian/test-apps/fuse/cxf-jaxrs-fuse7-undertow/pom.xml @@ -21,7 +21,7 @@ integration-arquillian-test-apps-fuse-parent org.keycloak.testsuite - 24.0.5-PS-1-SNAPSHOT + 24.0.5-PS-1 4.0.0 diff --git a/testsuite/integration-arquillian/test-apps/fuse/cxf-jaxrs/pom.xml b/testsuite/integration-arquillian/test-apps/fuse/cxf-jaxrs/pom.xml index 5fe2de5e0222..079078f0c85b 100755 --- a/testsuite/integration-arquillian/test-apps/fuse/cxf-jaxrs/pom.xml +++ b/testsuite/integration-arquillian/test-apps/fuse/cxf-jaxrs/pom.xml @@ -21,7 +21,7 @@ integration-arquillian-test-apps-fuse-parent org.keycloak.testsuite - 24.0.5-PS-1-SNAPSHOT + 24.0.5-PS-1 4.0.0 diff --git a/testsuite/integration-arquillian/test-apps/fuse/cxf-jaxws-fuse7-undertow/pom.xml b/testsuite/integration-arquillian/test-apps/fuse/cxf-jaxws-fuse7-undertow/pom.xml index 4b4b930a3d27..323ede236f35 100755 --- a/testsuite/integration-arquillian/test-apps/fuse/cxf-jaxws-fuse7-undertow/pom.xml +++ b/testsuite/integration-arquillian/test-apps/fuse/cxf-jaxws-fuse7-undertow/pom.xml @@ -21,7 +21,7 @@ integration-arquillian-test-apps-fuse-parent org.keycloak.testsuite - 24.0.5-PS-1-SNAPSHOT + 24.0.5-PS-1 4.0.0 diff --git a/testsuite/integration-arquillian/test-apps/fuse/cxf-jaxws/pom.xml b/testsuite/integration-arquillian/test-apps/fuse/cxf-jaxws/pom.xml index 10e5203e2c5f..259e22168ac0 100755 --- a/testsuite/integration-arquillian/test-apps/fuse/cxf-jaxws/pom.xml +++ b/testsuite/integration-arquillian/test-apps/fuse/cxf-jaxws/pom.xml @@ -21,7 +21,7 @@ integration-arquillian-test-apps-fuse-parent org.keycloak.testsuite - 24.0.5-PS-1-SNAPSHOT + 24.0.5-PS-1 4.0.0 diff --git a/testsuite/integration-arquillian/test-apps/fuse/external-config/pom.xml b/testsuite/integration-arquillian/test-apps/fuse/external-config/pom.xml index 98bb828d816b..981e24fae897 100755 --- a/testsuite/integration-arquillian/test-apps/fuse/external-config/pom.xml +++ b/testsuite/integration-arquillian/test-apps/fuse/external-config/pom.xml @@ -21,7 +21,7 @@ integration-arquillian-test-apps-fuse-parent org.keycloak.testsuite - 24.0.5-PS-1-SNAPSHOT + 24.0.5-PS-1 Keycloak Examples - External Config diff --git a/testsuite/integration-arquillian/test-apps/fuse/features/pom.xml b/testsuite/integration-arquillian/test-apps/fuse/features/pom.xml index bde0adee3a62..1c5be3c8a642 100755 --- a/testsuite/integration-arquillian/test-apps/fuse/features/pom.xml +++ b/testsuite/integration-arquillian/test-apps/fuse/features/pom.xml @@ -21,7 +21,7 @@ integration-arquillian-test-apps-fuse-parent org.keycloak.testsuite - 24.0.5-PS-1-SNAPSHOT + 24.0.5-PS-1 4.0.0 diff --git a/testsuite/integration-arquillian/test-apps/fuse/pom.xml b/testsuite/integration-arquillian/test-apps/fuse/pom.xml index 6a4747d0bd23..cda3dcec5a1f 100755 --- a/testsuite/integration-arquillian/test-apps/fuse/pom.xml +++ b/testsuite/integration-arquillian/test-apps/fuse/pom.xml @@ -20,7 +20,7 @@ integration-arquillian-test-apps org.keycloak.testsuite - 24.0.5-PS-1-SNAPSHOT + 24.0.5-PS-1 Fuse Test Applications diff --git a/testsuite/integration-arquillian/test-apps/fuse/product-app-fuse/pom.xml b/testsuite/integration-arquillian/test-apps/fuse/product-app-fuse/pom.xml index df21d1906033..d5985d6a28c3 100755 --- a/testsuite/integration-arquillian/test-apps/fuse/product-app-fuse/pom.xml +++ b/testsuite/integration-arquillian/test-apps/fuse/product-app-fuse/pom.xml @@ -21,7 +21,7 @@ integration-arquillian-test-apps-fuse-parent org.keycloak.testsuite - 24.0.5-PS-1-SNAPSHOT + 24.0.5-PS-1 4.0.0 diff --git a/testsuite/integration-arquillian/test-apps/fuse/product-app-fuse7-undertow/pom.xml b/testsuite/integration-arquillian/test-apps/fuse/product-app-fuse7-undertow/pom.xml index 8e3f3bde3e74..eaf9f0f33783 100755 --- a/testsuite/integration-arquillian/test-apps/fuse/product-app-fuse7-undertow/pom.xml +++ b/testsuite/integration-arquillian/test-apps/fuse/product-app-fuse7-undertow/pom.xml @@ -21,7 +21,7 @@ integration-arquillian-test-apps-fuse-parent org.keycloak.testsuite - 24.0.5-PS-1-SNAPSHOT + 24.0.5-PS-1 4.0.0 diff --git a/testsuite/integration-arquillian/test-apps/hello-world-authz-service/pom.xml b/testsuite/integration-arquillian/test-apps/hello-world-authz-service/pom.xml index 17b425670e7f..22395354667b 100755 --- a/testsuite/integration-arquillian/test-apps/hello-world-authz-service/pom.xml +++ b/testsuite/integration-arquillian/test-apps/hello-world-authz-service/pom.xml @@ -24,7 +24,7 @@ org.keycloak.testsuite integration-arquillian-test-apps - 24.0.5-PS-1-SNAPSHOT + 24.0.5-PS-1 hello-world-authz-service diff --git a/testsuite/integration-arquillian/test-apps/pom.xml b/testsuite/integration-arquillian/test-apps/pom.xml index 65aebe20bcd4..952d26ef7b2c 100644 --- a/testsuite/integration-arquillian/test-apps/pom.xml +++ b/testsuite/integration-arquillian/test-apps/pom.xml @@ -5,7 +5,7 @@ integration-arquillian org.keycloak.testsuite - 24.0.5-PS-1-SNAPSHOT + 24.0.5-PS-1 4.0.0 diff --git a/testsuite/integration-arquillian/test-apps/servlet-authz/pom.xml b/testsuite/integration-arquillian/test-apps/servlet-authz/pom.xml index 96b332fdb52c..71dc7a2a052b 100755 --- a/testsuite/integration-arquillian/test-apps/servlet-authz/pom.xml +++ b/testsuite/integration-arquillian/test-apps/servlet-authz/pom.xml @@ -6,7 +6,7 @@ org.keycloak.testsuite integration-arquillian-test-apps - 24.0.5-PS-1-SNAPSHOT + 24.0.5-PS-1 servlet-authz-app diff --git a/testsuite/integration-arquillian/test-apps/servlet-policy-enforcer/pom.xml b/testsuite/integration-arquillian/test-apps/servlet-policy-enforcer/pom.xml index a33c5218dbe6..1459785b7d90 100755 --- a/testsuite/integration-arquillian/test-apps/servlet-policy-enforcer/pom.xml +++ b/testsuite/integration-arquillian/test-apps/servlet-policy-enforcer/pom.xml @@ -24,7 +24,7 @@ org.keycloak.testsuite integration-arquillian-test-apps - 24.0.5-PS-1-SNAPSHOT + 24.0.5-PS-1 servlet-policy-enforcer diff --git a/testsuite/integration-arquillian/test-apps/servlets-jakarta/pom.xml b/testsuite/integration-arquillian/test-apps/servlets-jakarta/pom.xml index 40902fd4fe72..454106bdca31 100644 --- a/testsuite/integration-arquillian/test-apps/servlets-jakarta/pom.xml +++ b/testsuite/integration-arquillian/test-apps/servlets-jakarta/pom.xml @@ -6,7 +6,7 @@ org.keycloak.testsuite integration-arquillian-test-apps - 24.0.5-PS-1-SNAPSHOT + 24.0.5-PS-1 integration-arquillian-test-apps-servlets-jakarta diff --git a/testsuite/integration-arquillian/test-apps/servlets/pom.xml b/testsuite/integration-arquillian/test-apps/servlets/pom.xml index fc35964f0ce5..b225f73b7528 100644 --- a/testsuite/integration-arquillian/test-apps/servlets/pom.xml +++ b/testsuite/integration-arquillian/test-apps/servlets/pom.xml @@ -5,7 +5,7 @@ integration-arquillian-test-apps org.keycloak.testsuite - 24.0.5-PS-1-SNAPSHOT + 24.0.5-PS-1 4.0.0 diff --git a/testsuite/integration-arquillian/test-apps/spring-boot-adapter-app/pom.xml b/testsuite/integration-arquillian/test-apps/spring-boot-adapter-app/pom.xml index a32fa5c47899..8e5f2e6f7d46 100644 --- a/testsuite/integration-arquillian/test-apps/spring-boot-adapter-app/pom.xml +++ b/testsuite/integration-arquillian/test-apps/spring-boot-adapter-app/pom.xml @@ -5,7 +5,7 @@ org.keycloak.testsuite integration-arquillian-test-apps - 24.0.5-PS-1-SNAPSHOT + 24.0.5-PS-1 4.0.0 diff --git a/testsuite/integration-arquillian/test-apps/test-apps-dist/pom.xml b/testsuite/integration-arquillian/test-apps/test-apps-dist/pom.xml index 159d6106d774..a810f11d392b 100644 --- a/testsuite/integration-arquillian/test-apps/test-apps-dist/pom.xml +++ b/testsuite/integration-arquillian/test-apps/test-apps-dist/pom.xml @@ -5,7 +5,7 @@ integration-arquillian-test-apps org.keycloak.testsuite - 24.0.5-PS-1-SNAPSHOT + 24.0.5-PS-1 4.0.0 diff --git a/testsuite/integration-arquillian/tests/base/pom.xml b/testsuite/integration-arquillian/tests/base/pom.xml index a6c8f0e20de1..ddd05df6b0d2 100644 --- a/testsuite/integration-arquillian/tests/base/pom.xml +++ b/testsuite/integration-arquillian/tests/base/pom.xml @@ -21,7 +21,7 @@ org.keycloak.testsuite integration-arquillian-tests - 24.0.5-PS-1-SNAPSHOT + 24.0.5-PS-1 4.0.0 diff --git a/testsuite/integration-arquillian/tests/other/adapters/jboss/pom.xml b/testsuite/integration-arquillian/tests/other/adapters/jboss/pom.xml index 39b10fde50bb..21e6410c7062 100644 --- a/testsuite/integration-arquillian/tests/other/adapters/jboss/pom.xml +++ b/testsuite/integration-arquillian/tests/other/adapters/jboss/pom.xml @@ -24,7 +24,7 @@ org.keycloak.testsuite integration-arquillian-tests-adapters - 24.0.5-PS-1-SNAPSHOT + 24.0.5-PS-1 integration-arquillian-tests-adapters-jboss diff --git a/testsuite/integration-arquillian/tests/other/adapters/karaf/fuse61/pom.xml b/testsuite/integration-arquillian/tests/other/adapters/karaf/fuse61/pom.xml index 37e99766105f..f06b4d8792ef 100644 --- a/testsuite/integration-arquillian/tests/other/adapters/karaf/fuse61/pom.xml +++ b/testsuite/integration-arquillian/tests/other/adapters/karaf/fuse61/pom.xml @@ -24,7 +24,7 @@ org.keycloak.testsuite integration-arquillian-tests-adapters-karaf - 24.0.5-PS-1-SNAPSHOT + 24.0.5-PS-1 integration-arquillian-tests-adapters-fuse61 diff --git a/testsuite/integration-arquillian/tests/other/adapters/karaf/fuse62/pom.xml b/testsuite/integration-arquillian/tests/other/adapters/karaf/fuse62/pom.xml index 85a5af68f075..c06f566a7f7c 100644 --- a/testsuite/integration-arquillian/tests/other/adapters/karaf/fuse62/pom.xml +++ b/testsuite/integration-arquillian/tests/other/adapters/karaf/fuse62/pom.xml @@ -24,7 +24,7 @@ org.keycloak.testsuite integration-arquillian-tests-adapters-karaf - 24.0.5-PS-1-SNAPSHOT + 24.0.5-PS-1 integration-arquillian-tests-adapters-fuse62 diff --git a/testsuite/integration-arquillian/tests/other/adapters/karaf/karaf3/pom.xml b/testsuite/integration-arquillian/tests/other/adapters/karaf/karaf3/pom.xml index b8535d4075fc..495d88c660d5 100644 --- a/testsuite/integration-arquillian/tests/other/adapters/karaf/karaf3/pom.xml +++ b/testsuite/integration-arquillian/tests/other/adapters/karaf/karaf3/pom.xml @@ -24,7 +24,7 @@ org.keycloak.testsuite integration-arquillian-tests-adapters-karaf - 24.0.5-PS-1-SNAPSHOT + 24.0.5-PS-1 integration-arquillian-tests-adapters-karaf3 diff --git a/testsuite/integration-arquillian/tests/other/adapters/karaf/pom.xml b/testsuite/integration-arquillian/tests/other/adapters/karaf/pom.xml index a0cfc133cd8e..b3745945e150 100644 --- a/testsuite/integration-arquillian/tests/other/adapters/karaf/pom.xml +++ b/testsuite/integration-arquillian/tests/other/adapters/karaf/pom.xml @@ -24,7 +24,7 @@ org.keycloak.testsuite integration-arquillian-tests-adapters - 24.0.5-PS-1-SNAPSHOT + 24.0.5-PS-1 integration-arquillian-tests-adapters-karaf diff --git a/testsuite/integration-arquillian/tests/other/adapters/pom.xml b/testsuite/integration-arquillian/tests/other/adapters/pom.xml index 9cf8e969d64e..3c31f2f8cc41 100644 --- a/testsuite/integration-arquillian/tests/other/adapters/pom.xml +++ b/testsuite/integration-arquillian/tests/other/adapters/pom.xml @@ -24,7 +24,7 @@ org.keycloak.testsuite integration-arquillian-tests-other - 24.0.5-PS-1-SNAPSHOT + 24.0.5-PS-1 integration-arquillian-tests-adapters diff --git a/testsuite/integration-arquillian/tests/other/adapters/was/pom.xml b/testsuite/integration-arquillian/tests/other/adapters/was/pom.xml index 47f517981707..2483cb7637f0 100644 --- a/testsuite/integration-arquillian/tests/other/adapters/was/pom.xml +++ b/testsuite/integration-arquillian/tests/other/adapters/was/pom.xml @@ -24,7 +24,7 @@ org.keycloak.testsuite integration-arquillian-tests-adapters - 24.0.5-PS-1-SNAPSHOT + 24.0.5-PS-1 integration-arquillian-tests-adapters-was diff --git a/testsuite/integration-arquillian/tests/other/adapters/was/was8/pom.xml b/testsuite/integration-arquillian/tests/other/adapters/was/was8/pom.xml index 8fa1fa6bd7e5..bac518fe73a6 100644 --- a/testsuite/integration-arquillian/tests/other/adapters/was/was8/pom.xml +++ b/testsuite/integration-arquillian/tests/other/adapters/was/was8/pom.xml @@ -24,7 +24,7 @@ org.keycloak.testsuite integration-arquillian-tests-adapters-was - 24.0.5-PS-1-SNAPSHOT + 24.0.5-PS-1 integration-arquillian-tests-adapters-was8 diff --git a/testsuite/integration-arquillian/tests/other/adapters/wls/pom.xml b/testsuite/integration-arquillian/tests/other/adapters/wls/pom.xml index 00a5f48e610e..f8159ca1f2eb 100644 --- a/testsuite/integration-arquillian/tests/other/adapters/wls/pom.xml +++ b/testsuite/integration-arquillian/tests/other/adapters/wls/pom.xml @@ -24,7 +24,7 @@ org.keycloak.testsuite integration-arquillian-tests-adapters - 24.0.5-PS-1-SNAPSHOT + 24.0.5-PS-1 integration-arquillian-tests-adapters-wls diff --git a/testsuite/integration-arquillian/tests/other/adapters/wls/wls12/pom.xml b/testsuite/integration-arquillian/tests/other/adapters/wls/wls12/pom.xml index 1dc653512288..a9e97013d9c7 100644 --- a/testsuite/integration-arquillian/tests/other/adapters/wls/wls12/pom.xml +++ b/testsuite/integration-arquillian/tests/other/adapters/wls/wls12/pom.xml @@ -24,7 +24,7 @@ org.keycloak.testsuite integration-arquillian-tests-adapters-wls - 24.0.5-PS-1-SNAPSHOT + 24.0.5-PS-1 integration-arquillian-tests-adapters-wls12 diff --git a/testsuite/integration-arquillian/tests/other/base-ui/pom.xml b/testsuite/integration-arquillian/tests/other/base-ui/pom.xml index 9a655c658a2b..7f76d232f81d 100644 --- a/testsuite/integration-arquillian/tests/other/base-ui/pom.xml +++ b/testsuite/integration-arquillian/tests/other/base-ui/pom.xml @@ -22,7 +22,7 @@ integration-arquillian-tests-other org.keycloak.testsuite - 24.0.5-PS-1-SNAPSHOT + 24.0.5-PS-1 4.0.0 diff --git a/testsuite/integration-arquillian/tests/other/jpa-performance/pom.xml b/testsuite/integration-arquillian/tests/other/jpa-performance/pom.xml index af94476f12c9..ebfb045f1ba2 100644 --- a/testsuite/integration-arquillian/tests/other/jpa-performance/pom.xml +++ b/testsuite/integration-arquillian/tests/other/jpa-performance/pom.xml @@ -24,7 +24,7 @@ org.keycloak.testsuite integration-arquillian-tests-other - 24.0.5-PS-1-SNAPSHOT + 24.0.5-PS-1 integration-arquillian-tests-jpa-performance diff --git a/testsuite/integration-arquillian/tests/other/mod_auth_mellon/pom.xml b/testsuite/integration-arquillian/tests/other/mod_auth_mellon/pom.xml index a878ed7804f2..440c792e6f57 100644 --- a/testsuite/integration-arquillian/tests/other/mod_auth_mellon/pom.xml +++ b/testsuite/integration-arquillian/tests/other/mod_auth_mellon/pom.xml @@ -24,7 +24,7 @@ org.keycloak.testsuite integration-arquillian-tests-other - 24.0.5-PS-1-SNAPSHOT + 24.0.5-PS-1 integration-arquillian-tests-other-mod_auth_mellon diff --git a/testsuite/integration-arquillian/tests/other/pom.xml b/testsuite/integration-arquillian/tests/other/pom.xml index acedec213239..18d3be8147a9 100644 --- a/testsuite/integration-arquillian/tests/other/pom.xml +++ b/testsuite/integration-arquillian/tests/other/pom.xml @@ -24,7 +24,7 @@ org.keycloak.testsuite integration-arquillian-tests - 24.0.5-PS-1-SNAPSHOT + 24.0.5-PS-1 integration-arquillian-tests-other diff --git a/testsuite/integration-arquillian/tests/other/springboot-tests/pom.xml b/testsuite/integration-arquillian/tests/other/springboot-tests/pom.xml index fba69e990c78..76054d40d912 100644 --- a/testsuite/integration-arquillian/tests/other/springboot-tests/pom.xml +++ b/testsuite/integration-arquillian/tests/other/springboot-tests/pom.xml @@ -5,7 +5,7 @@ integration-arquillian-tests-other org.keycloak.testsuite - 24.0.5-PS-1-SNAPSHOT + 24.0.5-PS-1 4.0.0 diff --git a/testsuite/integration-arquillian/tests/other/sssd/pom.xml b/testsuite/integration-arquillian/tests/other/sssd/pom.xml index 4b4582987dcf..b2a40c51c281 100644 --- a/testsuite/integration-arquillian/tests/other/sssd/pom.xml +++ b/testsuite/integration-arquillian/tests/other/sssd/pom.xml @@ -5,7 +5,7 @@ integration-arquillian-tests-other org.keycloak.testsuite - 24.0.5-PS-1-SNAPSHOT + 24.0.5-PS-1 4.0.0 diff --git a/testsuite/integration-arquillian/tests/other/webauthn/pom.xml b/testsuite/integration-arquillian/tests/other/webauthn/pom.xml index 2512a291c6a3..28b97a693f2a 100644 --- a/testsuite/integration-arquillian/tests/other/webauthn/pom.xml +++ b/testsuite/integration-arquillian/tests/other/webauthn/pom.xml @@ -5,7 +5,7 @@ org.keycloak.testsuite integration-arquillian-tests-other - 24.0.5-PS-1-SNAPSHOT + 24.0.5-PS-1 4.0.0 jar diff --git a/testsuite/integration-arquillian/tests/pom.xml b/testsuite/integration-arquillian/tests/pom.xml index e27ad54c0b27..00c42162dad7 100644 --- a/testsuite/integration-arquillian/tests/pom.xml +++ b/testsuite/integration-arquillian/tests/pom.xml @@ -24,7 +24,7 @@ org.keycloak.testsuite integration-arquillian - 24.0.5-PS-1-SNAPSHOT + 24.0.5-PS-1 pom diff --git a/testsuite/integration-arquillian/util/pom.xml b/testsuite/integration-arquillian/util/pom.xml index 7cccc90b6492..0ce76606d94e 100644 --- a/testsuite/integration-arquillian/util/pom.xml +++ b/testsuite/integration-arquillian/util/pom.xml @@ -21,7 +21,7 @@ org.keycloak.testsuite integration-arquillian - 24.0.5-PS-1-SNAPSHOT + 24.0.5-PS-1 4.0.0 diff --git a/testsuite/model/pom.xml b/testsuite/model/pom.xml index c98dfefaa072..b13c91709653 100644 --- a/testsuite/model/pom.xml +++ b/testsuite/model/pom.xml @@ -4,7 +4,7 @@ org.keycloak keycloak-testsuite-pom - 24.0.5-PS-1-SNAPSHOT + 24.0.5-PS-1 ../pom.xml diff --git a/testsuite/pom.xml b/testsuite/pom.xml index 8eb6041370af..c95cc2b1d750 100755 --- a/testsuite/pom.xml +++ b/testsuite/pom.xml @@ -21,7 +21,7 @@ keycloak-parent org.keycloak - 24.0.5-PS-1-SNAPSHOT + 24.0.5-PS-1 ../pom.xml 4.0.0 diff --git a/testsuite/utils/pom.xml b/testsuite/utils/pom.xml index 249c69f12f2e..fd9ae5ed32f9 100755 --- a/testsuite/utils/pom.xml +++ b/testsuite/utils/pom.xml @@ -21,7 +21,7 @@ keycloak-testsuite-pom org.keycloak - 24.0.5-PS-1-SNAPSHOT + 24.0.5-PS-1 4.0.0 diff --git a/themes/pom.xml b/themes/pom.xml index ccd3249d5862..45c8397ac188 100755 --- a/themes/pom.xml +++ b/themes/pom.xml @@ -3,7 +3,7 @@ keycloak-parent org.keycloak - 24.0.5-PS-1-SNAPSHOT + 24.0.5-PS-1 4.0.0 diff --git a/util/embedded-ldap/pom.xml b/util/embedded-ldap/pom.xml index 252a9e781c7b..72dca921ebf2 100644 --- a/util/embedded-ldap/pom.xml +++ b/util/embedded-ldap/pom.xml @@ -21,7 +21,7 @@ keycloak-parent org.keycloak - 24.0.5-PS-1-SNAPSHOT + 24.0.5-PS-1 ../../pom.xml 4.0.0 diff --git a/util/pom.xml b/util/pom.xml index 5f6928364b21..270216d8f16f 100644 --- a/util/pom.xml +++ b/util/pom.xml @@ -20,7 +20,7 @@ keycloak-parent org.keycloak - 24.0.5-PS-1-SNAPSHOT + 24.0.5-PS-1 ../pom.xml From f3f56f9d440fd028d99d4d1a9166a97a5c5d6cea Mon Sep 17 00:00:00 2001 From: Otto Touzil Date: Wed, 10 Jul 2024 10:47:11 +0200 Subject: [PATCH 156/158] increase documentlimit for hash parameter in ciba/NOIS case --- .../BackchannelAuthenticationEndpointRequestParser.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/services/src/main/java/org/keycloak/protocol/oidc/grants/ciba/endpoints/request/BackchannelAuthenticationEndpointRequestParser.java b/services/src/main/java/org/keycloak/protocol/oidc/grants/ciba/endpoints/request/BackchannelAuthenticationEndpointRequestParser.java index 77743f08cd61..99641d7e01e0 100644 --- a/services/src/main/java/org/keycloak/protocol/oidc/grants/ciba/endpoints/request/BackchannelAuthenticationEndpointRequestParser.java +++ b/services/src/main/java/org/keycloak/protocol/oidc/grants/ciba/endpoints/request/BackchannelAuthenticationEndpointRequestParser.java @@ -87,6 +87,7 @@ public abstract class BackchannelAuthenticationEndpointRequestParser { * which leads to an HTTP 400 response in the ciba token call. */ KNOWN_REQ_PARAMS.add("jti"); + KNOWN_REQ_PARAMS.add("hash"); } public void parseRequest(BackchannelAuthenticationEndpointRequest request) { @@ -108,6 +109,8 @@ public void parseRequest(BackchannelAuthenticationEndpointRequest request) { request.claims = replaceIfNotNull(request.claims, getParameter(OIDCLoginProtocol.CLAIMS_PARAM)); extractAdditionalReqParams(request.additionalReqParams); + + request.additionalReqParams.put("hash", getParameter("hash")); } protected void extractAdditionalReqParams(Map additionalReqParams) { From 76a5c59dcba6d10049892c4506ae7ab15eb2c0d3 Mon Sep 17 00:00:00 2001 From: Otto Touzil Date: Wed, 10 Jul 2024 11:05:41 +0200 Subject: [PATCH 157/158] set version to 24.0.5-PS-2 --- .github/workflows/js-ci.yml | 12 ++++++------ adapters/oidc/adapter-core/pom.xml | 2 +- adapters/oidc/installed/pom.xml | 2 +- adapters/oidc/jakarta-servlet-filter/pom.xml | 2 +- adapters/oidc/jaxrs-oauth-client/pom.xml | 2 +- adapters/oidc/jetty/jetty-core/pom.xml | 2 +- adapters/oidc/jetty/jetty9.4/pom.xml | 2 +- adapters/oidc/jetty/pom.xml | 2 +- adapters/oidc/js/pom.xml | 2 +- adapters/oidc/pom.xml | 2 +- adapters/oidc/servlet-filter/pom.xml | 2 +- adapters/oidc/spring-boot-adapter-core/pom.xml | 2 +- adapters/oidc/spring-boot-container-bundle/pom.xml | 2 +- adapters/oidc/spring-boot2/pom.xml | 2 +- adapters/oidc/spring-security/pom.xml | 2 +- adapters/oidc/tomcat/pom.xml | 2 +- adapters/oidc/tomcat/tomcat-core/pom.xml | 2 +- adapters/oidc/tomcat/tomcat/pom.xml | 2 +- adapters/oidc/undertow/pom.xml | 2 +- adapters/oidc/wildfly-elytron/pom.xml | 2 +- adapters/oidc/wildfly/pom.xml | 2 +- adapters/oidc/wildfly/wildfly-subsystem/pom.xml | 2 +- adapters/pom.xml | 2 +- adapters/saml/core-jakarta/pom.xml | 2 +- adapters/saml/core-public/pom.xml | 2 +- adapters/saml/core/pom.xml | 2 +- adapters/saml/jakarta-servlet-filter/pom.xml | 2 +- adapters/saml/jetty/jetty-core/pom.xml | 2 +- adapters/saml/jetty/jetty9.4/pom.xml | 2 +- adapters/saml/jetty/pom.xml | 2 +- adapters/saml/pom.xml | 2 +- adapters/saml/servlet-filter/pom.xml | 2 +- adapters/saml/tomcat/pom.xml | 2 +- adapters/saml/tomcat/tomcat-core/pom.xml | 2 +- adapters/saml/tomcat/tomcat/pom.xml | 2 +- adapters/saml/undertow/pom.xml | 2 +- adapters/saml/wildfly-elytron-jakarta/pom.xml | 2 +- adapters/saml/wildfly-elytron/pom.xml | 2 +- adapters/saml/wildfly/pom.xml | 2 +- .../saml/wildfly/wildfly-jakarta-subsystem/pom.xml | 2 +- adapters/saml/wildfly/wildfly-subsystem/pom.xml | 2 +- adapters/spi/adapter-spi/pom.xml | 2 +- adapters/spi/jakarta-servlet-adapter-spi/pom.xml | 2 +- adapters/spi/jboss-adapter-core/pom.xml | 2 +- adapters/spi/jetty-adapter-spi/pom.xml | 2 +- adapters/spi/pom.xml | 2 +- adapters/spi/servlet-adapter-spi/pom.xml | 2 +- adapters/spi/tomcat-adapter-spi/pom.xml | 2 +- adapters/spi/undertow-adapter-spi/pom.xml | 2 +- authz/client/pom.xml | 2 +- authz/policy-enforcer/pom.xml | 2 +- authz/policy/common/pom.xml | 2 +- authz/policy/pom.xml | 2 +- authz/pom.xml | 2 +- boms/adapter/pom.xml | 2 +- boms/misc/pom.xml | 2 +- boms/pom.xml | 2 +- boms/spi/pom.xml | 2 +- common/pom.xml | 2 +- core/pom.xml | 2 +- crypto/default/pom.xml | 2 +- crypto/elytron/pom.xml | 2 +- crypto/fips1402/pom.xml | 2 +- crypto/pom.xml | 2 +- dependencies/pom.xml | 2 +- dependencies/server-all/pom.xml | 2 +- dependencies/server-min/pom.xml | 2 +- distribution/adapters/pom.xml | 2 +- distribution/adapters/tomcat-adapter-zip/pom.xml | 2 +- distribution/adapters/wildfly-adapter/pom.xml | 2 +- distribution/api-docs-dist/pom.xml | 2 +- distribution/downloads/pom.xml | 2 +- .../feature-packs/adapter-feature-pack/pom.xml | 2 +- distribution/feature-packs/pom.xml | 2 +- distribution/galleon-feature-packs/pom.xml | 2 +- .../pom.xml | 2 +- .../saml-adapter-galleon-pack/pom.xml | 2 +- distribution/licenses-common/pom.xml | 2 +- .../maven-plugins/licenses-processor/pom.xml | 2 +- distribution/maven-plugins/pom.xml | 2 +- distribution/pom.xml | 2 +- distribution/saml-adapters/pom.xml | 2 +- .../saml-adapters/tomcat-adapter-zip/pom.xml | 2 +- distribution/saml-adapters/wildfly-adapter/pom.xml | 2 +- .../wildfly-adapter-jakarta-zip/pom.xml | 2 +- .../wildfly-adapter/wildfly-adapter-zip/pom.xml | 2 +- .../wildfly-adapter/wildfly-jakarta-modules/pom.xml | 2 +- .../wildfly-adapter/wildfly-modules/pom.xml | 2 +- docs/documentation/aggregation/pom.xml | 2 +- docs/documentation/api_documentation/pom.xml | 2 +- docs/documentation/authorization_services/pom.xml | 2 +- docs/documentation/dist/pom.xml | 2 +- docs/documentation/header-maven-plugin/pom.xml | 4 ++-- docs/documentation/pom.xml | 4 ++-- docs/documentation/release_notes/pom.xml | 2 +- docs/documentation/securing_apps/pom.xml | 2 +- docs/documentation/server_admin/pom.xml | 2 +- docs/documentation/server_development/pom.xml | 2 +- docs/documentation/tests/pom.xml | 2 +- .../topics/templates/document-attributes.adoc | 8 ++++---- docs/documentation/upgrading/pom.xml | 2 +- docs/guides/pom.xml | 2 +- docs/maven-plugin/pom.xml | 2 +- docs/pom.xml | 2 +- examples/admin-client/pom.xml | 2 +- examples/js-console/pom.xml | 2 +- examples/kerberos/pom.xml | 2 +- examples/ldap/pom.xml | 2 +- examples/pom.xml | 2 +- examples/providers/authenticator/pom.xml | 2 +- examples/providers/pom.xml | 2 +- examples/providers/rest/pom.xml | 2 +- examples/saml/pom.xml | 2 +- examples/saml/servlet-filter/pom.xml | 2 +- examples/themes/pom.xml | 2 +- federation/kerberos/pom.xml | 2 +- federation/ldap/pom.xml | 2 +- federation/pom.xml | 2 +- federation/sssd/pom.xml | 2 +- integration/admin-client-jee/pom.xml | 2 +- integration/admin-client/pom.xml | 2 +- integration/client-cli/admin-cli/pom.xml | 2 +- integration/client-cli/client-cli-dist/pom.xml | 2 +- .../client-cli/client-registration-cli/pom.xml | 2 +- integration/client-cli/pom.xml | 2 +- integration/client-registration/pom.xml | 2 +- integration/pom.xml | 2 +- js/apps/account-ui/pom.xml | 2 +- js/apps/admin-ui/pom.xml | 2 +- js/libs/keycloak-admin-client/package.json | 2 +- js/libs/keycloak-admin-client/pom.xml | 2 +- js/libs/keycloak-js/package.json | 2 +- js/libs/keycloak-js/pom.xml | 2 +- js/pom.xml | 2 +- misc/keycloak-test-helper/pom.xml | 2 +- misc/pom.xml | 2 +- .../keycloak-spring-boot-starter/pom.xml | 2 +- misc/spring-boot-starter/pom.xml | 2 +- model/infinispan/pom.xml | 2 +- model/jpa/pom.xml | 2 +- model/legacy/pom.xml | 2 +- model/pom.xml | 2 +- model/storage-private/pom.xml | 2 +- model/storage-services/pom.xml | 2 +- model/storage/pom.xml | 2 +- operator/pom.xml | 2 +- pom.xml | 4 ++-- quarkus/config-api/pom.xml | 2 +- quarkus/container/Dockerfile | 2 +- quarkus/deployment/pom.xml | 2 +- quarkus/dist/pom.xml | 2 +- quarkus/pom.xml | 2 +- quarkus/runtime/pom.xml | 2 +- quarkus/server/pom.xml | 2 +- quarkus/tests/integration/pom.xml | 2 +- quarkus/tests/junit5/pom.xml | 2 +- quarkus/tests/pom.xml | 2 +- release-details | 6 +++--- rest/admin-ui-ext/pom.xml | 2 +- rest/pom.xml | 2 +- saml-core-api/pom.xml | 2 +- saml-core/pom.xml | 2 +- server-spi-private/pom.xml | 2 +- server-spi/pom.xml | 2 +- services/pom.xml | 2 +- testsuite/db-allocator-plugin/pom.xml | 2 +- testsuite/integration-arquillian/pom.xml | 2 +- .../servers/adapter-spi/pom.xml | 2 +- .../adapter-spi/undertow-adapter-jakarta/pom.xml | 2 +- .../undertow-adapter-saml-jakarta/pom.xml | 2 +- .../adapter-spi/undertow-adapter-spi-jakarta/pom.xml | 2 +- .../servers/app-server/app-server-spi/pom.xml | 2 +- .../servers/app-server/jboss/eap/pom.xml | 2 +- .../servers/app-server/jboss/eap6/pom.xml | 2 +- .../servers/app-server/jboss/galleon/pom.xml | 2 +- .../servers/app-server/jboss/pom.xml | 2 +- .../servers/app-server/jboss/wildfly/pom.xml | 2 +- .../servers/app-server/jetty/94/pom.xml | 2 +- .../servers/app-server/jetty/common/pom.xml | 2 +- .../servers/app-server/jetty/pom.xml | 2 +- .../servers/app-server/karaf/fuse63/pom.xml | 2 +- .../servers/app-server/karaf/fuse7x/pom.xml | 2 +- .../servers/app-server/karaf/pom.xml | 2 +- .../servers/app-server/pom.xml | 2 +- .../servers/app-server/tomcat/common/pom.xml | 2 +- .../servers/app-server/tomcat/pom.xml | 2 +- .../servers/app-server/tomcat/tomcat8/pom.xml | 2 +- .../servers/app-server/tomcat/tomcat9/pom.xml | 2 +- .../servers/app-server/undertow/pom.xml | 2 +- .../servers/auth-server/pom.xml | 2 +- .../servers/auth-server/quarkus/pom.xml | 2 +- .../servers/auth-server/services/pom.xml | 2 +- .../services/testsuite-providers-deployment/pom.xml | 2 +- .../auth-server/services/testsuite-providers/pom.xml | 2 +- .../servers/auth-server/undertow/pom.xml | 2 +- .../servers/cache-server/infinispan/datagrid/pom.xml | 2 +- .../cache-server/infinispan/infinispan/pom.xml | 2 +- .../servers/cache-server/infinispan/pom.xml | 2 +- .../servers/cache-server/legacy/datagrid/pom.xml | 2 +- .../servers/cache-server/legacy/infinispan/pom.xml | 2 +- .../servers/cache-server/legacy/pom.xml | 2 +- .../servers/cache-server/pom.xml | 2 +- .../integration-arquillian/servers/migration/pom.xml | 2 +- testsuite/integration-arquillian/servers/pom.xml | 2 +- .../test-apps/app-profile-jee/pom.xml | 2 +- .../test-apps/cors/angular-product/pom.xml | 2 +- .../test-apps/cors/database-service/pom.xml | 2 +- .../integration-arquillian/test-apps/cors/pom.xml | 2 +- .../test-apps/fuse/camel-fuse7-undertow/pom.xml | 2 +- .../test-apps/fuse/camel/pom.xml | 2 +- .../test-apps/fuse/customer-app-fuse/pom.xml | 2 +- .../test-apps/fuse/cxf-jaxrs-fuse7-undertow/pom.xml | 2 +- .../test-apps/fuse/cxf-jaxrs/pom.xml | 2 +- .../test-apps/fuse/cxf-jaxws-fuse7-undertow/pom.xml | 2 +- .../test-apps/fuse/cxf-jaxws/pom.xml | 2 +- .../test-apps/fuse/external-config/pom.xml | 2 +- .../test-apps/fuse/features/pom.xml | 2 +- .../integration-arquillian/test-apps/fuse/pom.xml | 2 +- .../test-apps/fuse/product-app-fuse/pom.xml | 2 +- .../fuse/product-app-fuse7-undertow/pom.xml | 2 +- .../test-apps/hello-world-authz-service/pom.xml | 2 +- testsuite/integration-arquillian/test-apps/pom.xml | 2 +- .../test-apps/servlet-authz/pom.xml | 2 +- .../test-apps/servlet-policy-enforcer/pom.xml | 2 +- .../test-apps/servlets-jakarta/pom.xml | 2 +- .../test-apps/servlets/pom.xml | 2 +- .../test-apps/spring-boot-adapter-app/pom.xml | 2 +- .../test-apps/test-apps-dist/pom.xml | 2 +- testsuite/integration-arquillian/tests/base/pom.xml | 2 +- .../tests/other/adapters/jboss/pom.xml | 2 +- .../tests/other/adapters/karaf/fuse61/pom.xml | 2 +- .../tests/other/adapters/karaf/fuse62/pom.xml | 2 +- .../tests/other/adapters/karaf/karaf3/pom.xml | 2 +- .../tests/other/adapters/karaf/pom.xml | 2 +- .../tests/other/adapters/pom.xml | 2 +- .../tests/other/adapters/was/pom.xml | 2 +- .../tests/other/adapters/was/was8/pom.xml | 2 +- .../tests/other/adapters/wls/pom.xml | 2 +- .../tests/other/adapters/wls/wls12/pom.xml | 2 +- .../tests/other/base-ui/pom.xml | 2 +- .../tests/other/jpa-performance/pom.xml | 2 +- .../tests/other/mod_auth_mellon/pom.xml | 2 +- testsuite/integration-arquillian/tests/other/pom.xml | 2 +- .../tests/other/springboot-tests/pom.xml | 2 +- .../integration-arquillian/tests/other/sssd/pom.xml | 2 +- .../tests/other/webauthn/pom.xml | 2 +- testsuite/integration-arquillian/tests/pom.xml | 2 +- testsuite/integration-arquillian/util/pom.xml | 2 +- testsuite/model/pom.xml | 2 +- testsuite/pom.xml | 2 +- testsuite/utils/pom.xml | 2 +- themes/pom.xml | 2 +- util/embedded-ldap/pom.xml | 2 +- util/pom.xml | 2 +- 254 files changed, 267 insertions(+), 267 deletions(-) diff --git a/.github/workflows/js-ci.yml b/.github/workflows/js-ci.yml index 5d828cbbd5e8..b6294f1860d8 100644 --- a/.github/workflows/js-ci.yml +++ b/.github/workflows/js-ci.yml @@ -54,13 +54,13 @@ jobs: - name: Build Keycloak run: | ./mvnw clean install --errors -DskipTests -DskipTestsuite -DskipExamples -Pdistribution - mv ./quarkus/dist/target/keycloak-24.0.5-PS-1.tar.gz ./keycloak-24.0.5-PS-1.tar.gz + mv ./quarkus/dist/target/keycloak-24.0.5-PS-2.tar.gz ./keycloak-24.0.5-PS-2.tar.gz - name: Upload Keycloak dist uses: actions/upload-artifact@v3 with: name: keycloak - path: keycloak-24.0.5-PS-1.tar.gz + path: keycloak-24.0.5-PS-2.tar.gz admin-client: name: Admin Client @@ -214,8 +214,8 @@ jobs: - name: Start Keycloak server run: | - tar xfvz keycloak-24.0.5-PS-1.tar.gz - keycloak-24.0.5-PS-1/bin/kc.sh start-dev --features=transient-users &> ~/server.log & + tar xfvz keycloak-24.0.5-PS-2.tar.gz + keycloak-24.0.5-PS-2/bin/kc.sh start-dev --features=transient-users &> ~/server.log & env: KEYCLOAK_ADMIN: admin KEYCLOAK_ADMIN_PASSWORD: admin @@ -297,8 +297,8 @@ jobs: - name: Start Keycloak server run: | - tar xfvz keycloak-24.0.5-PS-1.tar.gz - keycloak-24.0.5-PS-1/bin/kc.sh start-dev --features=admin-fine-grained-authz,transient-users &> ~/server.log & + tar xfvz keycloak-24.0.5-PS-2.tar.gz + keycloak-24.0.5-PS-2/bin/kc.sh start-dev --features=admin-fine-grained-authz,transient-users &> ~/server.log & env: KEYCLOAK_ADMIN: admin KEYCLOAK_ADMIN_PASSWORD: admin diff --git a/adapters/oidc/adapter-core/pom.xml b/adapters/oidc/adapter-core/pom.xml index 7a9cc3caca41..67419ec02959 100755 --- a/adapters/oidc/adapter-core/pom.xml +++ b/adapters/oidc/adapter-core/pom.xml @@ -21,7 +21,7 @@ keycloak-parent org.keycloak - 24.0.5-PS-1 + 24.0.5-PS-2 ../../../pom.xml 4.0.0 diff --git a/adapters/oidc/installed/pom.xml b/adapters/oidc/installed/pom.xml index 85a08037159c..87c15d322f30 100755 --- a/adapters/oidc/installed/pom.xml +++ b/adapters/oidc/installed/pom.xml @@ -21,7 +21,7 @@ keycloak-parent org.keycloak - 24.0.5-PS-1 + 24.0.5-PS-2 ../../../pom.xml 4.0.0 diff --git a/adapters/oidc/jakarta-servlet-filter/pom.xml b/adapters/oidc/jakarta-servlet-filter/pom.xml index 07d76d27f169..324cdcc02237 100755 --- a/adapters/oidc/jakarta-servlet-filter/pom.xml +++ b/adapters/oidc/jakarta-servlet-filter/pom.xml @@ -21,7 +21,7 @@ keycloak-parent org.keycloak - 24.0.5-PS-1 + 24.0.5-PS-2 ../../../pom.xml 4.0.0 diff --git a/adapters/oidc/jaxrs-oauth-client/pom.xml b/adapters/oidc/jaxrs-oauth-client/pom.xml index 9c856618c90e..e728e5c6649b 100755 --- a/adapters/oidc/jaxrs-oauth-client/pom.xml +++ b/adapters/oidc/jaxrs-oauth-client/pom.xml @@ -21,7 +21,7 @@ keycloak-parent org.keycloak - 24.0.5-PS-1 + 24.0.5-PS-2 ../../../pom.xml 4.0.0 diff --git a/adapters/oidc/jetty/jetty-core/pom.xml b/adapters/oidc/jetty/jetty-core/pom.xml index c0d6acf769bd..fbcb879e1465 100755 --- a/adapters/oidc/jetty/jetty-core/pom.xml +++ b/adapters/oidc/jetty/jetty-core/pom.xml @@ -21,7 +21,7 @@ keycloak-parent org.keycloak - 24.0.5-PS-1 + 24.0.5-PS-2 ../../../../pom.xml 4.0.0 diff --git a/adapters/oidc/jetty/jetty9.4/pom.xml b/adapters/oidc/jetty/jetty9.4/pom.xml index 9c02d709e8a1..dfc9fc673a71 100644 --- a/adapters/oidc/jetty/jetty9.4/pom.xml +++ b/adapters/oidc/jetty/jetty9.4/pom.xml @@ -21,7 +21,7 @@ keycloak-parent org.keycloak - 24.0.5-PS-1 + 24.0.5-PS-2 ../../../../pom.xml 4.0.0 diff --git a/adapters/oidc/jetty/pom.xml b/adapters/oidc/jetty/pom.xml index cba7925012a5..f9e9a7de8223 100755 --- a/adapters/oidc/jetty/pom.xml +++ b/adapters/oidc/jetty/pom.xml @@ -20,7 +20,7 @@ keycloak-parent org.keycloak - 24.0.5-PS-1 + 24.0.5-PS-2 ../../../pom.xml Keycloak Jetty Integration diff --git a/adapters/oidc/js/pom.xml b/adapters/oidc/js/pom.xml index a2b84676d729..ef4b3172fcf3 100644 --- a/adapters/oidc/js/pom.xml +++ b/adapters/oidc/js/pom.xml @@ -5,7 +5,7 @@ keycloak-parent org.keycloak - 24.0.5-PS-1 + 24.0.5-PS-2 ../../../pom.xml diff --git a/adapters/oidc/pom.xml b/adapters/oidc/pom.xml index 44481bade880..af3d337e2b06 100755 --- a/adapters/oidc/pom.xml +++ b/adapters/oidc/pom.xml @@ -20,7 +20,7 @@ keycloak-parent org.keycloak - 24.0.5-PS-1 + 24.0.5-PS-2 ../../pom.xml Keycloak OIDC Client Adapter Modules diff --git a/adapters/oidc/servlet-filter/pom.xml b/adapters/oidc/servlet-filter/pom.xml index 82a8ae29d524..724c531c1d5f 100755 --- a/adapters/oidc/servlet-filter/pom.xml +++ b/adapters/oidc/servlet-filter/pom.xml @@ -21,7 +21,7 @@ keycloak-parent org.keycloak - 24.0.5-PS-1 + 24.0.5-PS-2 ../../../pom.xml 4.0.0 diff --git a/adapters/oidc/spring-boot-adapter-core/pom.xml b/adapters/oidc/spring-boot-adapter-core/pom.xml index 8c7d7c15e978..2814849b5911 100755 --- a/adapters/oidc/spring-boot-adapter-core/pom.xml +++ b/adapters/oidc/spring-boot-adapter-core/pom.xml @@ -21,7 +21,7 @@ keycloak-parent org.keycloak - 24.0.5-PS-1 + 24.0.5-PS-2 ../../../pom.xml 4.0.0 diff --git a/adapters/oidc/spring-boot-container-bundle/pom.xml b/adapters/oidc/spring-boot-container-bundle/pom.xml index 8e8ed307fecc..f653d5cb7eff 100644 --- a/adapters/oidc/spring-boot-container-bundle/pom.xml +++ b/adapters/oidc/spring-boot-container-bundle/pom.xml @@ -4,7 +4,7 @@ keycloak-parent org.keycloak - 24.0.5-PS-1 + 24.0.5-PS-2 ../../../pom.xml spring-boot-container-bundle diff --git a/adapters/oidc/spring-boot2/pom.xml b/adapters/oidc/spring-boot2/pom.xml index d4f80533218c..882646dff81a 100755 --- a/adapters/oidc/spring-boot2/pom.xml +++ b/adapters/oidc/spring-boot2/pom.xml @@ -21,7 +21,7 @@ keycloak-parent org.keycloak - 24.0.5-PS-1 + 24.0.5-PS-2 ../../../pom.xml 4.0.0 diff --git a/adapters/oidc/spring-security/pom.xml b/adapters/oidc/spring-security/pom.xml index 37565ef1b7af..829b9c07163e 100644 --- a/adapters/oidc/spring-security/pom.xml +++ b/adapters/oidc/spring-security/pom.xml @@ -21,7 +21,7 @@ keycloak-parent org.keycloak - 24.0.5-PS-1 + 24.0.5-PS-2 ../../../pom.xml 4.0.0 diff --git a/adapters/oidc/tomcat/pom.xml b/adapters/oidc/tomcat/pom.xml index 174f5c6b7951..36ee1fc83c5c 100755 --- a/adapters/oidc/tomcat/pom.xml +++ b/adapters/oidc/tomcat/pom.xml @@ -20,7 +20,7 @@ keycloak-parent org.keycloak - 24.0.5-PS-1 + 24.0.5-PS-2 ../../../pom.xml Keycloak Tomcat Integration diff --git a/adapters/oidc/tomcat/tomcat-core/pom.xml b/adapters/oidc/tomcat/tomcat-core/pom.xml index d0db3e19d801..942924c80866 100755 --- a/adapters/oidc/tomcat/tomcat-core/pom.xml +++ b/adapters/oidc/tomcat/tomcat-core/pom.xml @@ -21,7 +21,7 @@ keycloak-tomcat-integration-pom org.keycloak - 24.0.5-PS-1 + 24.0.5-PS-2 ../pom.xml 4.0.0 diff --git a/adapters/oidc/tomcat/tomcat/pom.xml b/adapters/oidc/tomcat/tomcat/pom.xml index 464c086fb05b..f50fbd827216 100755 --- a/adapters/oidc/tomcat/tomcat/pom.xml +++ b/adapters/oidc/tomcat/tomcat/pom.xml @@ -21,7 +21,7 @@ keycloak-tomcat-integration-pom org.keycloak - 24.0.5-PS-1 + 24.0.5-PS-2 ../pom.xml 4.0.0 diff --git a/adapters/oidc/undertow/pom.xml b/adapters/oidc/undertow/pom.xml index bd8830f43d25..e0eb4bb30d0e 100755 --- a/adapters/oidc/undertow/pom.xml +++ b/adapters/oidc/undertow/pom.xml @@ -21,7 +21,7 @@ keycloak-parent org.keycloak - 24.0.5-PS-1 + 24.0.5-PS-2 ../../../pom.xml 4.0.0 diff --git a/adapters/oidc/wildfly-elytron/pom.xml b/adapters/oidc/wildfly-elytron/pom.xml index 9445991abf64..ebd68036e655 100755 --- a/adapters/oidc/wildfly-elytron/pom.xml +++ b/adapters/oidc/wildfly-elytron/pom.xml @@ -22,7 +22,7 @@ keycloak-parent org.keycloak - 24.0.5-PS-1 + 24.0.5-PS-2 ../../../pom.xml 4.0.0 diff --git a/adapters/oidc/wildfly/pom.xml b/adapters/oidc/wildfly/pom.xml index 148bc50332ad..26f479b309ea 100755 --- a/adapters/oidc/wildfly/pom.xml +++ b/adapters/oidc/wildfly/pom.xml @@ -20,7 +20,7 @@ keycloak-parent org.keycloak - 24.0.5-PS-1 + 24.0.5-PS-2 ../../../pom.xml Keycloak WildFly Integration diff --git a/adapters/oidc/wildfly/wildfly-subsystem/pom.xml b/adapters/oidc/wildfly/wildfly-subsystem/pom.xml index d90aef502e70..ee3373482c04 100755 --- a/adapters/oidc/wildfly/wildfly-subsystem/pom.xml +++ b/adapters/oidc/wildfly/wildfly-subsystem/pom.xml @@ -21,7 +21,7 @@ org.keycloak keycloak-parent - 24.0.5-PS-1 + 24.0.5-PS-2 ../../../../pom.xml diff --git a/adapters/pom.xml b/adapters/pom.xml index fda5c6972c2e..73b3af85f436 100755 --- a/adapters/pom.xml +++ b/adapters/pom.xml @@ -20,7 +20,7 @@ keycloak-parent org.keycloak - 24.0.5-PS-1 + 24.0.5-PS-2 ../pom.xml Keycloak Adapters diff --git a/adapters/saml/core-jakarta/pom.xml b/adapters/saml/core-jakarta/pom.xml index d35167a1526a..e3bea09820db 100644 --- a/adapters/saml/core-jakarta/pom.xml +++ b/adapters/saml/core-jakarta/pom.xml @@ -6,7 +6,7 @@ keycloak-parent org.keycloak - 24.0.5-PS-1 + 24.0.5-PS-2 ../../../pom.xml diff --git a/adapters/saml/core-public/pom.xml b/adapters/saml/core-public/pom.xml index fdc1fbb494fb..026a66757f63 100755 --- a/adapters/saml/core-public/pom.xml +++ b/adapters/saml/core-public/pom.xml @@ -21,7 +21,7 @@ keycloak-parent org.keycloak - 24.0.5-PS-1 + 24.0.5-PS-2 ../../../pom.xml 4.0.0 diff --git a/adapters/saml/core/pom.xml b/adapters/saml/core/pom.xml index f48c905a97b2..b38bae3363af 100755 --- a/adapters/saml/core/pom.xml +++ b/adapters/saml/core/pom.xml @@ -21,7 +21,7 @@ keycloak-parent org.keycloak - 24.0.5-PS-1 + 24.0.5-PS-2 ../../../pom.xml 4.0.0 diff --git a/adapters/saml/jakarta-servlet-filter/pom.xml b/adapters/saml/jakarta-servlet-filter/pom.xml index f4f06301064f..67c2aed81c75 100755 --- a/adapters/saml/jakarta-servlet-filter/pom.xml +++ b/adapters/saml/jakarta-servlet-filter/pom.xml @@ -21,7 +21,7 @@ keycloak-parent org.keycloak - 24.0.5-PS-1 + 24.0.5-PS-2 ../../../pom.xml 4.0.0 diff --git a/adapters/saml/jetty/jetty-core/pom.xml b/adapters/saml/jetty/jetty-core/pom.xml index 719edd0d3468..0e4702fc2907 100755 --- a/adapters/saml/jetty/jetty-core/pom.xml +++ b/adapters/saml/jetty/jetty-core/pom.xml @@ -21,7 +21,7 @@ keycloak-parent org.keycloak - 24.0.5-PS-1 + 24.0.5-PS-2 ../../../../pom.xml 4.0.0 diff --git a/adapters/saml/jetty/jetty9.4/pom.xml b/adapters/saml/jetty/jetty9.4/pom.xml index 9e44862955f7..faec4e68cd1e 100644 --- a/adapters/saml/jetty/jetty9.4/pom.xml +++ b/adapters/saml/jetty/jetty9.4/pom.xml @@ -21,7 +21,7 @@ keycloak-parent org.keycloak - 24.0.5-PS-1 + 24.0.5-PS-2 ../../../../pom.xml 4.0.0 diff --git a/adapters/saml/jetty/pom.xml b/adapters/saml/jetty/pom.xml index 7c3b937d657d..ab37cebfdda5 100755 --- a/adapters/saml/jetty/pom.xml +++ b/adapters/saml/jetty/pom.xml @@ -20,7 +20,7 @@ keycloak-parent org.keycloak - 24.0.5-PS-1 + 24.0.5-PS-2 ../../../pom.xml Keycloak SAML Jetty Integration diff --git a/adapters/saml/pom.xml b/adapters/saml/pom.xml index 8f4fe7ec9f3e..851bba52ed9d 100755 --- a/adapters/saml/pom.xml +++ b/adapters/saml/pom.xml @@ -20,7 +20,7 @@ keycloak-parent org.keycloak - 24.0.5-PS-1 + 24.0.5-PS-2 ../../pom.xml Keycloak SAML Client Adapter Modules diff --git a/adapters/saml/servlet-filter/pom.xml b/adapters/saml/servlet-filter/pom.xml index c1a45f1198b5..0a595dd8b8a1 100755 --- a/adapters/saml/servlet-filter/pom.xml +++ b/adapters/saml/servlet-filter/pom.xml @@ -21,7 +21,7 @@ keycloak-parent org.keycloak - 24.0.5-PS-1 + 24.0.5-PS-2 ../../../pom.xml 4.0.0 diff --git a/adapters/saml/tomcat/pom.xml b/adapters/saml/tomcat/pom.xml index 4281d19d6d15..e4402d9ae76d 100755 --- a/adapters/saml/tomcat/pom.xml +++ b/adapters/saml/tomcat/pom.xml @@ -20,7 +20,7 @@ keycloak-parent org.keycloak - 24.0.5-PS-1 + 24.0.5-PS-2 ../../../pom.xml Keycloak SAML Tomcat Integration diff --git a/adapters/saml/tomcat/tomcat-core/pom.xml b/adapters/saml/tomcat/tomcat-core/pom.xml index 3040f31e7545..f80827b4a6d5 100755 --- a/adapters/saml/tomcat/tomcat-core/pom.xml +++ b/adapters/saml/tomcat/tomcat-core/pom.xml @@ -21,7 +21,7 @@ keycloak-saml-tomcat-integration-pom org.keycloak - 24.0.5-PS-1 + 24.0.5-PS-2 ../pom.xml 4.0.0 diff --git a/adapters/saml/tomcat/tomcat/pom.xml b/adapters/saml/tomcat/tomcat/pom.xml index 8b55eafecc19..d164e40cece4 100755 --- a/adapters/saml/tomcat/tomcat/pom.xml +++ b/adapters/saml/tomcat/tomcat/pom.xml @@ -21,7 +21,7 @@ keycloak-saml-tomcat-integration-pom org.keycloak - 24.0.5-PS-1 + 24.0.5-PS-2 ../pom.xml 4.0.0 diff --git a/adapters/saml/undertow/pom.xml b/adapters/saml/undertow/pom.xml index b736c9b4edf2..31679ed27d06 100755 --- a/adapters/saml/undertow/pom.xml +++ b/adapters/saml/undertow/pom.xml @@ -21,7 +21,7 @@ keycloak-parent org.keycloak - 24.0.5-PS-1 + 24.0.5-PS-2 ../../../pom.xml 4.0.0 diff --git a/adapters/saml/wildfly-elytron-jakarta/pom.xml b/adapters/saml/wildfly-elytron-jakarta/pom.xml index 0ee6372b631b..e5814f62e8c7 100755 --- a/adapters/saml/wildfly-elytron-jakarta/pom.xml +++ b/adapters/saml/wildfly-elytron-jakarta/pom.xml @@ -21,7 +21,7 @@ keycloak-parent org.keycloak - 24.0.5-PS-1 + 24.0.5-PS-2 ../../../pom.xml 4.0.0 diff --git a/adapters/saml/wildfly-elytron/pom.xml b/adapters/saml/wildfly-elytron/pom.xml index 9f0ef66a74c0..5877c99bfb9f 100755 --- a/adapters/saml/wildfly-elytron/pom.xml +++ b/adapters/saml/wildfly-elytron/pom.xml @@ -21,7 +21,7 @@ keycloak-parent org.keycloak - 24.0.5-PS-1 + 24.0.5-PS-2 ../../../pom.xml 4.0.0 diff --git a/adapters/saml/wildfly/pom.xml b/adapters/saml/wildfly/pom.xml index a03f66306a27..1987d300a7bd 100755 --- a/adapters/saml/wildfly/pom.xml +++ b/adapters/saml/wildfly/pom.xml @@ -20,7 +20,7 @@ keycloak-parent org.keycloak - 24.0.5-PS-1 + 24.0.5-PS-2 ../../../pom.xml Keycloak SAML Wildfly Integration diff --git a/adapters/saml/wildfly/wildfly-jakarta-subsystem/pom.xml b/adapters/saml/wildfly/wildfly-jakarta-subsystem/pom.xml index 088097749888..15ed3ccb70d2 100755 --- a/adapters/saml/wildfly/wildfly-jakarta-subsystem/pom.xml +++ b/adapters/saml/wildfly/wildfly-jakarta-subsystem/pom.xml @@ -21,7 +21,7 @@ org.keycloak keycloak-parent - 24.0.5-PS-1 + 24.0.5-PS-2 ../../../../pom.xml diff --git a/adapters/saml/wildfly/wildfly-subsystem/pom.xml b/adapters/saml/wildfly/wildfly-subsystem/pom.xml index 45dd888d5464..f69fce4f6b5d 100755 --- a/adapters/saml/wildfly/wildfly-subsystem/pom.xml +++ b/adapters/saml/wildfly/wildfly-subsystem/pom.xml @@ -21,7 +21,7 @@ org.keycloak keycloak-parent - 24.0.5-PS-1 + 24.0.5-PS-2 ../../../../pom.xml diff --git a/adapters/spi/adapter-spi/pom.xml b/adapters/spi/adapter-spi/pom.xml index 8ee72d3542e3..29a57e26fefd 100755 --- a/adapters/spi/adapter-spi/pom.xml +++ b/adapters/spi/adapter-spi/pom.xml @@ -21,7 +21,7 @@ keycloak-parent org.keycloak - 24.0.5-PS-1 + 24.0.5-PS-2 ../../../pom.xml 4.0.0 diff --git a/adapters/spi/jakarta-servlet-adapter-spi/pom.xml b/adapters/spi/jakarta-servlet-adapter-spi/pom.xml index c9ef28722e3b..4df81636acf3 100755 --- a/adapters/spi/jakarta-servlet-adapter-spi/pom.xml +++ b/adapters/spi/jakarta-servlet-adapter-spi/pom.xml @@ -21,7 +21,7 @@ keycloak-parent org.keycloak - 24.0.5-PS-1 + 24.0.5-PS-2 ../../../pom.xml 4.0.0 diff --git a/adapters/spi/jboss-adapter-core/pom.xml b/adapters/spi/jboss-adapter-core/pom.xml index 98b9af9826f1..f3143b6cb633 100755 --- a/adapters/spi/jboss-adapter-core/pom.xml +++ b/adapters/spi/jboss-adapter-core/pom.xml @@ -21,7 +21,7 @@ keycloak-parent org.keycloak - 24.0.5-PS-1 + 24.0.5-PS-2 ../../../pom.xml 4.0.0 diff --git a/adapters/spi/jetty-adapter-spi/pom.xml b/adapters/spi/jetty-adapter-spi/pom.xml index e220e24b3567..e4297b8d53c6 100755 --- a/adapters/spi/jetty-adapter-spi/pom.xml +++ b/adapters/spi/jetty-adapter-spi/pom.xml @@ -21,7 +21,7 @@ keycloak-parent org.keycloak - 24.0.5-PS-1 + 24.0.5-PS-2 ../../../pom.xml 4.0.0 diff --git a/adapters/spi/pom.xml b/adapters/spi/pom.xml index a43bac320e72..8fcbe9e7eef9 100755 --- a/adapters/spi/pom.xml +++ b/adapters/spi/pom.xml @@ -20,7 +20,7 @@ keycloak-parent org.keycloak - 24.0.5-PS-1 + 24.0.5-PS-2 ../../pom.xml Keycloak Client Adapter SPI Modules diff --git a/adapters/spi/servlet-adapter-spi/pom.xml b/adapters/spi/servlet-adapter-spi/pom.xml index 5655d1316dae..145d666ab78c 100755 --- a/adapters/spi/servlet-adapter-spi/pom.xml +++ b/adapters/spi/servlet-adapter-spi/pom.xml @@ -21,7 +21,7 @@ keycloak-parent org.keycloak - 24.0.5-PS-1 + 24.0.5-PS-2 ../../../pom.xml 4.0.0 diff --git a/adapters/spi/tomcat-adapter-spi/pom.xml b/adapters/spi/tomcat-adapter-spi/pom.xml index ceebacd40686..eec4f8cadbc6 100755 --- a/adapters/spi/tomcat-adapter-spi/pom.xml +++ b/adapters/spi/tomcat-adapter-spi/pom.xml @@ -21,7 +21,7 @@ keycloak-parent org.keycloak - 24.0.5-PS-1 + 24.0.5-PS-2 ../../../pom.xml 4.0.0 diff --git a/adapters/spi/undertow-adapter-spi/pom.xml b/adapters/spi/undertow-adapter-spi/pom.xml index 17ac7a51f23f..30bdb2542233 100755 --- a/adapters/spi/undertow-adapter-spi/pom.xml +++ b/adapters/spi/undertow-adapter-spi/pom.xml @@ -21,7 +21,7 @@ keycloak-parent org.keycloak - 24.0.5-PS-1 + 24.0.5-PS-2 ../../../pom.xml 4.0.0 diff --git a/authz/client/pom.xml b/authz/client/pom.xml index 0b8dc45415b6..af27b451765d 100644 --- a/authz/client/pom.xml +++ b/authz/client/pom.xml @@ -7,7 +7,7 @@ org.keycloak keycloak-authz-parent - 24.0.5-PS-1 + 24.0.5-PS-2 ../pom.xml diff --git a/authz/policy-enforcer/pom.xml b/authz/policy-enforcer/pom.xml index 3f90f40ee955..ebb8944ed276 100755 --- a/authz/policy-enforcer/pom.xml +++ b/authz/policy-enforcer/pom.xml @@ -21,7 +21,7 @@ org.keycloak keycloak-authz-parent - 24.0.5-PS-1 + 24.0.5-PS-2 ../pom.xml 4.0.0 diff --git a/authz/policy/common/pom.xml b/authz/policy/common/pom.xml index eb16b8fe8f1c..e590e7dfb89a 100644 --- a/authz/policy/common/pom.xml +++ b/authz/policy/common/pom.xml @@ -25,7 +25,7 @@ org.keycloak keycloak-authz-provider-parent - 24.0.5-PS-1 + 24.0.5-PS-2 ../pom.xml diff --git a/authz/policy/pom.xml b/authz/policy/pom.xml index 9848a2d25578..d060e984c42d 100644 --- a/authz/policy/pom.xml +++ b/authz/policy/pom.xml @@ -7,7 +7,7 @@ org.keycloak keycloak-authz-parent - 24.0.5-PS-1 + 24.0.5-PS-2 ../pom.xml diff --git a/authz/pom.xml b/authz/pom.xml index 0fe1b84d7e0d..710dfa951375 100644 --- a/authz/pom.xml +++ b/authz/pom.xml @@ -7,7 +7,7 @@ org.keycloak keycloak-parent - 24.0.5-PS-1 + 24.0.5-PS-2 ../pom.xml diff --git a/boms/adapter/pom.xml b/boms/adapter/pom.xml index 2248976cfb36..475bd72e80e5 100644 --- a/boms/adapter/pom.xml +++ b/boms/adapter/pom.xml @@ -22,7 +22,7 @@ org.keycloak.bom keycloak-bom-parent - 24.0.5-PS-1 + 24.0.5-PS-2 org.keycloak.bom diff --git a/boms/misc/pom.xml b/boms/misc/pom.xml index c92767a37c97..b64e141731c7 100644 --- a/boms/misc/pom.xml +++ b/boms/misc/pom.xml @@ -22,7 +22,7 @@ org.keycloak.bom keycloak-bom-parent - 24.0.5-PS-1 + 24.0.5-PS-2 org.keycloak.bom diff --git a/boms/pom.xml b/boms/pom.xml index c097ce9bf34a..5d962cb073f1 100644 --- a/boms/pom.xml +++ b/boms/pom.xml @@ -27,7 +27,7 @@ org.keycloak.bom keycloak-bom-parent - 24.0.5-PS-1 + 24.0.5-PS-2 pom diff --git a/boms/spi/pom.xml b/boms/spi/pom.xml index a7834f7cfdb1..6cd6dfa4f446 100644 --- a/boms/spi/pom.xml +++ b/boms/spi/pom.xml @@ -23,7 +23,7 @@ org.keycloak.bom keycloak-bom-parent - 24.0.5-PS-1 + 24.0.5-PS-2 org.keycloak.bom diff --git a/common/pom.xml b/common/pom.xml index 7133ed24b2d1..c2942e44d23d 100755 --- a/common/pom.xml +++ b/common/pom.xml @@ -21,7 +21,7 @@ keycloak-parent org.keycloak - 24.0.5-PS-1 + 24.0.5-PS-2 ../pom.xml 4.0.0 diff --git a/core/pom.xml b/core/pom.xml index ccb19a1ee26f..94fdd884e1c5 100755 --- a/core/pom.xml +++ b/core/pom.xml @@ -21,7 +21,7 @@ keycloak-parent org.keycloak - 24.0.5-PS-1 + 24.0.5-PS-2 ../pom.xml 4.0.0 diff --git a/crypto/default/pom.xml b/crypto/default/pom.xml index 6968bc319cca..7a7b0dba2923 100644 --- a/crypto/default/pom.xml +++ b/crypto/default/pom.xml @@ -21,7 +21,7 @@ keycloak-crypto-parent org.keycloak - 24.0.5-PS-1 + 24.0.5-PS-2 ../pom.xml 4.0.0 diff --git a/crypto/elytron/pom.xml b/crypto/elytron/pom.xml index ed17f80b5478..613b55832cf8 100644 --- a/crypto/elytron/pom.xml +++ b/crypto/elytron/pom.xml @@ -21,7 +21,7 @@ keycloak-crypto-parent org.keycloak - 24.0.5-PS-1 + 24.0.5-PS-2 ../pom.xml 4.0.0 diff --git a/crypto/fips1402/pom.xml b/crypto/fips1402/pom.xml index 0a7201922f27..2b58299384b1 100644 --- a/crypto/fips1402/pom.xml +++ b/crypto/fips1402/pom.xml @@ -21,7 +21,7 @@ keycloak-crypto-parent org.keycloak - 24.0.5-PS-1 + 24.0.5-PS-2 ../pom.xml 4.0.0 diff --git a/crypto/pom.xml b/crypto/pom.xml index a9b305c970eb..a7a160c1ecdd 100644 --- a/crypto/pom.xml +++ b/crypto/pom.xml @@ -20,7 +20,7 @@ keycloak-parent org.keycloak - 24.0.5-PS-1 + 24.0.5-PS-2 ../pom.xml Keycloak Crypto Parent diff --git a/dependencies/pom.xml b/dependencies/pom.xml index 46890379d70d..cacafe14aa99 100755 --- a/dependencies/pom.xml +++ b/dependencies/pom.xml @@ -21,7 +21,7 @@ keycloak-parent org.keycloak - 24.0.5-PS-1 + 24.0.5-PS-2 4.0.0 diff --git a/dependencies/server-all/pom.xml b/dependencies/server-all/pom.xml index a24901cd1a66..8be89560b57d 100755 --- a/dependencies/server-all/pom.xml +++ b/dependencies/server-all/pom.xml @@ -21,7 +21,7 @@ keycloak-dependencies-parent org.keycloak - 24.0.5-PS-1 + 24.0.5-PS-2 4.0.0 diff --git a/dependencies/server-min/pom.xml b/dependencies/server-min/pom.xml index 179cd15b7c97..bef65f64a2a5 100755 --- a/dependencies/server-min/pom.xml +++ b/dependencies/server-min/pom.xml @@ -21,7 +21,7 @@ keycloak-dependencies-parent org.keycloak - 24.0.5-PS-1 + 24.0.5-PS-2 4.0.0 diff --git a/distribution/adapters/pom.xml b/distribution/adapters/pom.xml index 0478417bc882..37eed0063c5d 100755 --- a/distribution/adapters/pom.xml +++ b/distribution/adapters/pom.xml @@ -20,7 +20,7 @@ keycloak-distribution-parent org.keycloak - 24.0.5-PS-1 + 24.0.5-PS-2 Adapters Distribution Parent diff --git a/distribution/adapters/tomcat-adapter-zip/pom.xml b/distribution/adapters/tomcat-adapter-zip/pom.xml index 6540bdf9f726..2e0cef0f1a7b 100755 --- a/distribution/adapters/tomcat-adapter-zip/pom.xml +++ b/distribution/adapters/tomcat-adapter-zip/pom.xml @@ -21,7 +21,7 @@ keycloak-parent org.keycloak - 24.0.5-PS-1 + 24.0.5-PS-2 ../../../pom.xml diff --git a/distribution/adapters/wildfly-adapter/pom.xml b/distribution/adapters/wildfly-adapter/pom.xml index 8fd3b64309c1..257f3a4dc7f6 100644 --- a/distribution/adapters/wildfly-adapter/pom.xml +++ b/distribution/adapters/wildfly-adapter/pom.xml @@ -21,7 +21,7 @@ keycloak-adapters-distribution-parent org.keycloak - 24.0.5-PS-1 + 24.0.5-PS-2 diff --git a/distribution/api-docs-dist/pom.xml b/distribution/api-docs-dist/pom.xml index 99f4410f7bf6..dabeb8e808b4 100755 --- a/distribution/api-docs-dist/pom.xml +++ b/distribution/api-docs-dist/pom.xml @@ -21,7 +21,7 @@ keycloak-distribution-parent org.keycloak - 24.0.5-PS-1 + 24.0.5-PS-2 keycloak-api-docs-dist diff --git a/distribution/downloads/pom.xml b/distribution/downloads/pom.xml index 42412225fcfa..ca76405cc1ea 100755 --- a/distribution/downloads/pom.xml +++ b/distribution/downloads/pom.xml @@ -21,7 +21,7 @@ keycloak-distribution-parent org.keycloak - 24.0.5-PS-1 + 24.0.5-PS-2 keycloak-dist-downloads diff --git a/distribution/feature-packs/adapter-feature-pack/pom.xml b/distribution/feature-packs/adapter-feature-pack/pom.xml index 4def3d4e70a3..cd09fde7c0c1 100755 --- a/distribution/feature-packs/adapter-feature-pack/pom.xml +++ b/distribution/feature-packs/adapter-feature-pack/pom.xml @@ -19,7 +19,7 @@ org.keycloak feature-packs-parent - 24.0.5-PS-1 + 24.0.5-PS-2 diff --git a/distribution/feature-packs/pom.xml b/distribution/feature-packs/pom.xml index 88c5187eac8e..36d3faee9820 100644 --- a/distribution/feature-packs/pom.xml +++ b/distribution/feature-packs/pom.xml @@ -20,7 +20,7 @@ keycloak-distribution-parent org.keycloak - 24.0.5-PS-1 + 24.0.5-PS-2 Feature Pack Builds diff --git a/distribution/galleon-feature-packs/pom.xml b/distribution/galleon-feature-packs/pom.xml index e3508ee107c7..c40019ab338f 100644 --- a/distribution/galleon-feature-packs/pom.xml +++ b/distribution/galleon-feature-packs/pom.xml @@ -20,7 +20,7 @@ keycloak-distribution-parent org.keycloak - 24.0.5-PS-1 + 24.0.5-PS-2 Galleon Feature Pack Builds diff --git a/distribution/galleon-feature-packs/saml-adapter-galleon-pack-layer-metadata-tests/pom.xml b/distribution/galleon-feature-packs/saml-adapter-galleon-pack-layer-metadata-tests/pom.xml index d699231c52d5..1295160e5da4 100644 --- a/distribution/galleon-feature-packs/saml-adapter-galleon-pack-layer-metadata-tests/pom.xml +++ b/distribution/galleon-feature-packs/saml-adapter-galleon-pack-layer-metadata-tests/pom.xml @@ -19,7 +19,7 @@ org.keycloak galleon-feature-packs-parent - 24.0.5-PS-1 + 24.0.5-PS-2 4.0.0 diff --git a/distribution/galleon-feature-packs/saml-adapter-galleon-pack/pom.xml b/distribution/galleon-feature-packs/saml-adapter-galleon-pack/pom.xml index b40985603a54..f15b538894ce 100644 --- a/distribution/galleon-feature-packs/saml-adapter-galleon-pack/pom.xml +++ b/distribution/galleon-feature-packs/saml-adapter-galleon-pack/pom.xml @@ -19,7 +19,7 @@ org.keycloak galleon-feature-packs-parent - 24.0.5-PS-1 + 24.0.5-PS-2 4.0.0 diff --git a/distribution/licenses-common/pom.xml b/distribution/licenses-common/pom.xml index 07ae3854a707..c5bb46f950e7 100644 --- a/distribution/licenses-common/pom.xml +++ b/distribution/licenses-common/pom.xml @@ -20,7 +20,7 @@ keycloak-distribution-parent org.keycloak - 24.0.5-PS-1 + 24.0.5-PS-2 keycloak-distribution-licenses-common diff --git a/distribution/maven-plugins/licenses-processor/pom.xml b/distribution/maven-plugins/licenses-processor/pom.xml index 74bbb4014cb1..1b96541bc449 100644 --- a/distribution/maven-plugins/licenses-processor/pom.xml +++ b/distribution/maven-plugins/licenses-processor/pom.xml @@ -20,7 +20,7 @@ keycloak-distribution-maven-plugins-parent org.keycloak - 24.0.5-PS-1 + 24.0.5-PS-2 keycloak-distribution-licenses-maven-plugin diff --git a/distribution/maven-plugins/pom.xml b/distribution/maven-plugins/pom.xml index 28b8b9faf3cd..9efefd1f6a75 100644 --- a/distribution/maven-plugins/pom.xml +++ b/distribution/maven-plugins/pom.xml @@ -20,7 +20,7 @@ keycloak-distribution-parent org.keycloak - 24.0.5-PS-1 + 24.0.5-PS-2 keycloak-distribution-maven-plugins-parent diff --git a/distribution/pom.xml b/distribution/pom.xml index 7110cea423f3..94b9d3321a9d 100755 --- a/distribution/pom.xml +++ b/distribution/pom.xml @@ -20,7 +20,7 @@ keycloak-parent org.keycloak - 24.0.5-PS-1 + 24.0.5-PS-2 ../pom.xml diff --git a/distribution/saml-adapters/pom.xml b/distribution/saml-adapters/pom.xml index a6ed9a07ba05..1e10a15fa3d2 100755 --- a/distribution/saml-adapters/pom.xml +++ b/distribution/saml-adapters/pom.xml @@ -20,7 +20,7 @@ keycloak-distribution-parent org.keycloak - 24.0.5-PS-1 + 24.0.5-PS-2 SAML Adapters Distribution Parent diff --git a/distribution/saml-adapters/tomcat-adapter-zip/pom.xml b/distribution/saml-adapters/tomcat-adapter-zip/pom.xml index 1956cdf0477d..c71e1f63d495 100755 --- a/distribution/saml-adapters/tomcat-adapter-zip/pom.xml +++ b/distribution/saml-adapters/tomcat-adapter-zip/pom.xml @@ -21,7 +21,7 @@ keycloak-parent org.keycloak - 24.0.5-PS-1 + 24.0.5-PS-2 ../../../pom.xml diff --git a/distribution/saml-adapters/wildfly-adapter/pom.xml b/distribution/saml-adapters/wildfly-adapter/pom.xml index 3a58608a6ded..d1c4f819bb9b 100755 --- a/distribution/saml-adapters/wildfly-adapter/pom.xml +++ b/distribution/saml-adapters/wildfly-adapter/pom.xml @@ -20,7 +20,7 @@ keycloak-parent org.keycloak - 24.0.5-PS-1 + 24.0.5-PS-2 ../../../pom.xml Keycloak Wildfly SAML Adapter diff --git a/distribution/saml-adapters/wildfly-adapter/wildfly-adapter-jakarta-zip/pom.xml b/distribution/saml-adapters/wildfly-adapter/wildfly-adapter-jakarta-zip/pom.xml index 0a5fe0a35f97..c2c8a601ef91 100755 --- a/distribution/saml-adapters/wildfly-adapter/wildfly-adapter-jakarta-zip/pom.xml +++ b/distribution/saml-adapters/wildfly-adapter/wildfly-adapter-jakarta-zip/pom.xml @@ -21,7 +21,7 @@ keycloak-parent org.keycloak - 24.0.5-PS-1 + 24.0.5-PS-2 ../../../../pom.xml diff --git a/distribution/saml-adapters/wildfly-adapter/wildfly-adapter-zip/pom.xml b/distribution/saml-adapters/wildfly-adapter/wildfly-adapter-zip/pom.xml index 10ca2a21347d..14fd05fda3d1 100755 --- a/distribution/saml-adapters/wildfly-adapter/wildfly-adapter-zip/pom.xml +++ b/distribution/saml-adapters/wildfly-adapter/wildfly-adapter-zip/pom.xml @@ -21,7 +21,7 @@ keycloak-parent org.keycloak - 24.0.5-PS-1 + 24.0.5-PS-2 ../../../../pom.xml diff --git a/distribution/saml-adapters/wildfly-adapter/wildfly-jakarta-modules/pom.xml b/distribution/saml-adapters/wildfly-adapter/wildfly-jakarta-modules/pom.xml index eba195e1c782..905c9db3b83b 100755 --- a/distribution/saml-adapters/wildfly-adapter/wildfly-jakarta-modules/pom.xml +++ b/distribution/saml-adapters/wildfly-adapter/wildfly-jakarta-modules/pom.xml @@ -25,7 +25,7 @@ keycloak-parent org.keycloak - 24.0.5-PS-1 + 24.0.5-PS-2 ../../../../pom.xml diff --git a/distribution/saml-adapters/wildfly-adapter/wildfly-modules/pom.xml b/distribution/saml-adapters/wildfly-adapter/wildfly-modules/pom.xml index 35a032aa3ba2..729af504edbf 100755 --- a/distribution/saml-adapters/wildfly-adapter/wildfly-modules/pom.xml +++ b/distribution/saml-adapters/wildfly-adapter/wildfly-modules/pom.xml @@ -25,7 +25,7 @@ keycloak-parent org.keycloak - 24.0.5-PS-1 + 24.0.5-PS-2 ../../../../pom.xml diff --git a/docs/documentation/aggregation/pom.xml b/docs/documentation/aggregation/pom.xml index 308c2df53d49..0be7314368db 100644 --- a/docs/documentation/aggregation/pom.xml +++ b/docs/documentation/aggregation/pom.xml @@ -5,7 +5,7 @@ org.keycloak.documentation documentation-parent - 24.0.5-PS-1 + 24.0.5-PS-2 ../pom.xml diff --git a/docs/documentation/api_documentation/pom.xml b/docs/documentation/api_documentation/pom.xml index ab63ef07e71f..f59ec28e7099 100644 --- a/docs/documentation/api_documentation/pom.xml +++ b/docs/documentation/api_documentation/pom.xml @@ -5,7 +5,7 @@ org.keycloak.documentation documentation-parent - 24.0.5-PS-1 + 24.0.5-PS-2 ../pom.xml diff --git a/docs/documentation/authorization_services/pom.xml b/docs/documentation/authorization_services/pom.xml index 65ae9868a8f4..14f5329ff4e0 100644 --- a/docs/documentation/authorization_services/pom.xml +++ b/docs/documentation/authorization_services/pom.xml @@ -5,7 +5,7 @@ org.keycloak.documentation documentation-parent - 24.0.5-PS-1 + 24.0.5-PS-2 ../pom.xml diff --git a/docs/documentation/dist/pom.xml b/docs/documentation/dist/pom.xml index 9addaa55f70a..207b7964b0b3 100644 --- a/docs/documentation/dist/pom.xml +++ b/docs/documentation/dist/pom.xml @@ -5,7 +5,7 @@ org.keycloak.documentation documentation-parent - 24.0.5-PS-1 + 24.0.5-PS-2 ../pom.xml diff --git a/docs/documentation/header-maven-plugin/pom.xml b/docs/documentation/header-maven-plugin/pom.xml index 28346c40f99d..2b9ff178da19 100644 --- a/docs/documentation/header-maven-plugin/pom.xml +++ b/docs/documentation/header-maven-plugin/pom.xml @@ -5,12 +5,12 @@ documentation-parent org.keycloak.documentation - 24.0.5-PS-1 + 24.0.5-PS-2 org.keycloak.documentation header-maven-plugin - 24.0.5-PS-1 + 24.0.5-PS-2 maven-plugin github-maven-plugin diff --git a/docs/documentation/pom.xml b/docs/documentation/pom.xml index 60331f5d4e7f..22ccefa859d0 100644 --- a/docs/documentation/pom.xml +++ b/docs/documentation/pom.xml @@ -5,14 +5,14 @@ keycloak-docs-parent org.keycloak - 24.0.5-PS-1 + 24.0.5-PS-2 ../pom.xml Keycloak Documentation Parent org.keycloak.documentation documentation-parent - 24.0.5-PS-1 + 24.0.5-PS-2 pom diff --git a/docs/documentation/release_notes/pom.xml b/docs/documentation/release_notes/pom.xml index 0a14326c25fc..5b7f09380f53 100644 --- a/docs/documentation/release_notes/pom.xml +++ b/docs/documentation/release_notes/pom.xml @@ -5,7 +5,7 @@ org.keycloak.documentation documentation-parent - 24.0.5-PS-1 + 24.0.5-PS-2 ../pom.xml diff --git a/docs/documentation/securing_apps/pom.xml b/docs/documentation/securing_apps/pom.xml index 715ae2a5b620..8411f230bc5f 100644 --- a/docs/documentation/securing_apps/pom.xml +++ b/docs/documentation/securing_apps/pom.xml @@ -5,7 +5,7 @@ org.keycloak.documentation documentation-parent - 24.0.5-PS-1 + 24.0.5-PS-2 ../pom.xml diff --git a/docs/documentation/server_admin/pom.xml b/docs/documentation/server_admin/pom.xml index 2848c18f995f..1268a213237f 100644 --- a/docs/documentation/server_admin/pom.xml +++ b/docs/documentation/server_admin/pom.xml @@ -5,7 +5,7 @@ org.keycloak.documentation documentation-parent - 24.0.5-PS-1 + 24.0.5-PS-2 ../pom.xml diff --git a/docs/documentation/server_development/pom.xml b/docs/documentation/server_development/pom.xml index 45e3e24f6012..0f6b46eb3f40 100644 --- a/docs/documentation/server_development/pom.xml +++ b/docs/documentation/server_development/pom.xml @@ -5,7 +5,7 @@ org.keycloak.documentation documentation-parent - 24.0.5-PS-1 + 24.0.5-PS-2 ../pom.xml diff --git a/docs/documentation/tests/pom.xml b/docs/documentation/tests/pom.xml index acb884105859..105908a12b5c 100644 --- a/docs/documentation/tests/pom.xml +++ b/docs/documentation/tests/pom.xml @@ -60,7 +60,7 @@ org.keycloak.documentation documentation-parent - 24.0.5-PS-1 + 24.0.5-PS-2 ../pom.xml diff --git a/docs/documentation/topics/templates/document-attributes.adoc b/docs/documentation/topics/templates/document-attributes.adoc index 136e578fa061..4cdc1c76fe81 100644 --- a/docs/documentation/topics/templates/document-attributes.adoc +++ b/docs/documentation/topics/templates/document-attributes.adoc @@ -2,10 +2,10 @@ :project_name_full: Keycloak :project_community: true :project_product: false -:project_version: 24.0.5-PS-1 -:project_versionMvn: 24.0.5-PS-1 -:project_versionNpm: 24.0.5-PS-1 -:project_versionDoc: 24.0.5-PS-1 +:project_version: 24.0.5-PS-2 +:project_versionMvn: 24.0.5-PS-2 +:project_versionNpm: 24.0.5-PS-2 +:project_versionDoc: 24.0.5-PS-2 :archivebasename: keycloak :archivedownloadurl: https://github.com/keycloak/keycloak/releases/download/{project_version}/keycloak-{project_version}.zip diff --git a/docs/documentation/upgrading/pom.xml b/docs/documentation/upgrading/pom.xml index 3b6245e252c2..71184aa0807d 100644 --- a/docs/documentation/upgrading/pom.xml +++ b/docs/documentation/upgrading/pom.xml @@ -5,7 +5,7 @@ org.keycloak.documentation documentation-parent - 24.0.5-PS-1 + 24.0.5-PS-2 ../pom.xml diff --git a/docs/guides/pom.xml b/docs/guides/pom.xml index 67ae377e1012..46b050156893 100644 --- a/docs/guides/pom.xml +++ b/docs/guides/pom.xml @@ -19,7 +19,7 @@ keycloak-docs-parent org.keycloak - 24.0.5-PS-1 + 24.0.5-PS-2 ../pom.xml diff --git a/docs/maven-plugin/pom.xml b/docs/maven-plugin/pom.xml index 743b87779e77..7e79db8f6d66 100644 --- a/docs/maven-plugin/pom.xml +++ b/docs/maven-plugin/pom.xml @@ -20,7 +20,7 @@ keycloak-docs-parent org.keycloak - 24.0.5-PS-1 + 24.0.5-PS-2 ../pom.xml diff --git a/docs/pom.xml b/docs/pom.xml index 529cc0b0aac7..e721df31da61 100755 --- a/docs/pom.xml +++ b/docs/pom.xml @@ -19,7 +19,7 @@ keycloak-parent org.keycloak - 24.0.5-PS-1 + 24.0.5-PS-2 ../pom.xml Keycloak Docs Parent diff --git a/examples/admin-client/pom.xml b/examples/admin-client/pom.xml index 0335d4d4ceeb..90af2e89d5df 100755 --- a/examples/admin-client/pom.xml +++ b/examples/admin-client/pom.xml @@ -22,7 +22,7 @@ keycloak-examples-parent org.keycloak - 24.0.5-PS-1 + 24.0.5-PS-2 Keycloak Examples - Admin Client diff --git a/examples/js-console/pom.xml b/examples/js-console/pom.xml index af88d14694d1..f6d639ad831e 100755 --- a/examples/js-console/pom.xml +++ b/examples/js-console/pom.xml @@ -21,7 +21,7 @@ keycloak-examples-parent org.keycloak - 24.0.5-PS-1 + 24.0.5-PS-2 4.0.0 diff --git a/examples/kerberos/pom.xml b/examples/kerberos/pom.xml index 7f7c8c88e226..0c13682e2a73 100755 --- a/examples/kerberos/pom.xml +++ b/examples/kerberos/pom.xml @@ -22,7 +22,7 @@ keycloak-examples-parent org.keycloak - 24.0.5-PS-1 + 24.0.5-PS-2 Keycloak Examples - Kerberos Credential Delegation diff --git a/examples/ldap/pom.xml b/examples/ldap/pom.xml index 964b9d146b9d..778b7a59cfea 100644 --- a/examples/ldap/pom.xml +++ b/examples/ldap/pom.xml @@ -22,7 +22,7 @@ keycloak-examples-parent org.keycloak - 24.0.5-PS-1 + 24.0.5-PS-2 4.0.0 diff --git a/examples/pom.xml b/examples/pom.xml index 34758219a0bc..d1a4f4be5686 100755 --- a/examples/pom.xml +++ b/examples/pom.xml @@ -20,7 +20,7 @@ keycloak-parent org.keycloak - 24.0.5-PS-1 + 24.0.5-PS-2 Keycloak Examples diff --git a/examples/providers/authenticator/pom.xml b/examples/providers/authenticator/pom.xml index f56abcdcdbfa..6b84f960627f 100755 --- a/examples/providers/authenticator/pom.xml +++ b/examples/providers/authenticator/pom.xml @@ -20,7 +20,7 @@ keycloak-examples-providers-parent org.keycloak - 24.0.5-PS-1 + 24.0.5-PS-2 Authenticator Example diff --git a/examples/providers/pom.xml b/examples/providers/pom.xml index 9178b53879a5..cf2a52a6bffd 100755 --- a/examples/providers/pom.xml +++ b/examples/providers/pom.xml @@ -20,7 +20,7 @@ keycloak-examples-parent org.keycloak - 24.0.5-PS-1 + 24.0.5-PS-2 Provider Examples diff --git a/examples/providers/rest/pom.xml b/examples/providers/rest/pom.xml index 3c559d213392..28491bcea0d6 100755 --- a/examples/providers/rest/pom.xml +++ b/examples/providers/rest/pom.xml @@ -20,7 +20,7 @@ keycloak-examples-providers-parent org.keycloak - 24.0.5-PS-1 + 24.0.5-PS-2 REST Example diff --git a/examples/saml/pom.xml b/examples/saml/pom.xml index 786d693b7892..4474c6506542 100755 --- a/examples/saml/pom.xml +++ b/examples/saml/pom.xml @@ -20,7 +20,7 @@ keycloak-examples-parent org.keycloak - 24.0.5-PS-1 + 24.0.5-PS-2 SAML Examples diff --git a/examples/saml/servlet-filter/pom.xml b/examples/saml/servlet-filter/pom.xml index 0cc06d709b1b..0ce593c7cc2a 100755 --- a/examples/saml/servlet-filter/pom.xml +++ b/examples/saml/servlet-filter/pom.xml @@ -22,7 +22,7 @@ keycloak-examples-saml-parent org.keycloak - 24.0.5-PS-1 + 24.0.5-PS-2 saml-servlet-filter diff --git a/examples/themes/pom.xml b/examples/themes/pom.xml index 9d3a14fb3f0a..e15bfec5accd 100755 --- a/examples/themes/pom.xml +++ b/examples/themes/pom.xml @@ -20,7 +20,7 @@ keycloak-examples-parent org.keycloak - 24.0.5-PS-1 + 24.0.5-PS-2 Themes Examples diff --git a/federation/kerberos/pom.xml b/federation/kerberos/pom.xml index 587e505c6e35..399ee9534048 100755 --- a/federation/kerberos/pom.xml +++ b/federation/kerberos/pom.xml @@ -20,7 +20,7 @@ keycloak-parent org.keycloak - 24.0.5-PS-1 + 24.0.5-PS-2 ../../pom.xml 4.0.0 diff --git a/federation/ldap/pom.xml b/federation/ldap/pom.xml index 14de145e7f22..736dfc43da77 100755 --- a/federation/ldap/pom.xml +++ b/federation/ldap/pom.xml @@ -20,7 +20,7 @@ keycloak-parent org.keycloak - 24.0.5-PS-1 + 24.0.5-PS-2 ../../pom.xml 4.0.0 diff --git a/federation/pom.xml b/federation/pom.xml index 68f7f7311b11..00d6d9eeea96 100755 --- a/federation/pom.xml +++ b/federation/pom.xml @@ -22,7 +22,7 @@ keycloak-parent org.keycloak - 24.0.5-PS-1 + 24.0.5-PS-2 ../pom.xml 4.0.0 diff --git a/federation/sssd/pom.xml b/federation/sssd/pom.xml index 3c273c3e7499..931c90ace950 100644 --- a/federation/sssd/pom.xml +++ b/federation/sssd/pom.xml @@ -4,7 +4,7 @@ keycloak-parent org.keycloak - 24.0.5-PS-1 + 24.0.5-PS-2 ../../pom.xml 4.0.0 diff --git a/integration/admin-client-jee/pom.xml b/integration/admin-client-jee/pom.xml index 34465ce456ae..a3fa9c3fec51 100755 --- a/integration/admin-client-jee/pom.xml +++ b/integration/admin-client-jee/pom.xml @@ -22,7 +22,7 @@ keycloak-integration-parent org.keycloak - 24.0.5-PS-1 + 24.0.5-PS-2 4.0.0 diff --git a/integration/admin-client/pom.xml b/integration/admin-client/pom.xml index 729e81acb8bc..9ac7f472974a 100755 --- a/integration/admin-client/pom.xml +++ b/integration/admin-client/pom.xml @@ -22,7 +22,7 @@ keycloak-integration-parent org.keycloak - 24.0.5-PS-1 + 24.0.5-PS-2 4.0.0 diff --git a/integration/client-cli/admin-cli/pom.xml b/integration/client-cli/admin-cli/pom.xml index 1248a5782fab..e50632332dcf 100755 --- a/integration/client-cli/admin-cli/pom.xml +++ b/integration/client-cli/admin-cli/pom.xml @@ -21,7 +21,7 @@ keycloak-client-cli-parent org.keycloak - 24.0.5-PS-1 + 24.0.5-PS-2 4.0.0 diff --git a/integration/client-cli/client-cli-dist/pom.xml b/integration/client-cli/client-cli-dist/pom.xml index 7a3373db2c6a..1a80fae45799 100755 --- a/integration/client-cli/client-cli-dist/pom.xml +++ b/integration/client-cli/client-cli-dist/pom.xml @@ -21,7 +21,7 @@ keycloak-client-cli-parent org.keycloak - 24.0.5-PS-1 + 24.0.5-PS-2 keycloak-client-cli-dist diff --git a/integration/client-cli/client-registration-cli/pom.xml b/integration/client-cli/client-registration-cli/pom.xml index a516ad6b3053..2536c5d075ba 100755 --- a/integration/client-cli/client-registration-cli/pom.xml +++ b/integration/client-cli/client-registration-cli/pom.xml @@ -21,7 +21,7 @@ keycloak-client-cli-parent org.keycloak - 24.0.5-PS-1 + 24.0.5-PS-2 4.0.0 diff --git a/integration/client-cli/pom.xml b/integration/client-cli/pom.xml index 6984d5cb4840..4698ce739b8a 100644 --- a/integration/client-cli/pom.xml +++ b/integration/client-cli/pom.xml @@ -20,7 +20,7 @@ keycloak-integration-parent org.keycloak - 24.0.5-PS-1 + 24.0.5-PS-2 Keycloak Client CLI diff --git a/integration/client-registration/pom.xml b/integration/client-registration/pom.xml index 960d976c6e11..2911874e6631 100755 --- a/integration/client-registration/pom.xml +++ b/integration/client-registration/pom.xml @@ -21,7 +21,7 @@ keycloak-integration-parent org.keycloak - 24.0.5-PS-1 + 24.0.5-PS-2 4.0.0 diff --git a/integration/pom.xml b/integration/pom.xml index 1b1ab33dcfaa..143f6df88ae6 100755 --- a/integration/pom.xml +++ b/integration/pom.xml @@ -20,7 +20,7 @@ keycloak-parent org.keycloak - 24.0.5-PS-1 + 24.0.5-PS-2 ../pom.xml Keycloak Integration diff --git a/js/apps/account-ui/pom.xml b/js/apps/account-ui/pom.xml index 1dfda3254f59..6ba44bc644cc 100644 --- a/js/apps/account-ui/pom.xml +++ b/js/apps/account-ui/pom.xml @@ -7,7 +7,7 @@ keycloak-js-parent org.keycloak - 24.0.5-PS-1 + 24.0.5-PS-2 ../../pom.xml diff --git a/js/apps/admin-ui/pom.xml b/js/apps/admin-ui/pom.xml index f663ed942666..cb290266d4a2 100644 --- a/js/apps/admin-ui/pom.xml +++ b/js/apps/admin-ui/pom.xml @@ -7,7 +7,7 @@ keycloak-js-parent org.keycloak - 24.0.5-PS-1 + 24.0.5-PS-2 ../../pom.xml diff --git a/js/libs/keycloak-admin-client/package.json b/js/libs/keycloak-admin-client/package.json index 1e852457c478..7225b6a513dc 100644 --- a/js/libs/keycloak-admin-client/package.json +++ b/js/libs/keycloak-admin-client/package.json @@ -1,6 +1,6 @@ { "name": "@keycloak/keycloak-admin-client", - "version": "24.0.5-PS-1", + "version": "24.0.5-PS-2", "description": "A client to interact with Keycloak's Administration API", "type": "module", "main": "lib/index.js", diff --git a/js/libs/keycloak-admin-client/pom.xml b/js/libs/keycloak-admin-client/pom.xml index 61c3bebea7c4..f0b82e120b60 100644 --- a/js/libs/keycloak-admin-client/pom.xml +++ b/js/libs/keycloak-admin-client/pom.xml @@ -5,7 +5,7 @@ keycloak-js-parent org.keycloak - 24.0.5-PS-1 + 24.0.5-PS-2 ../../pom.xml diff --git a/js/libs/keycloak-js/package.json b/js/libs/keycloak-js/package.json index fca0520d1bf9..92470b27c769 100644 --- a/js/libs/keycloak-js/package.json +++ b/js/libs/keycloak-js/package.json @@ -1,6 +1,6 @@ { "name": "keycloak-js", - "version": "24.0.5-PS-1", + "version": "24.0.5-PS-2", "description": "A client-side JavaScript OpenID Connect library that can be used to secure web applications", "main": "./dist/keycloak.js", "module": "./dist/keycloak.mjs", diff --git a/js/libs/keycloak-js/pom.xml b/js/libs/keycloak-js/pom.xml index 962b90c6cf1f..d018097e5393 100644 --- a/js/libs/keycloak-js/pom.xml +++ b/js/libs/keycloak-js/pom.xml @@ -5,7 +5,7 @@ keycloak-js-parent org.keycloak - 24.0.5-PS-1 + 24.0.5-PS-2 ../../pom.xml diff --git a/js/pom.xml b/js/pom.xml index 5eb4975dffbc..f2d8a2292c6c 100644 --- a/js/pom.xml +++ b/js/pom.xml @@ -5,7 +5,7 @@ keycloak-parent org.keycloak - 24.0.5-PS-1 + 24.0.5-PS-2 ../pom.xml diff --git a/misc/keycloak-test-helper/pom.xml b/misc/keycloak-test-helper/pom.xml index 990a04770e1d..071c5d518f32 100644 --- a/misc/keycloak-test-helper/pom.xml +++ b/misc/keycloak-test-helper/pom.xml @@ -6,7 +6,7 @@ keycloak-misc-parent org.keycloak - 24.0.5-PS-1 + 24.0.5-PS-2 org.keycloak keycloak-test-helper diff --git a/misc/pom.xml b/misc/pom.xml index 5d140bacd276..6dbb96632efd 100644 --- a/misc/pom.xml +++ b/misc/pom.xml @@ -3,7 +3,7 @@ keycloak-parent org.keycloak - 24.0.5-PS-1 + 24.0.5-PS-2 Keycloak Misc diff --git a/misc/spring-boot-starter/keycloak-spring-boot-starter/pom.xml b/misc/spring-boot-starter/keycloak-spring-boot-starter/pom.xml index 49b0227bf69d..edcc23cec78c 100644 --- a/misc/spring-boot-starter/keycloak-spring-boot-starter/pom.xml +++ b/misc/spring-boot-starter/keycloak-spring-boot-starter/pom.xml @@ -4,7 +4,7 @@ org.keycloak keycloak-spring-boot-starter-parent - 24.0.5-PS-1 + 24.0.5-PS-2 keycloak-spring-boot-starter Keycloak :: Spring :: Boot :: Default :: Starter diff --git a/misc/spring-boot-starter/pom.xml b/misc/spring-boot-starter/pom.xml index 08a79a1ec753..5a3de2ba648e 100644 --- a/misc/spring-boot-starter/pom.xml +++ b/misc/spring-boot-starter/pom.xml @@ -5,7 +5,7 @@ keycloak-misc-parent org.keycloak - 24.0.5-PS-1 + 24.0.5-PS-2 org.keycloak keycloak-spring-boot-starter-parent diff --git a/model/infinispan/pom.xml b/model/infinispan/pom.xml index 6a2384aaca65..005746a890f7 100755 --- a/model/infinispan/pom.xml +++ b/model/infinispan/pom.xml @@ -21,7 +21,7 @@ keycloak-model-pom org.keycloak - 24.0.5-PS-1 + 24.0.5-PS-2 17 diff --git a/model/jpa/pom.xml b/model/jpa/pom.xml index 34eb9114e084..b18c5e73e55b 100755 --- a/model/jpa/pom.xml +++ b/model/jpa/pom.xml @@ -21,7 +21,7 @@ keycloak-model-pom org.keycloak - 24.0.5-PS-1 + 24.0.5-PS-2 4.0.0 diff --git a/model/legacy/pom.xml b/model/legacy/pom.xml index aa2fc45d3592..2c5b0253826a 100644 --- a/model/legacy/pom.xml +++ b/model/legacy/pom.xml @@ -3,7 +3,7 @@ keycloak-model-pom org.keycloak - 24.0.5-PS-1 + 24.0.5-PS-2 4.0.0 diff --git a/model/pom.xml b/model/pom.xml index 244b02a99878..460f88e58ddd 100755 --- a/model/pom.xml +++ b/model/pom.xml @@ -20,7 +20,7 @@ keycloak-parent org.keycloak - 24.0.5-PS-1 + 24.0.5-PS-2 ../pom.xml Keycloak Model Parent diff --git a/model/storage-private/pom.xml b/model/storage-private/pom.xml index 300fa9bec341..f06e7b209c57 100644 --- a/model/storage-private/pom.xml +++ b/model/storage-private/pom.xml @@ -3,7 +3,7 @@ keycloak-model-pom org.keycloak - 24.0.5-PS-1 + 24.0.5-PS-2 4.0.0 diff --git a/model/storage-services/pom.xml b/model/storage-services/pom.xml index 164d317a361a..12d4563da5cd 100644 --- a/model/storage-services/pom.xml +++ b/model/storage-services/pom.xml @@ -3,7 +3,7 @@ keycloak-model-pom org.keycloak - 24.0.5-PS-1 + 24.0.5-PS-2 4.0.0 diff --git a/model/storage/pom.xml b/model/storage/pom.xml index 839329b9d759..c319e6194ef8 100644 --- a/model/storage/pom.xml +++ b/model/storage/pom.xml @@ -3,7 +3,7 @@ keycloak-model-pom org.keycloak - 24.0.5-PS-1 + 24.0.5-PS-2 4.0.0 diff --git a/operator/pom.xml b/operator/pom.xml index 0821071fc7b3..5b66c5c29466 100644 --- a/operator/pom.xml +++ b/operator/pom.xml @@ -7,7 +7,7 @@ keycloak-parent org.keycloak - 24.0.5-PS-1 + 24.0.5-PS-2 ../pom.xml diff --git a/pom.xml b/pom.xml index cab56345ad54..3ab201d5035e 100644 --- a/pom.xml +++ b/pom.xml @@ -31,11 +31,11 @@ org.keycloak keycloak-parent - 24.0.5-PS-1 + 24.0.5-PS-2 pom - 24.0.5-PS-1 + 24.0.5-PS-2 1.5.8 diff --git a/quarkus/config-api/pom.xml b/quarkus/config-api/pom.xml index d601986281a8..f84c5c2c383c 100755 --- a/quarkus/config-api/pom.xml +++ b/quarkus/config-api/pom.xml @@ -21,7 +21,7 @@ keycloak-quarkus-parent org.keycloak - 24.0.5-PS-1 + 24.0.5-PS-2 ../pom.xml 4.0.0 diff --git a/quarkus/container/Dockerfile b/quarkus/container/Dockerfile index 248b1b97c170..c86b5d1d5c2c 100644 --- a/quarkus/container/Dockerfile +++ b/quarkus/container/Dockerfile @@ -1,6 +1,6 @@ FROM registry.access.redhat.com/ubi9 AS ubi-micro-build -ENV KEYCLOAK_VERSION 24.0.5-PS-1 +ENV KEYCLOAK_VERSION 24.0.5-PS-2 ARG KEYCLOAK_DIST=https://github.com/keycloak/keycloak/releases/download/$KEYCLOAK_VERSION/keycloak-$KEYCLOAK_VERSION.tar.gz RUN dnf install -y tar gzip diff --git a/quarkus/deployment/pom.xml b/quarkus/deployment/pom.xml index 62e62b094b45..02214359186c 100644 --- a/quarkus/deployment/pom.xml +++ b/quarkus/deployment/pom.xml @@ -5,7 +5,7 @@ keycloak-quarkus-parent org.keycloak - 24.0.5-PS-1 + 24.0.5-PS-2 ../pom.xml 4.0.0 diff --git a/quarkus/dist/pom.xml b/quarkus/dist/pom.xml index 87c58449df62..8ead849777bc 100755 --- a/quarkus/dist/pom.xml +++ b/quarkus/dist/pom.xml @@ -21,7 +21,7 @@ keycloak-quarkus-parent org.keycloak - 24.0.5-PS-1 + 24.0.5-PS-2 keycloak-quarkus-dist diff --git a/quarkus/pom.xml b/quarkus/pom.xml index 39334b1d0e68..4e85c34302ed 100644 --- a/quarkus/pom.xml +++ b/quarkus/pom.xml @@ -20,7 +20,7 @@ keycloak-parent org.keycloak - 24.0.5-PS-1 + 24.0.5-PS-2 ../pom.xml Keycloak Quarkus Parent diff --git a/quarkus/runtime/pom.xml b/quarkus/runtime/pom.xml index 768ad817a4f4..5d1b8575d582 100644 --- a/quarkus/runtime/pom.xml +++ b/quarkus/runtime/pom.xml @@ -5,7 +5,7 @@ keycloak-quarkus-parent org.keycloak - 24.0.5-PS-1 + 24.0.5-PS-2 ../pom.xml 4.0.0 diff --git a/quarkus/server/pom.xml b/quarkus/server/pom.xml index b9ed602771ad..19abb81f004b 100644 --- a/quarkus/server/pom.xml +++ b/quarkus/server/pom.xml @@ -7,7 +7,7 @@ keycloak-quarkus-parent org.keycloak - 24.0.5-PS-1 + 24.0.5-PS-2 ../pom.xml diff --git a/quarkus/tests/integration/pom.xml b/quarkus/tests/integration/pom.xml index ce5864c24267..1c0ae45c0e2b 100644 --- a/quarkus/tests/integration/pom.xml +++ b/quarkus/tests/integration/pom.xml @@ -24,7 +24,7 @@ keycloak-quarkus-test-parent org.keycloak - 24.0.5-PS-1 + 24.0.5-PS-2 ../pom.xml diff --git a/quarkus/tests/junit5/pom.xml b/quarkus/tests/junit5/pom.xml index 154a129dc7fd..00f419c6799a 100644 --- a/quarkus/tests/junit5/pom.xml +++ b/quarkus/tests/junit5/pom.xml @@ -24,7 +24,7 @@ keycloak-quarkus-test-parent org.keycloak - 24.0.5-PS-1 + 24.0.5-PS-2 ../pom.xml diff --git a/quarkus/tests/pom.xml b/quarkus/tests/pom.xml index 17c68758b94f..3b616fc87c2b 100644 --- a/quarkus/tests/pom.xml +++ b/quarkus/tests/pom.xml @@ -24,7 +24,7 @@ keycloak-quarkus-parent org.keycloak - 24.0.5-PS-1 + 24.0.5-PS-2 ../pom.xml diff --git a/release-details b/release-details index e31b429559dd..bfe0f9dfb36b 100644 --- a/release-details +++ b/release-details @@ -1,3 +1,3 @@ -VERSION=24.0.5-PS-1 -SHORT_VERSION=24.0.5-PS-1 -NPM_VERSION=24.0.5-PS-1 +VERSION=24.0.5-PS-2 +SHORT_VERSION=24.0.5-PS-2 +NPM_VERSION=24.0.5-PS-2 diff --git a/rest/admin-ui-ext/pom.xml b/rest/admin-ui-ext/pom.xml index 88e1a2fef197..184b254ca98b 100644 --- a/rest/admin-ui-ext/pom.xml +++ b/rest/admin-ui-ext/pom.xml @@ -22,7 +22,7 @@ org.keycloak keycloak-rest-parent - 24.0.5-PS-1 + 24.0.5-PS-2 keycloak-rest-admin-ui-ext diff --git a/rest/pom.xml b/rest/pom.xml index dd23b44a453d..eb6fdcc28887 100644 --- a/rest/pom.xml +++ b/rest/pom.xml @@ -22,7 +22,7 @@ keycloak-parent org.keycloak - 24.0.5-PS-1 + 24.0.5-PS-2 Keycloak Administration UI diff --git a/saml-core-api/pom.xml b/saml-core-api/pom.xml index d46e729c4957..c060d583cdd7 100755 --- a/saml-core-api/pom.xml +++ b/saml-core-api/pom.xml @@ -21,7 +21,7 @@ keycloak-parent org.keycloak - 24.0.5-PS-1 + 24.0.5-PS-2 ../pom.xml 4.0.0 diff --git a/saml-core/pom.xml b/saml-core/pom.xml index 6c440f74a50b..00c94d80f491 100755 --- a/saml-core/pom.xml +++ b/saml-core/pom.xml @@ -21,7 +21,7 @@ keycloak-parent org.keycloak - 24.0.5-PS-1 + 24.0.5-PS-2 ../pom.xml 4.0.0 diff --git a/server-spi-private/pom.xml b/server-spi-private/pom.xml index bbf2b5a8f661..8aae56df2077 100755 --- a/server-spi-private/pom.xml +++ b/server-spi-private/pom.xml @@ -21,7 +21,7 @@ keycloak-parent org.keycloak - 24.0.5-PS-1 + 24.0.5-PS-2 ../pom.xml 4.0.0 diff --git a/server-spi/pom.xml b/server-spi/pom.xml index 182a8ec35098..13ed544b748d 100755 --- a/server-spi/pom.xml +++ b/server-spi/pom.xml @@ -21,7 +21,7 @@ keycloak-parent org.keycloak - 24.0.5-PS-1 + 24.0.5-PS-2 ../pom.xml 4.0.0 diff --git a/services/pom.xml b/services/pom.xml index 5996a3cc7d00..814bc030f110 100755 --- a/services/pom.xml +++ b/services/pom.xml @@ -21,7 +21,7 @@ keycloak-parent org.keycloak - 24.0.5-PS-1 + 24.0.5-PS-2 ../pom.xml 4.0.0 diff --git a/testsuite/db-allocator-plugin/pom.xml b/testsuite/db-allocator-plugin/pom.xml index 2a119f102c9a..626960aa01ed 100644 --- a/testsuite/db-allocator-plugin/pom.xml +++ b/testsuite/db-allocator-plugin/pom.xml @@ -22,7 +22,7 @@ keycloak-testsuite-pom org.keycloak - 24.0.5-PS-1 + 24.0.5-PS-2 4.0.0 diff --git a/testsuite/integration-arquillian/pom.xml b/testsuite/integration-arquillian/pom.xml index e654d2bef401..c0e5fca2365e 100644 --- a/testsuite/integration-arquillian/pom.xml +++ b/testsuite/integration-arquillian/pom.xml @@ -22,7 +22,7 @@ org.keycloak keycloak-testsuite-pom - 24.0.5-PS-1 + 24.0.5-PS-2 ../pom.xml diff --git a/testsuite/integration-arquillian/servers/adapter-spi/pom.xml b/testsuite/integration-arquillian/servers/adapter-spi/pom.xml index 9192dfb9df3b..7a2b57bc8c77 100644 --- a/testsuite/integration-arquillian/servers/adapter-spi/pom.xml +++ b/testsuite/integration-arquillian/servers/adapter-spi/pom.xml @@ -5,7 +5,7 @@ integration-arquillian-servers org.keycloak.testsuite - 24.0.5-PS-1 + 24.0.5-PS-2 4.0.0 pom diff --git a/testsuite/integration-arquillian/servers/adapter-spi/undertow-adapter-jakarta/pom.xml b/testsuite/integration-arquillian/servers/adapter-spi/undertow-adapter-jakarta/pom.xml index 892dea32ae96..bc4674f6d5eb 100644 --- a/testsuite/integration-arquillian/servers/adapter-spi/undertow-adapter-jakarta/pom.xml +++ b/testsuite/integration-arquillian/servers/adapter-spi/undertow-adapter-jakarta/pom.xml @@ -5,7 +5,7 @@ integration-arquillian-servers-adapter-spi org.keycloak.testsuite - 24.0.5-PS-1 + 24.0.5-PS-2 ../pom.xml 4.0.0 diff --git a/testsuite/integration-arquillian/servers/adapter-spi/undertow-adapter-saml-jakarta/pom.xml b/testsuite/integration-arquillian/servers/adapter-spi/undertow-adapter-saml-jakarta/pom.xml index 8c872b1fc92d..df7683a7ac40 100644 --- a/testsuite/integration-arquillian/servers/adapter-spi/undertow-adapter-saml-jakarta/pom.xml +++ b/testsuite/integration-arquillian/servers/adapter-spi/undertow-adapter-saml-jakarta/pom.xml @@ -5,7 +5,7 @@ integration-arquillian-servers-adapter-spi org.keycloak.testsuite - 24.0.5-PS-1 + 24.0.5-PS-2 ../pom.xml 4.0.0 diff --git a/testsuite/integration-arquillian/servers/adapter-spi/undertow-adapter-spi-jakarta/pom.xml b/testsuite/integration-arquillian/servers/adapter-spi/undertow-adapter-spi-jakarta/pom.xml index 48d2b5c19c5a..43233b630a2c 100644 --- a/testsuite/integration-arquillian/servers/adapter-spi/undertow-adapter-spi-jakarta/pom.xml +++ b/testsuite/integration-arquillian/servers/adapter-spi/undertow-adapter-spi-jakarta/pom.xml @@ -5,7 +5,7 @@ integration-arquillian-servers-adapter-spi org.keycloak.testsuite - 24.0.5-PS-1 + 24.0.5-PS-2 ../pom.xml 4.0.0 diff --git a/testsuite/integration-arquillian/servers/app-server/app-server-spi/pom.xml b/testsuite/integration-arquillian/servers/app-server/app-server-spi/pom.xml index 923f7ddc5543..0c3d6650383b 100644 --- a/testsuite/integration-arquillian/servers/app-server/app-server-spi/pom.xml +++ b/testsuite/integration-arquillian/servers/app-server/app-server-spi/pom.xml @@ -21,7 +21,7 @@ org.keycloak.testsuite integration-arquillian-servers-app-server - 24.0.5-PS-1 + 24.0.5-PS-2 4.0.0 diff --git a/testsuite/integration-arquillian/servers/app-server/jboss/eap/pom.xml b/testsuite/integration-arquillian/servers/app-server/jboss/eap/pom.xml index f23f8900207d..10982bc6f9b7 100644 --- a/testsuite/integration-arquillian/servers/app-server/jboss/eap/pom.xml +++ b/testsuite/integration-arquillian/servers/app-server/jboss/eap/pom.xml @@ -21,7 +21,7 @@ org.keycloak.testsuite integration-arquillian-servers-app-server-jboss - 24.0.5-PS-1 + 24.0.5-PS-2 4.0.0 diff --git a/testsuite/integration-arquillian/servers/app-server/jboss/eap6/pom.xml b/testsuite/integration-arquillian/servers/app-server/jboss/eap6/pom.xml index 5f5bf427b61d..79272ae46869 100644 --- a/testsuite/integration-arquillian/servers/app-server/jboss/eap6/pom.xml +++ b/testsuite/integration-arquillian/servers/app-server/jboss/eap6/pom.xml @@ -21,7 +21,7 @@ org.keycloak.testsuite integration-arquillian-servers-app-server-jboss - 24.0.5-PS-1 + 24.0.5-PS-2 4.0.0 diff --git a/testsuite/integration-arquillian/servers/app-server/jboss/galleon/pom.xml b/testsuite/integration-arquillian/servers/app-server/jboss/galleon/pom.xml index 7b95a8875fa5..f9cf3748c864 100644 --- a/testsuite/integration-arquillian/servers/app-server/jboss/galleon/pom.xml +++ b/testsuite/integration-arquillian/servers/app-server/jboss/galleon/pom.xml @@ -5,7 +5,7 @@ org.keycloak.testsuite integration-arquillian-servers-app-server-jboss - 24.0.5-PS-1 + 24.0.5-PS-2 4.0.0 diff --git a/testsuite/integration-arquillian/servers/app-server/jboss/pom.xml b/testsuite/integration-arquillian/servers/app-server/jboss/pom.xml index d8a1adb10dd4..a996a6eadd12 100644 --- a/testsuite/integration-arquillian/servers/app-server/jboss/pom.xml +++ b/testsuite/integration-arquillian/servers/app-server/jboss/pom.xml @@ -22,7 +22,7 @@ org.keycloak.testsuite integration-arquillian-servers-app-server - 24.0.5-PS-1 + 24.0.5-PS-2 4.0.0 diff --git a/testsuite/integration-arquillian/servers/app-server/jboss/wildfly/pom.xml b/testsuite/integration-arquillian/servers/app-server/jboss/wildfly/pom.xml index 3b1ea2d334de..954601335b26 100644 --- a/testsuite/integration-arquillian/servers/app-server/jboss/wildfly/pom.xml +++ b/testsuite/integration-arquillian/servers/app-server/jboss/wildfly/pom.xml @@ -21,7 +21,7 @@ org.keycloak.testsuite integration-arquillian-servers-app-server-jboss - 24.0.5-PS-1 + 24.0.5-PS-2 4.0.0 diff --git a/testsuite/integration-arquillian/servers/app-server/jetty/94/pom.xml b/testsuite/integration-arquillian/servers/app-server/jetty/94/pom.xml index ceb342774467..3a0daafbc2de 100644 --- a/testsuite/integration-arquillian/servers/app-server/jetty/94/pom.xml +++ b/testsuite/integration-arquillian/servers/app-server/jetty/94/pom.xml @@ -18,7 +18,7 @@ org.keycloak.testsuite integration-arquillian-servers-app-server-jetty - 24.0.5-PS-1 + 24.0.5-PS-2 4.0.0 diff --git a/testsuite/integration-arquillian/servers/app-server/jetty/common/pom.xml b/testsuite/integration-arquillian/servers/app-server/jetty/common/pom.xml index 975ab9b43097..b10a9c9d0f17 100644 --- a/testsuite/integration-arquillian/servers/app-server/jetty/common/pom.xml +++ b/testsuite/integration-arquillian/servers/app-server/jetty/common/pom.xml @@ -18,7 +18,7 @@ org.keycloak.testsuite integration-arquillian-servers-app-server-jetty - 24.0.5-PS-1 + 24.0.5-PS-2 4.0.0 diff --git a/testsuite/integration-arquillian/servers/app-server/jetty/pom.xml b/testsuite/integration-arquillian/servers/app-server/jetty/pom.xml index 4a48128e7820..a43cf381e473 100644 --- a/testsuite/integration-arquillian/servers/app-server/jetty/pom.xml +++ b/testsuite/integration-arquillian/servers/app-server/jetty/pom.xml @@ -18,7 +18,7 @@ org.keycloak.testsuite integration-arquillian-servers-app-server - 24.0.5-PS-1 + 24.0.5-PS-2 4.0.0 diff --git a/testsuite/integration-arquillian/servers/app-server/karaf/fuse63/pom.xml b/testsuite/integration-arquillian/servers/app-server/karaf/fuse63/pom.xml index 8afff103c5f7..df360634a3dc 100644 --- a/testsuite/integration-arquillian/servers/app-server/karaf/fuse63/pom.xml +++ b/testsuite/integration-arquillian/servers/app-server/karaf/fuse63/pom.xml @@ -21,7 +21,7 @@ org.keycloak.testsuite integration-arquillian-servers-app-server-karaf - 24.0.5-PS-1 + 24.0.5-PS-2 4.0.0 diff --git a/testsuite/integration-arquillian/servers/app-server/karaf/fuse7x/pom.xml b/testsuite/integration-arquillian/servers/app-server/karaf/fuse7x/pom.xml index e180e84ae06f..7a0e3517415c 100644 --- a/testsuite/integration-arquillian/servers/app-server/karaf/fuse7x/pom.xml +++ b/testsuite/integration-arquillian/servers/app-server/karaf/fuse7x/pom.xml @@ -21,7 +21,7 @@ org.keycloak.testsuite integration-arquillian-servers-app-server-karaf - 24.0.5-PS-1 + 24.0.5-PS-2 4.0.0 diff --git a/testsuite/integration-arquillian/servers/app-server/karaf/pom.xml b/testsuite/integration-arquillian/servers/app-server/karaf/pom.xml index 410632c521d7..0c843bb73220 100644 --- a/testsuite/integration-arquillian/servers/app-server/karaf/pom.xml +++ b/testsuite/integration-arquillian/servers/app-server/karaf/pom.xml @@ -21,7 +21,7 @@ org.keycloak.testsuite integration-arquillian-servers-app-server - 24.0.5-PS-1 + 24.0.5-PS-2 4.0.0 diff --git a/testsuite/integration-arquillian/servers/app-server/pom.xml b/testsuite/integration-arquillian/servers/app-server/pom.xml index 3ec76a11e299..9b21c3cde54a 100644 --- a/testsuite/integration-arquillian/servers/app-server/pom.xml +++ b/testsuite/integration-arquillian/servers/app-server/pom.xml @@ -21,7 +21,7 @@ org.keycloak.testsuite integration-arquillian-servers - 24.0.5-PS-1 + 24.0.5-PS-2 4.0.0 diff --git a/testsuite/integration-arquillian/servers/app-server/tomcat/common/pom.xml b/testsuite/integration-arquillian/servers/app-server/tomcat/common/pom.xml index 380bb92cbaf9..3c4b4918d58f 100644 --- a/testsuite/integration-arquillian/servers/app-server/tomcat/common/pom.xml +++ b/testsuite/integration-arquillian/servers/app-server/tomcat/common/pom.xml @@ -5,7 +5,7 @@ integration-arquillian-servers-app-server-tomcat org.keycloak.testsuite - 24.0.5-PS-1 + 24.0.5-PS-2 4.0.0 diff --git a/testsuite/integration-arquillian/servers/app-server/tomcat/pom.xml b/testsuite/integration-arquillian/servers/app-server/tomcat/pom.xml index 5bdd15f7bd54..39a627459510 100644 --- a/testsuite/integration-arquillian/servers/app-server/tomcat/pom.xml +++ b/testsuite/integration-arquillian/servers/app-server/tomcat/pom.xml @@ -21,7 +21,7 @@ org.keycloak.testsuite integration-arquillian-servers-app-server - 24.0.5-PS-1 + 24.0.5-PS-2 4.0.0 diff --git a/testsuite/integration-arquillian/servers/app-server/tomcat/tomcat8/pom.xml b/testsuite/integration-arquillian/servers/app-server/tomcat/tomcat8/pom.xml index da73f6d7b327..6161639d5cf8 100644 --- a/testsuite/integration-arquillian/servers/app-server/tomcat/tomcat8/pom.xml +++ b/testsuite/integration-arquillian/servers/app-server/tomcat/tomcat8/pom.xml @@ -21,7 +21,7 @@ org.keycloak.testsuite integration-arquillian-servers-app-server-tomcat - 24.0.5-PS-1 + 24.0.5-PS-2 4.0.0 diff --git a/testsuite/integration-arquillian/servers/app-server/tomcat/tomcat9/pom.xml b/testsuite/integration-arquillian/servers/app-server/tomcat/tomcat9/pom.xml index 098839d4ed61..2ea7daa7e350 100644 --- a/testsuite/integration-arquillian/servers/app-server/tomcat/tomcat9/pom.xml +++ b/testsuite/integration-arquillian/servers/app-server/tomcat/tomcat9/pom.xml @@ -21,7 +21,7 @@ org.keycloak.testsuite integration-arquillian-servers-app-server-tomcat - 24.0.5-PS-1 + 24.0.5-PS-2 4.0.0 diff --git a/testsuite/integration-arquillian/servers/app-server/undertow/pom.xml b/testsuite/integration-arquillian/servers/app-server/undertow/pom.xml index 211d83e769ac..47a51b23c8ba 100644 --- a/testsuite/integration-arquillian/servers/app-server/undertow/pom.xml +++ b/testsuite/integration-arquillian/servers/app-server/undertow/pom.xml @@ -18,7 +18,7 @@ org.keycloak.testsuite integration-arquillian-servers-app-server - 24.0.5-PS-1 + 24.0.5-PS-2 4.0.0 diff --git a/testsuite/integration-arquillian/servers/auth-server/pom.xml b/testsuite/integration-arquillian/servers/auth-server/pom.xml index 499c0f57793e..1f1bea543b92 100644 --- a/testsuite/integration-arquillian/servers/auth-server/pom.xml +++ b/testsuite/integration-arquillian/servers/auth-server/pom.xml @@ -21,7 +21,7 @@ org.keycloak.testsuite integration-arquillian-servers - 24.0.5-PS-1 + 24.0.5-PS-2 4.0.0 diff --git a/testsuite/integration-arquillian/servers/auth-server/quarkus/pom.xml b/testsuite/integration-arquillian/servers/auth-server/quarkus/pom.xml index 604ecb2841e2..897e2753b669 100644 --- a/testsuite/integration-arquillian/servers/auth-server/quarkus/pom.xml +++ b/testsuite/integration-arquillian/servers/auth-server/quarkus/pom.xml @@ -5,7 +5,7 @@ integration-arquillian-servers-auth-server org.keycloak.testsuite - 24.0.5-PS-1 + 24.0.5-PS-2 4.0.0 diff --git a/testsuite/integration-arquillian/servers/auth-server/services/pom.xml b/testsuite/integration-arquillian/servers/auth-server/services/pom.xml index 0ddbc2b020d3..2f44ef6dc36f 100644 --- a/testsuite/integration-arquillian/servers/auth-server/services/pom.xml +++ b/testsuite/integration-arquillian/servers/auth-server/services/pom.xml @@ -21,7 +21,7 @@ org.keycloak.testsuite integration-arquillian-servers-auth-server - 24.0.5-PS-1 + 24.0.5-PS-2 4.0.0 diff --git a/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers-deployment/pom.xml b/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers-deployment/pom.xml index 147a60f09042..966e1b125f8a 100644 --- a/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers-deployment/pom.xml +++ b/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers-deployment/pom.xml @@ -24,7 +24,7 @@ org.keycloak.testsuite integration-arquillian-servers-auth-server-services - 24.0.5-PS-1 + 24.0.5-PS-2 integration-arquillian-testsuite-providers-deployment diff --git a/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/pom.xml b/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/pom.xml index 62bf817f2ca6..02c8296927ee 100644 --- a/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/pom.xml +++ b/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/pom.xml @@ -24,7 +24,7 @@ org.keycloak.testsuite integration-arquillian-servers-auth-server-services - 24.0.5-PS-1 + 24.0.5-PS-2 integration-arquillian-testsuite-providers diff --git a/testsuite/integration-arquillian/servers/auth-server/undertow/pom.xml b/testsuite/integration-arquillian/servers/auth-server/undertow/pom.xml index 00b81e632bc9..f50e1df51c00 100644 --- a/testsuite/integration-arquillian/servers/auth-server/undertow/pom.xml +++ b/testsuite/integration-arquillian/servers/auth-server/undertow/pom.xml @@ -21,7 +21,7 @@ org.keycloak.testsuite integration-arquillian-servers-auth-server - 24.0.5-PS-1 + 24.0.5-PS-2 4.0.0 diff --git a/testsuite/integration-arquillian/servers/cache-server/infinispan/datagrid/pom.xml b/testsuite/integration-arquillian/servers/cache-server/infinispan/datagrid/pom.xml index e822c790586a..553c0f189b3b 100644 --- a/testsuite/integration-arquillian/servers/cache-server/infinispan/datagrid/pom.xml +++ b/testsuite/integration-arquillian/servers/cache-server/infinispan/datagrid/pom.xml @@ -21,7 +21,7 @@ org.keycloak.testsuite integration-arquillian-servers-cache-server-infinispan - 24.0.5-PS-1 + 24.0.5-PS-2 4.0.0 diff --git a/testsuite/integration-arquillian/servers/cache-server/infinispan/infinispan/pom.xml b/testsuite/integration-arquillian/servers/cache-server/infinispan/infinispan/pom.xml index 6ee8903007e6..0ebbcc317131 100644 --- a/testsuite/integration-arquillian/servers/cache-server/infinispan/infinispan/pom.xml +++ b/testsuite/integration-arquillian/servers/cache-server/infinispan/infinispan/pom.xml @@ -21,7 +21,7 @@ org.keycloak.testsuite integration-arquillian-servers-cache-server-infinispan - 24.0.5-PS-1 + 24.0.5-PS-2 4.0.0 diff --git a/testsuite/integration-arquillian/servers/cache-server/infinispan/pom.xml b/testsuite/integration-arquillian/servers/cache-server/infinispan/pom.xml index 2f59d194a0c2..8498b5f697de 100644 --- a/testsuite/integration-arquillian/servers/cache-server/infinispan/pom.xml +++ b/testsuite/integration-arquillian/servers/cache-server/infinispan/pom.xml @@ -24,7 +24,7 @@ org.keycloak.testsuite integration-arquillian-servers-cache-server - 24.0.5-PS-1 + 24.0.5-PS-2 pom diff --git a/testsuite/integration-arquillian/servers/cache-server/legacy/datagrid/pom.xml b/testsuite/integration-arquillian/servers/cache-server/legacy/datagrid/pom.xml index 7f9072d423c1..a822438c685f 100644 --- a/testsuite/integration-arquillian/servers/cache-server/legacy/datagrid/pom.xml +++ b/testsuite/integration-arquillian/servers/cache-server/legacy/datagrid/pom.xml @@ -21,7 +21,7 @@ org.keycloak.testsuite integration-arquillian-servers-cache-server-legacy - 24.0.5-PS-1 + 24.0.5-PS-2 4.0.0 diff --git a/testsuite/integration-arquillian/servers/cache-server/legacy/infinispan/pom.xml b/testsuite/integration-arquillian/servers/cache-server/legacy/infinispan/pom.xml index 7bf501712f8f..55592fdaf7e5 100644 --- a/testsuite/integration-arquillian/servers/cache-server/legacy/infinispan/pom.xml +++ b/testsuite/integration-arquillian/servers/cache-server/legacy/infinispan/pom.xml @@ -21,7 +21,7 @@ org.keycloak.testsuite integration-arquillian-servers-cache-server-legacy - 24.0.5-PS-1 + 24.0.5-PS-2 4.0.0 diff --git a/testsuite/integration-arquillian/servers/cache-server/legacy/pom.xml b/testsuite/integration-arquillian/servers/cache-server/legacy/pom.xml index 2fd91a250c8a..3b90020ff397 100644 --- a/testsuite/integration-arquillian/servers/cache-server/legacy/pom.xml +++ b/testsuite/integration-arquillian/servers/cache-server/legacy/pom.xml @@ -21,7 +21,7 @@ org.keycloak.testsuite integration-arquillian-servers-cache-server - 24.0.5-PS-1 + 24.0.5-PS-2 4.0.0 diff --git a/testsuite/integration-arquillian/servers/cache-server/pom.xml b/testsuite/integration-arquillian/servers/cache-server/pom.xml index 8a9006be6a5d..44665453e7cf 100644 --- a/testsuite/integration-arquillian/servers/cache-server/pom.xml +++ b/testsuite/integration-arquillian/servers/cache-server/pom.xml @@ -21,7 +21,7 @@ org.keycloak.testsuite integration-arquillian-servers - 24.0.5-PS-1 + 24.0.5-PS-2 4.0.0 diff --git a/testsuite/integration-arquillian/servers/migration/pom.xml b/testsuite/integration-arquillian/servers/migration/pom.xml index 265a37441a5a..d71fbd53c07b 100644 --- a/testsuite/integration-arquillian/servers/migration/pom.xml +++ b/testsuite/integration-arquillian/servers/migration/pom.xml @@ -21,7 +21,7 @@ org.keycloak.testsuite integration-arquillian-servers - 24.0.5-PS-1 + 24.0.5-PS-2 4.0.0 diff --git a/testsuite/integration-arquillian/servers/pom.xml b/testsuite/integration-arquillian/servers/pom.xml index dd162ed5d193..7ede4cc5515c 100644 --- a/testsuite/integration-arquillian/servers/pom.xml +++ b/testsuite/integration-arquillian/servers/pom.xml @@ -21,7 +21,7 @@ org.keycloak.testsuite integration-arquillian - 24.0.5-PS-1 + 24.0.5-PS-2 4.0.0 diff --git a/testsuite/integration-arquillian/test-apps/app-profile-jee/pom.xml b/testsuite/integration-arquillian/test-apps/app-profile-jee/pom.xml index 85927416956a..0e3188904c34 100644 --- a/testsuite/integration-arquillian/test-apps/app-profile-jee/pom.xml +++ b/testsuite/integration-arquillian/test-apps/app-profile-jee/pom.xml @@ -5,7 +5,7 @@ org.keycloak.testsuite integration-arquillian-test-apps - 24.0.5-PS-1 + 24.0.5-PS-2 keycloak-test-app-profile-jee diff --git a/testsuite/integration-arquillian/test-apps/cors/angular-product/pom.xml b/testsuite/integration-arquillian/test-apps/cors/angular-product/pom.xml index 91c4b7f3f48f..ab7f6d923e20 100755 --- a/testsuite/integration-arquillian/test-apps/cors/angular-product/pom.xml +++ b/testsuite/integration-arquillian/test-apps/cors/angular-product/pom.xml @@ -21,7 +21,7 @@ org.keycloak.testsuite integration-arquillian-test-apps-cors-parent - 24.0.5-PS-1 + 24.0.5-PS-2 4.0.0 diff --git a/testsuite/integration-arquillian/test-apps/cors/database-service/pom.xml b/testsuite/integration-arquillian/test-apps/cors/database-service/pom.xml index dbb2a47e872b..faa6fc2b4513 100755 --- a/testsuite/integration-arquillian/test-apps/cors/database-service/pom.xml +++ b/testsuite/integration-arquillian/test-apps/cors/database-service/pom.xml @@ -21,7 +21,7 @@ org.keycloak.testsuite integration-arquillian-test-apps-cors-parent - 24.0.5-PS-1 + 24.0.5-PS-2 4.0.0 diff --git a/testsuite/integration-arquillian/test-apps/cors/pom.xml b/testsuite/integration-arquillian/test-apps/cors/pom.xml index 05f9aefce704..667c5a1a61bc 100644 --- a/testsuite/integration-arquillian/test-apps/cors/pom.xml +++ b/testsuite/integration-arquillian/test-apps/cors/pom.xml @@ -5,7 +5,7 @@ integration-arquillian-test-apps org.keycloak.testsuite - 24.0.5-PS-1 + 24.0.5-PS-2 4.0.0 diff --git a/testsuite/integration-arquillian/test-apps/fuse/camel-fuse7-undertow/pom.xml b/testsuite/integration-arquillian/test-apps/fuse/camel-fuse7-undertow/pom.xml index 0cecbbab6c76..9fa6374fedff 100755 --- a/testsuite/integration-arquillian/test-apps/fuse/camel-fuse7-undertow/pom.xml +++ b/testsuite/integration-arquillian/test-apps/fuse/camel-fuse7-undertow/pom.xml @@ -21,7 +21,7 @@ integration-arquillian-test-apps-fuse-parent org.keycloak.testsuite - 24.0.5-PS-1 + 24.0.5-PS-2 4.0.0 diff --git a/testsuite/integration-arquillian/test-apps/fuse/camel/pom.xml b/testsuite/integration-arquillian/test-apps/fuse/camel/pom.xml index f60a61293bb9..2f2514f8bde2 100755 --- a/testsuite/integration-arquillian/test-apps/fuse/camel/pom.xml +++ b/testsuite/integration-arquillian/test-apps/fuse/camel/pom.xml @@ -21,7 +21,7 @@ integration-arquillian-test-apps-fuse-parent org.keycloak.testsuite - 24.0.5-PS-1 + 24.0.5-PS-2 4.0.0 diff --git a/testsuite/integration-arquillian/test-apps/fuse/customer-app-fuse/pom.xml b/testsuite/integration-arquillian/test-apps/fuse/customer-app-fuse/pom.xml index 0135b2303116..1908c4954c6a 100755 --- a/testsuite/integration-arquillian/test-apps/fuse/customer-app-fuse/pom.xml +++ b/testsuite/integration-arquillian/test-apps/fuse/customer-app-fuse/pom.xml @@ -21,7 +21,7 @@ integration-arquillian-test-apps-fuse-parent org.keycloak.testsuite - 24.0.5-PS-1 + 24.0.5-PS-2 4.0.0 diff --git a/testsuite/integration-arquillian/test-apps/fuse/cxf-jaxrs-fuse7-undertow/pom.xml b/testsuite/integration-arquillian/test-apps/fuse/cxf-jaxrs-fuse7-undertow/pom.xml index 0ec99ef885f1..42c7a4de22f7 100755 --- a/testsuite/integration-arquillian/test-apps/fuse/cxf-jaxrs-fuse7-undertow/pom.xml +++ b/testsuite/integration-arquillian/test-apps/fuse/cxf-jaxrs-fuse7-undertow/pom.xml @@ -21,7 +21,7 @@ integration-arquillian-test-apps-fuse-parent org.keycloak.testsuite - 24.0.5-PS-1 + 24.0.5-PS-2 4.0.0 diff --git a/testsuite/integration-arquillian/test-apps/fuse/cxf-jaxrs/pom.xml b/testsuite/integration-arquillian/test-apps/fuse/cxf-jaxrs/pom.xml index 079078f0c85b..ef5eae77e9f8 100755 --- a/testsuite/integration-arquillian/test-apps/fuse/cxf-jaxrs/pom.xml +++ b/testsuite/integration-arquillian/test-apps/fuse/cxf-jaxrs/pom.xml @@ -21,7 +21,7 @@ integration-arquillian-test-apps-fuse-parent org.keycloak.testsuite - 24.0.5-PS-1 + 24.0.5-PS-2 4.0.0 diff --git a/testsuite/integration-arquillian/test-apps/fuse/cxf-jaxws-fuse7-undertow/pom.xml b/testsuite/integration-arquillian/test-apps/fuse/cxf-jaxws-fuse7-undertow/pom.xml index 323ede236f35..8f6782bdd193 100755 --- a/testsuite/integration-arquillian/test-apps/fuse/cxf-jaxws-fuse7-undertow/pom.xml +++ b/testsuite/integration-arquillian/test-apps/fuse/cxf-jaxws-fuse7-undertow/pom.xml @@ -21,7 +21,7 @@ integration-arquillian-test-apps-fuse-parent org.keycloak.testsuite - 24.0.5-PS-1 + 24.0.5-PS-2 4.0.0 diff --git a/testsuite/integration-arquillian/test-apps/fuse/cxf-jaxws/pom.xml b/testsuite/integration-arquillian/test-apps/fuse/cxf-jaxws/pom.xml index 259e22168ac0..1fdf85e19e44 100755 --- a/testsuite/integration-arquillian/test-apps/fuse/cxf-jaxws/pom.xml +++ b/testsuite/integration-arquillian/test-apps/fuse/cxf-jaxws/pom.xml @@ -21,7 +21,7 @@ integration-arquillian-test-apps-fuse-parent org.keycloak.testsuite - 24.0.5-PS-1 + 24.0.5-PS-2 4.0.0 diff --git a/testsuite/integration-arquillian/test-apps/fuse/external-config/pom.xml b/testsuite/integration-arquillian/test-apps/fuse/external-config/pom.xml index 981e24fae897..72da7ac6c39f 100755 --- a/testsuite/integration-arquillian/test-apps/fuse/external-config/pom.xml +++ b/testsuite/integration-arquillian/test-apps/fuse/external-config/pom.xml @@ -21,7 +21,7 @@ integration-arquillian-test-apps-fuse-parent org.keycloak.testsuite - 24.0.5-PS-1 + 24.0.5-PS-2 Keycloak Examples - External Config diff --git a/testsuite/integration-arquillian/test-apps/fuse/features/pom.xml b/testsuite/integration-arquillian/test-apps/fuse/features/pom.xml index 1c5be3c8a642..587e75782c83 100755 --- a/testsuite/integration-arquillian/test-apps/fuse/features/pom.xml +++ b/testsuite/integration-arquillian/test-apps/fuse/features/pom.xml @@ -21,7 +21,7 @@ integration-arquillian-test-apps-fuse-parent org.keycloak.testsuite - 24.0.5-PS-1 + 24.0.5-PS-2 4.0.0 diff --git a/testsuite/integration-arquillian/test-apps/fuse/pom.xml b/testsuite/integration-arquillian/test-apps/fuse/pom.xml index cda3dcec5a1f..2ea0d335f235 100755 --- a/testsuite/integration-arquillian/test-apps/fuse/pom.xml +++ b/testsuite/integration-arquillian/test-apps/fuse/pom.xml @@ -20,7 +20,7 @@ integration-arquillian-test-apps org.keycloak.testsuite - 24.0.5-PS-1 + 24.0.5-PS-2 Fuse Test Applications diff --git a/testsuite/integration-arquillian/test-apps/fuse/product-app-fuse/pom.xml b/testsuite/integration-arquillian/test-apps/fuse/product-app-fuse/pom.xml index d5985d6a28c3..ff127a4d34e6 100755 --- a/testsuite/integration-arquillian/test-apps/fuse/product-app-fuse/pom.xml +++ b/testsuite/integration-arquillian/test-apps/fuse/product-app-fuse/pom.xml @@ -21,7 +21,7 @@ integration-arquillian-test-apps-fuse-parent org.keycloak.testsuite - 24.0.5-PS-1 + 24.0.5-PS-2 4.0.0 diff --git a/testsuite/integration-arquillian/test-apps/fuse/product-app-fuse7-undertow/pom.xml b/testsuite/integration-arquillian/test-apps/fuse/product-app-fuse7-undertow/pom.xml index eaf9f0f33783..8cf4e1de02d7 100755 --- a/testsuite/integration-arquillian/test-apps/fuse/product-app-fuse7-undertow/pom.xml +++ b/testsuite/integration-arquillian/test-apps/fuse/product-app-fuse7-undertow/pom.xml @@ -21,7 +21,7 @@ integration-arquillian-test-apps-fuse-parent org.keycloak.testsuite - 24.0.5-PS-1 + 24.0.5-PS-2 4.0.0 diff --git a/testsuite/integration-arquillian/test-apps/hello-world-authz-service/pom.xml b/testsuite/integration-arquillian/test-apps/hello-world-authz-service/pom.xml index 22395354667b..e778184efb72 100755 --- a/testsuite/integration-arquillian/test-apps/hello-world-authz-service/pom.xml +++ b/testsuite/integration-arquillian/test-apps/hello-world-authz-service/pom.xml @@ -24,7 +24,7 @@ org.keycloak.testsuite integration-arquillian-test-apps - 24.0.5-PS-1 + 24.0.5-PS-2 hello-world-authz-service diff --git a/testsuite/integration-arquillian/test-apps/pom.xml b/testsuite/integration-arquillian/test-apps/pom.xml index 952d26ef7b2c..e1f71bbaf67a 100644 --- a/testsuite/integration-arquillian/test-apps/pom.xml +++ b/testsuite/integration-arquillian/test-apps/pom.xml @@ -5,7 +5,7 @@ integration-arquillian org.keycloak.testsuite - 24.0.5-PS-1 + 24.0.5-PS-2 4.0.0 diff --git a/testsuite/integration-arquillian/test-apps/servlet-authz/pom.xml b/testsuite/integration-arquillian/test-apps/servlet-authz/pom.xml index 71dc7a2a052b..c244e5743ae3 100755 --- a/testsuite/integration-arquillian/test-apps/servlet-authz/pom.xml +++ b/testsuite/integration-arquillian/test-apps/servlet-authz/pom.xml @@ -6,7 +6,7 @@ org.keycloak.testsuite integration-arquillian-test-apps - 24.0.5-PS-1 + 24.0.5-PS-2 servlet-authz-app diff --git a/testsuite/integration-arquillian/test-apps/servlet-policy-enforcer/pom.xml b/testsuite/integration-arquillian/test-apps/servlet-policy-enforcer/pom.xml index 1459785b7d90..7ad5c4668231 100755 --- a/testsuite/integration-arquillian/test-apps/servlet-policy-enforcer/pom.xml +++ b/testsuite/integration-arquillian/test-apps/servlet-policy-enforcer/pom.xml @@ -24,7 +24,7 @@ org.keycloak.testsuite integration-arquillian-test-apps - 24.0.5-PS-1 + 24.0.5-PS-2 servlet-policy-enforcer diff --git a/testsuite/integration-arquillian/test-apps/servlets-jakarta/pom.xml b/testsuite/integration-arquillian/test-apps/servlets-jakarta/pom.xml index 454106bdca31..a5e767847869 100644 --- a/testsuite/integration-arquillian/test-apps/servlets-jakarta/pom.xml +++ b/testsuite/integration-arquillian/test-apps/servlets-jakarta/pom.xml @@ -6,7 +6,7 @@ org.keycloak.testsuite integration-arquillian-test-apps - 24.0.5-PS-1 + 24.0.5-PS-2 integration-arquillian-test-apps-servlets-jakarta diff --git a/testsuite/integration-arquillian/test-apps/servlets/pom.xml b/testsuite/integration-arquillian/test-apps/servlets/pom.xml index b225f73b7528..d1f6e0ee9223 100644 --- a/testsuite/integration-arquillian/test-apps/servlets/pom.xml +++ b/testsuite/integration-arquillian/test-apps/servlets/pom.xml @@ -5,7 +5,7 @@ integration-arquillian-test-apps org.keycloak.testsuite - 24.0.5-PS-1 + 24.0.5-PS-2 4.0.0 diff --git a/testsuite/integration-arquillian/test-apps/spring-boot-adapter-app/pom.xml b/testsuite/integration-arquillian/test-apps/spring-boot-adapter-app/pom.xml index 8e5f2e6f7d46..8bbfe25843ab 100644 --- a/testsuite/integration-arquillian/test-apps/spring-boot-adapter-app/pom.xml +++ b/testsuite/integration-arquillian/test-apps/spring-boot-adapter-app/pom.xml @@ -5,7 +5,7 @@ org.keycloak.testsuite integration-arquillian-test-apps - 24.0.5-PS-1 + 24.0.5-PS-2 4.0.0 diff --git a/testsuite/integration-arquillian/test-apps/test-apps-dist/pom.xml b/testsuite/integration-arquillian/test-apps/test-apps-dist/pom.xml index a810f11d392b..660ca20e0200 100644 --- a/testsuite/integration-arquillian/test-apps/test-apps-dist/pom.xml +++ b/testsuite/integration-arquillian/test-apps/test-apps-dist/pom.xml @@ -5,7 +5,7 @@ integration-arquillian-test-apps org.keycloak.testsuite - 24.0.5-PS-1 + 24.0.5-PS-2 4.0.0 diff --git a/testsuite/integration-arquillian/tests/base/pom.xml b/testsuite/integration-arquillian/tests/base/pom.xml index ddd05df6b0d2..4fab313a7373 100644 --- a/testsuite/integration-arquillian/tests/base/pom.xml +++ b/testsuite/integration-arquillian/tests/base/pom.xml @@ -21,7 +21,7 @@ org.keycloak.testsuite integration-arquillian-tests - 24.0.5-PS-1 + 24.0.5-PS-2 4.0.0 diff --git a/testsuite/integration-arquillian/tests/other/adapters/jboss/pom.xml b/testsuite/integration-arquillian/tests/other/adapters/jboss/pom.xml index 21e6410c7062..b47e9803dcf4 100644 --- a/testsuite/integration-arquillian/tests/other/adapters/jboss/pom.xml +++ b/testsuite/integration-arquillian/tests/other/adapters/jboss/pom.xml @@ -24,7 +24,7 @@ org.keycloak.testsuite integration-arquillian-tests-adapters - 24.0.5-PS-1 + 24.0.5-PS-2 integration-arquillian-tests-adapters-jboss diff --git a/testsuite/integration-arquillian/tests/other/adapters/karaf/fuse61/pom.xml b/testsuite/integration-arquillian/tests/other/adapters/karaf/fuse61/pom.xml index f06b4d8792ef..80fe9cf9a07d 100644 --- a/testsuite/integration-arquillian/tests/other/adapters/karaf/fuse61/pom.xml +++ b/testsuite/integration-arquillian/tests/other/adapters/karaf/fuse61/pom.xml @@ -24,7 +24,7 @@ org.keycloak.testsuite integration-arquillian-tests-adapters-karaf - 24.0.5-PS-1 + 24.0.5-PS-2 integration-arquillian-tests-adapters-fuse61 diff --git a/testsuite/integration-arquillian/tests/other/adapters/karaf/fuse62/pom.xml b/testsuite/integration-arquillian/tests/other/adapters/karaf/fuse62/pom.xml index c06f566a7f7c..57604f50e6b3 100644 --- a/testsuite/integration-arquillian/tests/other/adapters/karaf/fuse62/pom.xml +++ b/testsuite/integration-arquillian/tests/other/adapters/karaf/fuse62/pom.xml @@ -24,7 +24,7 @@ org.keycloak.testsuite integration-arquillian-tests-adapters-karaf - 24.0.5-PS-1 + 24.0.5-PS-2 integration-arquillian-tests-adapters-fuse62 diff --git a/testsuite/integration-arquillian/tests/other/adapters/karaf/karaf3/pom.xml b/testsuite/integration-arquillian/tests/other/adapters/karaf/karaf3/pom.xml index 495d88c660d5..db79a7fbf078 100644 --- a/testsuite/integration-arquillian/tests/other/adapters/karaf/karaf3/pom.xml +++ b/testsuite/integration-arquillian/tests/other/adapters/karaf/karaf3/pom.xml @@ -24,7 +24,7 @@ org.keycloak.testsuite integration-arquillian-tests-adapters-karaf - 24.0.5-PS-1 + 24.0.5-PS-2 integration-arquillian-tests-adapters-karaf3 diff --git a/testsuite/integration-arquillian/tests/other/adapters/karaf/pom.xml b/testsuite/integration-arquillian/tests/other/adapters/karaf/pom.xml index b3745945e150..c9158f4620a3 100644 --- a/testsuite/integration-arquillian/tests/other/adapters/karaf/pom.xml +++ b/testsuite/integration-arquillian/tests/other/adapters/karaf/pom.xml @@ -24,7 +24,7 @@ org.keycloak.testsuite integration-arquillian-tests-adapters - 24.0.5-PS-1 + 24.0.5-PS-2 integration-arquillian-tests-adapters-karaf diff --git a/testsuite/integration-arquillian/tests/other/adapters/pom.xml b/testsuite/integration-arquillian/tests/other/adapters/pom.xml index 3c31f2f8cc41..74daa0e7a5c5 100644 --- a/testsuite/integration-arquillian/tests/other/adapters/pom.xml +++ b/testsuite/integration-arquillian/tests/other/adapters/pom.xml @@ -24,7 +24,7 @@ org.keycloak.testsuite integration-arquillian-tests-other - 24.0.5-PS-1 + 24.0.5-PS-2 integration-arquillian-tests-adapters diff --git a/testsuite/integration-arquillian/tests/other/adapters/was/pom.xml b/testsuite/integration-arquillian/tests/other/adapters/was/pom.xml index 2483cb7637f0..ee461a99b593 100644 --- a/testsuite/integration-arquillian/tests/other/adapters/was/pom.xml +++ b/testsuite/integration-arquillian/tests/other/adapters/was/pom.xml @@ -24,7 +24,7 @@ org.keycloak.testsuite integration-arquillian-tests-adapters - 24.0.5-PS-1 + 24.0.5-PS-2 integration-arquillian-tests-adapters-was diff --git a/testsuite/integration-arquillian/tests/other/adapters/was/was8/pom.xml b/testsuite/integration-arquillian/tests/other/adapters/was/was8/pom.xml index bac518fe73a6..1ce769f028ac 100644 --- a/testsuite/integration-arquillian/tests/other/adapters/was/was8/pom.xml +++ b/testsuite/integration-arquillian/tests/other/adapters/was/was8/pom.xml @@ -24,7 +24,7 @@ org.keycloak.testsuite integration-arquillian-tests-adapters-was - 24.0.5-PS-1 + 24.0.5-PS-2 integration-arquillian-tests-adapters-was8 diff --git a/testsuite/integration-arquillian/tests/other/adapters/wls/pom.xml b/testsuite/integration-arquillian/tests/other/adapters/wls/pom.xml index f8159ca1f2eb..9bb61e946938 100644 --- a/testsuite/integration-arquillian/tests/other/adapters/wls/pom.xml +++ b/testsuite/integration-arquillian/tests/other/adapters/wls/pom.xml @@ -24,7 +24,7 @@ org.keycloak.testsuite integration-arquillian-tests-adapters - 24.0.5-PS-1 + 24.0.5-PS-2 integration-arquillian-tests-adapters-wls diff --git a/testsuite/integration-arquillian/tests/other/adapters/wls/wls12/pom.xml b/testsuite/integration-arquillian/tests/other/adapters/wls/wls12/pom.xml index a9e97013d9c7..1717f9bef0b4 100644 --- a/testsuite/integration-arquillian/tests/other/adapters/wls/wls12/pom.xml +++ b/testsuite/integration-arquillian/tests/other/adapters/wls/wls12/pom.xml @@ -24,7 +24,7 @@ org.keycloak.testsuite integration-arquillian-tests-adapters-wls - 24.0.5-PS-1 + 24.0.5-PS-2 integration-arquillian-tests-adapters-wls12 diff --git a/testsuite/integration-arquillian/tests/other/base-ui/pom.xml b/testsuite/integration-arquillian/tests/other/base-ui/pom.xml index 7f76d232f81d..98d44e654363 100644 --- a/testsuite/integration-arquillian/tests/other/base-ui/pom.xml +++ b/testsuite/integration-arquillian/tests/other/base-ui/pom.xml @@ -22,7 +22,7 @@ integration-arquillian-tests-other org.keycloak.testsuite - 24.0.5-PS-1 + 24.0.5-PS-2 4.0.0 diff --git a/testsuite/integration-arquillian/tests/other/jpa-performance/pom.xml b/testsuite/integration-arquillian/tests/other/jpa-performance/pom.xml index ebfb045f1ba2..44b71021c76b 100644 --- a/testsuite/integration-arquillian/tests/other/jpa-performance/pom.xml +++ b/testsuite/integration-arquillian/tests/other/jpa-performance/pom.xml @@ -24,7 +24,7 @@ org.keycloak.testsuite integration-arquillian-tests-other - 24.0.5-PS-1 + 24.0.5-PS-2 integration-arquillian-tests-jpa-performance diff --git a/testsuite/integration-arquillian/tests/other/mod_auth_mellon/pom.xml b/testsuite/integration-arquillian/tests/other/mod_auth_mellon/pom.xml index 440c792e6f57..a9aaf0d8992e 100644 --- a/testsuite/integration-arquillian/tests/other/mod_auth_mellon/pom.xml +++ b/testsuite/integration-arquillian/tests/other/mod_auth_mellon/pom.xml @@ -24,7 +24,7 @@ org.keycloak.testsuite integration-arquillian-tests-other - 24.0.5-PS-1 + 24.0.5-PS-2 integration-arquillian-tests-other-mod_auth_mellon diff --git a/testsuite/integration-arquillian/tests/other/pom.xml b/testsuite/integration-arquillian/tests/other/pom.xml index 18d3be8147a9..e576f668eb81 100644 --- a/testsuite/integration-arquillian/tests/other/pom.xml +++ b/testsuite/integration-arquillian/tests/other/pom.xml @@ -24,7 +24,7 @@ org.keycloak.testsuite integration-arquillian-tests - 24.0.5-PS-1 + 24.0.5-PS-2 integration-arquillian-tests-other diff --git a/testsuite/integration-arquillian/tests/other/springboot-tests/pom.xml b/testsuite/integration-arquillian/tests/other/springboot-tests/pom.xml index 76054d40d912..bb00ea6281b7 100644 --- a/testsuite/integration-arquillian/tests/other/springboot-tests/pom.xml +++ b/testsuite/integration-arquillian/tests/other/springboot-tests/pom.xml @@ -5,7 +5,7 @@ integration-arquillian-tests-other org.keycloak.testsuite - 24.0.5-PS-1 + 24.0.5-PS-2 4.0.0 diff --git a/testsuite/integration-arquillian/tests/other/sssd/pom.xml b/testsuite/integration-arquillian/tests/other/sssd/pom.xml index b2a40c51c281..3df92b7bdb54 100644 --- a/testsuite/integration-arquillian/tests/other/sssd/pom.xml +++ b/testsuite/integration-arquillian/tests/other/sssd/pom.xml @@ -5,7 +5,7 @@ integration-arquillian-tests-other org.keycloak.testsuite - 24.0.5-PS-1 + 24.0.5-PS-2 4.0.0 diff --git a/testsuite/integration-arquillian/tests/other/webauthn/pom.xml b/testsuite/integration-arquillian/tests/other/webauthn/pom.xml index 28b97a693f2a..6dd1dceb966e 100644 --- a/testsuite/integration-arquillian/tests/other/webauthn/pom.xml +++ b/testsuite/integration-arquillian/tests/other/webauthn/pom.xml @@ -5,7 +5,7 @@ org.keycloak.testsuite integration-arquillian-tests-other - 24.0.5-PS-1 + 24.0.5-PS-2 4.0.0 jar diff --git a/testsuite/integration-arquillian/tests/pom.xml b/testsuite/integration-arquillian/tests/pom.xml index 00c42162dad7..141e6e8aec7c 100644 --- a/testsuite/integration-arquillian/tests/pom.xml +++ b/testsuite/integration-arquillian/tests/pom.xml @@ -24,7 +24,7 @@ org.keycloak.testsuite integration-arquillian - 24.0.5-PS-1 + 24.0.5-PS-2 pom diff --git a/testsuite/integration-arquillian/util/pom.xml b/testsuite/integration-arquillian/util/pom.xml index 0ce76606d94e..82d4ad580d2c 100644 --- a/testsuite/integration-arquillian/util/pom.xml +++ b/testsuite/integration-arquillian/util/pom.xml @@ -21,7 +21,7 @@ org.keycloak.testsuite integration-arquillian - 24.0.5-PS-1 + 24.0.5-PS-2 4.0.0 diff --git a/testsuite/model/pom.xml b/testsuite/model/pom.xml index b13c91709653..c4bd1fd582ec 100644 --- a/testsuite/model/pom.xml +++ b/testsuite/model/pom.xml @@ -4,7 +4,7 @@ org.keycloak keycloak-testsuite-pom - 24.0.5-PS-1 + 24.0.5-PS-2 ../pom.xml diff --git a/testsuite/pom.xml b/testsuite/pom.xml index c95cc2b1d750..a91cf883eace 100755 --- a/testsuite/pom.xml +++ b/testsuite/pom.xml @@ -21,7 +21,7 @@ keycloak-parent org.keycloak - 24.0.5-PS-1 + 24.0.5-PS-2 ../pom.xml 4.0.0 diff --git a/testsuite/utils/pom.xml b/testsuite/utils/pom.xml index fd9ae5ed32f9..dfe3aa1e76d2 100755 --- a/testsuite/utils/pom.xml +++ b/testsuite/utils/pom.xml @@ -21,7 +21,7 @@ keycloak-testsuite-pom org.keycloak - 24.0.5-PS-1 + 24.0.5-PS-2 4.0.0 diff --git a/themes/pom.xml b/themes/pom.xml index 45c8397ac188..5f8d605c32b0 100755 --- a/themes/pom.xml +++ b/themes/pom.xml @@ -3,7 +3,7 @@ keycloak-parent org.keycloak - 24.0.5-PS-1 + 24.0.5-PS-2 4.0.0 diff --git a/util/embedded-ldap/pom.xml b/util/embedded-ldap/pom.xml index 72dca921ebf2..f5973f2af788 100644 --- a/util/embedded-ldap/pom.xml +++ b/util/embedded-ldap/pom.xml @@ -21,7 +21,7 @@ keycloak-parent org.keycloak - 24.0.5-PS-1 + 24.0.5-PS-2 ../../pom.xml 4.0.0 diff --git a/util/pom.xml b/util/pom.xml index 270216d8f16f..00e9d8c3e874 100644 --- a/util/pom.xml +++ b/util/pom.xml @@ -20,7 +20,7 @@ keycloak-parent org.keycloak - 24.0.5-PS-1 + 24.0.5-PS-2 ../pom.xml From 2bc6404214e5dac6cc729a8547c19d1184d2369f Mon Sep 17 00:00:00 2001 From: Manuel Schallar Date: Tue, 8 Oct 2024 14:44:35 +0200 Subject: [PATCH 158/158] Use optional realm attribute for request param max size/number Enable fail-fast toggle for additional request parameter parsing Enable configuration of an overall size of additional request parameters Everything is backwardscompatible. No configuration necessary when upgrading. Signed-off-by: Manuel Schallar --- services/pom.xml | 10 + ...izationEndpointRequestParserProcessor.java | 2 +- .../AuthzEndpointQueryStringParser.java | 4 +- .../AuthzEndpointRequestObjectParser.java | 1 + .../request/AuthzEndpointRequestParser.java | 144 ++++++-- ...thenticationEndpointRequestBodyParser.java | 4 +- ...elAuthenticationEndpointRequestParser.java | 20 +- ...icationEndpointRequestParserProcessor.java | 2 +- ...enticationEndpointSignedRequestParser.java | 1 + .../request/AuthzEndpointParParser.java | 1 + .../ParEndpointRequestParserProcessor.java | 2 +- .../AuthzEndpointRequestParserTest.java | 34 +- .../authz/AuthzEndpointRequestParserTest.java | 348 ++++++++++++++++++ 13 files changed, 539 insertions(+), 34 deletions(-) create mode 100644 testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/authz/AuthzEndpointRequestParserTest.java diff --git a/services/pom.xml b/services/pom.xml index 814bc030f110..1e988e0694ac 100755 --- a/services/pom.xml +++ b/services/pom.xml @@ -228,6 +228,16 @@ org.keycloak keycloak-model-storage-private + + org.mockito + mockito-core + test + + + org.mockito + mockito-core + test + diff --git a/services/src/main/java/org/keycloak/protocol/oidc/endpoints/request/AuthorizationEndpointRequestParserProcessor.java b/services/src/main/java/org/keycloak/protocol/oidc/endpoints/request/AuthorizationEndpointRequestParserProcessor.java index 929d64027fdc..fa745cd3c237 100644 --- a/services/src/main/java/org/keycloak/protocol/oidc/endpoints/request/AuthorizationEndpointRequestParserProcessor.java +++ b/services/src/main/java/org/keycloak/protocol/oidc/endpoints/request/AuthorizationEndpointRequestParserProcessor.java @@ -54,7 +54,7 @@ public static AuthorizationEndpointRequest parseRequest(EventBuilder event, Keyc try { AuthorizationEndpointRequest request = new AuthorizationEndpointRequest(); boolean isResponseTypeParameterRequired = isResponseTypeParameterRequired(requestParams, endpointType); - AuthzEndpointQueryStringParser parser = new AuthzEndpointQueryStringParser(requestParams, isResponseTypeParameterRequired); + AuthzEndpointQueryStringParser parser = new AuthzEndpointQueryStringParser(session, requestParams, isResponseTypeParameterRequired); parser.parseRequest(request); if (parser.getInvalidRequestMessage() != null) { diff --git a/services/src/main/java/org/keycloak/protocol/oidc/endpoints/request/AuthzEndpointQueryStringParser.java b/services/src/main/java/org/keycloak/protocol/oidc/endpoints/request/AuthzEndpointQueryStringParser.java index 3cb65424fada..120f7c6e22da 100644 --- a/services/src/main/java/org/keycloak/protocol/oidc/endpoints/request/AuthzEndpointQueryStringParser.java +++ b/services/src/main/java/org/keycloak/protocol/oidc/endpoints/request/AuthzEndpointQueryStringParser.java @@ -22,6 +22,7 @@ import java.util.Set; import org.jboss.logging.Logger; +import org.keycloak.models.KeycloakSession; import org.keycloak.protocol.oidc.OIDCLoginProtocol; /** @@ -39,7 +40,8 @@ public class AuthzEndpointQueryStringParser extends AuthzEndpointRequestParser { private String invalidRequestMessage = null; - public AuthzEndpointQueryStringParser(MultivaluedMap requestParams, boolean isResponseTypeParameterRequired) { + public AuthzEndpointQueryStringParser(KeycloakSession keycloakSession, MultivaluedMap requestParams, boolean isResponseTypeParameterRequired) { + super(keycloakSession); this.requestParams = requestParams; this.isResponseTypeParameterRequired = isResponseTypeParameterRequired; } diff --git a/services/src/main/java/org/keycloak/protocol/oidc/endpoints/request/AuthzEndpointRequestObjectParser.java b/services/src/main/java/org/keycloak/protocol/oidc/endpoints/request/AuthzEndpointRequestObjectParser.java index 584230fba67f..c9ea65134b6c 100644 --- a/services/src/main/java/org/keycloak/protocol/oidc/endpoints/request/AuthzEndpointRequestObjectParser.java +++ b/services/src/main/java/org/keycloak/protocol/oidc/endpoints/request/AuthzEndpointRequestObjectParser.java @@ -41,6 +41,7 @@ public class AuthzEndpointRequestObjectParser extends AuthzEndpointRequestParser private final JsonNode requestParams; public AuthzEndpointRequestObjectParser(KeycloakSession session, String requestObject, ClientModel client) { + super(session); this.requestParams = session.tokens().decodeClientJWT(requestObject, client, createRequestObjectValidator(session), JsonNode.class); if (this.requestParams == null) { diff --git a/services/src/main/java/org/keycloak/protocol/oidc/endpoints/request/AuthzEndpointRequestParser.java b/services/src/main/java/org/keycloak/protocol/oidc/endpoints/request/AuthzEndpointRequestParser.java index 2739d3828f2a..ab7c87c2e82e 100644 --- a/services/src/main/java/org/keycloak/protocol/oidc/endpoints/request/AuthzEndpointRequestParser.java +++ b/services/src/main/java/org/keycloak/protocol/oidc/endpoints/request/AuthzEndpointRequestParser.java @@ -19,15 +19,41 @@ import org.jboss.logging.Logger; import org.keycloak.OAuth2Constants; +import org.keycloak.OAuthErrorException; import org.keycloak.constants.AdapterConstants; import org.keycloak.models.Constants; +import org.keycloak.models.KeycloakSession; +import org.keycloak.models.RealmModel; import org.keycloak.protocol.oidc.OIDCLoginProtocol; +import org.keycloak.services.ErrorResponseException; + +import jakarta.ws.rs.core.Response; import java.util.HashSet; import java.util.Map; import java.util.Set; /** + * This endpoint parser supports, per default, up to + * {@value #DEFAULT_ADDITIONAL_REQ_PARAMS_MAX_MUMBER} parameters with each + * having a total size of {@value #DEFAULT_ADDITIONAL_REQ_PARAMS_MAX_SIZE}. If + * there are more authentication request parameters, or a parameter has a size + * than allowed, those parameters are silently ignored. + *

    + * You can toggle the behavior by setting a realm specific attribute + * ({@code additionalReqParamsFailFast}) that enables the fail-fast principle. + * Any request parameter in violation of the configuration results in an + * error response, e.g., + *

      + *
    • for a Pushed Authorization Request (PAR) this results in a JSON response.
    • + *
    • For openid/auth in an error page with an "Back to Application" button using the client's base URL. (if valid) as redirect target.
    • + *
    + * + *

    + * Additionally a realm specific attribute ({@code additionalReqParamMaxOverallSize}) can be configured + * that sets the maximum of size of all parameters combined. If not provided, {@link Integer#MAX_VALUE} will be used. + * + * @author Manuel Schallar * @author Marek Posolda */ public abstract class AuthzEndpointRequestParser { @@ -35,16 +61,47 @@ public abstract class AuthzEndpointRequestParser { private static final Logger logger = Logger.getLogger(AuthzEndpointRequestParser.class); /** - * Max number of additional req params copied into client session note to prevent DoS attacks - * + * Default value for {@link #additionalReqParamsMaxNumber} if case no realm property is set. */ - public static final int ADDITIONAL_REQ_PARAMS_MAX_MUMBER = 10; + public static final int DEFAULT_ADDITIONAL_REQ_PARAMS_MAX_MUMBER = 10; + + /** + * Max number of additional request parameters copied into client session note to prevent DoS attacks. + */ + protected final int additionalReqParamsMaxNumber; + + /** + * Default value for {@link #additionalReqParamsMaxSize} if case no realm property is set. + */ + public static final int DEFAULT_ADDITIONAL_REQ_PARAMS_MAX_SIZE = 2000; + + /** + * Max size of additional request parameters value copied into client session note to prevent DoS attacks. + */ + protected final int additionalReqParamsMaxSize; + + /** + * Default value for {@link #additionalReqParamsFailFast} in case no realm property is set. + */ + private static final boolean DEFAULT_ADDITIONAL_REQ_PARAMS_FAIL_FAST = false; + /** - * Max size of additional req param value copied into client session note to prevent DoS attacks - params with longer value are ignored - * + * Whether the fail-fast strategy should be enforced. If false all additional request parameters + * that to not meet the configuration are silently ignored. If true an exception will be raised. */ - public static final int ADDITIONAL_REQ_PARAMS_MAX_SIZE = 2000; + protected final boolean additionalReqParamsFailFast; + + /** + * Default value for {@link #additionalReqParamsMaxOverallSize} in case no realm property is set. + * + */ + private static final int DEFAULT_ADDITIONAL_REQ_PARAMS_MAX_OVERALL_SIZE = Integer.MAX_VALUE; + + /** + * Max size of all additional request parameters value copied into client session note to prevent DoS attacks. + */ + protected final int additionalReqParamsMaxOverallSize; public static final String AUTHZ_REQUEST_OBJECT = "ParsedRequestObject"; public static final String AUTHZ_REQUEST_OBJECT_ENCRYPTED = "EncryptedRequestObject"; @@ -85,7 +142,14 @@ public abstract class AuthzEndpointRequestParser { KNOWN_REQ_PARAMS.add(OAuth2Constants.CLIENT_SECRET); } - + protected AuthzEndpointRequestParser(KeycloakSession keycloakSession) { + RealmModel realm = keycloakSession.getContext().getRealm(); + this.additionalReqParamsMaxNumber = realm.getAttribute("additionalReqParamsMaxNumber", DEFAULT_ADDITIONAL_REQ_PARAMS_MAX_MUMBER); + this.additionalReqParamsMaxSize = realm.getAttribute("additionalReqParamsMaxSize", DEFAULT_ADDITIONAL_REQ_PARAMS_MAX_SIZE); + this.additionalReqParamsFailFast = realm.getAttribute("additionalReqParamsFailFast", DEFAULT_ADDITIONAL_REQ_PARAMS_FAIL_FAST); + this.additionalReqParamsMaxOverallSize = realm.getAttribute("additionalReqParamsMaxOverallSize", DEFAULT_ADDITIONAL_REQ_PARAMS_MAX_OVERALL_SIZE); + } + public void parseRequest(AuthorizationEndpointRequest request) { String clientId = getParameter(OIDCLoginProtocol.CLIENT_ID_PARAM); if (clientId != null && request.clientId != null && !request.clientId.equals(clientId)) { @@ -131,23 +195,61 @@ protected void validateResponseTypeParameter(String responseTypeParameter, Autho } protected void extractAdditionalReqParams(Map additionalReqParams) { + int currentAdditionalReqParamMaxOverallSize = 0; for (String paramName : keySet()) { - if (!KNOWN_REQ_PARAMS.contains(paramName)) { - String value = getParameter(paramName); - if (value != null && value.trim().isEmpty()) { - value = null; - } - if (value != null && ((value.length() <= ADDITIONAL_REQ_PARAMS_MAX_SIZE)) || ADDITIONAL_REQ_PARAMS_MAX_SIZE_IGNORE.contains(paramName)) { - if (additionalReqParams.size() >= ADDITIONAL_REQ_PARAMS_MAX_MUMBER) { - logger.debug("Maximal number of additional OIDC params (" + ADDITIONAL_REQ_PARAMS_MAX_MUMBER + ") exceeded, ignoring rest of them!"); - break; - } - additionalReqParams.put(paramName, value); - } else { - logger.debug("OIDC Additional param " + paramName + " ignored because value is empty or longer than " + ADDITIONAL_REQ_PARAMS_MAX_SIZE); - } + + if (KNOWN_REQ_PARAMS.contains(paramName)) { + logger.debugv("The additional OIDC param ''{0}'' is well known. Continue with the other additional parameters.", paramName); + continue; + } + + final String value = getParameter(paramName); + + if (value == null || value.trim().isEmpty()) { + logger.debugv("The additional OIDC param ''{0}'' ignored because it's value is null or blank.", paramName); + continue; + } + + // Compare with ">=", as the currently processed parameter will be added at the END of this method. + if (additionalReqParams.size() >= additionalReqParamsMaxNumber) { + + if (additionalReqParamsFailFast) { + logger.infov("The maximum number of allowed parameters ({0}) is exceeded.", additionalReqParamsMaxNumber); + throw new ErrorResponseException(OAuthErrorException.INVALID_REQUEST, "The maximum number of allowed parameters (" + additionalReqParamsMaxNumber + ") is exceeded.", Response.Status.BAD_REQUEST); + } else { + logger.debugv("The maximum number of allowed parameters ({0}) is exceeded.", additionalReqParamsMaxNumber); + break; + } + + } + + if (value.length() + currentAdditionalReqParamMaxOverallSize > additionalReqParamsMaxOverallSize) { + + if (additionalReqParamsFailFast) { + logger.infov("The OIDC additional parameter '{0}''s size ({1}) exceeds the maximum allowed size of all parameters ({2}).", paramName, value.length(), additionalReqParamsMaxOverallSize); + throw new ErrorResponseException(OAuthErrorException.INVALID_REQUEST, "The OIDC additional parameter '" + paramName + "'s size (" + value.length() + ") exceeds the maximum allowed size of all parameters (" + additionalReqParamsMaxOverallSize + ").", Response.Status.BAD_REQUEST); + } else { + logger.debugv("The OIDC additional parameter '{0}''s size exceeds ({1}) the maximum allowed size of all parameters ({2}).", paramName, value.length(), additionalReqParamsMaxOverallSize); + break; } + } + + if (value.length() > additionalReqParamsMaxSize) { + + if (additionalReqParamsFailFast) { + logger.infov("The OIDC additional parameter '{0}''s size is longer ({1}) than allowed ({2}).", paramName, value.length(), additionalReqParamsMaxSize); + throw new ErrorResponseException(OAuthErrorException.INVALID_REQUEST, "The OIDC additional parameter '" + paramName + "'s size is longer (" + value.length() + ") than allowed (" + additionalReqParamsMaxSize + ").", Response.Status.BAD_REQUEST); + } else { + logger.debugv("The OIDC additional parameter '{0}''s size is longer ({1}) than allowed ({2}).", paramName, value.length(), additionalReqParamsMaxSize); + break; + } + + } + + logger.debugv("Adding OIDC additional parameter ''{0}'' as additional parameter.", paramName); + currentAdditionalReqParamMaxOverallSize += value.length(); + additionalReqParams.put(paramName, value); } } diff --git a/services/src/main/java/org/keycloak/protocol/oidc/grants/ciba/endpoints/request/BackchannelAuthenticationEndpointRequestBodyParser.java b/services/src/main/java/org/keycloak/protocol/oidc/grants/ciba/endpoints/request/BackchannelAuthenticationEndpointRequestBodyParser.java index 767e2c278729..99f90d21bf46 100644 --- a/services/src/main/java/org/keycloak/protocol/oidc/grants/ciba/endpoints/request/BackchannelAuthenticationEndpointRequestBodyParser.java +++ b/services/src/main/java/org/keycloak/protocol/oidc/grants/ciba/endpoints/request/BackchannelAuthenticationEndpointRequestBodyParser.java @@ -18,6 +18,7 @@ package org.keycloak.protocol.oidc.grants.ciba.endpoints.request; import jakarta.ws.rs.core.MultivaluedMap; +import org.keycloak.models.KeycloakSession; import java.util.Set; @@ -32,7 +33,8 @@ class BackchannelAuthenticationEndpointRequestBodyParser extends BackchannelAuth private String invalidRequestMessage = null; - public BackchannelAuthenticationEndpointRequestBodyParser(MultivaluedMap requestParams) { + public BackchannelAuthenticationEndpointRequestBodyParser(KeycloakSession session, MultivaluedMap requestParams) { + super(session); this.requestParams = requestParams; } diff --git a/services/src/main/java/org/keycloak/protocol/oidc/grants/ciba/endpoints/request/BackchannelAuthenticationEndpointRequestParser.java b/services/src/main/java/org/keycloak/protocol/oidc/grants/ciba/endpoints/request/BackchannelAuthenticationEndpointRequestParser.java index 99641d7e01e0..c7badcd5bec5 100644 --- a/services/src/main/java/org/keycloak/protocol/oidc/grants/ciba/endpoints/request/BackchannelAuthenticationEndpointRequestParser.java +++ b/services/src/main/java/org/keycloak/protocol/oidc/grants/ciba/endpoints/request/BackchannelAuthenticationEndpointRequestParser.java @@ -20,6 +20,8 @@ import org.jboss.logging.Logger; import org.keycloak.OAuth2Constants; +import org.keycloak.models.KeycloakSession; +import org.keycloak.models.RealmModel; import org.keycloak.protocol.oidc.OIDCLoginProtocol; import org.keycloak.protocol.oidc.endpoints.request.AuthzEndpointRequestParser; import org.keycloak.protocol.oidc.grants.ciba.CibaGrantType; @@ -39,13 +41,13 @@ public abstract class BackchannelAuthenticationEndpointRequestParser { * Max number of additional req params copied into client session note to prevent DoS attacks * */ - public static final int ADDITIONAL_REQ_PARAMS_MAX_MUMBER = AuthzEndpointRequestParser.ADDITIONAL_REQ_PARAMS_MAX_MUMBER; + private final int additionalReqParamsMaxNumber; /** * Max size of additional req param value copied into client session note to prevent DoS attacks - params with longer value are ignored * */ - public static final int ADDITIONAL_REQ_PARAMS_MAX_SIZE = AuthzEndpointRequestParser.ADDITIONAL_REQ_PARAMS_MAX_SIZE; + private final int additionalReqParamsMaxSize; public static final String CIBA_SIGNED_AUTHENTICATION_REQUEST = "ParsedSignedAuthenticationRequest"; @@ -90,6 +92,12 @@ public abstract class BackchannelAuthenticationEndpointRequestParser { KNOWN_REQ_PARAMS.add("hash"); } + BackchannelAuthenticationEndpointRequestParser(KeycloakSession keycloakSession) { + RealmModel realm = keycloakSession.getContext().getRealm(); + this.additionalReqParamsMaxNumber = realm.getAttribute("additionalReqParamsMaxNumber", AuthzEndpointRequestParser.DEFAULT_ADDITIONAL_REQ_PARAMS_MAX_MUMBER); + this.additionalReqParamsMaxSize = realm.getAttribute("additionalReqParamsMaxSize", AuthzEndpointRequestParser.DEFAULT_ADDITIONAL_REQ_PARAMS_MAX_SIZE); + } + public void parseRequest(BackchannelAuthenticationEndpointRequest request) { request.scope = replaceIfNotNull(request.scope, getParameter(OIDCLoginProtocol.SCOPE_PARAM)); @@ -120,14 +128,14 @@ protected void extractAdditionalReqParams(Map additionalReqParam if (value != null && value.trim().isEmpty()) { value = null; } - if (value != null && value.length() <= ADDITIONAL_REQ_PARAMS_MAX_SIZE) { - if (additionalReqParams.size() >= ADDITIONAL_REQ_PARAMS_MAX_MUMBER) { - logger.debug("Maximal number of additional OIDC CIBA params (" + ADDITIONAL_REQ_PARAMS_MAX_MUMBER + ") exceeded, ignoring rest of them!"); + if (value != null && value.length() <= additionalReqParamsMaxSize) { + if (additionalReqParams.size() >= additionalReqParamsMaxNumber) { + logger.debug("Maximal number of additional OIDC CIBA params (" + additionalReqParamsMaxNumber + ") exceeded, ignoring rest of them!"); break; } additionalReqParams.put(paramName, value); } else { - logger.debug("OIDC CIBA Additional param " + paramName + " ignored because value is empty or longer than " + ADDITIONAL_REQ_PARAMS_MAX_SIZE); + logger.debug("OIDC CIBA Additional param " + paramName + " ignored because value is empty or longer than " + additionalReqParamsMaxSize); } } diff --git a/services/src/main/java/org/keycloak/protocol/oidc/grants/ciba/endpoints/request/BackchannelAuthenticationEndpointRequestParserProcessor.java b/services/src/main/java/org/keycloak/protocol/oidc/grants/ciba/endpoints/request/BackchannelAuthenticationEndpointRequestParserProcessor.java index 618db3569684..6e7ee64753cd 100644 --- a/services/src/main/java/org/keycloak/protocol/oidc/grants/ciba/endpoints/request/BackchannelAuthenticationEndpointRequestParserProcessor.java +++ b/services/src/main/java/org/keycloak/protocol/oidc/grants/ciba/endpoints/request/BackchannelAuthenticationEndpointRequestParserProcessor.java @@ -45,7 +45,7 @@ public static BackchannelAuthenticationEndpointRequest parseRequest(EventBuilder try { BackchannelAuthenticationEndpointRequest request = new BackchannelAuthenticationEndpointRequest(); - BackchannelAuthenticationEndpointRequestBodyParser parser = new BackchannelAuthenticationEndpointRequestBodyParser(requestParams); + BackchannelAuthenticationEndpointRequestBodyParser parser = new BackchannelAuthenticationEndpointRequestBodyParser(session, requestParams); parser.parseRequest(request); if (parser.getInvalidRequestMessage() != null) { diff --git a/services/src/main/java/org/keycloak/protocol/oidc/grants/ciba/endpoints/request/BackchannelAuthenticationEndpointSignedRequestParser.java b/services/src/main/java/org/keycloak/protocol/oidc/grants/ciba/endpoints/request/BackchannelAuthenticationEndpointSignedRequestParser.java index 4baef290d406..1d86ea3203ec 100644 --- a/services/src/main/java/org/keycloak/protocol/oidc/grants/ciba/endpoints/request/BackchannelAuthenticationEndpointSignedRequestParser.java +++ b/services/src/main/java/org/keycloak/protocol/oidc/grants/ciba/endpoints/request/BackchannelAuthenticationEndpointSignedRequestParser.java @@ -43,6 +43,7 @@ class BackchannelAuthenticationEndpointSignedRequestParser extends BackchannelAu private final JsonNode requestParams; public BackchannelAuthenticationEndpointSignedRequestParser(KeycloakSession session, String signedAuthReq, ClientModel client, CibaConfig config) throws Exception { + super(session); JOSE jwt = JOSEParser.parse(signedAuthReq); if (jwt instanceof JWE) { diff --git a/services/src/main/java/org/keycloak/protocol/oidc/par/endpoints/request/AuthzEndpointParParser.java b/services/src/main/java/org/keycloak/protocol/oidc/par/endpoints/request/AuthzEndpointParParser.java index 57882c408f3a..36b6f9fb5413 100644 --- a/services/src/main/java/org/keycloak/protocol/oidc/par/endpoints/request/AuthzEndpointParParser.java +++ b/services/src/main/java/org/keycloak/protocol/oidc/par/endpoints/request/AuthzEndpointParParser.java @@ -47,6 +47,7 @@ public class AuthzEndpointParParser extends AuthzEndpointRequestParser { private String invalidRequestMessage = null; public AuthzEndpointParParser(KeycloakSession session, ClientModel client, String requestUri) { + super(session); this.session = session; this.client = client; SingleUseObjectProvider singleUseStore = session.singleUseObjects(); diff --git a/services/src/main/java/org/keycloak/protocol/oidc/par/endpoints/request/ParEndpointRequestParserProcessor.java b/services/src/main/java/org/keycloak/protocol/oidc/par/endpoints/request/ParEndpointRequestParserProcessor.java index 1efdf24feef6..b10e2591e005 100644 --- a/services/src/main/java/org/keycloak/protocol/oidc/par/endpoints/request/ParEndpointRequestParserProcessor.java +++ b/services/src/main/java/org/keycloak/protocol/oidc/par/endpoints/request/ParEndpointRequestParserProcessor.java @@ -50,7 +50,7 @@ public static AuthorizationEndpointRequest parseRequest(EventBuilder event, Keyc try { AuthorizationEndpointRequest request = new AuthorizationEndpointRequest(); - AuthzEndpointQueryStringParser parser = new AuthzEndpointQueryStringParser(requestParams, false); + AuthzEndpointQueryStringParser parser = new AuthzEndpointQueryStringParser(session, requestParams, false); parser.parseRequest(request); if (parser.getInvalidRequestMessage() != null) { diff --git a/services/src/test/java/org/keycloak/protocol/oidc/endpoints/request/AuthzEndpointRequestParserTest.java b/services/src/test/java/org/keycloak/protocol/oidc/endpoints/request/AuthzEndpointRequestParserTest.java index a383d82c931a..bdd90cf781d5 100644 --- a/services/src/test/java/org/keycloak/protocol/oidc/endpoints/request/AuthzEndpointRequestParserTest.java +++ b/services/src/test/java/org/keycloak/protocol/oidc/endpoints/request/AuthzEndpointRequestParserTest.java @@ -1,6 +1,13 @@ package org.keycloak.protocol.oidc.endpoints.request; +import org.junit.Before; import org.junit.Test; +import org.keycloak.models.KeycloakContext; +import org.keycloak.models.KeycloakSession; +import org.keycloak.models.RealmModel; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.mockito.MockitoAnnotations; import java.util.HashMap; import java.util.HashSet; @@ -12,12 +19,31 @@ import static org.junit.Assert.assertTrue; public class AuthzEndpointRequestParserTest { - + + @Mock + KeycloakSession keycloakSession; + + @Mock + RealmModel realmModel; + + @Mock + KeycloakContext keycloakContext; + + @Before + public void setUp() throws Exception { + MockitoAnnotations.initMocks(this); + Mockito.lenient().when(keycloakSession.getContext()).thenReturn(keycloakContext); + Mockito.lenient().when(keycloakContext.getRealm()).thenReturn(realmModel); + Mockito.lenient().when(realmModel.getAttribute("additionalReqParamsMaxNumber", 10)).thenReturn(10); + Mockito.lenient().when(realmModel.getAttribute("additionalReqParamsMaxSize", 2000)).thenReturn(2000); + Mockito.lenient().when(realmModel.getAttribute("additionalReqParamsFailFast", false)).thenReturn(false); + Mockito.lenient().when(realmModel.getAttribute("additionalReqParamsMaxOverallSize", Integer.MAX_VALUE)).thenReturn(Integer.MAX_VALUE); + } @Test public void testExtractAdditionalReqParams() { - TestAuthzEndpointRequestParser test = new TestAuthzEndpointRequestParser(); + TestAuthzEndpointRequestParser test = new TestAuthzEndpointRequestParser(keycloakSession); Map additionalReqParams = new HashMap<>(); @@ -32,6 +58,10 @@ public void testExtractAdditionalReqParams() { private class TestAuthzEndpointRequestParser extends AuthzEndpointRequestParser { + protected TestAuthzEndpointRequestParser(KeycloakSession keycloakSession) { + super(keycloakSession); + } + @Override protected String getParameter(String paramName) { return "VER2CFYJFMliVLAFlYUUE9Vj4FkbIhHq1Ufe_VnWcsY%2C7P2xRG3WGTXogxuhXOfIrZsfL8lrnCE-G7E95chcwBw%2C9ba29mAU6sK4O9cBk22sGe9bK0Y5ilNycVVx0yij5SM%2Cw6HfN_o5gO7s39azUBWTyCBQiEbbihAGddwr9eUteFA%2CjYYPTQT4nz79AYMNOM67TcnDABQXOMWK0yJ_91EXwaM%2CVER2CFYJFMliVLAFlYUUE9Vj4FkbIhHq1Ufe_VnWcsY%2C7P2xRG3WGTXogxuhXOfIrZsfL8lrnCE-G7E95chcwBw%2C9ba29mAU6sK4O9cBk22sGe9bK0Y5ilNycVVx0yij5SM%2Cw6HfN_o5gO7s39azUBWTyCBQiEbbihAGddwr9eUteFA%2CjYYPTQT4nz79AYMNOM67TcnDABQXOMWK0yJ_91EXwaMVER2CFYJFMliVLAFlYUUE9Vj4FkbIhHq1Ufe_VnWcsY%2C7P2xRG3WGTXogxuhXOfIrZsfL8lrnCE-G7E95chcwBw%2C9ba29mAU6sK4O9cBk22sGe9bK0Y5ilNycVVx0yij5SM%2Cw6HfN_o5gO7s39azUBWTyCBQiEbbihAGddwr9eUteFA%2CjYYPTQT4nz79AYMNOM67TcnDABQXOMWK0yJ_91EXwaM%2CVER2CFYJFMliVLAFlYUUE9Vj4FkbIhHq1Ufe_VnWcsY%2C7P2xRG3WGTXogxuhXOfIrZsfL8lrnCE-G7E95chcwBw%2C9ba29mAU6sK4O9cBk22sGe9bK0Y5ilNycVVx0yij5SM%2Cw6HfN_o5gO7s39azUBWTyCBQiEbbihAGddwr9eUteFA%2CjYYPTQT4nz79AYMNOM67TcnDABQXOMWK0yJ_91EXwaMVER2CFYJFMliVLAFlYUUE9Vj4FkbIhHq1Ufe_VnWcsY%2C7P2xRG3WGTXogxuhXOfIrZsfL8lrnCE-G7E95chcwBw%2C9ba29mAU6sK4O9cBk22sGe9bK0Y5ilNycVVx0yij5SM%2Cw6HfN_o5gO7s39azUBWTyCBQiEbbihAGddwr9eUteFA%2CjYYPTQT4nz79AYMNOM67TcnDABQXOMWK0yJ_91EXwaM%2CVER2CFYJFMliVLAFlYUUE9Vj4FkbIhHq1Ufe_VnWcsY%2C7P2xRG3WGTXogxuhXOfIrZsfL8lrnCE-G7E95chcwBw%2C9ba29mAU6sK4O9cBk22sGe9bK0Y5ilNycVVx0yij5SM%2Cw6HfN_o5gO7s39azUBWTyCBQiEbbihAGddwr9eUteFA%2CjYYPTQT4nz79AYMNOM67TcnDABQXOMWK0yJ_91EXwaMVER2CFYJFMliVLAFlYUUE9Vj4FkbIhHq1Ufe_VnWcsY%2C7P2xRG3WGTXogxuhXOfIrZsfL8lrnCE-G7E95chcwBw%2C9ba29mAU6sK4O9cBk22sGe9bK0Y5ilNycVVx0yij5SM%2Cw6HfN_o5gO7s39azUBWTyCBQiEbbihAGddwr9eUteFA%2CjYYPTQT4nz79AYMNOM67TcnDABQXOMWK0yJ_91EXwaM%2CVER2CFYJFMliVLAFlYUUE9Vj4FkbIhHq1Ufe_VnWcsY%2C7P2xRG3WGTXogxuhXOfIrZsfL8lrnCE-G7E95chcwBw%2C9ba29mAU6sK4O9cBk22sGe9bK0Y5ilNycVVx0yij5SM%2Cw6HfN_o5gO7s39azUBWTyCBQiEbbihAGddwr9eUteFA%2CjYYPTQT4nz79AYMNOM67TcnDABQXOMWK0yJ_91EXwaMVER2CFYJFMliVLAFlYUUE9Vj4FkbIhHq1Ufe_VnWcsY%2C7P2xRG3WGTXogxuhXOfIrZsfL8lrnCE-G7E95chcwBw%2C9ba29mAU6sK4O9cBk22sGe9bK0Y5ilNycVVx0yij5SM%2Cw6HfN_o5gO7s39azUBWTyCBQiEbbihAGddwr9eUteFA%2CjYYPTQT4nz79AYMNOM67TcnDABQXOMWK0yJ_91EXwaM%2CVER2CFYJFMliVLAFlYUUE9Vj4FkbIhHq1Ufe_VnWcsY%2C7P2xRG3WGTXogxuhXOfIrZsfL8lrnCE-G7E95chcwBw%2C9ba29mAU6sK4O9cBk22sGe9bK0Y5ilNycVVx0yij5SM%2Cw6HfN_o5gO7s39azUBWTyCBQiEbbihAGddwr9eUteFA%2CjYYPTQT4nz79AYMNOM67TcnDABQXOMWK0yJ_91EXwaMVER2CFYJFMliVLAFlYUUE9Vj4FkbIhHq1Ufe_VnWcsY%2C7P2xRG3WGTXogxuhXOfIrZsfL8lrnCE-G7E95chcwBw%2C9ba29mAU6sK4O9cBk22sGe9bK0Y5ilNycVVx0yij5SM%2Cw6HfN_o5gO7s39azUBWTyCBQiEbbihAGddwr9eUteFA%2CjYYPTQT4nz79AYMNOM67TcnDABQXOMWK0yJ_91EXwaM%2CVER2CFYJFMliVLAFlYUUE9Vj4FkbIhHq1Ufe_VnWcsY%2C7P2xRG3WGTXogxuhXOfIrZsfL8lrnCE-G7E95chcwBw%2C9ba29mAU6sK4O9cBk22sGe9bK0Y5ilNycVVx0yij5SM%2Cw6HfN_o5gO7s39azUBWTyCBQiEbbihAGddwr9eUteFA%2CjYYPTQT4nz79AYMNOM67TcnDABQXOMWK0yJ_91EXwaM"; diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/authz/AuthzEndpointRequestParserTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/authz/AuthzEndpointRequestParserTest.java new file mode 100644 index 000000000000..b5e3ea7d80cc --- /dev/null +++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/authz/AuthzEndpointRequestParserTest.java @@ -0,0 +1,348 @@ +package org.keycloak.testsuite.authz; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.containsString; +import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.is; +import static org.keycloak.testsuite.admin.AbstractAdminTest.loadJson; + +import jakarta.ws.rs.client.Client; +import jakarta.ws.rs.core.Response; + +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; + +import org.apache.commons.lang.RandomStringUtils; +import org.junit.Test; +import org.keycloak.protocol.oidc.OIDCLoginProtocol; +import org.keycloak.representations.idm.RealmRepresentation; +import org.keycloak.testsuite.AbstractTestRealmKeycloakTest; +import org.keycloak.testsuite.util.AdminClientUtil; +import org.keycloak.testsuite.util.Matchers; +import org.keycloak.testsuite.util.RealmBuilder; + +public class AuthzEndpointRequestParserTest extends AbstractTestRealmKeycloakTest { + + @Override + public void configureTestRealm(RealmRepresentation testRealm) { + + testRealm.setAttributes(new HashMap<>()); // no realm specific attributes yet + + RealmBuilder.edit(testRealm); + + } + + private void updateTestRealm(Map newAttributes) { + RealmRepresentation testRealm = loadJson(getClass().getResourceAsStream("/testrealm.json"), RealmRepresentation.class); + testRealm.setAttributes(newAttributes); + adminClient.realm("test").update(testRealm); + } + + @Test + public void test_authentication_backwards_compatible() { + + // no realm specific attribute set - test backwards compatibility + updateTestRealm(Collections.emptyMap()); + + try (Client client = AdminClientUtil.createResteasyClient()) { + + oauth.addCustomParameter("paramkey1_too_long", RandomStringUtils.random(2000 + 1)); + oauth.addCustomParameter("paramkey2", "paramvalue2"); + oauth.addCustomParameter("paramkey3", "paramvalue3"); + oauth.addCustomParameter("paramkey4", "paramvalue4"); + oauth.addCustomParameter("paramkey5", "paramvalue5"); + oauth.addCustomParameter("paramkey6_too_many", "paramvalue6"); + + try (Response response = client.target(oauth.getLoginFormUrl()).request().get()) { + + assertThat(response.getStatus(), is(equalTo(200))); + assertThat(response, Matchers.body(containsString("Sign in"))); + + } + + } + + } + + @Test + public void test_authentication_size_exceeds_failfast() { + + updateTestRealm( + Map.of( + "additionalReqParamsFailFast", "true", + "additionalReqParamsMaxSize", "42" + ) + ); + + try (Client client = AdminClientUtil.createResteasyClient()) { + + oauth.addCustomParameter("param_too_long", RandomStringUtils.random(42 + 1)); + + try (Response response = client.target(oauth.getLoginFormUrl()).request().get()) { + + assertThat(response.getStatus(), is(equalTo(400))); + assertThat(response, Matchers.body(containsString("Back to Application"))); + + } + + } + + } + + @Test + public void test_authentication_size_accepted() { + + updateTestRealm( + Map.of( + "additionalReqParamsFailFast", "true", + "additionalReqParamsMaxSize", "42" + ) + ); + + try (Client client = AdminClientUtil.createResteasyClient()) { + + oauth.addCustomParameter("param_accepted", RandomStringUtils.random(42)); + + try (Response response = client.target(oauth.getLoginFormUrl()).request().get()) { + + assertThat(response.getStatus(), is(equalTo(200))); + assertThat(response, Matchers.body(containsString("Sign in"))); + + } + + } + + } + + @Test + public void test_authentication_size_exceeds_ignore() { + + updateTestRealm( + Map.of( + "additionalReqParamsFailFast", "false", + "additionalReqParamsMaxSize", "42" + ) + ); + + try (Client client = AdminClientUtil.createResteasyClient()) { + + // well known params are ignored anyway + oauth.addCustomParameter(OIDCLoginProtocol.NONCE_PARAM, RandomStringUtils.random(100)); + oauth.addCustomParameter("param_too_long_silently_ignored", RandomStringUtils.random(42 + 1)); + + try (Response response = client.target(oauth.getLoginFormUrl()).request().get()) { + + assertThat(response.getStatus(), is(equalTo(200))); + assertThat(response, Matchers.body(containsString("Sign in"))); + + } + + } + + } + + @Test + public void test_authentication_maxnumber_exceeds_failfast() { + + updateTestRealm( + Map.of( + "additionalReqParamsFailFast", "true", + "additionalReqParamsMaxNumber", "2" + ) + ); + + try (Client client = AdminClientUtil.createResteasyClient()) { + + // well known params are ignored + oauth.addCustomParameter(OIDCLoginProtocol.NONCE_PARAM, RandomStringUtils.random(42)); + oauth.addCustomParameter("paramkey1", "paramvalue1"); + oauth.addCustomParameter("paramkey2", "paramvalue2"); + oauth.addCustomParameter("paramkey3", "paramvalue3"); + + try (Response response = client.target(oauth.getLoginFormUrl()).request().get()) { + + assertThat(response.getStatus(), is(equalTo(400))); + assertThat(response, Matchers.body(containsString("Back to Application"))); + + } + + } + + } + + @Test + public void test_authentication_maxnumber_accepted() { + + updateTestRealm( + Map.of( + "additionalReqParamsFailFast", "true", + "additionalReqParamsMaxNumber", "2" + ) + ); + + try (Client client = AdminClientUtil.createResteasyClient()) { + + // well known params are ignored + oauth.addCustomParameter(OIDCLoginProtocol.NONCE_PARAM, RandomStringUtils.random(42)); + oauth.addCustomParameter("paramkey1", "paramvalue1"); + oauth.addCustomParameter("paramkey2", "paramvalue2"); + + try (Response response = client.target(oauth.getLoginFormUrl()).request().get()) { + + assertThat(response.getStatus(), is(equalTo(200))); + assertThat(response, Matchers.body(containsString("Sign in"))); + + } + + } + + } + + @Test + public void test_authentication_maxnumber_exceeds_ignore() { + + updateTestRealm( + Map.of( + "additionalReqParamsFailFast", "false", + "additionalReqParamsMaxNumber", "2" + ) + ); + + try (Client client = AdminClientUtil.createResteasyClient()) { + + // well known params are ignored anyway + oauth.addCustomParameter(OIDCLoginProtocol.NONCE_PARAM, RandomStringUtils.random(42)); + oauth.addCustomParameter("paramkey1", "paramvalue1"); + oauth.addCustomParameter("paramkey2", "paramvalue2"); + oauth.addCustomParameter("paramkey3", "paramvalue3"); + + try (Response response = client.target(oauth.getLoginFormUrl()).request().get()) { + + assertThat(response.getStatus(), is(equalTo(200))); + assertThat(response, Matchers.body(containsString("Sign in"))); + + } + + } + + } + + @Test + public void test_authentication_maxoverallsize_exceeds_failfast() { + + updateTestRealm( + Map.of( + "additionalReqParamsFailFast", "true", + "additionalReqParamsMaxOverallSize", "42" + ) + ); + + try (Client client = AdminClientUtil.createResteasyClient()) { + + // 21 + 21 + 1 = 43 + oauth.addCustomParameter("paramkey1", RandomStringUtils.random(21)); + oauth.addCustomParameter("paramkey2", RandomStringUtils.random(21)); + oauth.addCustomParameter("paramkey3", RandomStringUtils.random(1)); + + try (Response response = client.target(oauth.getLoginFormUrl()).request().get()) { + + assertThat(response.getStatus(), is(equalTo(400))); + assertThat(response, Matchers.body(containsString("Back to Application"))); + + } + + } + + } + + @Test + public void test_authentication_maxoverallsize_accepted() { + + updateTestRealm( + Map.of( + "additionalReqParamsFailFast", "true", + "additionalReqParamsMaxOverallSize", "42" + ) + ); + + try (Client client = AdminClientUtil.createResteasyClient()) { + + // 21 + 21 = 42 + oauth.addCustomParameter("paramkey1", RandomStringUtils.random(21)); + oauth.addCustomParameter("paramkey2", RandomStringUtils.random(21)); + + try (Response response = client.target(oauth.getLoginFormUrl()).request().get()) { + + assertThat(response.getStatus(), is(equalTo(200))); + assertThat(response, Matchers.body(containsString("Sign in"))); + + } + + } + + } + + @Test + public void test_authentication_maxoverallsize_exceeds_ignore() { + + updateTestRealm( + Map.of( + "additionalReqParamsFailFast", "false", + "additionalReqParamsMaxOverallSize", "42" + ) + ); + + try (Client client = AdminClientUtil.createResteasyClient()) { + + // 21 + 21 + 1 = 43 + oauth.addCustomParameter("paramkey1", RandomStringUtils.random(21)); + oauth.addCustomParameter("paramkey2", RandomStringUtils.random(21)); + oauth.addCustomParameter("paramkey3", RandomStringUtils.random(1)); + + try (Response response = client.target(oauth.getLoginFormUrl()).request().get()) { + + assertThat(response.getStatus(), is(equalTo(200))); + assertThat(response, Matchers.body(containsString("Sign in"))); + + } + + } + + } + + @Test + public void test_authentication_knownparameters_dont_count() { + + updateTestRealm( + Map.of( + "additionalReqParamsFailFast", "true", + "additionalReqParamsMaxOverallSize", "42", + "additionalReqParamsMaxNumber", "2" + ) + ); + + try (Client client = AdminClientUtil.createResteasyClient()) { + + /* + * Well known parameter will neither be counted towards additionalReqParamsMaxSize nor + * additionalReqParamsMaxOverallSize. + */ + oauth.addCustomParameter(OIDCLoginProtocol.NONCE_PARAM, RandomStringUtils.random(100)); + oauth.addCustomParameter("paramkey1", RandomStringUtils.random(21)); + oauth.addCustomParameter(OIDCLoginProtocol.CODE_PARAM, ""); + oauth.addCustomParameter("paramkey2", RandomStringUtils.random(21)); + oauth.addCustomParameter(OIDCLoginProtocol.MAX_AGE_PARAM, "42"); + + try (Response response = client.target(oauth.getLoginFormUrl()).request().get()) { + + assertThat(response.getStatus(), is(equalTo(200))); + assertThat(response, Matchers.body(containsString("Sign in"))); + + } + + } + + } + +}