This is an automated email from the ASF dual-hosted git repository. ilgrosso pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/syncope.git
commit 47e99d02ebce5165fea1eaa3dd034574dfe48579 Author: Francesco Chicchiriccò <[email protected]> AuthorDate: Thu Aug 13 17:01:29 2026 +0200 [SYNCOPE-1988] Reviewing ConnInstanceTO and ResourceTO generation to strip out confidential property values (#1491) --- .../apache/syncope/core/logic/ConnectorLogic.java | 36 +++++------ .../apache/syncope/core/logic/IdMLogicContext.java | 2 - .../apache/syncope/core/logic/ResourceLogic.java | 7 +-- .../syncope/core/logic/ResourceLogicTest.java | 2 +- .../persistence/jpa/DummyConnectorManager.java | 3 +- .../persistence/neo4j/DummyConnectorManager.java | 3 +- .../core/provisioning/api/ConnectorManager.java | 3 +- .../api/data/ConnInstanceDataBinder.java | 67 +++++++++++++++++++- .../provisioning/java/ConnectorFacadeProxy.java | 13 ++-- .../provisioning/java/DefaultConnectorManager.java | 28 ++------- .../provisioning/java/ProvisioningContext.java | 6 -- .../java/data/ConnInstanceDataBinderImpl.java | 72 ++++++++++------------ .../java/data/ResourceDataBinderImpl.java | 30 ++++++++- .../provisioning/java/ConnectorManagerTest.java | 2 +- .../core/starter/SyncopeCoreApplication.java | 4 +- .../actuate/ExternalResourcesHealthIndicator.java | 7 +-- .../org/apache/syncope/fit/AbstractITCase.java | 48 +++++++-------- .../apache/syncope/fit/core/PullTaskITCase.java | 2 + .../src/test/resources/test.properties | 4 ++ 19 files changed, 192 insertions(+), 147 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 6097efc0b7..093d2d3eff 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.ConnInstanceTO; 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 class ConnectorLogic extends AbstractTransactionalLogic<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 + "')") @@ -164,10 +165,8 @@ public class ConnectorLogic extends AbstractTransactionalLogic<ConnInstanceTO> { 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); } @@ -196,7 +195,7 @@ public class ConnectorLogic extends AbstractTransactionalLogic<ConnInstanceTO> { 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; @@ -209,13 +208,12 @@ public class ConnectorLogic extends AbstractTransactionalLogic<ConnInstanceTO> { public List<ConnIdObjectClass> 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> 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 -> { @@ -245,24 +243,22 @@ public class ConnectorLogic extends AbstractTransactionalLogic<ConnInstanceTO> { + "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<ConnConfProperty> 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 f12d1cb581..c6eb2c038a 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 class IdMLogicContext { 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 class IdMLogicContext { 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 f28f07591e..1b77821b5f 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.persistence.api.utils.RealmUtils; 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<ResourceTO> { protected final ResourceDataBinder binder; - protected final ConnInstanceDataBinder connInstanceDataBinder; - protected final OutboundMatcher outboundMatcher; protected final MappingManager mappingManager; @@ -90,7 +87,6 @@ public class ResourceLogic extends AbstractTransactionalLogic<ResourceTO> { 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 class ResourceLogic extends AbstractTransactionalLogic<ResourceTO> { 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 class ResourceLogic extends AbstractTransactionalLogic<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 2b691415d7..2c31400209 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 class ResourceLogicTest extends AbstractTest { 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 f966bbf167..ff11928a6b 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 @@ package org.apache.syncope.core.persistence.jpa; 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 class DummyConnectorManager implements ConnectorManager { @Override public ConnInstance buildConnInstanceOverride( - final ConnInstanceTO connInstance, + final ConnInstance connInstance, final Optional<List<ConnConfProperty>> confOverride, final Optional<Set<ConnectorCapability>> 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 25562e16a1..63e1c23fc6 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 @@ package org.apache.syncope.core.persistence.neo4j; 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 class DummyConnectorManager implements ConnectorManager { @Override public ConnInstance buildConnInstanceOverride( - final ConnInstanceTO connInstance, + final ConnInstance connInstance, final Optional<List<ConnConfProperty>> confOverride, final Optional<Set<ConnectorCapability>> 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 75a0438fbf..9a0be7a278 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 @@ package org.apache.syncope.core.provisioning.api; 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<List<ConnConfProperty>> confOverride, Optional<Set<ConnectorCapability>> 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 b4b7c183c8..dd950cdb27 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<ConnConfProperty> newConf( + final List<ConnConfProperty> previousConf, + final List<ConnConfProperty> toConf) { + + List<ConnConfProperty> 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 294b7fe1e9..ebed45dd96 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.persistence.api.utils.ConnPoolConfUtils; 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 class ConnectorFacadeProxy implements Connector { // 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 @@ public class ConnectorFacadeProxy implements Connector { Class<?> propertySchemaClass = ClassUtils.forName(propType, ConnectorFacadeProxy.class.getClassLoader()); 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 9e371642e5..6551b60484 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.Locale; 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.persistence.api.utils.ConnPoolConfUtils; 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 @@ public class DefaultConnectorManager implements ConnectorManager { 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 @@ public class DefaultConnectorManager implements ConnectorManager { 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 class DefaultConnectorManager implements ConnectorManager { }); } + @Transactional(readOnly = true) @Override public ConnInstance buildConnInstanceOverride( - final ConnInstanceTO connInstance, + final ConnInstance connInstance, final Optional<List<ConnConfProperty>> confOverride, final Optional<Set<ConnectorCapability>> 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()); @@ -180,7 +160,7 @@ public class DefaultConnectorManager implements ConnectorManager { } 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 2d8b25861c..7ad761a991 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 @@ -332,19 +332,13 @@ public class ProvisioningContext { 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 9961cbfac2..cb89349210 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,7 +19,6 @@ 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 org.apache.commons.lang3.tuple.Pair; @@ -36,10 +35,9 @@ import org.apache.syncope.core.persistence.api.entity.EntityFactory; 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; @@ -68,7 +66,7 @@ public class ConnInstanceDataBinderImpl implements ConnInstanceDataBinder { } @Override - public ConnInstance getConnInstance(final ConnInstanceTO connInstanceTO) { + public ConnInstance create(final ConnInstanceTO connInstanceTO) { SyncopeClientException sce = SyncopeClientException.build(ClientExceptionType.RequiredValuesMissing); if (connInstanceTO.getLocation() == null) { @@ -107,7 +105,9 @@ public class ConnInstanceDataBinderImpl implements ConnInstanceDataBinder { } Optional.ofNullable(connInstanceTO.getLocation()).ifPresent(connInstance::setLocation); - connInstance.getConf().addAll(connInstanceTO.getConf()); + + connInstance.getConf().addAll(ConnInstanceDataBinder.newConf(List.of(), connInstanceTO.getConf())); + Optional.ofNullable(connInstanceTO.getPoolConf()). ifPresent(conf -> connInstance.setPoolConf(ConnPoolConfUtils.getConnPoolConf(conf))); @@ -129,20 +129,26 @@ public class ConnInstanceDataBinderImpl implements ConnInstanceDataBinder { Optional.ofNullable(connInstanceTO.getAdminRealm()). ifPresent(r -> connInstance.setAdminRealm(realmSearchDAO.findByFullPath(r). - orElseThrow(() -> { - SyncopeClientException sce = SyncopeClientException.build(ClientExceptionType.InvalidRealm); - sce.getElements().add("Invalid or null realm specified: " + connInstanceTO.getAdminRealm()); - return sce; - }))); + orElseThrow(() -> { + SyncopeClientException sce = SyncopeClientException.build(ClientExceptionType.InvalidRealm); + sce.getElements().add("Invalid or null realm specified: " + connInstanceTO.getAdminRealm()); + return sce; + }))); Optional.ofNullable(connInstanceTO.getLocation()).ifPresent(connInstance::setLocation); Optional.ofNullable(connInstanceTO.getBundleName()).ifPresent(connInstance::setBundleName); Optional.ofNullable(connInstanceTO.getVersion()).ifPresent(connInstance::setVersion); Optional.ofNullable(connInstanceTO.getConnectorName()).ifPresent(connInstance::setConnectorName); Optional.ofNullable(connInstanceTO.getDisplayName()).ifPresent(connInstance::setDisplayName); - connInstance.getConf().clear(); - connInstance.getConf().addAll(connInstanceTO.getConf()); Optional.ofNullable(connInstanceTO.getConnRequestTimeout()).ifPresent(connInstance::setConnRequestTimeout); + + if (!connInstanceTO.getConf().isEmpty()) { + List<ConnConfProperty> newConf = + ConnInstanceDataBinder.newConf(connInstance.getConf(), connInstanceTO.getConf()); + connInstance.getConf().clear(); + connInstance.getConf().addAll(newConf); + } + Optional.ofNullable(connInstanceTO.getPoolConf()).ifPresentOrElse( conf -> connInstance.setPoolConf(ConnPoolConfUtils.getConnPoolConf(conf)), () -> connInstance.setPoolConf(null)); @@ -150,31 +156,6 @@ public class ConnInstanceDataBinderImpl implements ConnInstanceDataBinder { 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(); @@ -186,7 +167,20 @@ public class ConnInstanceDataBinderImpl implements ConnInstanceDataBinder { connInstanceTO.setConnRequestTimeout(connInstance.getConnRequestTimeout()); connInstanceTO.setAdminRealm(connInstance.getAdminRealm().getFullPath()); connInstanceTO.getCapabilities().addAll(connInstance.getCapabilities()); - connInstanceTO.getConf().addAll(connInstance.getConf()); + + // do not export confidential property values + connInstance.getConf().forEach(property -> { + if (property.getSchema().isConfidential() + || GuardedString.class.getName().equals(property.getSchema().getType())) { + + ConnConfProperty empty = new ConnConfProperty(); + empty.setOverridable(property.isOverridable()); + empty.setSchema(property.getSchema()); + connInstanceTO.getConf().add(empty); + } else { + connInstanceTO.getConf().add(property); + } + }); try { Pair<URI, ConnectorInfo> info = connIdBundleManager.getConnectorInfo(connInstance); @@ -196,7 +190,7 @@ public class ConnInstanceDataBinderImpl implements ConnInstanceDataBinder { // 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(() -> { 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 604e91ab53..61a0095d57 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 @@ -19,6 +19,7 @@ package org.apache.syncope.core.provisioning.java.data; import java.text.ParseException; +import java.util.ArrayList; import java.util.List; import java.util.Optional; import java.util.stream.Stream; @@ -34,6 +35,7 @@ import org.apache.syncope.common.lib.to.Provision; 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 +60,11 @@ import org.apache.syncope.core.persistence.api.entity.policy.PropagationPolicy; 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; @@ -328,8 +332,12 @@ public class ResourceDataBinderImpl implements ResourceDataBinder { Implementation.class.getSimpleName(), resourceTO.getProvisionSorter())); } - resource.setConfOverride( - Optional.ofNullable(resourceTO.getConfOverride()).orElseGet(Optional::empty)); + resourceTO.getConfOverride().ifPresentOrElse( + confOverride -> { + List<ConnConfProperty> previousConf = resource.getConfOverride().orElseGet(() -> List.of()); + resource.setConfOverride(Optional.of(ConnInstanceDataBinder.newConf(previousConf, confOverride))); + }, + () -> resource.setConfOverride(Optional.empty())); resource.setCapabilitiesOverride( Optional.ofNullable(resourceTO.getCapabilitiesOverride()).orElseGet(Optional::empty)); @@ -586,7 +594,23 @@ public class ResourceDataBinderImpl implements ResourceDataBinder { resourceTO.setProvisionSorter(resource.getProvisionSorter() == null ? null : resource.getProvisionSorter().getKey()); - resourceTO.setConfOverride(resource.getConfOverride()); + // do not export confidential property values + resource.getConfOverride().ifPresent(conf -> { + List<ConnConfProperty> confOverride = new ArrayList<>(); + conf.forEach(property -> { + if (property.getSchema().isConfidential() + || GuardedString.class.getName().equals(property.getSchema().getType())) { + + ConnConfProperty empty = new ConnConfProperty(); + empty.setOverridable(property.isOverridable()); + empty.setSchema(property.getSchema()); + confOverride.add(empty); + } else { + confOverride.add(property); + } + }); + resourceTO.setConfOverride(Optional.of(confOverride)); + }); 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 d5f943cb53..22652cd6b7 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 280a650c0a..a3a8bda007 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.persistence.api.dao.UserDAO; 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; @@ -169,10 +168,9 @@ public class SyncopeCoreApplication extends SpringBootServletInitializer { 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 8efffb3757..e9ba72ec1c 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.keymaster.client.api.model.Domain; 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 class ExternalResourcesHealthIndicator implements HealthIndicator { 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 f3992b5336..e35c4832fd 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 @@ -95,7 +95,6 @@ import org.apache.syncope.common.lib.request.UserUR; 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.EntityTO; import org.apache.syncope.common.lib.to.ExecTO; import org.apache.syncope.common.lib.to.GroupTO; @@ -404,11 +403,19 @@ public abstract class AbstractITCase { protected static ImpersonationService IMPERSONATION_SERVICE; + protected static boolean IS_DEPLOYED_IN_PAYARA = false; + private static final String POP3_HOST = "localhost"; private static int POP3_PORT; - protected static boolean IS_DEPLOYED_IN_PAYARA = false; + 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; @@ -564,6 +571,16 @@ public abstract class AbstractITCase { 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 @@ -825,7 +842,7 @@ public abstract class AbstractITCase { List<String> failures = result.getPropagationStatuses().stream(). filter(status -> status.getStatus() != ExecStatus.SUCCESS). map(status -> "Propagation to " + status.getResource() - + " was not successful: " + status.getFailureReason()).toList(); + + " was not successful: " + status.getFailureReason()).toList(); if (!failures.isEmpty()) { fail(String.join("\n", failures)); } @@ -841,12 +858,11 @@ public abstract class AbstractITCase { private static <T> T execOnLDAP( final String bindDn, final String bindPassword, - final ConnInstanceTO connInstance, final ThrowingFunction<LDAPConnection, T> 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)) { @@ -854,24 +870,8 @@ public abstract class AbstractITCase { } } - private static <T> T execOnLDAP( - final String bindDn, - final String bindPassword, - final ThrowingFunction<LDAPConnection, T> function) throws LDAPException { - - ConnInstanceTO connInstance = CONNECTOR_SERVICE.read("74141a3b-0762-4720-a4aa-fc3e374ef3ef", null); - - return execOnLDAP(bindDn, bindPassword, connInstance, function); - } - private static <T> T execOnLDAP(final ThrowingFunction<LDAPConnection, T> 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) { @@ -886,7 +886,7 @@ public abstract class AbstractITCase { 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 281ac2acc9..06f9a1e1d8 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 @@ -702,6 +702,8 @@ public class PullTaskITCase extends AbstractTaskITCase { 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 6e96e78237..db0469d727 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}
