From 7571d90b2910740424988d0e7e5473dbd2d33cfa Mon Sep 17 00:00:00 2001 From: Thomas <> Date: Tue, 11 Oct 2022 19:16:17 +0200 Subject: feat(ejustice): add validation to disallow eJusticeRole for legal and natural person in one single request --- .../handler/EJusticePersonRoleHandler.java | 21 ++++++++++ .../handler/IEidasAttributeHandler.java | 26 ++++++++++++ .../protocol/EidasProxyServiceController.java | 39 ++++++++++++++++-- .../messages/eidasproxy_messages.properties | 1 + .../EidasProxyServiceControllerBorisTest.java | 48 ++++++++++++++++++++++ 5 files changed, 132 insertions(+), 3 deletions(-) diff --git a/modules/eidas_proxy-sevice/src/main/java/at/asitplus/eidas/specific/modules/msproxyservice/handler/EJusticePersonRoleHandler.java b/modules/eidas_proxy-sevice/src/main/java/at/asitplus/eidas/specific/modules/msproxyservice/handler/EJusticePersonRoleHandler.java index f8c14ceb..46dd714d 100644 --- a/modules/eidas_proxy-sevice/src/main/java/at/asitplus/eidas/specific/modules/msproxyservice/handler/EJusticePersonRoleHandler.java +++ b/modules/eidas_proxy-sevice/src/main/java/at/asitplus/eidas/specific/modules/msproxyservice/handler/EJusticePersonRoleHandler.java @@ -11,12 +11,14 @@ import org.apache.commons.lang3.StringUtils; import org.springframework.beans.factory.annotation.Autowired; import at.asitplus.eidas.specific.core.config.ServiceProviderConfiguration; +import at.asitplus.eidas.specific.modules.msproxyservice.exception.EidasProxyServiceException; import at.gv.egiz.eaaf.core.api.data.ExtendedPvpAttributeDefinitions.SpMandateModes; import at.gv.egiz.eaaf.core.api.data.PvpAttributeDefinitions; import at.gv.egiz.eaaf.core.api.idp.IEidAuthData; import at.gv.egiz.eaaf.core.api.idp.IExtendedConfiguration; import at.gv.egiz.eaaf.core.exceptions.EaafConfigurationException; import at.gv.egiz.eaaf.core.impl.utils.KeyValueUtils; +import eu.eidas.auth.commons.light.ILightRequest; import lombok.NonNull; import lombok.extern.slf4j.Slf4j; @@ -31,6 +33,11 @@ import lombok.extern.slf4j.Slf4j; @Slf4j public class EJusticePersonRoleHandler implements IEidasAttributeHandler { + public static final String EIDAS_ATTR_EJUSTIC_NAT = + "http://e-justice.europa.eu/attributes/naturalperson/eJusticeNaturalPersonRole"; + public static final String EIDAS_ATTR_EJUSTIC_JUR = + "http://e-justice.europa.eu/attributes/legalperson/eJusticeLegalPersonRole"; + public static final String CONFIG_PROP_IDA_MANDATE_PROFILE = "advanced.attributes.ejusticerole.mandate.profiles"; public static final String CONFIG_PROP_IDA_MANDATE_MODE = "advanced.attributes.ejusticerole.mandate.mode"; public static final String CONFIG_PROP_IDA_ADDITIONAL_ATTRIBUTES = @@ -140,5 +147,19 @@ public class EJusticePersonRoleHandler implements IEidasAttributeHandler { return value; } + + @Override + public final void validateAuthnRequest(ILightRequest eidasRequest) throws EidasProxyServiceException { + boolean isNatReq = + eidasRequest.getRequestedAttributes().getAttributeValuesByNameUri(EIDAS_ATTR_EJUSTIC_NAT) != null; + boolean isJurReq = + eidasRequest.getRequestedAttributes().getAttributeValuesByNameUri(EIDAS_ATTR_EJUSTIC_JUR) != null; + + if (isNatReq && isJurReq) { + log.warn("eJustice attributes for legal and natural person can NOT be requested at the same time."); + throw new EidasProxyServiceException("eidas.proxyservice.ejustice.01", null); + + } + } } diff --git a/modules/eidas_proxy-sevice/src/main/java/at/asitplus/eidas/specific/modules/msproxyservice/handler/IEidasAttributeHandler.java b/modules/eidas_proxy-sevice/src/main/java/at/asitplus/eidas/specific/modules/msproxyservice/handler/IEidasAttributeHandler.java index 36deba30..15650e29 100644 --- a/modules/eidas_proxy-sevice/src/main/java/at/asitplus/eidas/specific/modules/msproxyservice/handler/IEidasAttributeHandler.java +++ b/modules/eidas_proxy-sevice/src/main/java/at/asitplus/eidas/specific/modules/msproxyservice/handler/IEidasAttributeHandler.java @@ -3,12 +3,19 @@ package at.asitplus.eidas.specific.modules.msproxyservice.handler; import javax.annotation.Nullable; import at.asitplus.eidas.specific.core.config.ServiceProviderConfiguration; +import at.asitplus.eidas.specific.modules.msproxyservice.exception.EidasProxyServiceException; import at.gv.egiz.eaaf.core.api.idp.IEidAuthData; +import eu.eidas.auth.commons.light.ILightRequest; import lombok.NonNull; /** * Handlers for attribute-processing that requires more features than a simple mapping. * + *

Important: That interface provides attribute-specific extension- and modification hooks for + * post-processing of service configuration and authentication data. If more than one attribute-specific implementation + * operates on the same process, concurrency issues are possible because any implementation of that interface are + * executed independent from each other.

+ * * @author tlenz * */ @@ -17,6 +24,10 @@ public interface IEidasAttributeHandler { /** * Perform attribute-releated post-processing of internal Service-Provider configuration. * + *

Important: Modifications done by that method are independent from any other implementation of + * the same interface and there is no specific order if more than one handler are selected.
+ * Therefore, concurrency issues has to be noticed!

+ * * @param spConfig SP configuration that was build from incoming eIDAS Authn. request. */ void performSpConfigPostprocessing(@NonNull ServiceProviderConfiguration spConfig); @@ -25,12 +36,18 @@ public interface IEidasAttributeHandler { /** * Perform attribute-specific post-processing of authentication information. * + *

Important: Modifications done by that method are independent from any other implementation of + * the same interface and there is no specific order if more than one handler are selected.
+ * Therefore, concurrency issues has to be noticed!

+ * * @param authData authentication information from ID Austria system that should be post processed. */ @NonNull void performAuthDataPostprocessing(@NonNull IEidAuthData authData); + + /** * Build eIDAS attribute-value from authentication data. * @@ -40,6 +57,15 @@ public interface IEidasAttributeHandler { @Nullable String buildAttributeValue(@NonNull IEidAuthData eidAuthData); + + /** + * Extended validation of incoming eIDAS AuthnRequest. + * + * @param eidasRequest Incoming eIDAS authentication request + * @throws EidasProxyServiceException In case of a validation error + */ + void validateAuthnRequest(ILightRequest eidasRequest) throws EidasProxyServiceException; + } diff --git a/modules/eidas_proxy-sevice/src/main/java/at/asitplus/eidas/specific/modules/msproxyservice/protocol/EidasProxyServiceController.java b/modules/eidas_proxy-sevice/src/main/java/at/asitplus/eidas/specific/modules/msproxyservice/protocol/EidasProxyServiceController.java index d0e3d1ba..b5f6b6d2 100644 --- a/modules/eidas_proxy-sevice/src/main/java/at/asitplus/eidas/specific/modules/msproxyservice/protocol/EidasProxyServiceController.java +++ b/modules/eidas_proxy-sevice/src/main/java/at/asitplus/eidas/specific/modules/msproxyservice/protocol/EidasProxyServiceController.java @@ -21,6 +21,7 @@ import org.apache.commons.lang.StringEscapeUtils; import org.apache.commons.lang3.StringUtils; import org.opensaml.saml.saml2.core.NameIDType; import org.opensaml.saml.saml2.core.StatusCode; +import org.springframework.beans.BeansException; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; @@ -264,7 +265,25 @@ public class EidasProxyServiceController extends AbstractController implements I } - // TODO: validate some other stuff + // perform advanced request validation + Set requiredHandlers = eidasRequest.getRequestedAttributes().getAttributeMap().keySet().stream() + .map(el -> attrRegistry.mapEidasAttributeToAttributeHandler(el.getNameUri().toString()).orElse(null)) + .filter(Objects::nonNull) + .distinct() + .collect(Collectors.toSet()); + + if (!requiredHandlers.isEmpty()) { + log.info("eIDAS requested attributes requires #{} specific attribute-hander. " + + "Starting advanced attribute-validation ... ", requiredHandlers.size()); + for (String el : requiredHandlers) { + executeAdvancedRequestValidation(el, eidasRequest); + + } + + } else { + log.debug("No advanced eIDAS attribute-validation required."); + + } } @@ -283,8 +302,8 @@ public class EidasProxyServiceController extends AbstractController implements I && EidasProxyServiceUtils.isNaturalPersonRequested(eidasRequest)) { throw new EidasProxyServiceException(ERROR_08, null); - } - + } + // TODO: validate some other stuff } @@ -381,6 +400,20 @@ public class EidasProxyServiceController extends AbstractController implements I } } + private void executeAdvancedRequestValidation(String handlerClass, ILightRequest eidasRequest) + throws EidasProxyServiceException { + try { + IEidasAttributeHandler handler = applicationContext.getBean(handlerClass, IEidasAttributeHandler.class); + + log.trace("Perfom request-validastion by using: {}", handler.getClass().getName()); + handler.validateAuthnRequest(eidasRequest); + + } catch (BeansException e) { + log.error("No custom attribute-handler implementation for: {}. Operation can NOT be performed", handlerClass, e); + + } + } + private void buildNationalRequestedAttributes( ServiceProviderConfiguration spConfig, ILightRequest eidasRequest) { final boolean mandatesEnabled = !SpMandateModes.NONE.equals(spConfig.getMandateMode()); diff --git a/modules/eidas_proxy-sevice/src/main/resources/messages/eidasproxy_messages.properties b/modules/eidas_proxy-sevice/src/main/resources/messages/eidasproxy_messages.properties index 3f92d58a..34f0ee86 100644 --- a/modules/eidas_proxy-sevice/src/main/resources/messages/eidasproxy_messages.properties +++ b/modules/eidas_proxy-sevice/src/main/resources/messages/eidasproxy_messages.properties @@ -10,5 +10,6 @@ eidas.proxyservice.09=eIDAS authentication not possible, because legal person is eidas.proxyservice.10=eIDAS authentication not possible, because legal person is requested but not mandate profiles are defined eidas.proxyservice.11=No Authentication request with stated communication token. +eidas.proxyservice.ejustice.01=eJustice attributes for natural and legal person can not supported in a single request. eidas.proxyservice.99=Internal error during eIDAS Proxy-Service authentication \ No newline at end of file diff --git a/modules/eidas_proxy-sevice/src/test/java/at/asitplus/eidas/specific/modules/msproxyservice/test/protocol/EidasProxyServiceControllerBorisTest.java b/modules/eidas_proxy-sevice/src/test/java/at/asitplus/eidas/specific/modules/msproxyservice/test/protocol/EidasProxyServiceControllerBorisTest.java index dfa4e264..78ae87a5 100644 --- a/modules/eidas_proxy-sevice/src/test/java/at/asitplus/eidas/specific/modules/msproxyservice/test/protocol/EidasProxyServiceControllerBorisTest.java +++ b/modules/eidas_proxy-sevice/src/test/java/at/asitplus/eidas/specific/modules/msproxyservice/test/protocol/EidasProxyServiceControllerBorisTest.java @@ -3,6 +3,7 @@ package at.asitplus.eidas.specific.modules.msproxyservice.test.protocol; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertThrows; import java.io.IOException; import java.net.URISyntaxException; @@ -31,6 +32,7 @@ import at.asitplus.eidas.specific.modules.core.eidas.EidasConstants; import at.asitplus.eidas.specific.modules.core.eidas.service.EidasAttributeRegistry; import at.asitplus.eidas.specific.modules.core.eidas.test.dummy.DummySpecificCommunicationService; import at.asitplus.eidas.specific.modules.msproxyservice.MsProxyServiceConstants; +import at.asitplus.eidas.specific.modules.msproxyservice.exception.EidasProxyServiceException; import at.asitplus.eidas.specific.modules.msproxyservice.protocol.EidasProxyServiceController; import at.gv.egiz.eaaf.core.api.data.EaafConstants; import at.gv.egiz.eaaf.core.api.data.ExtendedPvpAttributeDefinitions.SpMandateModes; @@ -88,6 +90,52 @@ public class EidasProxyServiceControllerBorisTest { } + @Test + public void bothBorisAttributesInSingleRequest() throws IOException, EaafException { + //initialize state + httpReq.addParameter(EidasParameterKeys.TOKEN.toString(), RandomStringUtils.randomAlphanumeric(10)); + String spCountryCode = RandomStringUtils.randomAlphabetic(2).toUpperCase(); + String issuer = RandomStringUtils.randomAlphabetic(10); + LightRequest.Builder authnReqBuilder = LightRequest.builder() + .id(UUID.randomUUID().toString()) + .issuer(issuer) + .citizenCountryCode(RandomStringUtils.randomAlphabetic(2).toUpperCase()) + .levelOfAssurance(EaafConstants.EIDAS_LOA_HIGH) + .spCountryCode(spCountryCode) + .spType("public") + .requestedAttributes(ImmutableAttributeMap.builder() + .put(attrRegistry.getCoreAttributeRegistry().getByFriendlyName( + EidasConstants.eIDAS_ATTR_LEGALNAME).first()) + .put(attrRegistry.getCoreAttributeRegistry().getByFriendlyName( + EidasConstants.eIDAS_ATTR_LEGALPERSONIDENTIFIER).first()) + .put(attrRegistry.getCoreAttributeRegistry().getByFriendlyName( + "eJusticeLegalPersonRole").first()) + .put(attrRegistry.getCoreAttributeRegistry().getByFriendlyName( + "eJusticeNaturalPersonRole").first()) + .build()); + + proxyService.setiLightRequest(authnReqBuilder.build()); + + + // set default mandate configuration + List mandateProfilesNat = + Arrays.asList(RandomStringUtils.randomAlphabetic(5), RandomStringUtils.randomAlphabetic(5)); + List mandateProfilesJur = + Arrays.asList(RandomStringUtils.randomAlphabetic(5), RandomStringUtils.randomAlphabetic(5)); + config.putConfigValue(MsProxyServiceConstants.CONIG_PROPS_EIDAS_PROXY_MANDATES_ENABLED, "true"); + config.putConfigValue(MsProxyServiceConstants.CONIG_PROPS_EIDAS_PROXY_MANDATES_PROFILE_DEFAULT_NATURAL, + StringUtils.join(mandateProfilesNat, ",")); + config.putConfigValue(MsProxyServiceConstants.CONIG_PROPS_EIDAS_PROXY_MANDATES_PROFILE_DEFAULT_LEGAL, + StringUtils.join(mandateProfilesJur, ",")); + + + //execute + EidasProxyServiceException error = assertThrows("wrong exception", EidasProxyServiceException.class, + () -> controller.receiveEidasAuthnRequest(httpReq, httpResp)); + assertEquals("wrong errorCode", "eidas.proxyservice.ejustice.01", error.getErrorId()); + + } + @Test public void validAuthnRequestWithBorisAttributeLegal() throws IOException, EaafException { //initialize state -- cgit v1.2.3