From 96c017bd80dd9bf5c434ae8b392892a0eb8e534e Mon Sep 17 00:00:00 2001 From: Oleg Fomenko Date: Sat, 4 Dec 2021 11:25:39 +0100 Subject: [PATCH] Corrected the logic of the Keycloak default client level roles assignment synchronization. --- CHANGELOG.md | 3 +- .../config/service/UserImportService.java | 7 +- .../config/service/ImportUsersIT.java | 26 +++++++ ...loak_client_role_from_service_account.json | 76 +++++++++++++++++++ 4 files changed, 108 insertions(+), 4 deletions(-) create mode 100644 src/test/resources/import-files/users/60.3_update_realm_explicitly_remove_keycloak_client_role_from_service_account.json diff --git a/CHANGELOG.md b/CHANGELOG.md index 7033a6561..12d4af394 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,7 +18,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), ### Fixed -- Stale client level roles assignment, if all roles of the client are removed in configuration. The Keycloak default client roles (e.g. realm-management) will remain untouched though. +- Stale client level roles assignment on a user, if the client is not present in the `clientRoles` JSON object in the config file. + The Keycloak default client roles (e.g. realm-management) will remain untouched though. ## [4.3.0] - 2021-09-28 diff --git a/src/main/java/de/adorsys/keycloak/config/service/UserImportService.java b/src/main/java/de/adorsys/keycloak/config/service/UserImportService.java index fde3c2f4f..b78cc4cc1 100644 --- a/src/main/java/de/adorsys/keycloak/config/service/UserImportService.java +++ b/src/main/java/de/adorsys/keycloak/config/service/UserImportService.java @@ -266,14 +266,15 @@ private void handleClientRoles() { .getUserClientLevelRoles(realmName, userToImport.getUsername()); for (Map.Entry> existing : existingClientsRoles.entrySet()) { - List rolesToImport = clientRolesToImport.getOrDefault(existing.getKey(), Collections.emptyList()); + List rolesToImport = clientRolesToImport.get(existing.getKey()); - if (rolesToImport.isEmpty()) { + if (rolesToImport == null) { ClientRepresentation client = clientRepository.getByClientId(realmName, existing.getKey()); if (KeycloakUtil.isDefaultClient(client)) { - // Do not remove keycloak default client's roles even if they are not in configuration + // Do not remove keycloak default client's roles when they are not in the configuration continue; } + rolesToImport = Collections.emptyList(); } setupClientRoles( existing.getKey(), diff --git a/src/test/java/de/adorsys/keycloak/config/service/ImportUsersIT.java b/src/test/java/de/adorsys/keycloak/config/service/ImportUsersIT.java index 2a41e2287..7b1be3024 100644 --- a/src/test/java/de/adorsys/keycloak/config/service/ImportUsersIT.java +++ b/src/test/java/de/adorsys/keycloak/config/service/ImportUsersIT.java @@ -589,6 +589,32 @@ void shouldRemoveClientLevelRolesFromExistingServiceAccount() throws IOException assertThat(keycloakNativeClientLevelRoles, contains("view-realm")); } + @Test + @Order(100) + void shouldRemoveKeycloakDefaultClientLevelRolesFromExistingServiceAccount() throws IOException { + doImport("60.3_update_realm_explicitly_remove_keycloak_client_role_from_service_account.json"); + RealmRepresentation realm = keycloakProvider.getInstance().realm(REALM_NAME).toRepresentation(); + assertThat(realm.getRealm(), is(REALM_NAME)); + assertThat(realm.isEnabled(), is(true)); + assertThat(realm.isRegistrationAllowed(), is(true)); + assertThat(realm.isRegistrationEmailAsUsername(), is(true)); + + ClientRepresentation client = keycloakRepository.getClient(REALM_NAME, "technical-client"); + assertThat(client.getClientId(), is("technical-client")); + + UserRepresentation user = keycloakProvider.getInstance().realm(REALM_NAME) + .clients() + .get(client.getId()) + .getServiceAccountUser(); + + assertThat(user.getUsername(), is("service-account-technical-client")); + + List keycloakNativeClientLevelRoles = keycloakRepository.getServiceAccountUserClientLevelRoles( + REALM_NAME, client.getClientId(), "realm-management"); + + assertThat(keycloakNativeClientLevelRoles, empty()); + } + private List getGroupsByUser(UserRepresentation user) { return keycloakProvider.getInstance().realm(REALM_NAME).users().get(user.getId()).groups(); } diff --git a/src/test/resources/import-files/users/60.3_update_realm_explicitly_remove_keycloak_client_role_from_service_account.json b/src/test/resources/import-files/users/60.3_update_realm_explicitly_remove_keycloak_client_role_from_service_account.json new file mode 100644 index 000000000..17b830dd2 --- /dev/null +++ b/src/test/resources/import-files/users/60.3_update_realm_explicitly_remove_keycloak_client_role_from_service_account.json @@ -0,0 +1,76 @@ +{ + "enabled": true, + "realm": "realmWithUsers", + "registrationAllowed": true, + "registrationEmailAsUsername": true, + "roles": { + "client": { + "moped-client": [ + { + "name": "test_client_role", + "description": "My moped-client role", + "composite": false, + "clientRole": true + }, + { + "name": "other_test_client_role", + "description": "My changed other moped-client role", + "composite": false, + "clientRole": true + } + ] + } + }, + "clients": [ + { + "clientId": "technical-client", + "surrogateAuthRequired": false, + "enabled": true, + "alwaysDisplayInConsole": false, + "clientAuthenticatorType": "client-secret", + "redirectUris": [], + "webOrigins": [], + "notBefore": 0, + "bearerOnly": false, + "consentRequired": false, + "standardFlowEnabled": false, + "implicitFlowEnabled": false, + "directAccessGrantsEnabled": false, + "serviceAccountsEnabled": true, + "publicClient": false, + "frontchannelLogout": false, + "protocol": "openid-connect", + "defaultClientScopes": [ + "role_list", + "roles" + ], + "optionalClientScopes": [] + }, + { + "clientId": "moped-client", + "name": "moped-client", + "description": "Moped-Client", + "enabled": true, + "clientAuthenticatorType": "client-secret", + "secret": "my-special-client-secret", + "bearerOnly": true + } + ], + "users": [ + { + "username": "service-account-technical-client", + "enabled": true, + "totp": false, + "emailVerified": false, + "serviceAccountClientId": "technical-client", + "clientRoles": { + "account": [ + "manage-account", + "view-profile" + ], + "realm-management": [] + }, + "notBefore": 0 + } + ] +}