From 66e210b129c3a4401f1343ab2941f8ec7f60caef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Francesco=20Chicchiricc=C3=B2?= Date: Thu, 13 Aug 2026 16:08:19 +0200 Subject: [PATCH] [SYNCOPE-1988] Reviewing ConnInstanceTO and ResourceTO generation to strip out confidential property values --- .../syncope/core/logic/ConnectorLogic.java | 36 +++++----- .../syncope/core/logic/IdMLogicContext.java | 2 - .../syncope/core/logic/ResourceLogic.java | 7 +- .../syncope/core/logic/ResourceLogicTest.java | 2 +- .../jpa/DummyConnectorManager.java | 3 +- .../neo4j/DummyConnectorManager.java | 3 +- .../provisioning/api/ConnectorManager.java | 3 +- .../api/data/ConnInstanceDataBinder.java | 67 ++++++++++++++++++- .../java/ConnectorFacadeProxy.java | 13 ++-- .../java/DefaultConnectorManager.java | 28 ++------ .../java/ProvisioningContext.java | 6 -- .../java/data/ConnInstanceDataBinderImpl.java | 52 +++++--------- .../java/data/ResourceDataBinderImpl.java | 15 +++++ .../java/ConnectorManagerTest.java | 2 +- .../core/starter/SyncopeCoreApplication.java | 4 +- .../ExternalResourcesHealthIndicator.java | 7 +- .../apache/syncope/fit/AbstractITCase.java | 44 ++++++------ .../syncope/fit/core/PullTaskITCase.java | 2 + .../src/test/resources/test.properties | 4 ++ 19 files changed, 162 insertions(+), 138 deletions(-) diff --git a/core/idm/logic/src/main/java/org/apache/syncope/core/logic/ConnectorLogic.java b/core/idm/logic/src/main/java/org/apache/syncope/core/logic/ConnectorLogic.java index 9b955043dc7..967d451f216 100644 --- a/core/idm/logic/src/main/java/org/apache/syncope/core/logic/ConnectorLogic.java +++ b/core/idm/logic/src/main/java/org/apache/syncope/core/logic/ConnectorLogic.java @@ -34,6 +34,7 @@ import org.apache.syncope.common.lib.to.PlainSchemaTO; import org.apache.syncope.common.lib.types.AttrSchemaType; import org.apache.syncope.common.lib.types.ClientExceptionType; +import org.apache.syncope.common.lib.types.ConnConfProperty; import org.apache.syncope.common.lib.types.IdMEntitlement; import org.apache.syncope.core.persistence.api.dao.ConnInstanceDAO; import org.apache.syncope.core.persistence.api.dao.ExternalResourceDAO; @@ -109,7 +110,7 @@ public ConnInstanceTO create(final ConnInstanceTO connInstanceTO) { connInstanceTO.getAdminRealm()); securityChecks(effectiveRealms, connInstanceTO.getAdminRealm(), null); - return binder.getConnInstanceTO(doSave(binder.getConnInstance(connInstanceTO))); + return binder.getConnInstanceTO(doSave(binder.create(connInstanceTO))); } @PreAuthorize("hasRole('" + IdMEntitlement.CONNECTOR_UPDATE + "')") @@ -163,10 +164,8 @@ public List list(final String lang) { public ConnInstanceTO read(final String key, final String lang) { CurrentLocale.set(StringUtils.isBlank(lang) ? Locale.ENGLISH : Locale.of(lang)); - ConnInstance connInstance = connInstanceDAO.authFind(key); - if (connInstance == null) { - throw new NotFoundException("Connector '" + key + '\''); - } + ConnInstance connInstance = Optional.ofNullable(connInstanceDAO.authFind(key)). + orElseThrow(() -> new NotFoundException("Connector '" + key + '\'')); return binder.getConnInstanceTO(connInstance); } @@ -195,7 +194,7 @@ public List getBundles(final String lang) { ConfigurationProperties properties = connIdBundleManager.getConfigurationProperties(bundle); connBundleTO.getProperties().addAll(properties.getPropertyNames().stream(). - map(propName -> binder.build(properties.getProperty(propName))). + map(propName -> ConnInstanceDataBinder.build(properties.getProperty(propName))). toList()); return connBundleTO; @@ -208,13 +207,12 @@ public List getBundles(final String lang) { public List buildObjectClassInfo( final ConnInstanceTO connInstanceTO, final boolean includeSpecial) { - ConnInstanceTO actual = connInstanceDAO.findById(connInstanceTO.getKey()). - map(binder::getConnInstanceTO). - orElse(connInstanceTO); + ConnInstance connInstance = Optional.ofNullable(connInstanceDAO.authFind(connInstanceTO.getKey())). + orElseThrow(() -> new NotFoundException("Connector '" + connInstanceTO.getKey() + '\'')); Set objectClassInfo = connectorManager.createConnector( connectorManager.buildConnInstanceOverride( - actual, Optional.of(connInstanceTO.getConf()), Optional.empty())). + connInstance, Optional.of(connInstanceTO.getConf()), Optional.empty())). getObjectClassInfo(); return objectClassInfo.stream().map(info -> { @@ -244,24 +242,22 @@ public List buildObjectClassInfo( + "or hasRole('" + IdMEntitlement.CONNECTOR_UPDATE + "'))") @Transactional(readOnly = true) public void check(final ConnInstanceTO connInstanceTO) { - if (connInstanceTO.getAdminRealm() == null) { - throw SyncopeClientException.build(ClientExceptionType.InvalidRealm); - } + Optional.ofNullable(connInstanceTO.getKey()).flatMap(connInstanceDAO::findById).ifPresent(connInstance -> { + List newConf = + ConnInstanceDataBinder.newConf(connInstance.getConf(), connInstanceTO.getConf()); + connInstanceTO.getConf().clear(); + connInstanceTO.getConf().addAll(newConf); + }); - connectorManager.createConnector(binder.getConnInstance(connInstanceTO)).test(); + connectorManager.createConnector(binder.create(connInstanceTO)).test(); } @PreAuthorize("hasRole('" + IdMEntitlement.CONNECTOR_READ + "')") @Transactional(readOnly = true) public ConnInstanceTO readByResource(final String resourceName, final String lang) { - CurrentLocale.set(StringUtils.isBlank(lang) ? Locale.ENGLISH : Locale.of(lang)); - ExternalResource resource = resourceDAO.findById(resourceName). orElseThrow(() -> new NotFoundException("Resource " + resourceName)); - ConnInstanceTO connInstance = binder.getConnInstanceTO( - connectorManager.getConnector(resource).getConnInstance()); - connInstance.setKey(resource.getConnector().getKey()); - return connInstance; + return read(resource.getConnector().getKey(), lang); } @PreAuthorize("hasRole('" + IdMEntitlement.CONNECTOR_RELOAD + "')") diff --git a/core/idm/logic/src/main/java/org/apache/syncope/core/logic/IdMLogicContext.java b/core/idm/logic/src/main/java/org/apache/syncope/core/logic/IdMLogicContext.java index f12d1cb581c..c6eb2c038aa 100644 --- a/core/idm/logic/src/main/java/org/apache/syncope/core/logic/IdMLogicContext.java +++ b/core/idm/logic/src/main/java/org/apache/syncope/core/logic/IdMLogicContext.java @@ -124,7 +124,6 @@ public ResourceLogic resourceLogic( final AnyTypeDAO anyTypeDAO, final ExternalResourceDAO resourceDAO, final ConnInstanceDAO connInstanceDAO, - final ConnInstanceDataBinder connInstanceDataBinder, final ConnectorManager connectorManager, final OutboundMatcher outboundMatcher, final MappingManager mappingManager) { @@ -134,7 +133,6 @@ public ResourceLogic resourceLogic( anyTypeDAO, connInstanceDAO, resourceDataBinder, - connInstanceDataBinder, outboundMatcher, mappingManager, connectorManager, diff --git a/core/idm/logic/src/main/java/org/apache/syncope/core/logic/ResourceLogic.java b/core/idm/logic/src/main/java/org/apache/syncope/core/logic/ResourceLogic.java index f28f07591e2..1b77821b5f9 100644 --- a/core/idm/logic/src/main/java/org/apache/syncope/core/logic/ResourceLogic.java +++ b/core/idm/logic/src/main/java/org/apache/syncope/core/logic/ResourceLogic.java @@ -48,7 +48,6 @@ import org.apache.syncope.core.provisioning.api.Connector; import org.apache.syncope.core.provisioning.api.ConnectorManager; import org.apache.syncope.core.provisioning.api.MappingManager; -import org.apache.syncope.core.provisioning.api.data.ConnInstanceDataBinder; import org.apache.syncope.core.provisioning.api.data.ResourceDataBinder; import org.apache.syncope.core.provisioning.java.pushpull.OutboundMatcher; import org.apache.syncope.core.provisioning.java.utils.ConnObjectUtils; @@ -75,8 +74,6 @@ public class ResourceLogic extends AbstractTransactionalLogic { protected final ResourceDataBinder binder; - protected final ConnInstanceDataBinder connInstanceDataBinder; - protected final OutboundMatcher outboundMatcher; protected final MappingManager mappingManager; @@ -90,7 +87,6 @@ public ResourceLogic( final AnyTypeDAO anyTypeDAO, final ConnInstanceDAO connInstanceDAO, final ResourceDataBinder binder, - final ConnInstanceDataBinder connInstanceDataBinder, final OutboundMatcher outboundMatcher, final MappingManager mappingManager, final ConnectorManager connectorManager, @@ -100,7 +96,6 @@ public ResourceLogic( this.anyTypeDAO = anyTypeDAO; this.connInstanceDAO = connInstanceDAO; this.binder = binder; - this.connInstanceDataBinder = connInstanceDataBinder; this.outboundMatcher = outboundMatcher; this.mappingManager = mappingManager; this.connectorManager = connectorManager; @@ -430,7 +425,7 @@ public void check(final ResourceTO resourceTO) { connectorManager.createConnector( connectorManager.buildConnInstanceOverride( - connInstanceDataBinder.getConnInstanceTO(connInstance), + connInstance, resourceTO.getConfOverride(), resourceTO.getCapabilitiesOverride())). test(); diff --git a/core/idm/logic/src/test/java/org/apache/syncope/core/logic/ResourceLogicTest.java b/core/idm/logic/src/test/java/org/apache/syncope/core/logic/ResourceLogicTest.java index 2b691415d74..2c31400209c 100644 --- a/core/idm/logic/src/test/java/org/apache/syncope/core/logic/ResourceLogicTest.java +++ b/core/idm/logic/src/test/java/org/apache/syncope/core/logic/ResourceLogicTest.java @@ -211,7 +211,7 @@ public void setLatestSyncToken() { assertNull(resource.getProvision(AnyTypeKind.USER.name()).orElseThrow().getSyncToken()); ResourceLogic resourceLogic = new ResourceLogic( - resourceDAO, anyTypeDAO, null, null, null, null, null, connectorManager, null); + resourceDAO, anyTypeDAO, null, null, null, null, connectorManager, null); resourceLogic.setLatestSyncToken(resource.getKey(), AnyTypeKind.USER.name()); entityManager.flush(); diff --git a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/DummyConnectorManager.java b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/DummyConnectorManager.java index f966bbf167a..ff11928a6ba 100644 --- a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/DummyConnectorManager.java +++ b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/DummyConnectorManager.java @@ -21,7 +21,6 @@ import java.util.List; import java.util.Optional; import java.util.Set; -import org.apache.syncope.common.lib.to.ConnInstanceTO; import org.apache.syncope.common.lib.types.ConnConfProperty; import org.apache.syncope.common.lib.types.ConnectorCapability; import org.apache.syncope.core.persistence.api.entity.ConnInstance; @@ -41,7 +40,7 @@ public void unregisterConnector(final ExternalResource resource) { @Override public ConnInstance buildConnInstanceOverride( - final ConnInstanceTO connInstance, + final ConnInstance connInstance, final Optional> confOverride, final Optional> capabilitiesOverride) { diff --git a/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/DummyConnectorManager.java b/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/DummyConnectorManager.java index 25562e16a11..63e1c23fc63 100644 --- a/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/DummyConnectorManager.java +++ b/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/DummyConnectorManager.java @@ -21,7 +21,6 @@ import java.util.List; import java.util.Optional; import java.util.Set; -import org.apache.syncope.common.lib.to.ConnInstanceTO; import org.apache.syncope.common.lib.types.ConnConfProperty; import org.apache.syncope.common.lib.types.ConnectorCapability; import org.apache.syncope.core.persistence.api.entity.ConnInstance; @@ -41,7 +40,7 @@ public void unregisterConnector(final ExternalResource resource) { @Override public ConnInstance buildConnInstanceOverride( - final ConnInstanceTO connInstance, + final ConnInstance connInstance, final Optional> confOverride, final Optional> capabilitiesOverride) { diff --git a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/ConnectorManager.java b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/ConnectorManager.java index 75a0438fbfe..9a0be7a278e 100644 --- a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/ConnectorManager.java +++ b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/ConnectorManager.java @@ -21,7 +21,6 @@ import java.util.List; import java.util.Optional; import java.util.Set; -import org.apache.syncope.common.lib.to.ConnInstanceTO; import org.apache.syncope.common.lib.types.ConnConfProperty; import org.apache.syncope.common.lib.types.ConnectorCapability; import org.apache.syncope.core.persistence.api.entity.ConnInstance; @@ -43,7 +42,7 @@ public interface ConnectorManager { * @return connector instance override over base connector instance, configuration and capabilities */ ConnInstance buildConnInstanceOverride( - ConnInstanceTO connInstance, + ConnInstance connInstance, Optional> confOverride, Optional> capabilitiesOverride); diff --git a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/data/ConnInstanceDataBinder.java b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/data/ConnInstanceDataBinder.java index b4b7c183c85..dd950cdb270 100644 --- a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/data/ConnInstanceDataBinder.java +++ b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/data/ConnInstanceDataBinder.java @@ -18,18 +18,81 @@ */ package org.apache.syncope.core.provisioning.api.data; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; import org.apache.syncope.common.lib.to.ConnInstanceTO; import org.apache.syncope.common.lib.types.ConnConfPropSchema; +import org.apache.syncope.common.lib.types.ConnConfProperty; import org.apache.syncope.core.persistence.api.entity.ConnInstance; +import org.identityconnectors.common.security.GuardedString; import org.identityconnectors.framework.api.ConfigurationProperty; +import org.identityconnectors.framework.impl.api.ConfigurationPropertyImpl; public interface ConnInstanceDataBinder { - ConnConfPropSchema build(ConfigurationProperty property); + static ConnConfPropSchema build(final ConfigurationProperty property) { + ConnConfPropSchema connConfPropSchema = new ConnConfPropSchema(); - ConnInstance getConnInstance(ConnInstanceTO connInstanceTO); + connConfPropSchema.setName(property.getName()); + connConfPropSchema.setDisplayName(property.getDisplayName(property.getName())); + connConfPropSchema.setHelpMessage(property.getHelpMessage(property.getName())); + connConfPropSchema.setRequired(property.isRequired()); + connConfPropSchema.setType(property.getType().getName()); + connConfPropSchema.setOrder(((ConfigurationPropertyImpl) property).getOrder()); + connConfPropSchema.setConfidential(property.isConfidential()); + + if (property.getValue() != null) { + if (property.getValue().getClass().isArray()) { + connConfPropSchema.getDefaultValues().addAll(List.of((Object[]) property.getValue())); + } else if (property.getValue() instanceof Collection collection) { + connConfPropSchema.getDefaultValues().addAll(collection); + } else { + connConfPropSchema.getDefaultValues().add(property.getValue()); + } + } + + return connConfPropSchema; + } + + static List newConf( + final List previousConf, + final List toConf) { + + List newConf = new ArrayList<>(); + toConf.forEach(property -> { + if (property.getSchema().isConfidential() + || GuardedString.class.getName().equals(property.getSchema().getType())) { + + if (property.getValues().isEmpty()) { + // no values provided, keep existing + previousConf.stream(). + filter(p -> p.getSchema().getName().equals(property.getSchema().getName())). + findFirst().ifPresent(newConf::add); + } else { + // translate confidential properties' cleartext values into GuardedStrings + ConnConfProperty newProperty = new ConnConfProperty(); + newProperty.setSchema(property.getSchema()); + newProperty.setOverridable(property.isOverridable()); + property.getValues().forEach(value -> { + if (value instanceof String string) { + newProperty.getValues().add(new GuardedString(string.toCharArray())); + } else { + newProperty.getValues().add(value); + } + }); + } + } + + newConf.add(property); + }); + + return newConf; + } ConnInstanceTO getConnInstanceTO(ConnInstance connInstance); + ConnInstance create(ConnInstanceTO connInstanceTO); + ConnInstance update(ConnInstanceTO connInstanceTO); } diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/ConnectorFacadeProxy.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/ConnectorFacadeProxy.java index 082ff528255..04873ce32c1 100644 --- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/ConnectorFacadeProxy.java +++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/ConnectorFacadeProxy.java @@ -32,7 +32,6 @@ import org.apache.syncope.core.provisioning.api.Connector; import org.apache.syncope.core.provisioning.api.TimeoutException; import org.apache.syncope.core.provisioning.api.pushpull.ReconFilterBuilder; -import org.identityconnectors.common.CollectionUtil; import org.identityconnectors.common.security.GuardedByteArray; import org.identityconnectors.common.security.GuardedString; import org.identityconnectors.framework.api.APIConfiguration; @@ -95,7 +94,7 @@ public ConnectorFacadeProxy( // set connector configuration according to conninstance's ConfigurationProperties properties = apiConfig.getConfigurationProperties(); connInstance.getConf().stream(). - filter(property -> !CollectionUtil.isEmpty(property.getValues())). + filter(property -> !property.getValues().isEmpty()). forEach(property -> properties.setPropertyValue( property.getSchema().getName(), getPropertyValue(property.getSchema().getType(), property.getValues()))); @@ -509,9 +508,15 @@ private static Object getPropertyValue(final String propType, final List valu Class propertySchemaClass = ClassUtils.forName(propType, ClassUtils.getDefaultClassLoader()); if (GuardedString.class.equals(propertySchemaClass)) { - value = new GuardedString(values.getFirst().toString().toCharArray()); + if (values.getFirst() instanceof GuardedString) { + value = values.getFirst(); + } else { + value = new GuardedString(values.getFirst().toString().toCharArray()); + } } else if (GuardedByteArray.class.equals(propertySchemaClass)) { - value = new GuardedByteArray((byte[]) values.getFirst()); + if (values.getFirst() instanceof byte[] byteArray) { + value = new GuardedByteArray(byteArray); + } } else if (Character.class.equals(propertySchemaClass) || Character.TYPE.equals(propertySchemaClass)) { value = values.getFirst() == null || values.getFirst().toString().isEmpty() ? null : values.getFirst().toString().charAt(0); diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/DefaultConnectorManager.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/DefaultConnectorManager.java index 63b0a040517..12eff01d574 100644 --- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/DefaultConnectorManager.java +++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/DefaultConnectorManager.java @@ -25,13 +25,9 @@ import java.util.Map; import java.util.Optional; import java.util.Set; -import org.apache.syncope.common.lib.SyncopeConstants; -import org.apache.syncope.common.lib.to.ConnInstanceTO; import org.apache.syncope.common.lib.types.ConnConfProperty; import org.apache.syncope.common.lib.types.ConnectorCapability; import org.apache.syncope.core.persistence.api.dao.ExternalResourceDAO; -import org.apache.syncope.core.persistence.api.dao.RealmDAO; -import org.apache.syncope.core.persistence.api.dao.RealmSearchDAO; import org.apache.syncope.core.persistence.api.entity.ConnInstance; import org.apache.syncope.core.persistence.api.entity.EntityFactory; import org.apache.syncope.core.persistence.api.entity.ExternalResource; @@ -39,7 +35,6 @@ import org.apache.syncope.core.provisioning.api.ConnIdBundleManager; import org.apache.syncope.core.provisioning.api.Connector; import org.apache.syncope.core.provisioning.api.ConnectorManager; -import org.apache.syncope.core.provisioning.api.data.ConnInstanceDataBinder; import org.apache.syncope.core.spring.security.AuthContextUtils; import org.identityconnectors.common.l10n.CurrentLocale; import org.identityconnectors.framework.api.ConnectorFacadeFactory; @@ -60,14 +55,8 @@ protected static String getBeanName(final ExternalResource resource) { protected final ConnIdBundleManager connIdBundleManager; - protected final RealmDAO realmDAO; - - protected final RealmSearchDAO realmSearchDAO; - protected final ExternalResourceDAO resourceDAO; - protected final ConnInstanceDataBinder connInstanceDataBinder; - protected final AsyncConnectorFacade asyncFacade; protected final EntityFactory entityFactory; @@ -76,19 +65,13 @@ protected static String getBeanName(final ExternalResource resource) { public DefaultConnectorManager( final ConnIdBundleManager connIdBundleManager, - final RealmDAO realmDAO, - final RealmSearchDAO realmSearchDAO, final ExternalResourceDAO resourceDAO, - final ConnInstanceDataBinder connInstanceDataBinder, final AsyncConnectorFacade asyncFacade, final EntityFactory entityFactory, final ConfigurableApplicationContext ctx) { this.connIdBundleManager = connIdBundleManager; - this.realmDAO = realmDAO; - this.realmSearchDAO = realmSearchDAO; this.resourceDAO = resourceDAO; - this.connInstanceDataBinder = connInstanceDataBinder; this.asyncFacade = asyncFacade; this.entityFactory = entityFactory; this.ctx = ctx; @@ -104,18 +87,15 @@ public Connector getConnector(final ExternalResource resource) { }); } + @Transactional(readOnly = true) @Override public ConnInstance buildConnInstanceOverride( - final ConnInstanceTO connInstance, + final ConnInstance connInstance, final Optional> confOverride, final Optional> capabilitiesOverride) { ConnInstance override = entityFactory.newEntity(ConnInstance.class); - override.setAdminRealm(realmSearchDAO.findByFullPath(connInstance.getAdminRealm()).orElseGet(() -> { - LOG.warn("Could not find admin Realm {}, reverting to {}", - connInstance.getAdminRealm(), SyncopeConstants.ROOT_REALM); - return realmDAO.getRoot(); - })); + override.setAdminRealm(connInstance.getAdminRealm()); override.setConnectorName(connInstance.getConnectorName()); override.setDisplayName(connInstance.getDisplayName()); override.setBundleName(connInstance.getBundleName()); @@ -179,7 +159,7 @@ public void registerConnector(final ExternalResource resource) { } ConnInstance connInstance = buildConnInstanceOverride( - connInstanceDataBinder.getConnInstanceTO(resource.getConnector()), + resource.getConnector(), resource.getConfOverride(), resource.getCapabilitiesOverride()); Connector connector = createConnector(connInstance); diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/ProvisioningContext.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/ProvisioningContext.java index f95d8201dd1..6f654dc6351 100644 --- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/ProvisioningContext.java +++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/ProvisioningContext.java @@ -329,19 +329,13 @@ public JobStatusUpdater jobStatusUpdater(final JobStatusDAO jobStatusDAO) { public ConnectorManager connectorManager( final EntityFactory entityFactory, final ConnIdBundleManager connIdBundleManager, - final RealmDAO realmDAO, - final RealmSearchDAO realmSearchDAO, final ExternalResourceDAO resourceDAO, - final ConnInstanceDataBinder connInstanceDataBinder, final AsyncConnectorFacade asyncConnectorFacade, final ConfigurableApplicationContext ctx) { return new DefaultConnectorManager( connIdBundleManager, - realmDAO, - realmSearchDAO, resourceDAO, - connInstanceDataBinder, asyncConnectorFacade, entityFactory, ctx); diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/ConnInstanceDataBinderImpl.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/ConnInstanceDataBinderImpl.java index 64afec9f61d..8de7acac53d 100644 --- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/ConnInstanceDataBinderImpl.java +++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/ConnInstanceDataBinderImpl.java @@ -19,10 +19,8 @@ package org.apache.syncope.core.provisioning.java.data; import java.net.URI; -import java.util.Collection; import java.util.List; import java.util.Optional; -import java.util.function.Predicate; import org.apache.commons.lang3.tuple.Pair; import org.apache.syncope.common.lib.SyncopeClientException; import org.apache.syncope.common.lib.to.ConnInstanceTO; @@ -37,10 +35,9 @@ import org.apache.syncope.core.persistence.api.utils.ConnPoolConfUtils; import org.apache.syncope.core.provisioning.api.ConnIdBundleManager; import org.apache.syncope.core.provisioning.api.data.ConnInstanceDataBinder; +import org.identityconnectors.common.security.GuardedString; import org.identityconnectors.framework.api.ConfigurationProperties; -import org.identityconnectors.framework.api.ConfigurationProperty; import org.identityconnectors.framework.api.ConnectorInfo; -import org.identityconnectors.framework.impl.api.ConfigurationPropertyImpl; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -69,7 +66,7 @@ public ConnInstanceDataBinderImpl( } @Override - public ConnInstance getConnInstance(final ConnInstanceTO connInstanceTO) { + public ConnInstance create(final ConnInstanceTO connInstanceTO) { SyncopeClientException sce = SyncopeClientException.build(ClientExceptionType.RequiredValuesMissing); if (connInstanceTO.getLocation() == null) { @@ -108,7 +105,9 @@ public ConnInstance getConnInstance(final ConnInstanceTO connInstanceTO) { } Optional.ofNullable(connInstanceTO.getLocation()).ifPresent(connInstance::setLocation); - connInstance.setConf(connInstanceTO.getConf()); + + connInstance.setConf(ConnInstanceDataBinder.newConf(List.of(), connInstanceTO.getConf())); + Optional.ofNullable(connInstanceTO.getPoolConf()). ifPresent(conf -> connInstance.setPoolConf(ConnPoolConfUtils.getConnPoolConf(conf))); @@ -141,10 +140,12 @@ public ConnInstance update(final ConnInstanceTO connInstanceTO) { Optional.ofNullable(connInstanceTO.getVersion()).ifPresent(connInstance::setVersion); Optional.ofNullable(connInstanceTO.getConnectorName()).ifPresent(connInstance::setConnectorName); Optional.ofNullable(connInstanceTO.getDisplayName()).ifPresent(connInstance::setDisplayName); - Optional.ofNullable(connInstanceTO.getConf()). - filter(Predicate.not(Collection::isEmpty)). - ifPresent(connInstance::setConf); Optional.ofNullable(connInstanceTO.getConnRequestTimeout()).ifPresent(connInstance::setConnRequestTimeout); + + if (!connInstanceTO.getConf().isEmpty()) { + connInstance.setConf(ConnInstanceDataBinder.newConf(connInstance.getConf(), connInstanceTO.getConf())); + } + Optional.ofNullable(connInstanceTO.getPoolConf()).ifPresentOrElse( conf -> connInstance.setPoolConf(ConnPoolConfUtils.getConnPoolConf(conf)), () -> connInstance.setPoolConf(null)); @@ -152,31 +153,6 @@ public ConnInstance update(final ConnInstanceTO connInstanceTO) { return connInstance; } - @Override - public ConnConfPropSchema build(final ConfigurationProperty property) { - ConnConfPropSchema connConfPropSchema = new ConnConfPropSchema(); - - connConfPropSchema.setName(property.getName()); - connConfPropSchema.setDisplayName(property.getDisplayName(property.getName())); - connConfPropSchema.setHelpMessage(property.getHelpMessage(property.getName())); - connConfPropSchema.setRequired(property.isRequired()); - connConfPropSchema.setType(property.getType().getName()); - connConfPropSchema.setOrder(((ConfigurationPropertyImpl) property).getOrder()); - connConfPropSchema.setConfidential(property.isConfidential()); - - if (property.getValue() != null) { - if (property.getValue().getClass().isArray()) { - connConfPropSchema.getDefaultValues().addAll(List.of((Object[]) property.getValue())); - } else if (property.getValue() instanceof Collection collection) { - connConfPropSchema.getDefaultValues().addAll(collection); - } else { - connConfPropSchema.getDefaultValues().add(property.getValue()); - } - } - - return connConfPropSchema; - } - @Override public ConnInstanceTO getConnInstanceTO(final ConnInstance connInstance) { ConnInstanceTO connInstanceTO = new ConnInstanceTO(); @@ -198,7 +174,7 @@ public ConnInstanceTO getConnInstanceTO(final ConnInstance connInstance) { // refresh stored properties in the given connInstance with direct information from underlying connector ConfigurationProperties properties = connIdBundleManager.getConfigurationProperties(info.getRight()); properties.getPropertyNames().forEach(propName -> { - ConnConfPropSchema schema = build(properties.getProperty(propName)); + ConnConfPropSchema schema = ConnInstanceDataBinder.build(properties.getProperty(propName)); ConnConfProperty property = connInstanceTO.getConf(propName). orElseGet(() -> { @@ -217,6 +193,12 @@ public ConnInstanceTO getConnInstanceTO(final ConnInstance connInstance) { connInstanceTO.setLocation(connInstance.getLocation()); } + // do not export confidential property values + connInstanceTO.getConf().stream(). + filter(property -> property.getSchema().isConfidential() + || GuardedString.class.getName().equals(property.getSchema().getType())). + forEach(property -> property.getValues().clear()); + connInstanceTO.setPoolConf(connInstance.getPoolConf()); return connInstanceTO; diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/ResourceDataBinderImpl.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/ResourceDataBinderImpl.java index 9d6bb0009b5..30045dc9a04 100644 --- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/ResourceDataBinderImpl.java +++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/ResourceDataBinderImpl.java @@ -34,6 +34,7 @@ import org.apache.syncope.common.lib.to.ResourceTO; import org.apache.syncope.common.lib.types.AnyTypeKind; import org.apache.syncope.common.lib.types.ClientExceptionType; +import org.apache.syncope.common.lib.types.ConnConfProperty; import org.apache.syncope.common.lib.types.MappingPurpose; import org.apache.syncope.common.lib.types.SchemaType; import org.apache.syncope.core.persistence.api.dao.AnyTypeClassDAO; @@ -58,9 +59,11 @@ import org.apache.syncope.core.persistence.api.entity.policy.PushPolicy; import org.apache.syncope.core.provisioning.api.IntAttrName; import org.apache.syncope.core.provisioning.api.IntAttrNameParser; +import org.apache.syncope.core.provisioning.api.data.ConnInstanceDataBinder; import org.apache.syncope.core.provisioning.api.data.ResourceDataBinder; import org.apache.syncope.core.provisioning.api.jexl.JexlTools; import org.apache.syncope.core.provisioning.api.propagation.PropagationTaskExecutor; +import org.identityconnectors.common.security.GuardedString; import org.identityconnectors.framework.common.objects.ObjectClass; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -332,6 +335,13 @@ public ExternalResource update(final ExternalResource resource, final ResourceTO Implementation.class.getSimpleName(), resourceTO.getProvisionSorter())); } + resourceTO.getConfOverride().ifPresentOrElse( + confOverride -> { + List previousConf = resource.getConfOverride().orElseGet(() -> List.of()); + resource.setConfOverride(Optional.of(ConnInstanceDataBinder.newConf(previousConf, confOverride))); + }, + () -> resource.setConfOverride(Optional.empty())); + resource.setConfOverride( Optional.ofNullable(resourceTO.getConfOverride()).orElseGet(Optional::empty)); @@ -591,6 +601,11 @@ public ResourceTO getResourceTO(final ExternalResource resource) { ? null : resource.getProvisionSorter().getKey()); resourceTO.setConfOverride(resource.getConfOverride()); + // do not export confidential property values + resourceTO.getConfOverride().ifPresent(conf -> conf.stream(). + filter(property -> property.getSchema().isConfidential() + || GuardedString.class.getName().equals(property.getSchema().getType())). + forEach(property -> property.getValues().clear())); resourceTO.setCapabilitiesOverride(resource.getCapabilitiesOverride()); diff --git a/core/provisioning-java/src/test/java/org/apache/syncope/core/provisioning/java/ConnectorManagerTest.java b/core/provisioning-java/src/test/java/org/apache/syncope/core/provisioning/java/ConnectorManagerTest.java index d5f943cb53d..22652cd6b72 100644 --- a/core/provisioning-java/src/test/java/org/apache/syncope/core/provisioning/java/ConnectorManagerTest.java +++ b/core/provisioning-java/src/test/java/org/apache/syncope/core/provisioning/java/ConnectorManagerTest.java @@ -46,7 +46,7 @@ public class ConnectorManagerTest extends AbstractTest { @BeforeEach public void before() { - connManager = new DefaultConnectorManager(connIdBundleManager, null, null, resourceDAO, null, null, null, ctx); + connManager = new DefaultConnectorManager(connIdBundleManager, resourceDAO, null, null, ctx); // Remove any other connector instance bean set up by standard ConnectorManager.load() connManager.unload(); diff --git a/core/starter/src/main/java/org/apache/syncope/core/starter/SyncopeCoreApplication.java b/core/starter/src/main/java/org/apache/syncope/core/starter/SyncopeCoreApplication.java index ab67f6aaf10..2b43b83cc2c 100644 --- a/core/starter/src/main/java/org/apache/syncope/core/starter/SyncopeCoreApplication.java +++ b/core/starter/src/main/java/org/apache/syncope/core/starter/SyncopeCoreApplication.java @@ -41,7 +41,6 @@ import org.apache.syncope.core.provisioning.api.ConnIdBundleManager; import org.apache.syncope.core.provisioning.api.ConnectorManager; import org.apache.syncope.core.provisioning.api.ImplementationLookup; -import org.apache.syncope.core.provisioning.api.data.ConnInstanceDataBinder; import org.apache.syncope.core.provisioning.java.job.SyncopeTaskScheduler; import org.apache.syncope.core.starter.actuate.DefaultSyncopeCoreInfoContributor; import org.apache.syncope.core.starter.actuate.DomainsHealthIndicator; @@ -185,10 +184,9 @@ public MailHealthIndicator mailHealthIndicator(final JavaMailSender mailSender) public ExternalResourcesHealthIndicator externalResourcesHealthIndicator( final DomainOps domainOps, final ExternalResourceDAO resourceDAO, - final ConnInstanceDataBinder connInstanceDataBinder, final ConnectorManager connectorManager) { - return new ExternalResourcesHealthIndicator(domainOps, resourceDAO, connInstanceDataBinder, connectorManager); + return new ExternalResourcesHealthIndicator(domainOps, resourceDAO, connectorManager); } @ConditionalOnMissingBean diff --git a/core/starter/src/main/java/org/apache/syncope/core/starter/actuate/ExternalResourcesHealthIndicator.java b/core/starter/src/main/java/org/apache/syncope/core/starter/actuate/ExternalResourcesHealthIndicator.java index f283369d60f..302daa4ff1d 100644 --- a/core/starter/src/main/java/org/apache/syncope/core/starter/actuate/ExternalResourcesHealthIndicator.java +++ b/core/starter/src/main/java/org/apache/syncope/core/starter/actuate/ExternalResourcesHealthIndicator.java @@ -26,7 +26,6 @@ import org.apache.syncope.common.lib.SyncopeConstants; import org.apache.syncope.core.persistence.api.dao.ExternalResourceDAO; import org.apache.syncope.core.provisioning.api.ConnectorManager; -import org.apache.syncope.core.provisioning.api.data.ConnInstanceDataBinder; import org.apache.syncope.core.spring.security.AuthContextUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -42,19 +41,15 @@ public class ExternalResourcesHealthIndicator implements HealthIndicator { protected final ExternalResourceDAO resourceDAO; - protected final ConnInstanceDataBinder connInstanceDataBinder; - protected final ConnectorManager connectorManager; public ExternalResourcesHealthIndicator( final DomainOps domainOps, final ExternalResourceDAO resourceDAO, - final ConnInstanceDataBinder connInstanceDataBinder, final ConnectorManager connectorManager) { this.domainOps = domainOps; this.resourceDAO = resourceDAO; - this.connInstanceDataBinder = connInstanceDataBinder; this.connectorManager = connectorManager; } @@ -72,7 +67,7 @@ public Health health() { try { connectorManager.createConnector( connectorManager.buildConnInstanceOverride( - connInstanceDataBinder.getConnInstanceTO(resource.getConnector()), + resource.getConnector(), resource.getConfOverride(), resource.getCapabilitiesOverride())). test(); diff --git a/fit/core-reference/src/test/java/org/apache/syncope/fit/AbstractITCase.java b/fit/core-reference/src/test/java/org/apache/syncope/fit/AbstractITCase.java index 8822cfb1e5c..3c1f5912a56 100644 --- a/fit/core-reference/src/test/java/org/apache/syncope/fit/AbstractITCase.java +++ b/fit/core-reference/src/test/java/org/apache/syncope/fit/AbstractITCase.java @@ -91,7 +91,6 @@ import org.apache.syncope.common.lib.to.AnyObjectTO; import org.apache.syncope.common.lib.to.AuditEventTO; import org.apache.syncope.common.lib.to.ClientAppTO; -import org.apache.syncope.common.lib.to.ConnInstanceTO; import org.apache.syncope.common.lib.to.ExecTO; import org.apache.syncope.common.lib.to.GroupTO; import org.apache.syncope.common.lib.to.ImplementationTO; @@ -401,6 +400,14 @@ public void initialize(final ConfigurableApplicationContext ctx) { private static int POP3_PORT; + protected static String DB_PASSWORD; + + private static int LDAP_PORT; + + private static String LDAP_PRINCIPAL; + + private static String LDAP_PASSWORD; + protected static boolean IS_FLOWABLE_ENABLED = false; protected static boolean IS_ELASTICSEARCH_ENABLED = false; @@ -540,6 +547,16 @@ public static void conf() { POP3_PORT = Integer.parseInt(props.getProperty("testmail.pop3port")); assertNotNull(POP3_PORT); + + DB_PASSWORD = props.getProperty("testdb.password"); + assertNotNull(DB_PASSWORD); + + LDAP_PORT = Integer.parseInt(props.getProperty("testds.port")); + assertNotNull(LDAP_PORT); + LDAP_PRINCIPAL = props.getProperty("testds.bindDn"); + assertNotNull(LDAP_PRINCIPAL); + LDAP_PASSWORD = props.getProperty("testds.password"); + assertNotNull(LDAP_PASSWORD); } @BeforeAll @@ -794,12 +811,11 @@ protected static List parseBatchResponse(final Response respo private static T execOnLDAP( final String bindDn, final String bindPassword, - final ConnInstanceTO connInstance, final ThrowingFunction function) throws LDAPException { try (LDAPConnection ldapConn = new LDAPConnection( - connInstance.getConf("host").orElseThrow().getValues().getFirst().toString(), - Integer.parseInt(connInstance.getConf("port").orElseThrow().getValues().getFirst().toString()), + "localhost", + LDAP_PORT, bindDn, bindPassword)) { @@ -807,24 +823,8 @@ private static T execOnLDAP( } } - private static T execOnLDAP( - final String bindDn, - final String bindPassword, - final ThrowingFunction function) throws LDAPException { - - ConnInstanceTO connInstance = CONNECTOR_SERVICE.read("74141a3b-0762-4720-a4aa-fc3e374ef3ef", null); - - return execOnLDAP(bindDn, bindPassword, connInstance, function); - } - private static T execOnLDAP(final ThrowingFunction function) throws LDAPException { - ConnInstanceTO connInstance = CONNECTOR_SERVICE.read("74141a3b-0762-4720-a4aa-fc3e374ef3ef", null); - - return execOnLDAP( - connInstance.getConf("principal").orElseThrow().getValues().getFirst().toString(), - connInstance.getConf("credentials").orElseThrow().getValues().getFirst().toString(), - connInstance, - function); + return execOnLDAP(LDAP_PRINCIPAL, LDAP_PASSWORD, function); } protected static SearchResult ldapSearch(final String baseDn, final String filter) { @@ -839,7 +839,7 @@ protected static SearchResult ldapSearch(final String baseDn, final String filte protected static SearchResultEntry getLdapRemoteObject(final String objectDn) { try { - return execOnLDAP(ldapConn -> ldapConn.searchForEntry( + return execOnLDAP(LDAP_PRINCIPAL, LDAP_PASSWORD, ldapConn -> ldapConn.searchForEntry( new SearchRequest(objectDn, SearchScope.BASE, "objectClass=*"))); } catch (LDAPException e) { LOG.error("While reading {}", objectDn, e); diff --git a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/PullTaskITCase.java b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/PullTaskITCase.java index a0122e812d6..e93d93a76d7 100644 --- a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/PullTaskITCase.java +++ b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/PullTaskITCase.java @@ -727,6 +727,8 @@ public void syncTokenWithErrors() { resForTest.setKey("syncTokenWithErrors"); resForTest.setConnector(null); ConnInstanceTO connForTest = SerializationUtils.clone(origConnector); + connForTest.getConf().stream().filter(p -> "password".equals(p.getSchema().getName())).findFirst(). + ifPresent(p -> p.getValues().add(DB_PASSWORD)); connForTest.setKey(null); connForTest.setDisplayName("For syncTokenWithErrors"); diff --git a/fit/core-reference/src/test/resources/test.properties b/fit/core-reference/src/test/resources/test.properties index 6e96e78237c..db0469d7276 100644 --- a/fit/core-reference/src/test/resources/test.properties +++ b/fit/core-reference/src/test/resources/test.properties @@ -29,6 +29,10 @@ testdb.url=${testdb.url} testdb.username=${testdb.username} testdb.password=${testdb.password} +testds.port=${testds.port} +testds.bindDn=${testds.bindDn} +testds.password=${testds.password} + connid.soap.version=${connid.soap.version} connid.db.version=${connid.db.version}