From d85add548a35812e41c3c118fa55e02cfc794ed6 Mon Sep 17 00:00:00 2001 From: Boris Skert Date: Fri, 24 May 2019 10:12:41 +0200 Subject: [PATCH] Improve error handling while importing sub-components (#35) --- .../repository/ComponentRepository.java | 97 +++++++-- .../service/ComponentImportService.java | 110 +++++++--- .../keycloak/config/ImportComponentsIT.java | 127 ++++++++++-- ...ealm__add_component_with_subcomponent.json | 190 +++++++++++++++++ ..._realm__update_config_in_subcomponent.json | 193 ++++++++++++++++++ 5 files changed, 654 insertions(+), 63 deletions(-) create mode 100644 config-cli/src/test/resources/import-files/components/4_update_realm__add_component_with_subcomponent.json create mode 100644 config-cli/src/test/resources/import-files/components/5_update_realm__update_config_in_subcomponent.json diff --git a/config-cli/src/main/java/de/adorsys/keycloak/config/repository/ComponentRepository.java b/config-cli/src/main/java/de/adorsys/keycloak/config/repository/ComponentRepository.java index dc391c26a..0aeb4891a 100644 --- a/config-cli/src/main/java/de/adorsys/keycloak/config/repository/ComponentRepository.java +++ b/config-cli/src/main/java/de/adorsys/keycloak/config/repository/ComponentRepository.java @@ -1,27 +1,32 @@ package de.adorsys.keycloak.config.repository; +import com.fasterxml.jackson.databind.ObjectMapper; import de.adorsys.keycloak.config.exception.KeycloakRepositoryException; import de.adorsys.keycloak.config.util.ResponseUtil; import org.apache.logging.log4j.util.Strings; import org.keycloak.admin.client.resource.ComponentResource; import org.keycloak.admin.client.resource.RealmResource; +import org.keycloak.common.util.MultivaluedHashMap; +import org.keycloak.representations.idm.ComponentExportRepresentation; import org.keycloak.representations.idm.ComponentRepresentation; +import org.keycloak.representations.idm.RealmRepresentation; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import javax.ws.rs.core.Response; -import java.util.List; -import java.util.Objects; -import java.util.Optional; +import java.util.*; @Service public class ComponentRepository { private final RealmRepository realmRepository; + private final ObjectMapper mapper; + @Autowired - public ComponentRepository(RealmRepository realmRepository) { + public ComponentRepository(RealmRepository realmRepository, ObjectMapper mapper) { this.realmRepository = realmRepository; + this.mapper = mapper; } public void create(String realm, ComponentRepresentation componentToCreate) throws KeycloakRepositoryException { @@ -32,7 +37,7 @@ public void create(String realm, ComponentRepresentation componentToCreate) thro } public void update(String realm, ComponentRepresentation componentToUpdate) { - assert(Strings.isNotBlank(componentToUpdate.getId())); + assert (Strings.isNotBlank(componentToUpdate.getId())); RealmResource realmResource = realmRepository.loadRealm(realm); ComponentResource componentResource = realmResource.components().component(componentToUpdate.getId()); @@ -43,16 +48,19 @@ public void update(String realm, ComponentRepresentation componentToUpdate) { public ComponentRepresentation get(String realm, String subType, String name) { RealmResource realmResource = realmRepository.loadRealm(realm); - Optional maybeComponent = realmResource.components() - .query(realm, subType, name) + List realmComponents = realmResource.components().query(); + + Optional maybeComponent = realmComponents .stream() + .filter(c -> Objects.equals(c.getName(), name)) + .filter(c -> Objects.equals(c.getProviderType(), subType)) .findFirst(); - if(maybeComponent.isPresent()) { + if (maybeComponent.isPresent()) { return maybeComponent.get(); } - throw new RuntimeException("Cannot find component by name '" + name + "' and subtype '" + subType + "' in realm '" + realm + "' "); + throw new KeycloakRepositoryException("Cannot find component by name '" + name + "' and subtype '" + subType + "' in realm '" + realm + "' "); } public Optional tryToGet(String realm, String parentId, String subType, String name) { @@ -62,7 +70,7 @@ public Optional tryToGet(String realm, String parentId, List existingComponents = realmResource.components() .query(parentId, subType, name); - if(existingComponents.isEmpty()) { + if (existingComponents.isEmpty()) { maybeComponent = Optional.empty(); } else { maybeComponent = Optional.of(existingComponents.get(0)); @@ -73,6 +81,7 @@ public Optional tryToGet(String realm, String parentId, /** * Try to get a component by its properties. + * * @param subType may be null */ public Optional tryToGetComponent(String realm, String name, String subType) { @@ -88,21 +97,73 @@ public Optional tryToGetComponent(String realm, String .findFirst(); } - public ComponentRepresentation getSubComponent(String realm, String parentId, String subType, String name) { - RealmResource realmResource = realmRepository.loadRealm(realm); + public ComponentExportRepresentation getSubComponentByName(String realm, String parentId, String name) { + ComponentExportRepresentation parentComponent = getComponentById(realm, parentId); - Optional maybeComponent = realmResource.components() - .query(parentId, subType, name) - .stream() + MultivaluedHashMap subComponents = parentComponent.getSubComponents(); + List subComponentsAsList = toFlatList(subComponents); + + Optional maybeSubComponent = subComponentsAsList.stream() + .filter(c -> Objects.equals(c.getName(), name)) .findFirst(); - if(maybeComponent.isPresent()) { - return maybeComponent.get(); + if (maybeSubComponent.isPresent()) { + return maybeSubComponent.get(); } throw new RuntimeException("Cannot find sub-component by name '" + name - + "', subtype '" + subType + "' and parent-id '" + parentId + + "', and parent-id '" + parentId + "' in realm '" + realm + "' "); + } + + public ComponentExportRepresentation getComponentById(String realm, String id) { + Optional maybeComponent = tryToGetExportedComponentById(realm, id); + + if (maybeComponent.isPresent()) { + return maybeComponent.get(); + } + + throw new KeycloakRepositoryException("Cannot find component by id '" + id + "'"); + } + + private Optional tryToGetExportedComponentById(String realm, String id) { + return getFlatComponentsAndSubComponents(realm) + .stream() + .filter(c -> Objects.equals(c.getId(), id)) + .findFirst(); + } + + private List getFlatComponentsAndSubComponents(String realm) { + RealmRepresentation exportedRealm = realmRepository.partialExport(realm); + + MultivaluedHashMap components = exportedRealm.getComponents(); + + return toFlatList(components); + } + + private List getAllComponentsAndSubComponents(ComponentExportRepresentation component) { + List allComponents = new ArrayList<>(); + allComponents.add(component); + + MultivaluedHashMap subComponents = component.getSubComponents(); + allComponents.addAll(toFlatList(subComponents)); + + return allComponents; + } + + private List toFlatList(MultivaluedHashMap componentsMap) { + List allComponents = new ArrayList<>(); + + Set>> componentsLists = componentsMap.entrySet(); + + for (Map.Entry> componentsList : componentsLists) { + List components = componentsList.getValue(); + + for (ComponentExportRepresentation component : components) { + allComponents.addAll(getAllComponentsAndSubComponents(component)); + } + } + return allComponents; } } diff --git a/config-cli/src/main/java/de/adorsys/keycloak/config/service/ComponentImportService.java b/config-cli/src/main/java/de/adorsys/keycloak/config/service/ComponentImportService.java index 6d8f9ec1c..b33a03d06 100644 --- a/config-cli/src/main/java/de/adorsys/keycloak/config/service/ComponentImportService.java +++ b/config-cli/src/main/java/de/adorsys/keycloak/config/service/ComponentImportService.java @@ -1,5 +1,7 @@ package de.adorsys.keycloak.config.service; +import de.adorsys.keycloak.config.exception.ImportProcessingException; +import de.adorsys.keycloak.config.exception.KeycloakRepositoryException; import de.adorsys.keycloak.config.model.RealmImport; import de.adorsys.keycloak.config.repository.ComponentRepository; import de.adorsys.keycloak.config.util.CloneUtils; @@ -14,6 +16,7 @@ import java.util.List; import java.util.Map; import java.util.Optional; +import java.util.Set; @Service public class ComponentImportService { @@ -44,21 +47,19 @@ private void createOrUpdateComponents(String realm, String providerType, List maybeComponent = componentRepository.tryToGetComponent(realm, componentToImport.getName(), componentToImport.getSubType()); - MultivaluedHashMap subComponentsToImport = componentToImport.getSubComponents(); if (maybeComponent.isPresent()) { - updateComponentIfNeeded(realm, componentToImport, maybeComponent.get(), subComponentsToImport); + updateComponentIfNeeded(realm, providerType, componentToImport, maybeComponent.get()); } else { logger.debug("Creating component: {}/{}", providerType, componentToImport.getName()); - createComponent(realm, providerType, componentToImport, subComponentsToImport); + createComponent(realm, providerType, componentToImport); } } private void createComponent( String realm, String providerType, - ComponentExportRepresentation component, - MultivaluedHashMap subComponentChildren + ComponentExportRepresentation component ) { ComponentRepresentation subComponentToAdd = CloneUtils.deepClone(component, ComponentRepresentation.class); @@ -68,70 +69,117 @@ private void createComponent( componentRepository.create(realm, subComponentToAdd); - if (subComponentChildren != null && !subComponentChildren.isEmpty()) { - ComponentRepresentation exitingComponent = componentRepository.get(realm, component.getSubType(), component.getName()); - createOrUpdateSubComponents(realm, subComponentChildren, exitingComponent.getId()); + MultivaluedHashMap subComponents = component.getSubComponents(); + + if (subComponents != null && !subComponents.isEmpty()) { + ComponentRepresentation exitingComponent = componentRepository.get(realm, providerType, component.getName()); + createOrUpdateSubComponents(realm, subComponents, exitingComponent.getId()); } } private void updateComponentIfNeeded( String realm, + String providerType, ComponentExportRepresentation componentToImport, - ComponentRepresentation existingComponent, - MultivaluedHashMap subComponentChildren + ComponentRepresentation existingComponent ) { ComponentRepresentation patchedComponent = CloneUtils.patch(existingComponent, componentToImport, "id"); boolean hasToBeUpdated = !CloneUtils.deepEquals(existingComponent, patchedComponent); if (hasToBeUpdated) { - updateComponent(realm, componentToImport, patchedComponent, subComponentChildren); + updateComponent(realm, providerType, componentToImport, patchedComponent); } else { logger.debug("No need to update component: {}/{}", existingComponent.getProviderType(), componentToImport.getName()); } } - private void updateComponent(String realm, ComponentExportRepresentation componentToImport, ComponentRepresentation patchedComponent, MultivaluedHashMap subComponentChildren) { + private void updateComponent( + String realm, + String providerType, + ComponentExportRepresentation componentToImport, + ComponentRepresentation patchedComponent + ) { logger.debug("Updating component: {}/{}", patchedComponent.getProviderType(), componentToImport.getName()); + if (patchedComponent.getProviderType() == null) { + patchedComponent.setProviderType(providerType); + } + componentRepository.update(realm, patchedComponent); - if (subComponentChildren != null && !subComponentChildren.isEmpty()) { - createOrUpdateSubComponents(realm, subComponentChildren, patchedComponent.getId()); + MultivaluedHashMap subComponents = componentToImport.getSubComponents(); + + if (subComponents != null && !subComponents.isEmpty()) { + createOrUpdateSubComponents(realm, subComponents, patchedComponent.getId()); } } - private void createOrUpdateSubComponents(String realm, Map> componentsToImport, String parentId) { - for (Map.Entry> entry : componentsToImport.entrySet()) { - createOrUpdateSubComponents(realm, entry.getValue(), parentId); + private void createOrUpdateSubComponents(String realm, Map> subComponents, String parentId) { + for (Map.Entry> entry : subComponents.entrySet()) { + createOrUpdateSubComponents(realm, entry.getKey(), entry.getValue(), parentId); } } - private void createOrUpdateSubComponents(String realm, List components, String parentId) { - for (ComponentExportRepresentation component : components) { - createOrUpdateSubComponent(realm, parentId, component); + private void createOrUpdateSubComponents(String realm, String providerType, List subComponents, String parentId) { + for (ComponentExportRepresentation subComponent : subComponents) { + createOrUpdateSubComponent(realm, parentId, providerType, subComponent); } } - private void createOrUpdateSubComponent(String realm, String parentId, ComponentExportRepresentation component) { - Optional maybeComponent = componentRepository.tryToGet(realm, parentId, component.getSubType(), component.getName()); - MultivaluedHashMap subComponentChildren = component.getSubComponents(); + private void createOrUpdateSubComponent(String realm, String parentId, String providerType, ComponentExportRepresentation subComponent) { + Optional maybeComponent = componentRepository.tryToGet(realm, parentId, subComponent.getSubType(), subComponent.getName()); if (maybeComponent.isPresent()) { - updateComponentIfNeeded(realm, component, maybeComponent.get(), subComponentChildren); + updateComponentIfNeeded(realm, providerType, subComponent, maybeComponent.get()); } else { - updateSubComponent(realm, parentId, component, subComponentChildren); + createSubComponent(realm, parentId, providerType, subComponent); } } - private void updateSubComponent(String realm, String parentId, ComponentExportRepresentation component, MultivaluedHashMap subComponentChildren) { - ComponentRepresentation subComponentToAdd = CloneUtils.deepClone(component, ComponentRepresentation.class); + private void createSubComponent(String realm, String parentId, String providerType, ComponentExportRepresentation subComponent) { + logger.debug("Create sub-component '{}' for provider-type '{}' within component with id '{}' and realm '{}'", subComponent.getName(), providerType, parentId, realm); - componentRepository.create(realm, subComponentToAdd); + ComponentRepresentation clonedSubComponent = CloneUtils.deepClone(subComponent, ComponentRepresentation.class); + + if (clonedSubComponent.getProviderType() == null) { + clonedSubComponent.setProviderType(providerType); + } + + if (clonedSubComponent.getParentId() == null) { + clonedSubComponent.setParentId(parentId); + } + + try { + componentRepository.create(realm, clonedSubComponent); + } catch (KeycloakRepositoryException e) { + throw new ImportProcessingException("Cannot create sub-component '" + clonedSubComponent.getName() + "' in realm '" + realm + "'", e); + } + + createSubComponents(realm, parentId, subComponent); + } + + private void createSubComponents(String realm, String parentId, ComponentExportRepresentation subComponent) { + MultivaluedHashMap subComponents = subComponent.getSubComponents(); + + if (subComponents != null && !subComponents.isEmpty()) { + ComponentExportRepresentation parentComponent = componentRepository.getSubComponentByName(realm, parentId, subComponent.getName()); + + createSubComponents(realm, parentComponent.getId(), subComponents.entrySet()); + } + } + + private void createSubComponents(String realm, String parentId, Set>> subComponents) { + for (Map.Entry> subComponentsToCreate : subComponents) { + String providerType = subComponentsToCreate.getKey(); + + createSubComponents(realm, parentId, providerType, subComponentsToCreate); + } + } - if (subComponentChildren != null) { - ComponentRepresentation exitingComponent = componentRepository.getSubComponent(realm, parentId, component.getSubType(), component.getName()); - createOrUpdateSubComponents(realm, subComponentChildren, exitingComponent.getId()); + private void createSubComponents(String realm, String parentId, String subComponentsProviderType, Map.Entry> subComponentsToCreate) { + for (ComponentExportRepresentation subComponentToCreate : subComponentsToCreate.getValue()) { + createSubComponent(realm, parentId, subComponentsProviderType, subComponentToCreate); } } } diff --git a/config-cli/src/test/java/de/adorsys/keycloak/config/ImportComponentsIT.java b/config-cli/src/test/java/de/adorsys/keycloak/config/ImportComponentsIT.java index dd7cac9c4..b0ef52926 100644 --- a/config-cli/src/test/java/de/adorsys/keycloak/config/ImportComponentsIT.java +++ b/config-cli/src/test/java/de/adorsys/keycloak/config/ImportComponentsIT.java @@ -13,6 +13,7 @@ import org.junit.runner.RunWith; import org.keycloak.admin.client.resource.RealmResource; import org.keycloak.common.util.MultivaluedHashMap; +import org.keycloak.representations.idm.ComponentExportRepresentation; import org.keycloak.representations.idm.ComponentRepresentation; import org.keycloak.representations.idm.RealmRepresentation; import org.springframework.beans.factory.annotation.Autowired; @@ -25,11 +26,11 @@ import java.io.File; import java.util.List; import java.util.Map; +import java.util.Objects; import java.util.Optional; import java.util.stream.Collectors; -import static org.hamcrest.Matchers.containsInAnyOrder; -import static org.hamcrest.Matchers.hasSize; +import static org.hamcrest.Matchers.*; import static org.hamcrest.core.Is.is; import static org.hamcrest.core.IsNot.not; import static org.hamcrest.core.IsNull.nullValue; @@ -37,8 +38,8 @@ @RunWith(SpringRunner.class) @ContextConfiguration( - classes = { TestConfiguration.class }, - initializers = { ConfigFileApplicationContextInitializer.class } + classes = {TestConfiguration.class}, + initializers = {ConfigFileApplicationContextInitializer.class} ) @ActiveProfiles("IT") @DirtiesContext @@ -78,6 +79,8 @@ public void integrationTests() throws Exception { shouldUpdateComponentsConfig(); shouldUpdateAddComponentsConfig(); shouldAddComponentForSameProviderType(); + shouldAddComponentWithSubComponent(); + shouldUpdateConfigOfSubComponent(); } private void shouldCreateRealmWithComponent() throws Exception { @@ -97,8 +100,6 @@ private void shouldCreateRealmWithComponent() throws Exception { assertThat(createdComponent.getProviderId(), is("rsa-generated")); MultivaluedHashMap componentConfig = createdComponent.getConfig(); - System.out.println(componentConfig.keySet()); - List keySize = componentConfig.get("keySize"); assertThat(keySize, hasSize(1)); assertThat(keySize.get(0), is("4096")); @@ -121,8 +122,6 @@ private void shouldUpdateComponentsConfig() throws Exception { assertThat(createdComponent.getProviderId(), is("rsa-generated")); MultivaluedHashMap componentConfig = createdComponent.getConfig(); - System.out.println(componentConfig.keySet()); - List keySize = componentConfig.get("keySize"); assertThat(keySize, hasSize(1)); assertThat(keySize.get(0), is("2048")); @@ -147,8 +146,6 @@ private void shouldUpdateAddComponentsConfig() throws Exception { assertThat(createdComponent.getSubType(), is("authenticated")); MultivaluedHashMap componentConfig = createdComponent.getConfig(); - System.out.println(componentConfig.keySet()); - List mapperTypes = componentConfig.get("allowed-protocol-mapper-types"); assertThat(mapperTypes, hasSize(8)); assertThat(mapperTypes, containsInAnyOrder( @@ -181,13 +178,96 @@ private void shouldAddComponentForSameProviderType() throws Exception { assertThat(createdComponent.getProviderType(), is("org.keycloak.keys.KeyProvider")); MultivaluedHashMap componentConfig = createdComponent.getConfig(); - System.out.println(componentConfig.keySet()); - List secretSizeSize = componentConfig.get("secretSize"); assertThat(secretSizeSize, hasSize(1)); assertThat(secretSizeSize.get(0), is("32")); } + private void shouldAddComponentWithSubComponent() throws Exception { + doImport("4_update_realm__add_component_with_subcomponent.json"); + + ComponentExportRepresentation createdComponent = exportComponent( + REALM_NAME, + "org.keycloak.storage.UserStorageProvider", + "my-realm-userstorage" + ); + + assertThat(createdComponent.getName(), is("my-realm-userstorage")); + assertThat(createdComponent.getProviderId(), is("ldap")); + + MultivaluedHashMap subComponentsMap = createdComponent.getSubComponents(); + ComponentExportRepresentation subComponent = getSubComponent( + subComponentsMap, + "org.keycloak.storage.ldap.mappers.LDAPStorageMapper", + "my-realm-role-mapper" + ); + + assertThat(subComponent.getName(), is(equalTo("my-realm-role-mapper"))); + assertThat(subComponent.getProviderId(), is(equalTo("role-ldap-mapper"))); + + MultivaluedHashMap config = subComponent.getConfig(); + assertThat(config.size(), is(10)); + + assertConfigHasValue(config, "mode", "LDAP_ONLY"); + assertConfigHasValue(config, "membership.attribute.type", "DN"); + assertConfigHasValue(config, "user.roles.retrieve.strategy", "LOAD_ROLES_BY_MEMBER_ATTRIBUTE_RECURSIVELY"); + assertConfigHasValue(config, "roles.dn", "someDN"); + assertConfigHasValue(config, "membership.ldap.attribute", "member"); + assertConfigHasValue(config, "membership.user.ldap.attribute", "userPrincipalName"); + assertConfigHasValue(config, "memberof.ldap.attribute", "memberOf"); + assertConfigHasValue(config, "role.name.ldap.attribute", "cn"); + assertConfigHasValue(config, "use.realm.roles.mapping", "true"); + assertConfigHasValue(config, "role.object.classes", "group"); + } + + private void shouldUpdateConfigOfSubComponent() throws Exception { + doImport("5_update_realm__update_config_in_subcomponent.json"); + + ComponentExportRepresentation createdComponent = exportComponent( + REALM_NAME, + "org.keycloak.storage.UserStorageProvider", + "my-realm-userstorage" + ); + + assertThat(createdComponent.getName(), is("my-realm-userstorage")); + assertThat(createdComponent.getProviderId(), is("ldap")); + + MultivaluedHashMap subComponentsMap = createdComponent.getSubComponents(); + ComponentExportRepresentation subComponent = getSubComponent( + subComponentsMap, + "org.keycloak.storage.ldap.mappers.LDAPStorageMapper", + "my-realm-role-mapper" + ); + + assertThat(subComponent.getName(), is(equalTo("my-realm-role-mapper"))); + assertThat(subComponent.getProviderId(), is(equalTo("role-ldap-mapper"))); + + MultivaluedHashMap config = subComponent.getConfig(); + assertThat(config.size(), is(11)); + + assertConfigHasValue(config, "mode", "LDAP_ONLY"); + assertConfigHasValue(config, "membership.attribute.type", "DN"); + assertConfigHasValue(config, "user.roles.retrieve.strategy", "LOAD_ROLES_BY_MEMBER_ATTRIBUTE_RECURSIVELY"); + assertConfigHasValue(config, "roles.dn", "someDN"); + assertConfigHasValue(config, "membership.ldap.attribute", "member"); + assertConfigHasValue(config, "membership.user.ldap.attribute", "userPrincipalName"); + assertConfigHasValue(config, "memberof.ldap.attribute", "memberOf"); + assertConfigHasValue(config, "role.name.ldap.attribute", "cn"); + assertConfigHasValue(config, "use.realm.roles.mapping", "false"); + assertConfigHasValue(config, "role.object.classes", "group"); + assertConfigHasValue(config, "client.id", "my-client-id"); + } + + private void assertConfigHasValue(MultivaluedHashMap config, String configKey, String expectedConfigValue) { + assertThat(config, hasKey(configKey)); + List configValues = config.get(configKey); + + assertThat(configValues, hasSize(1)); + + String configValue = configValues.get(0); + assertThat(configValue, is(equalTo(expectedConfigValue))); + } + private ComponentRepresentation getComponent(String providerType, String name, String subType) { return tryToGetComponent(providerType, name, subType).get(); } @@ -196,6 +276,25 @@ private ComponentRepresentation getComponent(String providerType, String name) { return tryToGetComponent(providerType, name).get(); } + private ComponentExportRepresentation exportComponent(String realm, String providerType, String name) { + RealmRepresentation exportedRealm = keycloakProvider.get().realm(realm).partialExport(true, true); + + return exportedRealm.getComponents() + .get(providerType) + .stream() + .filter(c -> c.getName().equals(name)) + .findFirst() + .get(); + } + + private ComponentExportRepresentation getSubComponent(MultivaluedHashMap subComponents, String providerType, String name) { + return subComponents.get(providerType) + .stream() + .filter(c -> Objects.equals(c.getName(), name)) + .findFirst() + .get(); + } + private Optional tryToGetComponent(String providerType, String name, String subType) { RealmResource realmResource = keycloakProvider.get() .realm(REALM_NAME); @@ -211,7 +310,7 @@ private Optional tryToGetComponent(String providerType, assertThat(existingComponents, hasSize(1)); - if(existingComponents.isEmpty()) { + if (existingComponents.isEmpty()) { maybeComponent = Optional.empty(); } else { maybeComponent = Optional.of(existingComponents.get(0)); @@ -235,7 +334,7 @@ private Optional tryToGetComponent(String providerType, assertThat(existingComponents, hasSize(1)); - if(existingComponents.isEmpty()) { + if (existingComponents.isEmpty()) { maybeComponent = Optional.empty(); } else { maybeComponent = Optional.of(existingComponents.get(0)); diff --git a/config-cli/src/test/resources/import-files/components/4_update_realm__add_component_with_subcomponent.json b/config-cli/src/test/resources/import-files/components/4_update_realm__add_component_with_subcomponent.json new file mode 100644 index 000000000..deef6d6ce --- /dev/null +++ b/config-cli/src/test/resources/import-files/components/4_update_realm__add_component_with_subcomponent.json @@ -0,0 +1,190 @@ +{ + "enabled": true, + "realm": "realmWithComponents", + "components": { + "org.keycloak.keys.KeyProvider": [ + { + "name": "rsa-generated", + "providerId": "rsa-generated", + "config": { + "keySize": [ + "2048" + ], + "priority": [ + "100" + ] + } + }, + { + "name": "hmac-generated", + "providerId": "hmac-generated", + "config": { + "secretSize": [ + "32" + ], + "priority": [ + "100" + ] + } + } + ], + "org.keycloak.services.clientregistration.policy.ClientRegistrationPolicy": [ + { + "name": "Allowed Protocol Mapper Types", + "providerId": "allowed-protocol-mappers", + "subType": "authenticated", + "config": { + "allowed-protocol-mapper-types": [ + "oidc-full-name-mapper", + "oidc-sha256-pairwise-sub-mapper", + "oidc-address-mapper", + "saml-user-property-mapper", + "oidc-usermodel-property-mapper", + "saml-role-list-mapper", + "saml-user-attribute-mapper", + "oidc-usermodel-attribute-mapper" + ] + } + } + ], + "org.keycloak.storage.UserStorageProvider": [ + { + "name": "my-realm-userstorage", + "providerId": "ldap", + "subComponents": { + "org.keycloak.storage.ldap.mappers.LDAPStorageMapper": [ + { + "name": "my-realm-role-mapper", + "providerId": "role-ldap-mapper", + "subComponents": {}, + "config": { + "mode": [ + "LDAP_ONLY" + ], + "membership.attribute.type": [ + "DN" + ], + "user.roles.retrieve.strategy": [ + "LOAD_ROLES_BY_MEMBER_ATTRIBUTE_RECURSIVELY" + ], + "roles.dn": [ + "someDN" + ], + "membership.ldap.attribute": [ + "member" + ], + "membership.user.ldap.attribute": [ + "userPrincipalName" + ], + "memberof.ldap.attribute": [ + "memberOf" + ], + "role.name.ldap.attribute": [ + "cn" + ], + "use.realm.roles.mapping": [ + "true" + ], + "role.object.classes": [ + "group" + ] + } + } + ] + }, + "config": { + "pagination": [ + "true" + ], + "fullSyncPeriod": [ + "-1" + ], + "connectionPooling": [ + "true" + ], + "usersDn": [ + "someDN" + ], + "cachePolicy": [ + "DEFAULT" + ], + "useKerberosForPasswordAuthentication": [ + "false" + ], + "importEnabled": [ + "true" + ], + "enabled": [ + "true" + ], + "changedSyncPeriod": [ + "-1" + ], + "bindCredential": [ + "adadasdasdasdasdasdasdasd" + ], + "bindDn": [ + "someBindDN" + ], + "usernameLDAPAttribute": [ + "userPrincipalName" + ], + "lastSync": [ + "1539695370" + ], + "vendor": [ + "ad" + ], + "uuidLDAPAttribute": [ + "objectGUID" + ], + "allowKerberosAuthentication": [ + "false" + ], + "connectionUrl": [ + "ldaps://1.something.local ldaps://1.something.local" + ], + "syncRegistrations": [ + "false" + ], + "authType": [ + "simple" + ], + "connectionTimeout": [ + "60000" + ], + "debug": [ + "false" + ], + "searchScope": [ + "2" + ], + "useTruststoreSpi": [ + "never" + ], + "priority": [ + "0" + ], + "userObjectClasses": [ + "person, organizationalPerson, user" + ], + "rdnLDAPAttribute": [ + "cn" + ], + "readTimeout": [ + "60000" + ], + "editMode": [ + "WRITABLE" + ], + "validatePasswordPolicy": [ + "false" + ], + "batchSizeForSync": [ + "1000" + ] + } + } + ] + } +} diff --git a/config-cli/src/test/resources/import-files/components/5_update_realm__update_config_in_subcomponent.json b/config-cli/src/test/resources/import-files/components/5_update_realm__update_config_in_subcomponent.json new file mode 100644 index 000000000..827790101 --- /dev/null +++ b/config-cli/src/test/resources/import-files/components/5_update_realm__update_config_in_subcomponent.json @@ -0,0 +1,193 @@ +{ + "enabled": true, + "realm": "realmWithComponents", + "components": { + "org.keycloak.keys.KeyProvider": [ + { + "name": "rsa-generated", + "providerId": "rsa-generated", + "config": { + "keySize": [ + "2048" + ], + "priority": [ + "100" + ] + } + }, + { + "name": "hmac-generated", + "providerId": "hmac-generated", + "config": { + "secretSize": [ + "32" + ], + "priority": [ + "100" + ] + } + } + ], + "org.keycloak.services.clientregistration.policy.ClientRegistrationPolicy": [ + { + "name": "Allowed Protocol Mapper Types", + "providerId": "allowed-protocol-mappers", + "subType": "authenticated", + "config": { + "allowed-protocol-mapper-types": [ + "oidc-full-name-mapper", + "oidc-sha256-pairwise-sub-mapper", + "oidc-address-mapper", + "saml-user-property-mapper", + "oidc-usermodel-property-mapper", + "saml-role-list-mapper", + "saml-user-attribute-mapper", + "oidc-usermodel-attribute-mapper" + ] + } + } + ], + "org.keycloak.storage.UserStorageProvider": [ + { + "name": "my-realm-userstorage", + "providerId": "ldap", + "subComponents": { + "org.keycloak.storage.ldap.mappers.LDAPStorageMapper": [ + { + "name": "my-realm-role-mapper", + "providerId": "role-ldap-mapper", + "subComponents": {}, + "config": { + "mode": [ + "LDAP_ONLY" + ], + "membership.attribute.type": [ + "DN" + ], + "user.roles.retrieve.strategy": [ + "LOAD_ROLES_BY_MEMBER_ATTRIBUTE_RECURSIVELY" + ], + "roles.dn": [ + "someDN" + ], + "membership.ldap.attribute": [ + "member" + ], + "membership.user.ldap.attribute": [ + "userPrincipalName" + ], + "memberof.ldap.attribute": [ + "memberOf" + ], + "role.name.ldap.attribute": [ + "cn" + ], + "use.realm.roles.mapping": [ + "false" + ], + "role.object.classes": [ + "group" + ], + "client.id": [ + "my-client-id" + ] + } + } + ] + }, + "config": { + "pagination": [ + "true" + ], + "fullSyncPeriod": [ + "-1" + ], + "connectionPooling": [ + "true" + ], + "usersDn": [ + "someDN" + ], + "cachePolicy": [ + "DEFAULT" + ], + "useKerberosForPasswordAuthentication": [ + "false" + ], + "importEnabled": [ + "true" + ], + "enabled": [ + "true" + ], + "changedSyncPeriod": [ + "-1" + ], + "bindCredential": [ + "adadasdasdasdasdasdasdasd" + ], + "bindDn": [ + "someBindDN" + ], + "usernameLDAPAttribute": [ + "userPrincipalName" + ], + "lastSync": [ + "1539695370" + ], + "vendor": [ + "ad" + ], + "uuidLDAPAttribute": [ + "objectGUID" + ], + "allowKerberosAuthentication": [ + "false" + ], + "connectionUrl": [ + "ldaps://1.something.local ldaps://1.something.local" + ], + "syncRegistrations": [ + "false" + ], + "authType": [ + "simple" + ], + "connectionTimeout": [ + "60000" + ], + "debug": [ + "false" + ], + "searchScope": [ + "2" + ], + "useTruststoreSpi": [ + "never" + ], + "priority": [ + "0" + ], + "userObjectClasses": [ + "person, organizationalPerson, user" + ], + "rdnLDAPAttribute": [ + "cn" + ], + "readTimeout": [ + "60000" + ], + "editMode": [ + "WRITABLE" + ], + "validatePasswordPolicy": [ + "false" + ], + "batchSizeForSync": [ + "1000" + ] + } + } + ] + } +}