From 8463b7c44c3a356ccb75c844395471bde80e9283 Mon Sep 17 00:00:00 2001 From: Sebastian-Ion TINCU <Sebastian-Ion.TINCU@ext.ec.europa.eu> Date: Tue, 6 Feb 2024 17:10:37 +0100 Subject: [PATCH] EDELIVERY-11317 User should be able to check connection with SMP when changing certificate. Add domain validation integration with SML. Add exists participant integration with SML. --- .../conversion/SmlIdentifierConverter.java | 38 ++++--- .../edelivery/smp/exceptions/ErrorCode.java | 2 +- .../edelivery/smp/services/DomainService.java | 4 +- .../smp/services/SMLIntegrationService.java | 31 +++++- .../smp/services/ui/UIDomainService.java | 12 +- .../ec/edelivery/smp/sml/SmlConnector.java | 104 ++++++++++++++---- 6 files changed, 151 insertions(+), 40 deletions(-) diff --git a/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/conversion/SmlIdentifierConverter.java b/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/conversion/SmlIdentifierConverter.java index e65e73ef1..0e60154ab 100644 --- a/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/conversion/SmlIdentifierConverter.java +++ b/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/conversion/SmlIdentifierConverter.java @@ -19,6 +19,7 @@ package eu.europa.ec.edelivery.smp.conversion; +import ec.services.wsdl.bdmsl.data._1.ParticipantsType; import ec.services.wsdl.bdmsl.data._1.SMPAdvancedServiceForParticipantType; import eu.europa.ec.edelivery.smp.identifiers.Identifier; import org.busdox.servicemetadata.locator._1.ServiceMetadataPublisherServiceForParticipantType; @@ -31,12 +32,7 @@ import static org.apache.commons.lang3.StringUtils.isBlank; public class SmlIdentifierConverter { public static ServiceMetadataPublisherServiceForParticipantType toBusdoxParticipantId(Identifier participantId, String smpId) { - if (isBlank(smpId)) { - throw new IllegalStateException("SMP ID is null or empty"); - } - if (participantId == null || isBlank(participantId.getValue())) { - throw new IllegalStateException("Participant Scheme or Id is null or empty"); - } + validate(participantId, smpId); ServiceMetadataPublisherServiceForParticipantType busdoxIdentifier = new ServiceMetadataPublisherServiceForParticipantType(); busdoxIdentifier.setServiceMetadataPublisherID(smpId); @@ -47,20 +43,36 @@ public class SmlIdentifierConverter { return busdoxIdentifier; } + public static ParticipantsType toParticipantsType(Identifier participantId, String smpId) { + validate(participantId, smpId); + + ParticipantsType participantsType = new ParticipantsType(); + org.busdox.transport.identifiers._1.ParticipantIdentifierType parId = new org.busdox.transport.identifiers._1.ParticipantIdentifierType(); + parId.setScheme(participantId.getScheme()); + parId.setValue(participantId.getValue()); + participantsType.setParticipantIdentifier(parId); + participantsType.setServiceMetadataPublisherID(smpId); + return participantsType; + } + public static SMPAdvancedServiceForParticipantType toBDMSLAdvancedParticipantId(Identifier participantId, String smpId, String serviceMetadata) { - if (isBlank(smpId)) { - throw new IllegalStateException("SMP ID is null or empty"); - } - if (participantId == null || isBlank(participantId.getValue())) { - throw new IllegalStateException("Participant Scheme or Id is null or empty"); - } + validate(participantId, smpId); SMPAdvancedServiceForParticipantType bdmslRequest = new SMPAdvancedServiceForParticipantType(); bdmslRequest.setServiceName(serviceMetadata); - ServiceMetadataPublisherServiceForParticipantType bdxlRequest = toBusdoxParticipantId(participantId, smpId); bdmslRequest.setCreateParticipantIdentifier(bdxlRequest); + return bdmslRequest; } + + private static void validate(Identifier participantId, String smpId) { + if (isBlank(smpId)) { + throw new IllegalStateException("SMP ID is null or empty"); + } + if (participantId == null || isBlank(participantId.getValue())) { + throw new IllegalStateException("Participant Scheme or Id is null or empty"); + } + } } diff --git a/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/exceptions/ErrorCode.java b/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/exceptions/ErrorCode.java index 776116f9c..6efe2bb61 100644 --- a/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/exceptions/ErrorCode.java +++ b/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/exceptions/ErrorCode.java @@ -75,7 +75,7 @@ public enum ErrorCode { ILLEGAL_STATE_SMD_ON_MULTIPLE_SGD (500,"SMP:144",ErrorBusinessCode.TECHNICAL,"Found than one service group domain for metadata id [%s] and user id [%s]!"), // SML integration - SML_INTEGRATION_EXCEPTION (500,"SMP:150",ErrorBusinessCode.TECHNICAL,"Could not create new DNS entry through SML! Error: %s "), + SML_INTEGRATION_EXCEPTION (500,"SMP:150",ErrorBusinessCode.TECHNICAL,"SML integration error! Error: %s "), // XML_SIGNING_EXCEPTION (500,"SMP:500",ErrorBusinessCode.TECHNICAL,"Error occurred while signing response!"), JAXB_INITIALIZATION (500,"SMP:511",ErrorBusinessCode.TECHNICAL, "Could not create Unmarshaller for class [%s]!"), diff --git a/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/services/DomainService.java b/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/services/DomainService.java index b36bafe1e..19d2900c9 100644 --- a/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/services/DomainService.java +++ b/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/services/DomainService.java @@ -94,8 +94,8 @@ public class DomainService { } /** - * If domain is not yet registered and sml integration is on. Than it tries to register domain and all participants - * on that domain. If integration is off it return an configuration exception. + * If domain is not yet registered and SML integration is on, it tries to register a domain and all participants + * on that domain. If integration is off, it returns a configuration exception. * <p> * Method is not in transaction - but sub-methods are. if registering domain or particular serviceGroup succeed * then the database flag (SML_REGISTERED) is turned on ( if method fails diff --git a/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/services/SMLIntegrationService.java b/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/services/SMLIntegrationService.java index c97bfb50a..a3bf40b54 100644 --- a/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/services/SMLIntegrationService.java +++ b/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/services/SMLIntegrationService.java @@ -65,6 +65,22 @@ public class SMLIntegrationService { @Autowired private IdentifierService identifierService; + /** + * Checks whether the participant exists in SML or not. + * + * @param resource the resource entity + * @param domain the domain entity + * @return {@code true} if the participant exists in SML; otherwise, {@code false} (also when SML integration is disabled). + */ + public boolean participantExists(DBResource resource, DBDomain domain) { + if (!isSMLIntegrationEnabled()) { + throw new SMPRuntimeException(CONFIGURATION_ERROR, ERROR_MESSAGE_DNS_NOT_ENABLED); + } + Identifier normalizedParticipantId = identifierService + .normalizeParticipant(resource.getIdentifierScheme(), resource.getIdentifierValue()); + + return smlConnector.participantExists(normalizedParticipantId, domain); + } /** * Method in transaction update domain status and registers domain to SML. @@ -82,6 +98,19 @@ public class SMLIntegrationService { smlConnector.registerDomain(domain); } + /** + * Checks whether the domain is valid by trying to read it from SML. + * + * @param domain the domain entity to verify whether it's valid or not. + * + * @return {@code true} if the domain can be successfully read from SML; otherwise, {@code false} (also when SML integration is disabled). + */ + public boolean isDomainValid(DBDomain domain) { + if (!isSMLIntegrationEnabled()) { + throw new SMPRuntimeException(CONFIGURATION_ERROR, ERROR_MESSAGE_DNS_NOT_ENABLED); + } + return smlConnector.isDomainValid(domain); + } /** * Method in transaction update domain status and registers domain to SML. @@ -171,7 +200,7 @@ public class SMLIntegrationService { return; } - // unregister only registered participants + // unregister only registered participants if (resource.isSmlRegistered()) { // update value resource.setSmlRegistered(false); diff --git a/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/services/ui/UIDomainService.java b/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/services/ui/UIDomainService.java index 699aa2afb..f2394bf4e 100644 --- a/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/services/ui/UIDomainService.java +++ b/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/services/ui/UIDomainService.java @@ -35,6 +35,7 @@ import eu.europa.ec.edelivery.smp.exceptions.ErrorCode; import eu.europa.ec.edelivery.smp.exceptions.SMPRuntimeException; import eu.europa.ec.edelivery.smp.logging.SMPLogger; import eu.europa.ec.edelivery.smp.logging.SMPLoggerFactory; +import eu.europa.ec.edelivery.smp.services.SMLIntegrationService; import org.apache.commons.lang3.StringUtils; import org.springframework.core.convert.ConversionService; import org.springframework.stereotype.Service; @@ -64,7 +65,7 @@ public class UIDomainService extends UIServiceBase<DBDomain, DomainRO> { private final ConversionService conversionService; private final GroupDao groupDao; private final GroupMemberDao groupMemberDao; - + private final SMLIntegrationService smlIntegrationService; public UIDomainService(ConversionService conversionService, DomainDao domainDao, @@ -73,7 +74,7 @@ public class UIDomainService extends UIServiceBase<DBDomain, DomainRO> { ResourceDefDao resourceDefDao, DomainResourceDefDao domainResourceDefDao, GroupDao groupDao, - GroupMemberDao groupMemberDao) { + GroupMemberDao groupMemberDao, SMLIntegrationService smlIntegrationService) { this.conversionService = conversionService; this.domainDao = domainDao; this.resourceDao = resourceDao; @@ -82,6 +83,7 @@ public class UIDomainService extends UIServiceBase<DBDomain, DomainRO> { this.domainMemberDao = domainMemberDao; this.groupDao = groupDao; this.groupMemberDao = groupMemberDao; + this.smlIntegrationService = smlIntegrationService; } @Override @@ -175,6 +177,12 @@ public class UIDomainService extends UIServiceBase<DBDomain, DomainRO> { domain.setSmlSmpId(StringUtils.trim(data.getSmlSmpId())); domain.setSmlClientKeyAlias(data.getSmlClientKeyAlias()); domain.setSmlClientCertAuth(data.isSmlClientCertAuth()); + + // if registered, validate the updated domain to ensure its SML integration certificate is valid + if(domain.isSmlRegistered() && !smlIntegrationService.isDomainValid(domain)) { + String msg = "The SML-SMP certificate for domain [" + domain.getDomainCode() + "] is not valid!"; + throw new BadRequestException(ErrorBusinessCode.NOT_FOUND, msg); + } } @Transactional diff --git a/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/sml/SmlConnector.java b/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/sml/SmlConnector.java index 269fdc29c..2fa54ea76 100644 --- a/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/sml/SmlConnector.java +++ b/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/sml/SmlConnector.java @@ -19,6 +19,8 @@ package eu.europa.ec.edelivery.smp.sml; +import ec.services.wsdl.bdmsl.data._1.ExistsParticipantResponseType; +import ec.services.wsdl.bdmsl.data._1.ParticipantsType; import ec.services.wsdl.bdmsl.data._1.SMPAdvancedServiceForParticipantType; import eu.europa.ec.bdmsl.ws.soap.*; import eu.europa.ec.edelivery.smp.config.enums.SMPPropertyEnum; @@ -61,8 +63,7 @@ import java.net.MalformedURLException; import java.net.URL; import java.util.*; -import static eu.europa.ec.edelivery.smp.conversion.SmlIdentifierConverter.toBDMSLAdvancedParticipantId; -import static eu.europa.ec.edelivery.smp.conversion.SmlIdentifierConverter.toBusdoxParticipantId; +import static eu.europa.ec.edelivery.smp.conversion.SmlIdentifierConverter.*; import static eu.europa.ec.edelivery.smp.exceptions.SMLErrorMessages.*; /** @@ -97,16 +98,13 @@ public class SmlConnector implements ApplicationContextAware { private ApplicationContext ctx; - public boolean registerInDns(Identifier normalizedParticipantId, DBDomain domain, String customNaptrService) { - - if (!configurationService.isSMLIntegrationEnabled()) { return false; } String normalizedParticipantString = identifierService.formatParticipant(normalizedParticipantId); if (!domain.isSmlRegistered()) { - LOG.info("Participant {} is not registered to SML because domain {} is not registered!", + LOG.warn("Participant {} is not registered to SML because domain {} is not registered!", normalizedParticipantString, domain.getDomainCode()); return false; } @@ -131,11 +129,47 @@ public class SmlConnector implements ApplicationContextAware { } } + /** + * Checks whether the participant identified by the provided ID exists or not. In case the integration with SML is + * disabled, it returns {@code false}. + * + * @param normalizedParticipantId the participant ID + * @param domain the domain entity + * @return {@code true} if the participant exists; otherwise, {@code false} (also when SML integration is disabled). + */ + public boolean participantExists(Identifier normalizedParticipantId, DBDomain domain) { + if (!configurationService.isSMLIntegrationEnabled()) { + return false; + } + + String normalizedParticipantString = identifierService.formatParticipant(normalizedParticipantId); + if (!domain.isSmlRegistered()) { + LOG.warn("Cannot check if Participant {} exists when domain {} is not registered!", + normalizedParticipantString, domain.getDomainCode()); + return false; + } + + LOG.debug("Checking if Participant: {} exists in domain: {}.", normalizedParticipantString, domain.getDomainCode()); + try { + ParticipantsType smlRequest = toParticipantsType(normalizedParticipantId, domain.getSmlSmpId()); + ExistsParticipantResponseType existsParticipantResponseType = getBDMSLWSClient(domain).existsParticipantIdentifier(smlRequest); + return existsParticipantResponseType.isExist(); + } catch (BadRequestFault e) { + return processSMLErrorMessage(e, normalizedParticipantId); + } catch (NotFoundFault e) { + return processSMLErrorMessage(e, normalizedParticipantId); + } catch (Exception e) { + LOG.error(e.getClass().getName() + e.getMessage(), e); + throw new SMPRuntimeException(ErrorCode.SML_INTEGRATION_EXCEPTION, e, ExceptionUtils.getRootCauseMessage(e)); + } + } + protected void createRegularDNSRecord(Identifier normalizedParticipantId, DBDomain domain) throws UnauthorizedFault, BadRequestFault, NotFoundFault, InternalErrorFault { LOG.debug("Set regular DNS record for Participant: [{}] and domain: [{}].", normalizedParticipantId, domain.getDomainCode()); ServiceMetadataPublisherServiceForParticipantType smlRequest = toBusdoxParticipantId(normalizedParticipantId, domain.getSmlSmpId()); getParticipantWSClient(domain).create(smlRequest); } + protected void createCustomServiceNaptrDNSRecord(Identifier normalizedParticipantId, DBDomain domain, String customNaptrService) throws UnauthorizedFault, BadRequestFault, NotFoundFault, InternalErrorFault { LOG.debug("Set custom naptr service [{}] DNS record for Participant: [{}] and domain: [{}].", customNaptrService, normalizedParticipantId, domain.getDomainCode()); SMPAdvancedServiceForParticipantType smlRequest = toBDMSLAdvancedParticipantId(normalizedParticipantId, domain.getSmlSmpId(), customNaptrService); @@ -182,19 +216,13 @@ public class SmlConnector implements ApplicationContextAware { * @return */ public boolean registerDomain(DBDomain domain) { - if (!configurationService.isSMLIntegrationEnabled()) { return false; } - String smpLogicalAddress = configurationService.getSMLIntegrationSMPLogicalAddress(); - String smpPhysicalAddress = configurationService.getSMLIntegrationSMPPhysicalAddress(); - LOG.info("Registering new Domain to SML: (smpCode {} smp-smp-id {}) ", domain.getDomainCode(), domain.getSmlSmpId()); + String smlSmpId = domain.getSmlSmpId(); + LOG.info("Registering new Domain to SML: (smpCode {} smp-smp-id {}) ", domain.getDomainCode(), smlSmpId); try { - ServiceMetadataPublisherServiceType smlSmpRequest = new ServiceMetadataPublisherServiceType(); - smlSmpRequest.setPublisherEndpoint(new PublisherEndpointType()); - smlSmpRequest.getPublisherEndpoint().setLogicalAddress(smpLogicalAddress); - smlSmpRequest.getPublisherEndpoint().setPhysicalAddress(smpPhysicalAddress); - smlSmpRequest.setServiceMetadataPublisherID(domain.getSmlSmpId()); + ServiceMetadataPublisherServiceType smlSmpRequest = getServiceMetadataPublisherServiceType(smlSmpId); getSMPManagerWSClient(domain).create(smlSmpRequest); } catch (BadRequestFault e) { processSMLErrorMessage(e, domain); @@ -206,6 +234,46 @@ public class SmlConnector implements ApplicationContextAware { return true; } + /** + * Checks whether a domain is valid or not. In case the integration with SML is disabled, it returns {@code false}. + * + * @param domain the domain entity + * @return {@code true} if the domain exists and is valid; otherwise, {@code false} (also when SML integration is disabled). + */ + public boolean isDomainValid(DBDomain domain) { + if (!configurationService.isSMLIntegrationEnabled()) { + return false; + } + String smlSmpId = domain.getSmlSmpId(); + LOG.info("Validating Domain to SML: (smpCode {} smp-smp-id {}) ", domain.getDomainCode(), smlSmpId); + try { + ServiceMetadataPublisherServiceType smlSmpRequest = getServiceMetadataPublisherServiceType(smlSmpId); + getSMPManagerWSClient(domain).read(smlSmpRequest); + } catch (BadRequestFault e) { + processSMLErrorMessage(e, domain); + } catch (NotFoundFault e) { + processSMLErrorMessage(e, domain); + } catch (Exception e) { + LOG.error(e.getClass().getName() + e.getMessage(), e); + throw new SMPRuntimeException(ErrorCode.SML_INTEGRATION_EXCEPTION, e, ExceptionUtils.getRootCauseMessage(e)); + } + // if not error is thrown - the domain exists and is valid + return true; + } + + private ServiceMetadataPublisherServiceType getServiceMetadataPublisherServiceType(String smlSmpId) { + String smpLogicalAddress = configurationService.getSMLIntegrationSMPLogicalAddress(); + String smpPhysicalAddress = configurationService.getSMLIntegrationSMPPhysicalAddress(); + + ServiceMetadataPublisherServiceType smlSmpRequest = new ServiceMetadataPublisherServiceType(); + smlSmpRequest.setPublisherEndpoint(new PublisherEndpointType()); + smlSmpRequest.getPublisherEndpoint().setLogicalAddress(smpLogicalAddress); + smlSmpRequest.getPublisherEndpoint().setPhysicalAddress(smpPhysicalAddress); + smlSmpRequest.setServiceMetadataPublisherID(smlSmpId); + + return smlSmpRequest; + } + private void processSMLErrorMessage(BadRequestFault e, DBDomain domain) { if (!isOkMessage(domain, e.getMessage())) { LOG.error(e.getMessage(), e); @@ -303,13 +371,10 @@ public class SmlConnector implements ApplicationContextAware { } private IManageServiceMetadataWS getSMPManagerWSClient(DBDomain domain) { - - IManageServiceMetadataWS iManageServiceMetadataWS = ctx.getBean(IManageServiceMetadataWS.class); // configure value connection configureClient(SERVICE_METADATA_CONTEXT, iManageServiceMetadataWS, domain); - return iManageServiceMetadataWS; } @@ -330,9 +395,7 @@ public class SmlConnector implements ApplicationContextAware { return alias; } - public void configureClient(String serviceEndpoint, Object smlPort, DBDomain domain) { - String clientKeyAlias = getSmlClientKeyAliasForDomain(domain); boolean clientCertAuthentication = domain.isSmlClientCertAuth(); Client client = ClientProxy.getClient(smlPort); @@ -383,7 +446,6 @@ public class SmlConnector implements ApplicationContextAware { } - public void configureClientAuthentication(HTTPConduit httpConduit, Map<String, Object> requestContext, CertificateRO certificateRO, boolean clientCertAuthentication, boolean useTLS) { LOG.info("Connect to SML (smlClientAuthentication: [{}] use Client-CertHeader: [{}])", certificateRO, clientCertAuthentication); -- GitLab