diff options
author | Thomas <> | 2021-05-24 14:49:48 +0200 |
---|---|---|
committer | Thomas <> | 2022-03-03 16:31:56 +0100 |
commit | a2eba5646b5b43d549993859849cdcf2baae5eb2 (patch) | |
tree | b6815e24f56f09a9b01101c179cb0f231f2914f2 /eidas_modules/eidas_proxy-sevice/src/main/java/at/asitplus/eidas/specific/modules/msproxyservice/protocol/EidasProxyServiceController.java | |
parent | 45b0a790ad412e6b7118f1c937b620c66a32fd64 (diff) | |
download | National_eIDAS_Gateway-a2eba5646b5b43d549993859849cdcf2baae5eb2.tar.gz National_eIDAS_Gateway-a2eba5646b5b43d549993859849cdcf2baae5eb2.tar.bz2 National_eIDAS_Gateway-a2eba5646b5b43d549993859849cdcf2baae5eb2.zip |
select mandate based on eIDAS Proxy-Service request information
Diffstat (limited to 'eidas_modules/eidas_proxy-sevice/src/main/java/at/asitplus/eidas/specific/modules/msproxyservice/protocol/EidasProxyServiceController.java')
-rw-r--r-- | eidas_modules/eidas_proxy-sevice/src/main/java/at/asitplus/eidas/specific/modules/msproxyservice/protocol/EidasProxyServiceController.java | 253 |
1 files changed, 205 insertions, 48 deletions
diff --git a/eidas_modules/eidas_proxy-sevice/src/main/java/at/asitplus/eidas/specific/modules/msproxyservice/protocol/EidasProxyServiceController.java b/eidas_modules/eidas_proxy-sevice/src/main/java/at/asitplus/eidas/specific/modules/msproxyservice/protocol/EidasProxyServiceController.java index fda1652e..4b699bae 100644 --- a/eidas_modules/eidas_proxy-sevice/src/main/java/at/asitplus/eidas/specific/modules/msproxyservice/protocol/EidasProxyServiceController.java +++ b/eidas_modules/eidas_proxy-sevice/src/main/java/at/asitplus/eidas/specific/modules/msproxyservice/protocol/EidasProxyServiceController.java @@ -4,14 +4,18 @@ import java.io.IOException; import java.text.MessageFormat; import java.util.Collections; import java.util.HashMap; -import java.util.List; import java.util.Map; +import java.util.UUID; import java.util.stream.Collectors; +import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; +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.factory.annotation.Autowired; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; @@ -19,6 +23,7 @@ import org.springframework.web.bind.annotation.RequestMethod; import com.google.common.collect.ImmutableSortedSet; +import at.asitplus.eidas.specific.connector.MsEidasNodeConstants; import at.asitplus.eidas.specific.connector.config.ServiceProviderConfiguration; import at.asitplus.eidas.specific.modules.auth.eidas.v2.Constants; import at.asitplus.eidas.specific.modules.auth.eidas.v2.service.EidasAttributeRegistry; @@ -28,13 +33,18 @@ import at.gv.egiz.components.eventlog.api.EventConstants; import at.gv.egiz.eaaf.core.api.IRequest; import at.gv.egiz.eaaf.core.api.data.EaafConfigConstants; import at.gv.egiz.eaaf.core.api.data.EaafConstants; +import at.gv.egiz.eaaf.core.api.data.ExtendedPvpAttributeDefinitions.SpMandateModes; import at.gv.egiz.eaaf.core.api.idp.IModulInfo; import at.gv.egiz.eaaf.core.api.idp.ISpConfiguration; import at.gv.egiz.eaaf.core.exceptions.EaafException; +import at.gv.egiz.eaaf.core.exceptions.GuiBuildException; import at.gv.egiz.eaaf.core.impl.idp.controller.AbstractController; import at.gv.egiz.eaaf.core.impl.utils.KeyValueUtils; import eu.eidas.auth.commons.EidasParameterKeys; import eu.eidas.auth.commons.light.ILightRequest; +import eu.eidas.auth.commons.light.impl.LightResponse; +import eu.eidas.auth.commons.light.impl.LightResponse.Builder; +import eu.eidas.auth.commons.light.impl.ResponseStatus; import eu.eidas.specificcommunication.SpecificCommunicationDefinitionBeanNames; import eu.eidas.specificcommunication.exception.SpecificCommunicationException; import eu.eidas.specificcommunication.protocol.SpecificCommunicationService; @@ -56,11 +66,15 @@ public class EidasProxyServiceController extends AbstractController implements I private static final String ERROR_03 = "eidas.proxyservice.03"; private static final String ERROR_04 = "eidas.proxyservice.04"; private static final String ERROR_05 = "eidas.proxyservice.05"; - + private static final String ERROR_07 = "eidas.proxyservice.07"; + private static final String ERROR_08 = "eidas.proxyservice.08"; + private static final String ERROR_09 = "eidas.proxyservice.09"; + private static final String ERROR_10 = "eidas.proxyservice.10"; + public static final String PROTOCOL_ID = "eidasProxy"; - @Autowired - private EidasAttributeRegistry attrRegistry; + @Autowired EidasAttributeRegistry attrRegistry; + @Autowired ProxyServiceAuthenticationAction responseAction; /** * End-point that receives authentication requests from eIDAS Node. @@ -101,7 +115,8 @@ public class EidasProxyServiceController extends AbstractController implements I ImmutableSortedSet.copyOf(attrRegistry.getCoreAttributeRegistry().getAttributes())); log.debug("Received eIDAS auth. request from: {}, Initializing authentication environment ... ", eidasRequest.getSpCountryCode() != null ? eidasRequest.getSpCountryCode() : "'missing SP-country'"); - + log.trace("Received eIDAS requst: {}", eidasRequest); + // create pendingRequest object pendingReq = applicationContext.getBean(ProxyServicePendingRequest.class); pendingReq.initialize(httpReq, authConfig); @@ -114,6 +129,10 @@ public class EidasProxyServiceController extends AbstractController implements I pendingReq.getUniqueTransactionIdentifier(), EventConstants.TRANSACTION_IP, httpReq.getRemoteAddr()); + + //TODO: map issuer from eIDAS request to countryCode in special cases + + // validate eIDAS Authn. request and set into pending-request validateEidasAuthnRequest(eidasRequest); pendingReq.setEidasRequest(eidasRequest); @@ -156,10 +175,43 @@ public class EidasProxyServiceController extends AbstractController implements I } @Override - public boolean generateErrorMessage(Throwable e, HttpServletRequest request, HttpServletResponse response, - IRequest protocolRequest) throws Throwable { + public boolean generateErrorMessage(Throwable e, HttpServletRequest httpReq, HttpServletResponse httpResp, + IRequest pendingReq) throws Throwable { + if (pendingReq instanceof ProxyServicePendingRequest) { + try { + ILightRequest eidasReq = ((ProxyServicePendingRequest) pendingReq).getEidasRequest(); + + //build eIDAS response + Builder lightRespBuilder = LightResponse.builder(); + lightRespBuilder.id(UUID.randomUUID().toString()); + lightRespBuilder.inResponseToId(eidasReq.getId()); + lightRespBuilder.relayState(eidasReq.getRelayState()); + lightRespBuilder.issuer(authConfig.getBasicConfiguration( + MsProxyServiceConstants.CONIG_PROPS_EIDAS_PROXY_NODE_ENTITYID)); + lightRespBuilder.subject(UUID.randomUUID().toString()); + lightRespBuilder.subjectNameIdFormat(NameIDType.TRANSIENT); + lightRespBuilder.status(ResponseStatus.builder() + .statusCode(StatusCode.RESPONDER) + .subStatusCode(statusMessager.mapInternalErrorToExternalError(statusMessager.getResponseErrorCode(e))) + .statusMessage(StringEscapeUtils.escapeXml(e.getLocalizedMessage())) + .build()); + + // forward to eIDAS Proxy-Service + responseAction.forwardToEidasProxy(pendingReq, httpReq, httpResp, lightRespBuilder.build()); + + return true; + + } catch (ServletException | IOException | GuiBuildException e1) { + log.warn("Forward error to eIDAS Proxy-Service FAILED. Handle error localy ... ", e1); + + } + + } else { + log.error("eIDAS Proxy-Service authentication requires PendingRequest of Type: {}", + ProxyServicePendingRequest.class.getName()); - // TODO: implement error handling for eIDAS Node communication + } + return false; } @@ -189,16 +241,17 @@ public class EidasProxyServiceController extends AbstractController implements I * @throws EidasProxyServiceException In case of a validation error */ private void validateEidasAuthnRequest(ILightRequest eidasRequest) throws EidasProxyServiceException { - if (StringUtils.isEmpty(eidasRequest.getSpCountryCode())) { + if (StringUtils.isEmpty(eidasRequest.getIssuer())) { throw new EidasProxyServiceException(ERROR_05, null); } - - /* - * TODO: validate requested attributes --> check if natural-person and - * legal-person attributes requested in parallel - */ - + + // check if natural-person and legal-person attributes requested in parallel + if (isLegalPersonRequested(eidasRequest) && isNaturalPersonRequested(eidasRequest)) { + throw new EidasProxyServiceException(ERROR_08, null); + + } + // TODO: validate some other stuff } @@ -213,63 +266,167 @@ public class EidasProxyServiceController extends AbstractController implements I private ISpConfiguration generateSpConfigurationFromEidasRequest(ILightRequest eidasRequest) throws EidasProxyServiceException { try { - final String spCountry = eidasRequest.getSpCountryCode(); - final Map<String, String> spConfigMap = new HashMap<>(); + + Map<String, String> connectorConfigMap = extractRawConnectorConfiguration(eidasRequest); + + // check if country-code is available + String spCountry = connectorConfigMap.get(MsProxyServiceConstants.CONIG_PROPS_CONNECTOR_COUNTRYCODE); + if (StringUtils.isEmpty(spCountry)) { + throw new EidasProxyServiceException(ERROR_07, null); - // TODO: how we get the EntityId from eIDAS connector? - spConfigMap.put(EaafConfigConstants.SERVICE_UNIQUEIDENTIFIER, + } + + // build FriendyName from CountryCode and SPType + connectorConfigMap.put(MsEidasNodeConstants.PROP_CONFIG_SP_FRIENDLYNAME, MessageFormat.format(MsProxyServiceConstants.TEMPLATE_SP_UNIQUE_ID, spCountry, eidasRequest.getSpType())); - final ServiceProviderConfiguration spConfig = new ServiceProviderConfiguration(spConfigMap, authConfig); + // build Service-Provider configuration object + final ServiceProviderConfiguration spConfig = new ServiceProviderConfiguration(connectorConfigMap, authConfig); + // build bPK target from Country-Code final String ccCountry = authConfig.getBasicConfiguration(Constants.CONIG_PROPS_EIDAS_NODE_COUNTRYCODE, Constants.DEFAULT_MS_NODE_COUNTRY_CODE); - spConfig.setBpkTargetIdentifier( EaafConstants.URN_PREFIX_EIDAS + ccCountry + "+" + spCountry); + + // set required LoA from eIDAS request spConfig.setRequiredLoA( eidasRequest.getLevelsOfAssurance().stream().map(el -> el.getValue()).collect(Collectors.toList())); - // TODO: check if only mandates are allowed in case of legal person requested - // --> set force-mandate flag - spConfig.setMandateProfiles(buildMandateProfileConfiguration(eidasRequest)); - + //build mandate profiles for this specific request + buildMandateProfileConfiguration(spConfig, eidasRequest); + return spConfig; + } catch (EidasProxyServiceException e) { + throw e; + } catch (final EaafException e) { throw new EidasProxyServiceException(ERROR_04, new Object[] { e.getMessage() }, e); } } - private List<String> buildMandateProfileConfiguration(ILightRequest eidasRequest) { - if (authConfig.getBasicConfigurationBoolean( - MsProxyServiceConstants.CONIG_PROPS_EIDAS_PROXY_MANDATES_ENABLED, false)) { - log.trace("eIDAS Proxy-Service allows mandates. Selecting profiles ... "); - - /* - * TODO: split profiles in natural-person and legal-person profiles and select - * correct one based on requested attributes - */ - final List<String> spMandateProfiles = authConfig.getBasicConfigurationWithPrefix( - MsProxyServiceConstants.CONIG_PROPS_EIDAS_PROXY_MANDATES_PROFILE_SPECIFIC) - .entrySet().stream() - .filter(el -> el.getKey().endsWith(eidasRequest.getSpCountryCode().toLowerCase())) - .findFirst() - .map(el -> KeyValueUtils.getListOfCsvValues(el.getValue())) - .orElse(KeyValueUtils.getListOfCsvValues( - authConfig.getBasicConfiguration( - MsProxyServiceConstants.CONIG_PROPS_EIDAS_PROXY_MANDATES_PROFILE_DEFAULT))); - - log.debug("Set mandate-profiles: {} to request from country: {}", - spMandateProfiles, eidasRequest.getSpCountryCode()); - return spMandateProfiles; + + private Map<String, String> extractRawConnectorConfiguration(ILightRequest eidasRequest) { + Map<String, String> allConnectorConfigs = authConfig.getBasicConfigurationWithPrefix( + MsProxyServiceConstants.CONIG_PROPS_CONNECTOR_PREFIX); + if (log.isTraceEnabled()) { + log.trace("Full-connector configuration:"); + allConnectorConfigs.entrySet().stream().forEach( + el -> log.trace("Key: {} -> Value: {}", el.getKey(), el.getValue())); + + } + + + Map<String, String> connectorConfig = allConnectorConfigs.entrySet().stream() + .filter(el -> el.getKey().endsWith(MsEidasNodeConstants.PROP_CONFIG_SP_UNIQUEIDENTIFIER) + && el.getValue().equals(eidasRequest.getIssuer())) + .findFirst() + .map(el -> KeyValueUtils.getSubSetWithPrefix(allConnectorConfigs, + KeyValueUtils.getParentKey(el.getKey()) + KeyValueUtils.KEY_DELIMITER)) + .orElse(new HashMap<>()); + + + if (connectorConfig.isEmpty()) { + log.debug("No specific configuration for eIDAS Connector: {} Using default configuration ... ", + eidasRequest.getIssuer()); + + // set EntityId of the requesting eIDAS Connector + connectorConfig.put(EaafConfigConstants.SERVICE_UNIQUEIDENTIFIER, eidasRequest.getIssuer()); + + // set country-code from eIDAS request + connectorConfig.put(MsProxyServiceConstants.CONIG_PROPS_CONNECTOR_COUNTRYCODE, + eidasRequest.getSpCountryCode()); + + // set default mandate configuration + connectorConfig.put(MsProxyServiceConstants.CONIG_PROPS_CONNECTOR_MANDATES_ENABLED, + String.valueOf(authConfig.getBasicConfigurationBoolean( + MsProxyServiceConstants.CONIG_PROPS_EIDAS_PROXY_MANDATES_ENABLED, false))); + connectorConfig.put(MsProxyServiceConstants.CONIG_PROPS_CONNECTOR_MANDATES_PROFILE_NATURAL, + authConfig.getBasicConfiguration( + MsProxyServiceConstants.CONIG_PROPS_EIDAS_PROXY_MANDATES_PROFILE_DEFAULT_NATURAL)); + connectorConfig.put(MsProxyServiceConstants.CONIG_PROPS_CONNECTOR_MANDATES_PROFILE_LEGAL, + authConfig.getBasicConfiguration( + MsProxyServiceConstants.CONIG_PROPS_EIDAS_PROXY_MANDATES_PROFILE_DEFAULT_LEGAL)); + + } else { + log.debug("Find specific configuration for eIDAS Connector: {}", eidasRequest.getIssuer()); + + } + + return connectorConfig; + + } + + + private void buildMandateProfileConfiguration(ServiceProviderConfiguration spConfig, ILightRequest eidasRequest) + throws EidasProxyServiceException { + // check if mandates are enabled + if (spConfig.isConfigurationValue(MsProxyServiceConstants.CONIG_PROPS_CONNECTOR_MANDATES_ENABLED, false)) { + injectMandateInfosIntoSpConfig(spConfig, eidasRequest); + + } else { + if (isLegalPersonRequested(eidasRequest)) { + throw new EidasProxyServiceException(ERROR_09, null); + + } + + spConfig.setMandateProfiles(Collections.emptyList()); + spConfig.setMandateMode(SpMandateModes.NONE); + + } + } + + private void injectMandateInfosIntoSpConfig(ServiceProviderConfiguration spConfig, + ILightRequest eidasRequest) throws EidasProxyServiceException { + log.trace("eIDAS Proxy-Service allows mandates for Connector: {}. Selecting profiles ... ", + spConfig.getUniqueIdentifier()); + + //check if legal person is requested + boolean isLegalPersonRequested = isLegalPersonRequested(eidasRequest); + + // set mandate profiles + if (isLegalPersonRequested) { + spConfig.setMandateProfiles(KeyValueUtils.getListOfCsvValues( + spConfig.getConfigurationValue(MsProxyServiceConstants.CONIG_PROPS_CONNECTOR_MANDATES_PROFILE_LEGAL))); + + spConfig.setMandateMode(SpMandateModes.LEGAL_FORCE); + + } else if (isNaturalPersonRequested(eidasRequest)) { + spConfig.setMandateProfiles(KeyValueUtils.getListOfCsvValues( + spConfig.getConfigurationValue(MsProxyServiceConstants.CONIG_PROPS_CONNECTOR_MANDATES_PROFILE_NATURAL))); + + spConfig.setMandateMode(SpMandateModes.NATURAL); + } - return Collections.emptyList(); + log.debug("Set mandate-profiles: {} to request from issuer: {}", + spConfig.getMandateProfiles(), spConfig.getUniqueIdentifier()); + + if (isLegalPersonRequested && spConfig.getMandateProfiles().isEmpty()) { + throw new EidasProxyServiceException(ERROR_10, null); + + } + } + private boolean isLegalPersonRequested(ILightRequest eidasRequest) { + return eidasRequest.getRequestedAttributes().entrySet().stream() + .filter(el -> el.getKey().getFriendlyName().equals(Constants.eIDAS_ATTR_LEGALPERSONIDENTIFIER)) + .findFirst() + .isPresent(); + + } + + private boolean isNaturalPersonRequested(ILightRequest eidasRequest) { + return eidasRequest.getRequestedAttributes().entrySet().stream() + .filter(el -> el.getKey().getFriendlyName().equals(Constants.eIDAS_ATTR_PERSONALIDENTIFIER)) + .findFirst() + .isPresent(); + + } } |