From 931cae9848c67a3b0e633019284ff59d3d974ee6 Mon Sep 17 00:00:00 2001 From: Laurent Meunier Date: Fri, 7 Apr 2023 15:56:41 +0200 Subject: [PATCH 1/7] Allow importing attribute groups in User Declarative Profile --- .../de/adorsys/keycloak/config/model/RealmImport.java | 6 +++--- .../config/service/UserProfileImportService.java | 11 ++--------- 2 files changed, 5 insertions(+), 12 deletions(-) diff --git a/src/main/java/de/adorsys/keycloak/config/model/RealmImport.java b/src/main/java/de/adorsys/keycloak/config/model/RealmImport.java index 2d4666b35..20af2a824 100644 --- a/src/main/java/de/adorsys/keycloak/config/model/RealmImport.java +++ b/src/main/java/de/adorsys/keycloak/config/model/RealmImport.java @@ -34,7 +34,7 @@ public class RealmImport extends RealmRepresentation { private List authenticationFlowImports; - private List> userProfile; + private LinkedHashMap>> userProfile; private String checksum; @@ -54,11 +54,11 @@ public void setAuthenticationFlowImports(List authenti @SuppressWarnings("unused") @JsonSetter("userProfile") - public void setUserProfile(List> userProfile) { + public void setUserProfile(LinkedHashMap>> userProfile) { this.userProfile = userProfile; } - public List> getUserProfile() { + public LinkedHashMap>> getUserProfile() { return userProfile; } diff --git a/src/main/java/de/adorsys/keycloak/config/service/UserProfileImportService.java b/src/main/java/de/adorsys/keycloak/config/service/UserProfileImportService.java index c7bcb6108..a57a799fb 100644 --- a/src/main/java/de/adorsys/keycloak/config/service/UserProfileImportService.java +++ b/src/main/java/de/adorsys/keycloak/config/service/UserProfileImportService.java @@ -28,8 +28,6 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; -import java.util.LinkedHashMap; - @Service public class UserProfileImportService { private static final Logger logger = LoggerFactory.getLogger(UserProfileImportService.class); @@ -42,7 +40,6 @@ public UserProfileImportService(UserProfileRepository userProfileRepository) { } public void doImport(RealmImport realmImport) { - var userProfileEnabledString = realmImport.getAttributesOrEmpty().get(UserProfileRepository.REALM_ATTRIBUTES_USER_PROFILE_ENABLED_STRING); if (userProfileEnabledString == null) { //if not defined at all, ignore everything else @@ -59,14 +56,10 @@ public void doImport(RealmImport realmImport) { } private String buildUserProfileConfigurationString(RealmImport realmImport) { - - var userProfile = new LinkedHashMap(); - var userProfileAttributes = realmImport.getUserProfile(); - if (userProfileAttributes == null || userProfileAttributes.isEmpty()) { + var userProfile = realmImport.getUserProfile(); + if (userProfile == null || userProfile.isEmpty()) { return null; } - - userProfile.put("attributes", userProfileAttributes); return JsonUtil.toJson(userProfile); } From 32320cac53ea9e97db361ec18116a33d68ee1b6b Mon Sep 17 00:00:00 2001 From: Laurent Meunier Date: Wed, 19 Apr 2023 15:39:48 +0200 Subject: [PATCH 2/7] Update ImportUserProfileIT Tests --- .../config/service/ImportUserProfileIT.java | 3 + .../00_ignore_realm_with_user_profile.json | 128 ++++++++-------- .../01_create_realm_with_user_profile.json | 128 ++++++++-------- .../02_update_realm_with_user_profile.json | 139 ++++++++++-------- 4 files changed, 210 insertions(+), 188 deletions(-) diff --git a/src/test/java/de/adorsys/keycloak/config/service/ImportUserProfileIT.java b/src/test/java/de/adorsys/keycloak/config/service/ImportUserProfileIT.java index 446a543a3..d7c0a407b 100644 --- a/src/test/java/de/adorsys/keycloak/config/service/ImportUserProfileIT.java +++ b/src/test/java/de/adorsys/keycloak/config/service/ImportUserProfileIT.java @@ -87,6 +87,9 @@ void shouldUpdateRealm() throws IOException { assertThat(configurationNode.at("/attributes/0/name").asText(), is("username")); assertThat(configurationNode.at("/attributes/0/validations/length/min").asInt(), is(5)); + assertThat(configurationNode.at("/attributes/1/name").asText(), is("email")); + assertThat(configurationNode.at("/attributes/1/group").asText(), is("user_informations")); + assertThat(configurationNode.at("/groups/0/name").asText(), is("user_informations")); } @Test diff --git a/src/test/resources/import-files/user-profile/00_ignore_realm_with_user_profile.json b/src/test/resources/import-files/user-profile/00_ignore_realm_with_user_profile.json index c8d225555..b0d8906de 100644 --- a/src/test/resources/import-files/user-profile/00_ignore_realm_with_user_profile.json +++ b/src/test/resources/import-files/user-profile/00_ignore_realm_with_user_profile.json @@ -1,74 +1,76 @@ { "enabled": true, "realm": "realmWithProfile", - "userProfile": [ - { - "name": "username", - "displayName": "${username}", - "validations": { - "length": { - "min": 1, - "max": 20 - }, - "username-prohibited-characters": {} - } - }, - { - "name": "email", - "displayName": "${email}", - "validations": { - "email": {}, - "length": { - "max": 255 + "userProfile": { + "attributes": [ + { + "name": "username", + "displayName": "${username}", + "validations": { + "length": { + "min": 1, + "max": 20 + }, + "username-prohibited-characters": {} } - } - }, - { - "name": "firstName", - "displayName": "${firstName}", - "required": { - "roles": [ - "user" - ] }, - "permissions": { - "view": [ - "admin", - "user" - ], - "edit": [ - "admin" - ] + { + "name": "email", + "displayName": "${email}", + "validations": { + "email": {}, + "length": { + "max": 255 + } + } }, - "validations": { - "length": { - "max": 169 + { + "name": "firstName", + "displayName": "${firstName}", + "required": { + "roles": [ + "user" + ] }, - "person-name-prohibited-characters": {} - } - }, - { - "name": "lastName", - "displayName": "${lastName}", - "required": { - "roles": [ - "user" - ] - }, - "permissions": { - "view": [ - "user" - ], - "edit": [ - "admin" - ] + "permissions": { + "view": [ + "admin", + "user" + ], + "edit": [ + "admin" + ] + }, + "validations": { + "length": { + "max": 169 + }, + "person-name-prohibited-characters": {} + } }, - "validations": { - "length": { - "max": 238 + { + "name": "lastName", + "displayName": "${lastName}", + "required": { + "roles": [ + "user" + ] }, - "person-name-prohibited-characters": {} + "permissions": { + "view": [ + "user" + ], + "edit": [ + "admin" + ] + }, + "validations": { + "length": { + "max": 238 + }, + "person-name-prohibited-characters": {} + } } - } - ] + ] + } } diff --git a/src/test/resources/import-files/user-profile/01_create_realm_with_user_profile.json b/src/test/resources/import-files/user-profile/01_create_realm_with_user_profile.json index b2bb2b91b..d9edb9cb1 100644 --- a/src/test/resources/import-files/user-profile/01_create_realm_with_user_profile.json +++ b/src/test/resources/import-files/user-profile/01_create_realm_with_user_profile.json @@ -4,74 +4,76 @@ "attributes": { "userProfileEnabled": true }, - "userProfile": [ - { - "name": "username", - "displayName": "${username}", - "validations": { - "length": { - "min": 1, - "max": 20 - }, - "username-prohibited-characters": {} - } - }, - { - "name": "email", - "displayName": "${email}", - "validations": { - "email": {}, - "length": { - "max": 255 + "userProfile": { + "attributes": [ + { + "name": "username", + "displayName": "${username}", + "validations": { + "length": { + "min": 1, + "max": 20 + }, + "username-prohibited-characters": {} } - } - }, - { - "name": "firstName", - "displayName": "${firstName}", - "required": { - "roles": [ - "user" - ] }, - "permissions": { - "view": [ - "admin", - "user" - ], - "edit": [ - "admin" - ] + { + "name": "email", + "displayName": "${email}", + "validations": { + "email": {}, + "length": { + "max": 255 + } + } }, - "validations": { - "length": { - "max": 169 + { + "name": "firstName", + "displayName": "${firstName}", + "required": { + "roles": [ + "user" + ] }, - "person-name-prohibited-characters": {} - } - }, - { - "name": "lastName", - "displayName": "${lastName}", - "required": { - "roles": [ - "user" - ] - }, - "permissions": { - "view": [ - "user" - ], - "edit": [ - "admin" - ] + "permissions": { + "view": [ + "admin", + "user" + ], + "edit": [ + "admin" + ] + }, + "validations": { + "length": { + "max": 169 + }, + "person-name-prohibited-characters": {} + } }, - "validations": { - "length": { - "max": 238 + { + "name": "lastName", + "displayName": "${lastName}", + "required": { + "roles": [ + "user" + ] }, - "person-name-prohibited-characters": {} + "permissions": { + "view": [ + "user" + ], + "edit": [ + "admin" + ] + }, + "validations": { + "length": { + "max": 238 + }, + "person-name-prohibited-characters": {} + } } - } - ] + ] + } } diff --git a/src/test/resources/import-files/user-profile/02_update_realm_with_user_profile.json b/src/test/resources/import-files/user-profile/02_update_realm_with_user_profile.json index 19bc67613..0262547e0 100644 --- a/src/test/resources/import-files/user-profile/02_update_realm_with_user_profile.json +++ b/src/test/resources/import-files/user-profile/02_update_realm_with_user_profile.json @@ -4,73 +4,88 @@ "attributes": { "userProfileEnabled": true }, - "userProfile": [ - { - "name": "username", - "displayName": "${username}", - "validations": { - "length": { - "min": 5, - "max": 20 - }, - "username-prohibited-characters": {} - } - }, - { - "name": "email", - "displayName": "${email}", - "validations": { - "email": {}, - "length": { - "max": 255 + "userProfile": { + "attributes": [ + { + "name": "username", + "displayName": "${username}", + "validations": { + "length": { + "min": 5, + "max": 20 + }, + "username-prohibited-characters": {} } - } - }, - { - "name": "firstName", - "displayName": "${firstName}", - "required": { - "roles": [ - "user" - ] }, - "permissions": { - "view": [ - "admin" - ], - "edit": [ - "admin" - ] - }, - "validations": { - "length": { - "max": 10 + { + "name": "email", + "displayName": "${email}", + "validations": { + "email": {}, + "length": { + "max": 255 + } }, - "person-name-prohibited-characters": {} - } - }, - { - "name": "lastName", - "displayName": "${lastName}", - "required": { - "roles": [ - "user" - ] + "group": "user_informations" }, - "permissions": { - "view": [ - "user" - ], - "edit": [ - "admin" - ] + { + "name": "firstName", + "displayName": "${firstName}", + "required": { + "roles": [ + "user" + ] + }, + "permissions": { + "view": [ + "admin" + ], + "edit": [ + "admin" + ] + }, + "validations": { + "length": { + "max": 10 + }, + "person-name-prohibited-characters": {} + }, + "group": "name_informations" }, - "validations": { - "length": { - "max": 240 + { + "name": "lastName", + "displayName": "${lastName}", + "required": { + "roles": [ + "user" + ] + }, + "permissions": { + "view": [ + "user" + ], + "edit": [ + "admin" + ] }, - "person-name-prohibited-characters": {} + "validations": { + "length": { + "max": 240 + }, + "person-name-prohibited-characters": {} + }, + "group": "name_informations" + } + ], + "groups": [ + { + "displayHeader": "User informations", + "name": "user_informations" + }, + { + "displayHeader": "Name informations", + "name": "name_informations" } - } - ] + ] + } } From 42c03ebee3d8f92ba92c24d2a2384de261fafc81 Mon Sep 17 00:00:00 2001 From: Laurent Meunier Date: Wed, 19 Apr 2023 15:46:55 +0200 Subject: [PATCH 3/7] Update CHANGELOG.md --- CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 04b01da08..b2dde2250 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,7 +5,10 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). ## [Unreleased] + +### Changed - Refactored support for user profile updates +- Attribute groups are now allowed in the `userProfile` property in json import ## [5.6.1] - 2023-03-05 From d6dce5f7d7a310fa69e8ea1ddd4d4ffd6833aaa9 Mon Sep 17 00:00:00 2001 From: Laurent Meunier Date: Wed, 19 Apr 2023 17:06:04 +0200 Subject: [PATCH 4/7] Trying to fix "connection timed out" errors with repo.maven.apache.org --- .github/workflows/ci.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index d2c7b1839..e2eabd9ba 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -3,7 +3,7 @@ name: CI env: # https://github.com/actions/virtual-environments/issues/1499 - MAVEN_CLI_OPTS: '-ntp --batch-mode --errors --fail-at-end --show-version -Dhttp.keepAlive=false -Dmaven.wagon.http.pool=false -Dmaven.wagon.httpconnectionManager.ttlSeconds=120 -Dstyle.color=always' + MAVEN_CLI_OPTS: '-ntp --batch-mode --errors --fail-at-end --show-version -Dmaven.wagon.httpconnectionManager.ttlSeconds=60 -Dmaven.wagon.http.retryHandler.count=3 -Dstyle.color=always' TESTCONTAINERS_RYUK_DISABLED: 'true' CT_CHART_DIRS: 'contrib/charts/' CT_BUILD_ID: '${{ github.run_id }}' From 3fe0b49be32f042f794b5d1e5d0f72c3acc69fcc Mon Sep 17 00:00:00 2001 From: Laurent Meunier Date: Fri, 28 Apr 2023 17:04:58 +0200 Subject: [PATCH 5/7] Add a brief migration guide for the new `userProfile` property --- CHANGELOG.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b2dde2250..ef5befff9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,7 +8,11 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), ### Changed - Refactored support for user profile updates -- Attribute groups are now allowed in the `userProfile` property in json import +- Attribute groups are now allowed in the `userProfile` property in json import. The format to import User Declarative Profile attributes (and attribute groups) has slightly changed. To migrate to the new format: + - transform the `userProfile` property to a JSON object with two properties: `attributes` and `groups` + - copy the JSON array of the old `userProfile` property to the new `userProfile.attributes` property + - create a new JSON array for the `userProfile.groups` property (containing the attribute groups definitions) + - in the end, the `userProfile` property should match the content of the "JSON editor" tab in the "Realm settings > User profile" page from the Keycloak admin console ## [5.6.1] - 2023-03-05 From e3d96f6ecfd698c32cca05dc520b3121744379ea Mon Sep 17 00:00:00 2001 From: Laurent Meunier Date: Wed, 31 May 2023 10:33:07 +0200 Subject: [PATCH 6/7] retrigger checks From 8b1c2f17257fe8e34e3eebf9dfe039fb9a3fa5c7 Mon Sep 17 00:00:00 2001 From: Laurent Meunier Date: Mon, 19 Jun 2023 11:26:28 +0200 Subject: [PATCH 7/7] Replace LinkedHashMap with Map --- .../de/adorsys/keycloak/config/model/RealmImport.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/main/java/de/adorsys/keycloak/config/model/RealmImport.java b/src/main/java/de/adorsys/keycloak/config/model/RealmImport.java index 20af2a824..02e670556 100644 --- a/src/main/java/de/adorsys/keycloak/config/model/RealmImport.java +++ b/src/main/java/de/adorsys/keycloak/config/model/RealmImport.java @@ -27,14 +27,14 @@ import org.springframework.stereotype.Component; import java.util.ArrayList; -import java.util.LinkedHashMap; import java.util.List; +import java.util.Map; @Component public class RealmImport extends RealmRepresentation { private List authenticationFlowImports; - private LinkedHashMap>> userProfile; + private Map>> userProfile; private String checksum; @@ -54,11 +54,11 @@ public void setAuthenticationFlowImports(List authenti @SuppressWarnings("unused") @JsonSetter("userProfile") - public void setUserProfile(LinkedHashMap>> userProfile) { + public void setUserProfile(Map>> userProfile) { this.userProfile = userProfile; } - public LinkedHashMap>> getUserProfile() { + public Map>> getUserProfile() { return userProfile; }