From 7bf7c3c03fd3a1efeaf3f8e3dd75922e2f5f9921 Mon Sep 17 00:00:00 2001 From: Thomas <> Date: Tue, 8 Mar 2022 19:06:10 +0100 Subject: refactor(core): move all project libs into sub-project 'modules' --- .../msproxyservice/EidasProxyMessageSource.java | 22 + .../msproxyservice/MsProxyServiceConstants.java | 54 +++ .../MsProxyServiceSpringResourceProvider.java | 52 +++ .../exception/EidasProxyServiceException.java | 19 + .../protocol/EidasProxyServiceController.java | 443 +++++++++++++++++++++ .../protocol/ProxyServiceAuthenticationAction.java | 374 +++++++++++++++++ .../protocol/ProxyServicePendingRequest.java | 28 ++ .../utils/EidasProxyServiceUtils.java | 45 +++ ...iz.components.spring.api.SpringResourceProvider | 1 + .../messages/eidasproxy_messages.properties | 14 + .../resources/spring/eidas_proxy-service.beans.xml | 28 ++ 11 files changed, 1080 insertions(+) create mode 100644 modules/eidas_proxy-sevice/src/main/java/at/asitplus/eidas/specific/modules/msproxyservice/EidasProxyMessageSource.java create mode 100644 modules/eidas_proxy-sevice/src/main/java/at/asitplus/eidas/specific/modules/msproxyservice/MsProxyServiceConstants.java create mode 100644 modules/eidas_proxy-sevice/src/main/java/at/asitplus/eidas/specific/modules/msproxyservice/MsProxyServiceSpringResourceProvider.java create mode 100644 modules/eidas_proxy-sevice/src/main/java/at/asitplus/eidas/specific/modules/msproxyservice/exception/EidasProxyServiceException.java create mode 100644 modules/eidas_proxy-sevice/src/main/java/at/asitplus/eidas/specific/modules/msproxyservice/protocol/EidasProxyServiceController.java create mode 100644 modules/eidas_proxy-sevice/src/main/java/at/asitplus/eidas/specific/modules/msproxyservice/protocol/ProxyServiceAuthenticationAction.java create mode 100644 modules/eidas_proxy-sevice/src/main/java/at/asitplus/eidas/specific/modules/msproxyservice/protocol/ProxyServicePendingRequest.java create mode 100644 modules/eidas_proxy-sevice/src/main/java/at/asitplus/eidas/specific/modules/msproxyservice/utils/EidasProxyServiceUtils.java create mode 100644 modules/eidas_proxy-sevice/src/main/resources/META-INF/services/at.gv.egiz.components.spring.api.SpringResourceProvider create mode 100644 modules/eidas_proxy-sevice/src/main/resources/messages/eidasproxy_messages.properties create mode 100644 modules/eidas_proxy-sevice/src/main/resources/spring/eidas_proxy-service.beans.xml (limited to 'modules/eidas_proxy-sevice/src/main') diff --git a/modules/eidas_proxy-sevice/src/main/java/at/asitplus/eidas/specific/modules/msproxyservice/EidasProxyMessageSource.java b/modules/eidas_proxy-sevice/src/main/java/at/asitplus/eidas/specific/modules/msproxyservice/EidasProxyMessageSource.java new file mode 100644 index 00000000..23390da8 --- /dev/null +++ b/modules/eidas_proxy-sevice/src/main/java/at/asitplus/eidas/specific/modules/msproxyservice/EidasProxyMessageSource.java @@ -0,0 +1,22 @@ +package at.asitplus.eidas.specific.modules.msproxyservice; + +import java.util.Arrays; +import java.util.List; + +import at.gv.egiz.eaaf.core.api.logging.IMessageSourceLocation; + +/** + * i18n Message-Source for eIDAS Proxy-Service messages. + * + * @author tlenz + * + */ +public class EidasProxyMessageSource implements IMessageSourceLocation { + + @Override + public List getMessageSourceLocation() { + return Arrays.asList("classpath:messages/eidasproxy_messages"); + + } + +} diff --git a/modules/eidas_proxy-sevice/src/main/java/at/asitplus/eidas/specific/modules/msproxyservice/MsProxyServiceConstants.java b/modules/eidas_proxy-sevice/src/main/java/at/asitplus/eidas/specific/modules/msproxyservice/MsProxyServiceConstants.java new file mode 100644 index 00000000..f6a88aa3 --- /dev/null +++ b/modules/eidas_proxy-sevice/src/main/java/at/asitplus/eidas/specific/modules/msproxyservice/MsProxyServiceConstants.java @@ -0,0 +1,54 @@ +package at.asitplus.eidas.specific.modules.msproxyservice; + +import at.asitplus.eidas.specific.modules.auth.eidas.v2.Constants; +import at.gv.egiz.eaaf.core.api.data.EaafConfigConstants; + +/** + * Constants for MS-specific eIDAS Proxy-Service. + * + * @author tlenz + * + */ +public class MsProxyServiceConstants { + + // general constants + public static final String TEMPLATE_SP_UNIQUE_ID = "eidasProxyAuth_from_{0}_type_{1}"; + + // configuration constants + public static final String CONIG_PROPS_EIDAS_PROXY_NODE_ENTITYID = Constants.CONIG_PROPS_EIDAS_NODE + + ".proxy.entityId"; + public static final String CONIG_PROPS_EIDAS_PROXY_NODE_FORWARD_URL = Constants.CONIG_PROPS_EIDAS_NODE + + ".proxy.forward.endpoint"; + + // mandate configuration + public static final String CONIG_PROPS_EIDAS_PROXY_MANDATES_ENABLED = + Constants.CONIG_PROPS_EIDAS_PREFIX + ".proxy.mandates.enabled"; + public static final String CONIG_PROPS_EIDAS_PROXY_MANDATES_PROFILE_DEFAULT_NATURAL = + Constants.CONIG_PROPS_EIDAS_PREFIX + ".proxy.mandates.profiles.natural.default"; + public static final String CONIG_PROPS_EIDAS_PROXY_MANDATES_PROFILE_DEFAULT_LEGAL = + Constants.CONIG_PROPS_EIDAS_PREFIX + ".proxy.mandates.profiles.legal.default"; + + + public static final String CONIG_PROPS_EIDAS_PROXY_WORKAROUND_MANDATES_LEGAL_PERSON = + Constants.CONIG_PROPS_EIDAS_PREFIX + ".proxy.workaround.mandates.legalperson"; + + // specific eIDAS-Connector configuration + public static final String CONIG_PROPS_CONNECTOR_PREFIX = "connector"; + public static final String CONIG_PROPS_CONNECTOR_UNIQUEID = EaafConfigConstants.SERVICE_UNIQUEIDENTIFIER; + public static final String CONIG_PROPS_CONNECTOR_COUNTRYCODE = "countryCode"; + public static final String CONIG_PROPS_CONNECTOR_MANDATES_ENABLED = "mandates.enabled"; + public static final String CONIG_PROPS_CONNECTOR_MANDATES_PROFILE_NATURAL = "mandates.natural"; + public static final String CONIG_PROPS_CONNECTOR_MANDATES_PROFILE_LEGAL = "mandates.legal"; + public static final String CONIG_PROPS_CONNECTOR_VALIDATION_ATTR_MDS = "validation.attributes.mds"; + + + //http end-points + public static final String EIDAS_HTTP_ENDPOINT_IDP_POST = "/eidas/light/idp/post"; + public static final String EIDAS_HTTP_ENDPOINT_IDP_REDIRECT = "/eidas/light/idp/redirect"; + + private MsProxyServiceConstants() { + //private constructor for class with only constant values + + } + +} diff --git a/modules/eidas_proxy-sevice/src/main/java/at/asitplus/eidas/specific/modules/msproxyservice/MsProxyServiceSpringResourceProvider.java b/modules/eidas_proxy-sevice/src/main/java/at/asitplus/eidas/specific/modules/msproxyservice/MsProxyServiceSpringResourceProvider.java new file mode 100644 index 00000000..d36e4712 --- /dev/null +++ b/modules/eidas_proxy-sevice/src/main/java/at/asitplus/eidas/specific/modules/msproxyservice/MsProxyServiceSpringResourceProvider.java @@ -0,0 +1,52 @@ +/* + * Copyright 2018 A-SIT Plus GmbH + * AT-specific eIDAS Connector has been developed in a cooperation between EGIZ, + * A-SIT Plus GmbH, A-SIT, and Graz University of Technology. + * + * Licensed under the EUPL, Version 1.2 or - as soon they will be approved by + * the European Commission - subsequent versions of the EUPL (the "License"); + * You may not use this work except in compliance with the License. + * You may obtain a copy of the License at: + * https://joinup.ec.europa.eu/news/understanding-eupl-v12 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * This product combines work with different licenses. See the "NOTICE" text + * file for details on the various modules and licenses. + * The "NOTICE" text file is part of the distribution. Any derivative works + * that you distribute must include a readable copy of the "NOTICE" text file. +*/ + +package at.asitplus.eidas.specific.modules.msproxyservice; + +import org.springframework.core.io.ClassPathResource; +import org.springframework.core.io.Resource; + +import at.gv.egiz.components.spring.api.SpringResourceProvider; + +public class MsProxyServiceSpringResourceProvider implements SpringResourceProvider { + + @Override + public String getName() { + return "MS-specific eIDAS Proxy-Service module"; + } + + @Override + public String[] getPackagesToScan() { + return null; + + } + + @Override + public Resource[] getResourcesToLoad() { + final ClassPathResource eidasProxyServiceConfig = + new ClassPathResource("/spring/eidas_proxy-service.beans.xml", MsProxyServiceSpringResourceProvider.class); + + return new Resource[] { eidasProxyServiceConfig }; + } + +} diff --git a/modules/eidas_proxy-sevice/src/main/java/at/asitplus/eidas/specific/modules/msproxyservice/exception/EidasProxyServiceException.java b/modules/eidas_proxy-sevice/src/main/java/at/asitplus/eidas/specific/modules/msproxyservice/exception/EidasProxyServiceException.java new file mode 100644 index 00000000..43592a28 --- /dev/null +++ b/modules/eidas_proxy-sevice/src/main/java/at/asitplus/eidas/specific/modules/msproxyservice/exception/EidasProxyServiceException.java @@ -0,0 +1,19 @@ +package at.asitplus.eidas.specific.modules.msproxyservice.exception; + +import at.gv.egiz.eaaf.core.exceptions.EaafException; + +public class EidasProxyServiceException extends EaafException { + + private static final long serialVersionUID = 1L; + + public EidasProxyServiceException(String errorId, Object[] params) { + super(errorId, params); + + } + + public EidasProxyServiceException(String errorId, Object[] params, Throwable e) { + super(errorId, params, e); + + } + +} 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 new file mode 100644 index 00000000..e24c753e --- /dev/null +++ b/modules/eidas_proxy-sevice/src/main/java/at/asitplus/eidas/specific/modules/msproxyservice/protocol/EidasProxyServiceController.java @@ -0,0 +1,443 @@ +package at.asitplus.eidas.specific.modules.msproxyservice.protocol; + +import java.io.IOException; +import java.text.MessageFormat; +import java.util.Collections; +import java.util.HashMap; +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; +import org.springframework.web.bind.annotation.RequestMethod; + +import com.google.common.collect.ImmutableSortedSet; + +import at.asitplus.eidas.specific.core.MsEidasNodeConstants; +import at.asitplus.eidas.specific.core.config.ServiceProviderConfiguration; +import at.asitplus.eidas.specific.modules.auth.eidas.v2.Constants; +import at.asitplus.eidas.specific.modules.auth.eidas.v2.service.EidasAttributeRegistry; +import at.asitplus.eidas.specific.modules.msproxyservice.MsProxyServiceConstants; +import at.asitplus.eidas.specific.modules.msproxyservice.exception.EidasProxyServiceException; +import at.asitplus.eidas.specific.modules.msproxyservice.utils.EidasProxyServiceUtils; +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.EIDASSubStatusCode; +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; +import lombok.extern.slf4j.Slf4j; + +/** + * End-point implementation for authentication requests from eIDAS Proxy-Service + * to MS-specific eIDAS Proxy-Service. + * + * @author tlenz + * + */ +@Slf4j +@Controller +public class EidasProxyServiceController extends AbstractController implements IModulInfo { + + private static final String ERROR_01 = "eidas.proxyservice.01"; + private static final String ERROR_02 = "eidas.proxyservice.02"; + 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"; + private static final String ERROR_11 = "eidas.proxyservice.11"; + + public static final String PROTOCOL_ID = "eidasProxy"; + + @Autowired EidasAttributeRegistry attrRegistry; + @Autowired ProxyServiceAuthenticationAction responseAction; + + /** + * End-point that receives authentication requests from eIDAS Node. + * + * @param httpReq Http request + * @param httpResp Http response + * @throws IOException In case of general error + * @throws EaafException In case of a validation or processing error + */ + @RequestMapping(value = { + MsProxyServiceConstants.EIDAS_HTTP_ENDPOINT_IDP_POST, + MsProxyServiceConstants.EIDAS_HTTP_ENDPOINT_IDP_REDIRECT + }, + method = { RequestMethod.POST, RequestMethod.GET }) + public void receiveEidasAuthnRequest(HttpServletRequest httpReq, HttpServletResponse httpResp) + throws IOException, + EaafException { + log.trace("Receive request on eidas proxy-service end-points"); + ProxyServicePendingRequest pendingReq = null; + try { + // get token from Request + final String tokenBase64 = httpReq.getParameter(EidasParameterKeys.TOKEN.toString()); + if (StringUtils.isEmpty(tokenBase64)) { + log.warn("NO eIDAS message token found."); + throw new EidasProxyServiceException(ERROR_02, null); + + } + log.trace("Receive eIDAS-node token: {}. Searching authentication request from eIDAS Proxy-Service ...", + tokenBase64); + + // read authentication request from shared cache + final SpecificCommunicationService specificProxyCommunicationService = + (SpecificCommunicationService) applicationContext.getBean( + SpecificCommunicationDefinitionBeanNames.SPECIFIC_PROXYSERVICE_COMMUNICATION_SERVICE + .toString()); + final ILightRequest eidasRequest = specificProxyCommunicationService.getAndRemoveRequest( + tokenBase64, + ImmutableSortedSet.copyOf(attrRegistry.getCoreAttributeRegistry().getAttributes())); + if (eidasRequest == null) { + log.info("Find no eIDAS Authn. Request with stated token."); + throw new EidasProxyServiceException(ERROR_11, null); + + } + + 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); + pendingReq.setModule(getName()); + + // log 'transaction created' event + revisionsLogger.logEvent(EventConstants.TRANSACTION_CREATED, + pendingReq.getUniqueTransactionIdentifier()); + revisionsLogger.logEvent(pendingReq.getUniqueSessionIdentifier(), + pendingReq.getUniqueTransactionIdentifier(), EventConstants.TRANSACTION_IP, + httpReq.getRemoteAddr()); + + // validate eIDAS Authn. request and set into pending-request + validateEidasAuthnRequest(eidasRequest); + pendingReq.setEidasRequest(eidasRequest); + + // generate Service-Provider configuration from eIDAS request + final ISpConfiguration spConfig = generateSpConfigurationFromEidasRequest(eidasRequest); + + // validate eIDAS Authn. request by using eIDAS Connector specifc parameters + validateEidasAuthnRequest(spConfig, eidasRequest); + + // populate pendingRequest with parameters + pendingReq.setOnlineApplicationConfiguration(spConfig); + pendingReq.setSpEntityId(spConfig.getUniqueIdentifier()); + pendingReq.setPassiv(false); + pendingReq.setForce(true); + + // AuthnRequest needs authentication + pendingReq.setNeedAuthentication(true); + + // set protocol action, which should be executed after authentication + pendingReq.setAction(ProxyServiceAuthenticationAction.class.getName()); + + // switch to session authentication + protAuthService.performAuthentication(httpReq, httpResp, pendingReq); + + } catch (final EidasProxyServiceException e) { + throw e; + + } catch (final SpecificCommunicationException e) { + log.error("Can not read eIDAS Authn request from shared cache. Reason: {}", e.getMessage()); + throw new EidasProxyServiceException(ERROR_03, new Object[] { e.getMessage() }, e); + + } catch (final Throwable e) { + // write revision log entries + if (pendingReq != null) { + revisionsLogger.logEvent(pendingReq, EventConstants.TRANSACTION_ERROR, + pendingReq.getUniqueTransactionIdentifier()); + } + + throw new EidasProxyServiceException(ERROR_01, new Object[] { e.getMessage() }, e); + } + + } + + @Override + 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(EIDASSubStatusCode.AUTHN_FAILED_URI.getValue()) + .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()); + + } + + return false; + + } + + @Override + public String getName() { + return EidasProxyServiceController.class.getName(); + + } + + @Override + public String getAuthProtocolIdentifier() { + return PROTOCOL_ID; + + } + + @Override + public boolean validate(HttpServletRequest request, HttpServletResponse response, IRequest pending) { + return true; + + } + + /** + * Generic validation of incoming eIDAS request. + * + * @param eidasRequest Incoming eIDAS authentication request + * @throws EidasProxyServiceException In case of a validation error + */ + private void validateEidasAuthnRequest(ILightRequest eidasRequest) throws EidasProxyServiceException { + if (StringUtils.isEmpty(eidasRequest.getIssuer())) { + throw new EidasProxyServiceException(ERROR_05, null); + + } + + // TODO: validate some other stuff + + } + + /** + * eIDAS Connector specific validation of incoming eIDAS request. + * + * @param eidasRequest Incoming eIDAS authentication request + * @param spConfig eIDAS Connector configuration + * @throws EidasProxyServiceException In case of a validation error + */ + private void validateEidasAuthnRequest(ISpConfiguration spConfig, ILightRequest eidasRequest) + throws EidasProxyServiceException { + // check if natural-person and legal-person attributes requested in parallel + if (spConfig.isConfigurationValue(MsProxyServiceConstants.CONIG_PROPS_CONNECTOR_VALIDATION_ATTR_MDS, true) + && EidasProxyServiceUtils.isLegalPersonRequested(eidasRequest) + && EidasProxyServiceUtils.isNaturalPersonRequested(eidasRequest)) { + throw new EidasProxyServiceException(ERROR_08, null); + + } + + // TODO: validate some other stuff + + } + + /** + * Generate a dummy Service-Provider configuration for processing. + * + * @param eidasRequest Incoming eIDAS authentication request + * @return Service-Provider configuration that can be used for authentication + * @throws EidasProxyServiceException In case of a configuration error + */ + private ISpConfiguration generateSpConfigurationFromEidasRequest(ILightRequest eidasRequest) + throws EidasProxyServiceException { + try { + + Map 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); + + } + + // build FriendyName from CountryCode and SPType + connectorConfigMap.put(MsEidasNodeConstants.PROP_CONFIG_SP_FRIENDLYNAME, + MessageFormat.format(MsProxyServiceConstants.TEMPLATE_SP_UNIQUE_ID, + spCountry, eidasRequest.getSpType())); + + // 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())); + + //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 Map extractRawConnectorConfiguration(ILightRequest eidasRequest) { + Map 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 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 (EidasProxyServiceUtils.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 + if (EidasProxyServiceUtils.isLegalPersonRequested(eidasRequest)) { + spConfig.setMandateProfiles(KeyValueUtils.getListOfCsvValues( + spConfig.getConfigurationValue(MsProxyServiceConstants.CONIG_PROPS_CONNECTOR_MANDATES_PROFILE_LEGAL))); + spConfig.setMandateMode(SpMandateModes.LEGAL_FORCE); + + if (spConfig.getMandateProfiles().isEmpty()) { + throw new EidasProxyServiceException(ERROR_10, null); + + } + + } else if (EidasProxyServiceUtils.isNaturalPersonRequested(eidasRequest)) { + spConfig.setMandateProfiles(KeyValueUtils.getListOfCsvValues( + spConfig.getConfigurationValue(MsProxyServiceConstants.CONIG_PROPS_CONNECTOR_MANDATES_PROFILE_NATURAL))); + + spConfig.setMandateMode(SpMandateModes.NATURAL); + + } + + + if (spConfig.getMandateProfiles().isEmpty()) { + log.debug("No mandate-profiles for issure: {}. Set mandate-mode to 'none'", + spConfig.getUniqueIdentifier()); + spConfig.setMandateMode(SpMandateModes.NONE); + + } else { + log.debug("Set mandate-profiles: {} to request from issuer: {}", + spConfig.getMandateProfiles(), spConfig.getUniqueIdentifier()); + + } + + } +} diff --git a/modules/eidas_proxy-sevice/src/main/java/at/asitplus/eidas/specific/modules/msproxyservice/protocol/ProxyServiceAuthenticationAction.java b/modules/eidas_proxy-sevice/src/main/java/at/asitplus/eidas/specific/modules/msproxyservice/protocol/ProxyServiceAuthenticationAction.java new file mode 100644 index 00000000..15524005 --- /dev/null +++ b/modules/eidas_proxy-sevice/src/main/java/at/asitplus/eidas/specific/modules/msproxyservice/protocol/ProxyServiceAuthenticationAction.java @@ -0,0 +1,374 @@ +package at.asitplus.eidas.specific.modules.msproxyservice.protocol; + +import java.io.IOException; +import java.util.UUID; + +import javax.annotation.PostConstruct; +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.apache.commons.lang3.StringUtils; +import org.opensaml.saml.saml2.core.NameIDType; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.ApplicationContext; +import org.springframework.core.io.ResourceLoader; +import org.springframework.web.util.UriComponentsBuilder; + +import at.asitplus.eidas.specific.core.MsEidasNodeConstants; +import at.asitplus.eidas.specific.core.gui.StaticGuiBuilderConfiguration; +import at.asitplus.eidas.specific.modules.auth.eidas.v2.Constants; +import at.asitplus.eidas.specific.modules.auth.eidas.v2.service.EidasAttributeRegistry; +import at.asitplus.eidas.specific.modules.msproxyservice.MsProxyServiceConstants; +import at.asitplus.eidas.specific.modules.msproxyservice.exception.EidasProxyServiceException; +import at.asitplus.eidas.specific.modules.msproxyservice.utils.EidasProxyServiceUtils; +import at.gv.egiz.eaaf.core.api.IRequest; +import at.gv.egiz.eaaf.core.api.data.PvpAttributeDefinitions; +import at.gv.egiz.eaaf.core.api.gui.ISpringMvcGuiFormBuilder; +import at.gv.egiz.eaaf.core.api.idp.IAction; +import at.gv.egiz.eaaf.core.api.idp.IAuthData; +import at.gv.egiz.eaaf.core.api.idp.IConfiguration; +import at.gv.egiz.eaaf.core.api.idp.IEidAuthData; +import at.gv.egiz.eaaf.core.api.idp.slo.SloInformationInterface; +import at.gv.egiz.eaaf.core.exceptions.EaafConfigurationException; +import at.gv.egiz.eaaf.core.exceptions.EaafException; +import at.gv.egiz.eaaf.core.exceptions.GuiBuildException; +import at.gv.egiz.eaaf.core.impl.data.SloInformationImpl; +import eu.eidas.auth.commons.EidasParameterKeys; +import eu.eidas.auth.commons.attribute.AttributeDefinition; +import eu.eidas.auth.commons.attribute.ImmutableAttributeMap; +import eu.eidas.auth.commons.light.ILightRequest; +import eu.eidas.auth.commons.light.ILightResponse; +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.auth.commons.tx.BinaryLightToken; +import eu.eidas.specificcommunication.BinaryLightTokenHelper; +import eu.eidas.specificcommunication.SpecificCommunicationDefinitionBeanNames; +import eu.eidas.specificcommunication.exception.SpecificCommunicationException; +import eu.eidas.specificcommunication.protocol.SpecificCommunicationService; +import lombok.extern.slf4j.Slf4j; + +/** + * Result action of a successfully performed eIDAS Proxy-Service authentication. + * + * @author tlenz + * + */ +@Slf4j +public class ProxyServiceAuthenticationAction implements IAction { + + private static final String PROXYSERVICE_AUTH_ACTION_NAME = "MS-specific eIDAS-Proxy action"; + + @Autowired + ApplicationContext context; + @Autowired + IConfiguration basicConfig; + @Autowired + ResourceLoader resourceLoader; + @Autowired + ISpringMvcGuiFormBuilder guiBuilder; + @Autowired + EidasAttributeRegistry attrRegistry; + + @Override + public SloInformationInterface processRequest(IRequest pendingReq, HttpServletRequest httpReq, + HttpServletResponse httpResp, IAuthData authData) throws EaafException { + 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.status(ResponseStatus.builder() + .statusCode(Constants.SUCCESS_URI) + .build()); + + //TODO: check if we can use transient subjectNameIds + lightRespBuilder.subject(UUID.randomUUID().toString()); + lightRespBuilder.subjectNameIdFormat(NameIDType.TRANSIENT); + + //TODO: + lightRespBuilder.issuer(basicConfig.getBasicConfiguration( + MsProxyServiceConstants.CONIG_PROPS_EIDAS_PROXY_NODE_ENTITYID)); + lightRespBuilder.levelOfAssurance(authData.getEidasQaaLevel()); + lightRespBuilder.attributes(buildAttributesFromAuthData(authData, eidasReq)); + + // set SLO response object of EAAF framework + final SloInformationImpl sloInformation = new SloInformationImpl(); + sloInformation.setProtocolType(pendingReq.requestedModule()); + sloInformation + .setSpEntityID(pendingReq.getServiceProviderConfiguration().getUniqueIdentifier()); + + // forward to eIDAS Proxy-Service + forwardToEidasProxy(pendingReq, httpReq, httpResp, lightRespBuilder.build()); + + return sloInformation; + + } catch (ServletException | IOException | GuiBuildException e) { + throw new EidasProxyServiceException("eidas.proxyservice.06", null, e); + + } + + } else { + log.error("eIDAS Proxy-Service authentication requires PendingRequest of Type: {}", + ProxyServicePendingRequest.class.getName()); + throw new EaafException("eidas.proxyservice.99"); + + } + } + + @Override + public boolean needAuthentication(IRequest req, HttpServletRequest httpReq, HttpServletResponse httpResp) { + return true; + + } + + @Override + public String getDefaultActionName() { + return PROXYSERVICE_AUTH_ACTION_NAME; + + } + + + /** + * Forward eIDAS Light response to eIDAS node. + * + * @param pendingReq Current pending request. + * @param httpReq Current HTTP request + * @param httpResp Current HTTP response + * @param lightResponse eIDAS LightResponse + * @throws EaafConfigurationException In case of a configuration error + * @throws IOException In case of a general error + * @throws GuiBuildException In case of a GUI rendering error, if http POST binding is used + * @throws ServletException In case of a general error + */ + public void forwardToEidasProxy(IRequest pendingReq, HttpServletRequest httpReq, + HttpServletResponse httpResp, LightResponse lightResponse) throws EaafConfigurationException, IOException, + GuiBuildException, ServletException { + + // put request into shared cache + final BinaryLightToken token = putResponseInCommunicationCache(lightResponse); + final String tokenBase64 = BinaryLightTokenHelper.encodeBinaryLightTokenBase64(token); + + // select forward URL regarding the selected environment + final String forwardUrl = basicConfig.getBasicConfiguration( + MsProxyServiceConstants.CONIG_PROPS_EIDAS_PROXY_NODE_FORWARD_URL); + + if (StringUtils.isEmpty(forwardUrl)) { + log.warn("NO ForwardURL defined in configuration. Can NOT forward to eIDAS node! Process stops"); + throw new EaafConfigurationException("config.08", + new Object[] { MsProxyServiceConstants.CONIG_PROPS_EIDAS_PROXY_NODE_FORWARD_URL }); + + } + log.debug("ForwardURL: " + forwardUrl + " selected to forward eIDAS request"); + + if (basicConfig.getBasicConfiguration( + Constants.CONIG_PROPS_EIDAS_NODE_FORWARD_METHOD, + Constants.FORWARD_METHOD_GET).equals(Constants.FORWARD_METHOD_GET)) { + + log.debug("Use http-redirect for eIDAS node forwarding ... "); + // send redirect + final UriComponentsBuilder redirectUrl = UriComponentsBuilder.fromHttpUrl(forwardUrl); + redirectUrl.queryParam(EidasParameterKeys.TOKEN.toString(), tokenBase64); + httpResp.sendRedirect(redirectUrl.build().encode().toString()); + + } else { + log.debug("Use http-post for eIDAS node forwarding ... "); + final StaticGuiBuilderConfiguration config = new StaticGuiBuilderConfiguration( + basicConfig, + pendingReq, + Constants.TEMPLATE_POST_FORWARD_NAME, + null, + resourceLoader); + + config.putCustomParameter(null, Constants.TEMPLATE_POST_FORWARD_ENDPOINT, forwardUrl); + config.putCustomParameter(null, Constants.TEMPLATE_POST_FORWARD_TOKEN_NAME, + EidasParameterKeys.TOKEN.toString()); + config.putCustomParameter(null, Constants.TEMPLATE_POST_FORWARD_TOKEN_VALUE, + tokenBase64); + + guiBuilder.build(httpReq, httpResp, config, "Forward to eIDASNode form"); + + } + } + + @PostConstruct + private void checkConfiguration() { + //TODO: validate configuration on start-up + + } + + + private ImmutableAttributeMap buildAttributesFromAuthData(IAuthData authData, + ILightRequest eidasReq) { + IEidAuthData eidAuthData = (IEidAuthData) authData; + if (eidAuthData.isUseMandate()) { + log.debug("Building eIDAS Proxy-Service response with mandate ... "); + final ImmutableAttributeMap.Builder attributeMap = ImmutableAttributeMap.builder(); + injectRepesentativeInformation(attributeMap, eidAuthData); + injectMandatorInformation(attributeMap, eidAuthData); + + // work-around that injects nat. person subject to bypass validation on eIDAS Node + injectJurPersonWorkaroundIfRequired(attributeMap, eidasReq, authData); + + return attributeMap.build(); + + } else { + log.debug("Building eIDAS Proxy-Service response without mandates ... "); + return buildAttributesWithoutMandate(eidAuthData); + + } + } + + private void injectMandatorInformation( + ImmutableAttributeMap.Builder attributeMap, IEidAuthData eidAuthData) { + String natMandatorId = eidAuthData.getGenericData( + MsEidasNodeConstants.ATTR_EIDAS_NAT_MANDATOR_PERSONAL_IDENTIFIER, String.class); + + if (StringUtils.isNotEmpty(natMandatorId)) { + log.debug("Injecting natural mandator informations ... "); + final AttributeDefinition attrDefPersonalId = attrRegistry.getCoreAttributeRegistry().getByFriendlyName( + Constants.eIDAS_ATTR_PERSONALIDENTIFIER).first(); + final AttributeDefinition attrDefFamilyName = attrRegistry.getCoreAttributeRegistry().getByFriendlyName( + Constants.eIDAS_ATTR_CURRENTFAMILYNAME).first(); + final AttributeDefinition attrDefGivenName = attrRegistry.getCoreAttributeRegistry().getByFriendlyName( + Constants.eIDAS_ATTR_CURRENTGIVENNAME).first(); + final AttributeDefinition attrDefDateOfBirth = attrRegistry.getCoreAttributeRegistry().getByFriendlyName( + Constants.eIDAS_ATTR_DATEOFBIRTH).first(); + + attributeMap.put(attrDefPersonalId, natMandatorId); + attributeMap.put(attrDefFamilyName, eidAuthData.getGenericData( + PvpAttributeDefinitions.MANDATE_NAT_PER_FAMILY_NAME_NAME, String.class)); + attributeMap.put(attrDefGivenName, eidAuthData.getGenericData( + PvpAttributeDefinitions.MANDATE_NAT_PER_GIVEN_NAME_NAME, String.class)); + attributeMap.put(attrDefDateOfBirth, eidAuthData.getGenericData( + PvpAttributeDefinitions.MANDATE_NAT_PER_BIRTHDATE_NAME, String.class)); + + } else { + log.debug("Injecting legal mandator informations ... "); + final AttributeDefinition commonName = attrRegistry.getCoreAttributeRegistry().getByFriendlyName( + Constants.eIDAS_ATTR_LEGALNAME).first(); + final AttributeDefinition legalPersonId = attrRegistry.getCoreAttributeRegistry().getByFriendlyName( + Constants.eIDAS_ATTR_LEGALPERSONIDENTIFIER).first(); + + attributeMap.put(commonName, eidAuthData.getGenericData( + PvpAttributeDefinitions.MANDATE_LEG_PER_FULL_NAME_NAME, String.class)); + attributeMap.put(legalPersonId, eidAuthData.getGenericData( + MsEidasNodeConstants.ATTR_EIDAS_JUR_MANDATOR_PERSONAL_IDENTIFIER, String.class)); + + } + } + + private void injectRepesentativeInformation( + ImmutableAttributeMap.Builder attributeMap, IEidAuthData eidAuthData) { + final AttributeDefinition attrDefPersonalId = attrRegistry.getCoreAttributeRegistry().getByFriendlyName( + Constants.eIDAS_ATTR_REPRESENTATIVE_PERSONALIDENTIFIER).first(); + final AttributeDefinition attrDefFamilyName = attrRegistry.getCoreAttributeRegistry().getByFriendlyName( + Constants.eIDAS_ATTR_REPRESENTATIVE_CURRENTFAMILYNAME).first(); + final AttributeDefinition attrDefGivenName = attrRegistry.getCoreAttributeRegistry().getByFriendlyName( + Constants.eIDAS_ATTR_REPRESENTATIVE_CURRENTGIVENNAME).first(); + final AttributeDefinition attrDefDateOfBirth = attrRegistry.getCoreAttributeRegistry().getByFriendlyName( + Constants.eIDAS_ATTR_REPRESENTATIVE_DATEOFBIRTH).first(); + + attributeMap.put(attrDefPersonalId, + eidAuthData.getGenericData(MsEidasNodeConstants.ATTR_EIDAS_PERSONAL_IDENTIFIER, String.class)); + attributeMap.put(attrDefFamilyName, eidAuthData.getFamilyName()); + attributeMap.put(attrDefGivenName, eidAuthData.getGivenName()); + + //TODO: throw an error in case of SZR Date with month or day = "00" + attributeMap.put(attrDefDateOfBirth, eidAuthData.getDateOfBirth()); + + } + + /** + * Work-around to inject representative information as nat. person subject to bypass eIDAS Node validation. + * + *

Injection will only be done if this work-around is enabled by configuration, + * the mandator is a legal person, and both legal and natural person subject's is requested.

+ * + * @param attributeMap Attribute set for eIDAS response + * @param eidasReq Incoming eIDAS request + * @param authData Authentication data + */ + private void injectJurPersonWorkaroundIfRequired( + ImmutableAttributeMap.Builder attributeMap, ILightRequest eidasReq, IAuthData authData) { + if (isLegalPersonWorkaroundActive() && isLegalPersonMandateAvailable(authData) + && EidasProxyServiceUtils.isNaturalPersonRequested(eidasReq) + && EidasProxyServiceUtils.isLegalPersonRequested(eidasReq)) { + log.debug("Injecting representative information as nat. person subject to bypass eIDAS Node validation"); + attributeMap.putAll(buildAttributesWithoutMandate(authData)); + + } + } + + private ImmutableAttributeMap buildAttributesWithoutMandate(IAuthData eidAuthData) { + //TODO: throw an error in case of SZR Date with month or day = "00" + return buildAttributesWithoutMandate( + eidAuthData.getGenericData(MsEidasNodeConstants.ATTR_EIDAS_PERSONAL_IDENTIFIER, String.class), + eidAuthData.getFamilyName(), + eidAuthData.getGivenName(), + eidAuthData.getDateOfBirth()); + + } + + private ImmutableAttributeMap buildAttributesWithoutMandate(String personalIdentifier, String familyName, + String givenName, String dateOfBirth) { + final AttributeDefinition attrDefPersonalId = attrRegistry.getCoreAttributeRegistry().getByFriendlyName( + Constants.eIDAS_ATTR_PERSONALIDENTIFIER).first(); + final AttributeDefinition attrDefFamilyName = attrRegistry.getCoreAttributeRegistry().getByFriendlyName( + Constants.eIDAS_ATTR_CURRENTFAMILYNAME).first(); + final AttributeDefinition attrDefGivenName = attrRegistry.getCoreAttributeRegistry().getByFriendlyName( + Constants.eIDAS_ATTR_CURRENTGIVENNAME).first(); + final AttributeDefinition attrDefDateOfBirth = attrRegistry.getCoreAttributeRegistry().getByFriendlyName( + Constants.eIDAS_ATTR_DATEOFBIRTH).first(); + + final ImmutableAttributeMap.Builder attributeMap = + ImmutableAttributeMap.builder() + .put(attrDefPersonalId, personalIdentifier) + .put(attrDefFamilyName, familyName) + .put(attrDefGivenName, givenName) + .put(attrDefDateOfBirth, dateOfBirth); + + return attributeMap.build(); + + } + + private BinaryLightToken putResponseInCommunicationCache(ILightResponse lightResponse) + throws ServletException { + final BinaryLightToken binaryLightToken; + try { + final SpecificCommunicationService springManagedSpecificConnectorCommunicationService = + (SpecificCommunicationService) context.getBean( + SpecificCommunicationDefinitionBeanNames.SPECIFIC_PROXYSERVICE_COMMUNICATION_SERVICE + .toString()); + + binaryLightToken = springManagedSpecificConnectorCommunicationService.putResponse(lightResponse); + + } catch (final SpecificCommunicationException e) { + log.error("Unable to process specific request"); + throw new ServletException(e); + + } + + return binaryLightToken; + } + + private boolean isLegalPersonWorkaroundActive() { + return basicConfig.getBasicConfigurationBoolean( + MsProxyServiceConstants.CONIG_PROPS_EIDAS_PROXY_WORKAROUND_MANDATES_LEGAL_PERSON, + false); + + } + + private boolean isLegalPersonMandateAvailable(IAuthData authData) { + return StringUtils.isNoneEmpty(authData.getGenericData( + MsEidasNodeConstants.ATTR_EIDAS_JUR_MANDATOR_PERSONAL_IDENTIFIER, String.class)); + + } + +} diff --git a/modules/eidas_proxy-sevice/src/main/java/at/asitplus/eidas/specific/modules/msproxyservice/protocol/ProxyServicePendingRequest.java b/modules/eidas_proxy-sevice/src/main/java/at/asitplus/eidas/specific/modules/msproxyservice/protocol/ProxyServicePendingRequest.java new file mode 100644 index 00000000..a3b5007a --- /dev/null +++ b/modules/eidas_proxy-sevice/src/main/java/at/asitplus/eidas/specific/modules/msproxyservice/protocol/ProxyServicePendingRequest.java @@ -0,0 +1,28 @@ +package at.asitplus.eidas.specific.modules.msproxyservice.protocol; + +import org.springframework.beans.factory.config.BeanDefinition; +import org.springframework.context.annotation.Scope; +import org.springframework.stereotype.Component; + +import at.gv.egiz.eaaf.core.impl.idp.controller.protocols.RequestImpl; +import eu.eidas.auth.commons.light.ILightRequest; +import lombok.Getter; +import lombok.Setter; + +/** + * Pending-request of an authentication process from eIDAS Proxy-Service. + * + * @author tlenz + * + */ +@Component("ProxyServicePendingRequest") +@Scope(value = BeanDefinition.SCOPE_PROTOTYPE) +public class ProxyServicePendingRequest extends RequestImpl { + + private static final long serialVersionUID = 4227378344716277935L; + + @Getter + @Setter + ILightRequest eidasRequest; + +} diff --git a/modules/eidas_proxy-sevice/src/main/java/at/asitplus/eidas/specific/modules/msproxyservice/utils/EidasProxyServiceUtils.java b/modules/eidas_proxy-sevice/src/main/java/at/asitplus/eidas/specific/modules/msproxyservice/utils/EidasProxyServiceUtils.java new file mode 100644 index 00000000..4cd7ba6c --- /dev/null +++ b/modules/eidas_proxy-sevice/src/main/java/at/asitplus/eidas/specific/modules/msproxyservice/utils/EidasProxyServiceUtils.java @@ -0,0 +1,45 @@ +package at.asitplus.eidas.specific.modules.msproxyservice.utils; + +import at.asitplus.eidas.specific.modules.auth.eidas.v2.Constants; +import eu.eidas.auth.commons.light.ILightRequest; + +/** + * Common utils for eIDAS Proxy-Service implementation. + * + * @author tlenz + * + */ +public class EidasProxyServiceUtils { + + /** + * Check if legal person subject is requested by eIDAS Connector. + * + * @param eidasRequest Authentication request from eIDAS Connector. + * @return true if LegalPersonIdentifier is requested, otherwise falselse + */ + public static boolean isLegalPersonRequested(ILightRequest eidasRequest) { + return eidasRequest.getRequestedAttributes().entrySet().stream() + .filter(el -> el.getKey().getFriendlyName().equals(Constants.eIDAS_ATTR_LEGALPERSONIDENTIFIER)) + .findFirst() + .isPresent(); + + } + + /** + * Check if natural person subject is requested by eIDAS Connector. + * + * @param eidasRequest Authentication request from eIDAS Connector. + * @return true if PersonIdentifier is requested, otherwise falselse + */ + public static boolean isNaturalPersonRequested(ILightRequest eidasRequest) { + return eidasRequest.getRequestedAttributes().entrySet().stream() + .filter(el -> el.getKey().getFriendlyName().equals(Constants.eIDAS_ATTR_PERSONALIDENTIFIER)) + .findFirst() + .isPresent(); + + } + + private EidasProxyServiceUtils() { + //hide constructor for class with static methods only + } +} diff --git a/modules/eidas_proxy-sevice/src/main/resources/META-INF/services/at.gv.egiz.components.spring.api.SpringResourceProvider b/modules/eidas_proxy-sevice/src/main/resources/META-INF/services/at.gv.egiz.components.spring.api.SpringResourceProvider new file mode 100644 index 00000000..9158d2e6 --- /dev/null +++ b/modules/eidas_proxy-sevice/src/main/resources/META-INF/services/at.gv.egiz.components.spring.api.SpringResourceProvider @@ -0,0 +1 @@ +at.asitplus.eidas.specific.modules.msproxyservice.MsProxyServiceSpringResourceProvider \ No newline at end of file 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 new file mode 100644 index 00000000..3f92d58a --- /dev/null +++ b/modules/eidas_proxy-sevice/src/main/resources/messages/eidasproxy_messages.properties @@ -0,0 +1,14 @@ +eidas.proxyservice.01=General error on request-validation from national eIDAS Proxy-Service +eidas.proxyservice.02=Authentication request contains not communication token. +eidas.proxyservice.03=General error during eIDAS-Node communication. Reason: {} +eidas.proxyservice.04=Validation of eIDAS Authn request failed. Reason: {} +eidas.proxyservice.05=No eIDAS-Connector Issuer in Authn. request. Authentication not possible +eidas.proxyservice.06=Can not build eIDAS Proxy-Service response. Authentication FAILED. +eidas.proxyservice.07=Can not determine eIDAS-Connector CountryCode. Authentication not possible +eidas.proxyservice.08=Validation of eIDAS Authn request failed. Reason: Legal person and natural person can not be requested at once. +eidas.proxyservice.09=eIDAS authentication not possible, because legal person is requested but mandates are disabled in general +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.99=Internal error during eIDAS Proxy-Service authentication \ No newline at end of file diff --git a/modules/eidas_proxy-sevice/src/main/resources/spring/eidas_proxy-service.beans.xml b/modules/eidas_proxy-sevice/src/main/resources/spring/eidas_proxy-service.beans.xml new file mode 100644 index 00000000..2055b5a9 --- /dev/null +++ b/modules/eidas_proxy-sevice/src/main/resources/spring/eidas_proxy-service.beans.xml @@ -0,0 +1,28 @@ + + + + + + + + + + + + + + + \ No newline at end of file -- cgit v1.2.3 From 38d7758281b9cb8ba0f1a7e8a8d10098bcf2dcb8 Mon Sep 17 00:00:00 2001 From: Thomas <> Date: Fri, 3 Jun 2022 11:40:52 +0200 Subject: refactor(eidas): split 'authmodule-eIDAS-v2' into 'common-eidas' code and connector-specific elements --- .../msproxyservice/MsProxyServiceConstants.java | 14 +++---- .../protocol/EidasProxyServiceController.java | 8 ++-- .../protocol/ProxyServiceAuthenticationAction.java | 46 +++++++++++----------- .../utils/EidasProxyServiceUtils.java | 6 +-- 4 files changed, 37 insertions(+), 37 deletions(-) (limited to 'modules/eidas_proxy-sevice/src/main') diff --git a/modules/eidas_proxy-sevice/src/main/java/at/asitplus/eidas/specific/modules/msproxyservice/MsProxyServiceConstants.java b/modules/eidas_proxy-sevice/src/main/java/at/asitplus/eidas/specific/modules/msproxyservice/MsProxyServiceConstants.java index f6a88aa3..fd6b45bb 100644 --- a/modules/eidas_proxy-sevice/src/main/java/at/asitplus/eidas/specific/modules/msproxyservice/MsProxyServiceConstants.java +++ b/modules/eidas_proxy-sevice/src/main/java/at/asitplus/eidas/specific/modules/msproxyservice/MsProxyServiceConstants.java @@ -1,6 +1,6 @@ package at.asitplus.eidas.specific.modules.msproxyservice; -import at.asitplus.eidas.specific.modules.auth.eidas.v2.Constants; +import at.asitplus.eidas.specific.modules.core.eidas.EidasConstants; import at.gv.egiz.eaaf.core.api.data.EaafConfigConstants; /** @@ -15,22 +15,22 @@ public class MsProxyServiceConstants { public static final String TEMPLATE_SP_UNIQUE_ID = "eidasProxyAuth_from_{0}_type_{1}"; // configuration constants - public static final String CONIG_PROPS_EIDAS_PROXY_NODE_ENTITYID = Constants.CONIG_PROPS_EIDAS_NODE + public static final String CONIG_PROPS_EIDAS_PROXY_NODE_ENTITYID = EidasConstants.CONIG_PROPS_EIDAS_NODE + ".proxy.entityId"; - public static final String CONIG_PROPS_EIDAS_PROXY_NODE_FORWARD_URL = Constants.CONIG_PROPS_EIDAS_NODE + public static final String CONIG_PROPS_EIDAS_PROXY_NODE_FORWARD_URL = EidasConstants.CONIG_PROPS_EIDAS_NODE + ".proxy.forward.endpoint"; // mandate configuration public static final String CONIG_PROPS_EIDAS_PROXY_MANDATES_ENABLED = - Constants.CONIG_PROPS_EIDAS_PREFIX + ".proxy.mandates.enabled"; + EidasConstants.CONIG_PROPS_EIDAS_PREFIX + ".proxy.mandates.enabled"; public static final String CONIG_PROPS_EIDAS_PROXY_MANDATES_PROFILE_DEFAULT_NATURAL = - Constants.CONIG_PROPS_EIDAS_PREFIX + ".proxy.mandates.profiles.natural.default"; + EidasConstants.CONIG_PROPS_EIDAS_PREFIX + ".proxy.mandates.profiles.natural.default"; public static final String CONIG_PROPS_EIDAS_PROXY_MANDATES_PROFILE_DEFAULT_LEGAL = - Constants.CONIG_PROPS_EIDAS_PREFIX + ".proxy.mandates.profiles.legal.default"; + EidasConstants.CONIG_PROPS_EIDAS_PREFIX + ".proxy.mandates.profiles.legal.default"; public static final String CONIG_PROPS_EIDAS_PROXY_WORKAROUND_MANDATES_LEGAL_PERSON = - Constants.CONIG_PROPS_EIDAS_PREFIX + ".proxy.workaround.mandates.legalperson"; + EidasConstants.CONIG_PROPS_EIDAS_PREFIX + ".proxy.workaround.mandates.legalperson"; // specific eIDAS-Connector configuration public static final String CONIG_PROPS_CONNECTOR_PREFIX = "connector"; 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 e24c753e..cd404cee 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 @@ -25,8 +25,8 @@ import com.google.common.collect.ImmutableSortedSet; import at.asitplus.eidas.specific.core.MsEidasNodeConstants; import at.asitplus.eidas.specific.core.config.ServiceProviderConfiguration; -import at.asitplus.eidas.specific.modules.auth.eidas.v2.Constants; -import at.asitplus.eidas.specific.modules.auth.eidas.v2.service.EidasAttributeRegistry; +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.msproxyservice.MsProxyServiceConstants; import at.asitplus.eidas.specific.modules.msproxyservice.exception.EidasProxyServiceException; import at.asitplus.eidas.specific.modules.msproxyservice.utils.EidasProxyServiceUtils; @@ -308,8 +308,8 @@ public class EidasProxyServiceController extends AbstractController implements I 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); + final String ccCountry = authConfig.getBasicConfiguration(EidasConstants.CONIG_PROPS_EIDAS_NODE_COUNTRYCODE, + EidasConstants.DEFAULT_MS_NODE_COUNTRY_CODE); spConfig.setBpkTargetIdentifier( EaafConstants.URN_PREFIX_EIDAS + ccCountry + "+" + spCountry); diff --git a/modules/eidas_proxy-sevice/src/main/java/at/asitplus/eidas/specific/modules/msproxyservice/protocol/ProxyServiceAuthenticationAction.java b/modules/eidas_proxy-sevice/src/main/java/at/asitplus/eidas/specific/modules/msproxyservice/protocol/ProxyServiceAuthenticationAction.java index 15524005..92165412 100644 --- a/modules/eidas_proxy-sevice/src/main/java/at/asitplus/eidas/specific/modules/msproxyservice/protocol/ProxyServiceAuthenticationAction.java +++ b/modules/eidas_proxy-sevice/src/main/java/at/asitplus/eidas/specific/modules/msproxyservice/protocol/ProxyServiceAuthenticationAction.java @@ -17,8 +17,8 @@ import org.springframework.web.util.UriComponentsBuilder; import at.asitplus.eidas.specific.core.MsEidasNodeConstants; import at.asitplus.eidas.specific.core.gui.StaticGuiBuilderConfiguration; -import at.asitplus.eidas.specific.modules.auth.eidas.v2.Constants; -import at.asitplus.eidas.specific.modules.auth.eidas.v2.service.EidasAttributeRegistry; +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.msproxyservice.MsProxyServiceConstants; import at.asitplus.eidas.specific.modules.msproxyservice.exception.EidasProxyServiceException; import at.asitplus.eidas.specific.modules.msproxyservice.utils.EidasProxyServiceUtils; @@ -85,7 +85,7 @@ public class ProxyServiceAuthenticationAction implements IAction { lightRespBuilder.relayState(eidasReq.getRelayState()); lightRespBuilder.status(ResponseStatus.builder() - .statusCode(Constants.SUCCESS_URI) + .statusCode(EidasConstants.SUCCESS_URI) .build()); //TODO: check if we can use transient subjectNameIds @@ -168,8 +168,8 @@ public class ProxyServiceAuthenticationAction implements IAction { log.debug("ForwardURL: " + forwardUrl + " selected to forward eIDAS request"); if (basicConfig.getBasicConfiguration( - Constants.CONIG_PROPS_EIDAS_NODE_FORWARD_METHOD, - Constants.FORWARD_METHOD_GET).equals(Constants.FORWARD_METHOD_GET)) { + EidasConstants.CONIG_PROPS_EIDAS_NODE_FORWARD_METHOD, + EidasConstants.FORWARD_METHOD_GET).equals(EidasConstants.FORWARD_METHOD_GET)) { log.debug("Use http-redirect for eIDAS node forwarding ... "); // send redirect @@ -182,14 +182,14 @@ public class ProxyServiceAuthenticationAction implements IAction { final StaticGuiBuilderConfiguration config = new StaticGuiBuilderConfiguration( basicConfig, pendingReq, - Constants.TEMPLATE_POST_FORWARD_NAME, + EidasConstants.TEMPLATE_POST_FORWARD_NAME, null, resourceLoader); - config.putCustomParameter(null, Constants.TEMPLATE_POST_FORWARD_ENDPOINT, forwardUrl); - config.putCustomParameter(null, Constants.TEMPLATE_POST_FORWARD_TOKEN_NAME, + config.putCustomParameter(null, EidasConstants.TEMPLATE_POST_FORWARD_ENDPOINT, forwardUrl); + config.putCustomParameter(null, EidasConstants.TEMPLATE_POST_FORWARD_TOKEN_NAME, EidasParameterKeys.TOKEN.toString()); - config.putCustomParameter(null, Constants.TEMPLATE_POST_FORWARD_TOKEN_VALUE, + config.putCustomParameter(null, EidasConstants.TEMPLATE_POST_FORWARD_TOKEN_VALUE, tokenBase64); guiBuilder.build(httpReq, httpResp, config, "Forward to eIDASNode form"); @@ -233,13 +233,13 @@ public class ProxyServiceAuthenticationAction implements IAction { if (StringUtils.isNotEmpty(natMandatorId)) { log.debug("Injecting natural mandator informations ... "); final AttributeDefinition attrDefPersonalId = attrRegistry.getCoreAttributeRegistry().getByFriendlyName( - Constants.eIDAS_ATTR_PERSONALIDENTIFIER).first(); + EidasConstants.eIDAS_ATTR_PERSONALIDENTIFIER).first(); final AttributeDefinition attrDefFamilyName = attrRegistry.getCoreAttributeRegistry().getByFriendlyName( - Constants.eIDAS_ATTR_CURRENTFAMILYNAME).first(); + EidasConstants.eIDAS_ATTR_CURRENTFAMILYNAME).first(); final AttributeDefinition attrDefGivenName = attrRegistry.getCoreAttributeRegistry().getByFriendlyName( - Constants.eIDAS_ATTR_CURRENTGIVENNAME).first(); + EidasConstants.eIDAS_ATTR_CURRENTGIVENNAME).first(); final AttributeDefinition attrDefDateOfBirth = attrRegistry.getCoreAttributeRegistry().getByFriendlyName( - Constants.eIDAS_ATTR_DATEOFBIRTH).first(); + EidasConstants.eIDAS_ATTR_DATEOFBIRTH).first(); attributeMap.put(attrDefPersonalId, natMandatorId); attributeMap.put(attrDefFamilyName, eidAuthData.getGenericData( @@ -252,9 +252,9 @@ public class ProxyServiceAuthenticationAction implements IAction { } else { log.debug("Injecting legal mandator informations ... "); final AttributeDefinition commonName = attrRegistry.getCoreAttributeRegistry().getByFriendlyName( - Constants.eIDAS_ATTR_LEGALNAME).first(); + EidasConstants.eIDAS_ATTR_LEGALNAME).first(); final AttributeDefinition legalPersonId = attrRegistry.getCoreAttributeRegistry().getByFriendlyName( - Constants.eIDAS_ATTR_LEGALPERSONIDENTIFIER).first(); + EidasConstants.eIDAS_ATTR_LEGALPERSONIDENTIFIER).first(); attributeMap.put(commonName, eidAuthData.getGenericData( PvpAttributeDefinitions.MANDATE_LEG_PER_FULL_NAME_NAME, String.class)); @@ -267,13 +267,13 @@ public class ProxyServiceAuthenticationAction implements IAction { private void injectRepesentativeInformation( ImmutableAttributeMap.Builder attributeMap, IEidAuthData eidAuthData) { final AttributeDefinition attrDefPersonalId = attrRegistry.getCoreAttributeRegistry().getByFriendlyName( - Constants.eIDAS_ATTR_REPRESENTATIVE_PERSONALIDENTIFIER).first(); + EidasConstants.eIDAS_ATTR_REPRESENTATIVE_PERSONALIDENTIFIER).first(); final AttributeDefinition attrDefFamilyName = attrRegistry.getCoreAttributeRegistry().getByFriendlyName( - Constants.eIDAS_ATTR_REPRESENTATIVE_CURRENTFAMILYNAME).first(); + EidasConstants.eIDAS_ATTR_REPRESENTATIVE_CURRENTFAMILYNAME).first(); final AttributeDefinition attrDefGivenName = attrRegistry.getCoreAttributeRegistry().getByFriendlyName( - Constants.eIDAS_ATTR_REPRESENTATIVE_CURRENTGIVENNAME).first(); + EidasConstants.eIDAS_ATTR_REPRESENTATIVE_CURRENTGIVENNAME).first(); final AttributeDefinition attrDefDateOfBirth = attrRegistry.getCoreAttributeRegistry().getByFriendlyName( - Constants.eIDAS_ATTR_REPRESENTATIVE_DATEOFBIRTH).first(); + EidasConstants.eIDAS_ATTR_REPRESENTATIVE_DATEOFBIRTH).first(); attributeMap.put(attrDefPersonalId, eidAuthData.getGenericData(MsEidasNodeConstants.ATTR_EIDAS_PERSONAL_IDENTIFIER, String.class)); @@ -319,13 +319,13 @@ public class ProxyServiceAuthenticationAction implements IAction { private ImmutableAttributeMap buildAttributesWithoutMandate(String personalIdentifier, String familyName, String givenName, String dateOfBirth) { final AttributeDefinition attrDefPersonalId = attrRegistry.getCoreAttributeRegistry().getByFriendlyName( - Constants.eIDAS_ATTR_PERSONALIDENTIFIER).first(); + EidasConstants.eIDAS_ATTR_PERSONALIDENTIFIER).first(); final AttributeDefinition attrDefFamilyName = attrRegistry.getCoreAttributeRegistry().getByFriendlyName( - Constants.eIDAS_ATTR_CURRENTFAMILYNAME).first(); + EidasConstants.eIDAS_ATTR_CURRENTFAMILYNAME).first(); final AttributeDefinition attrDefGivenName = attrRegistry.getCoreAttributeRegistry().getByFriendlyName( - Constants.eIDAS_ATTR_CURRENTGIVENNAME).first(); + EidasConstants.eIDAS_ATTR_CURRENTGIVENNAME).first(); final AttributeDefinition attrDefDateOfBirth = attrRegistry.getCoreAttributeRegistry().getByFriendlyName( - Constants.eIDAS_ATTR_DATEOFBIRTH).first(); + EidasConstants.eIDAS_ATTR_DATEOFBIRTH).first(); final ImmutableAttributeMap.Builder attributeMap = ImmutableAttributeMap.builder() diff --git a/modules/eidas_proxy-sevice/src/main/java/at/asitplus/eidas/specific/modules/msproxyservice/utils/EidasProxyServiceUtils.java b/modules/eidas_proxy-sevice/src/main/java/at/asitplus/eidas/specific/modules/msproxyservice/utils/EidasProxyServiceUtils.java index 4cd7ba6c..b8a4c598 100644 --- a/modules/eidas_proxy-sevice/src/main/java/at/asitplus/eidas/specific/modules/msproxyservice/utils/EidasProxyServiceUtils.java +++ b/modules/eidas_proxy-sevice/src/main/java/at/asitplus/eidas/specific/modules/msproxyservice/utils/EidasProxyServiceUtils.java @@ -1,6 +1,6 @@ package at.asitplus.eidas.specific.modules.msproxyservice.utils; -import at.asitplus.eidas.specific.modules.auth.eidas.v2.Constants; +import at.asitplus.eidas.specific.modules.core.eidas.EidasConstants; import eu.eidas.auth.commons.light.ILightRequest; /** @@ -19,7 +19,7 @@ public class EidasProxyServiceUtils { */ public static boolean isLegalPersonRequested(ILightRequest eidasRequest) { return eidasRequest.getRequestedAttributes().entrySet().stream() - .filter(el -> el.getKey().getFriendlyName().equals(Constants.eIDAS_ATTR_LEGALPERSONIDENTIFIER)) + .filter(el -> el.getKey().getFriendlyName().equals(EidasConstants.eIDAS_ATTR_LEGALPERSONIDENTIFIER)) .findFirst() .isPresent(); @@ -33,7 +33,7 @@ public class EidasProxyServiceUtils { */ public static boolean isNaturalPersonRequested(ILightRequest eidasRequest) { return eidasRequest.getRequestedAttributes().entrySet().stream() - .filter(el -> el.getKey().getFriendlyName().equals(Constants.eIDAS_ATTR_PERSONALIDENTIFIER)) + .filter(el -> el.getKey().getFriendlyName().equals(EidasConstants.eIDAS_ATTR_PERSONALIDENTIFIER)) .findFirst() .isPresent(); -- cgit v1.2.3 From 7f0a925a72dc9841280e66fcba1515af62b9efdf Mon Sep 17 00:00:00 2001 From: Thomas <> Date: Fri, 3 Jun 2022 15:24:01 +0200 Subject: test(core): add smoke test with full eIDAS OutGoing login and error-handling --- .../msproxyservice/MsProxyServiceSpringResourceProvider.java | 5 ++++- .../src/main/resources/spring/eidas_proxy-service.beans.xml | 7 +++++++ 2 files changed, 11 insertions(+), 1 deletion(-) (limited to 'modules/eidas_proxy-sevice/src/main') diff --git a/modules/eidas_proxy-sevice/src/main/java/at/asitplus/eidas/specific/modules/msproxyservice/MsProxyServiceSpringResourceProvider.java b/modules/eidas_proxy-sevice/src/main/java/at/asitplus/eidas/specific/modules/msproxyservice/MsProxyServiceSpringResourceProvider.java index d36e4712..571ad8ab 100644 --- a/modules/eidas_proxy-sevice/src/main/java/at/asitplus/eidas/specific/modules/msproxyservice/MsProxyServiceSpringResourceProvider.java +++ b/modules/eidas_proxy-sevice/src/main/java/at/asitplus/eidas/specific/modules/msproxyservice/MsProxyServiceSpringResourceProvider.java @@ -45,8 +45,11 @@ public class MsProxyServiceSpringResourceProvider implements SpringResourceProvi public Resource[] getResourcesToLoad() { final ClassPathResource eidasProxyServiceConfig = new ClassPathResource("/spring/eidas_proxy-service.beans.xml", MsProxyServiceSpringResourceProvider.class); + final ClassPathResource eidasRefImplConfig = new ClassPathResource("/eidas_v2_auth_ref_impl_config.beans.xml", + MsProxyServiceSpringResourceProvider.class); + - return new Resource[] { eidasProxyServiceConfig }; + return new Resource[] { eidasProxyServiceConfig, eidasRefImplConfig }; } } diff --git a/modules/eidas_proxy-sevice/src/main/resources/spring/eidas_proxy-service.beans.xml b/modules/eidas_proxy-sevice/src/main/resources/spring/eidas_proxy-service.beans.xml index 2055b5a9..1eb33e93 100644 --- a/modules/eidas_proxy-sevice/src/main/resources/spring/eidas_proxy-service.beans.xml +++ b/modules/eidas_proxy-sevice/src/main/resources/spring/eidas_proxy-service.beans.xml @@ -24,5 +24,12 @@ + + + + \ No newline at end of file -- cgit v1.2.3 From b3bbdc754025246c3de2a8e04a7ed2f085c5d19e Mon Sep 17 00:00:00 2001 From: Thomas <> Date: Tue, 7 Jun 2022 13:21:48 +0200 Subject: feat(eidas): add attribute-mapping service to map eIDAS attributs to IDA attributes --- .../msproxyservice/MsProxyServiceConstants.java | 5 + .../dto/attributes/AttrMappingElement.java | 37 +++++ .../dto/attributes/IdaAttribute.java | 29 ++++ .../msproxyservice/dto/attributes/Type.java | 79 +++++++++ .../service/ProxyEidasAttributeRegistry.java | 176 +++++++++++++++++++++ .../resources/spring/eidas_proxy-service.beans.xml | 3 + 6 files changed, 329 insertions(+) create mode 100644 modules/eidas_proxy-sevice/src/main/java/at/asitplus/eidas/specific/modules/msproxyservice/dto/attributes/AttrMappingElement.java create mode 100644 modules/eidas_proxy-sevice/src/main/java/at/asitplus/eidas/specific/modules/msproxyservice/dto/attributes/IdaAttribute.java create mode 100644 modules/eidas_proxy-sevice/src/main/java/at/asitplus/eidas/specific/modules/msproxyservice/dto/attributes/Type.java create mode 100644 modules/eidas_proxy-sevice/src/main/java/at/asitplus/eidas/specific/modules/msproxyservice/service/ProxyEidasAttributeRegistry.java (limited to 'modules/eidas_proxy-sevice/src/main') diff --git a/modules/eidas_proxy-sevice/src/main/java/at/asitplus/eidas/specific/modules/msproxyservice/MsProxyServiceConstants.java b/modules/eidas_proxy-sevice/src/main/java/at/asitplus/eidas/specific/modules/msproxyservice/MsProxyServiceConstants.java index fd6b45bb..a2a2e78f 100644 --- a/modules/eidas_proxy-sevice/src/main/java/at/asitplus/eidas/specific/modules/msproxyservice/MsProxyServiceConstants.java +++ b/modules/eidas_proxy-sevice/src/main/java/at/asitplus/eidas/specific/modules/msproxyservice/MsProxyServiceConstants.java @@ -20,6 +20,11 @@ public class MsProxyServiceConstants { public static final String CONIG_PROPS_EIDAS_PROXY_NODE_FORWARD_URL = EidasConstants.CONIG_PROPS_EIDAS_NODE + ".proxy.forward.endpoint"; + + public static final String CONIG_PROPS_EIDAS_PROXY_ATTIBUTE_CONFIGURATION = + EidasConstants.CONIG_PROPS_EIDAS_PREFIX + ".proxy.attribute.mapping.config"; + + // mandate configuration public static final String CONIG_PROPS_EIDAS_PROXY_MANDATES_ENABLED = EidasConstants.CONIG_PROPS_EIDAS_PREFIX + ".proxy.mandates.enabled"; diff --git a/modules/eidas_proxy-sevice/src/main/java/at/asitplus/eidas/specific/modules/msproxyservice/dto/attributes/AttrMappingElement.java b/modules/eidas_proxy-sevice/src/main/java/at/asitplus/eidas/specific/modules/msproxyservice/dto/attributes/AttrMappingElement.java new file mode 100644 index 00000000..d6ed1147 --- /dev/null +++ b/modules/eidas_proxy-sevice/src/main/java/at/asitplus/eidas/specific/modules/msproxyservice/dto/attributes/AttrMappingElement.java @@ -0,0 +1,37 @@ + +package at.asitplus.eidas.specific.modules.msproxyservice.dto.attributes; + +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonPropertyOrder; + +import lombok.Data; + +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonPropertyOrder({ + "eidasAttribute", + "idaAttribute", + "type" +}) +@Data +public class AttrMappingElement { + + /** + * eIDAS specific attribute name. + */ + @JsonProperty("eidasAttribute") + private String eidasAttributeName; + + /** + * IDA specific attribute name. + */ + @JsonProperty("idaAttribute") + private IdaAttribute idaAttribute; + + /** + * attribute characteristics. + */ + @JsonProperty("type") + private Type type; + +} diff --git a/modules/eidas_proxy-sevice/src/main/java/at/asitplus/eidas/specific/modules/msproxyservice/dto/attributes/IdaAttribute.java b/modules/eidas_proxy-sevice/src/main/java/at/asitplus/eidas/specific/modules/msproxyservice/dto/attributes/IdaAttribute.java new file mode 100644 index 00000000..ee5fc810 --- /dev/null +++ b/modules/eidas_proxy-sevice/src/main/java/at/asitplus/eidas/specific/modules/msproxyservice/dto/attributes/IdaAttribute.java @@ -0,0 +1,29 @@ +package at.asitplus.eidas.specific.modules.msproxyservice.dto.attributes; + +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonPropertyOrder; + +import lombok.Data; + +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonPropertyOrder({ + "basic", + "withMandates" +}) +@Data +public class IdaAttribute { + + /** + * IDA attribute name, in case of simple process without mandates. + */ + @JsonProperty("basic") + private String basic; + + /** + * IDA attribute name, in case of mandate process. + */ + @JsonProperty("withMandates") + private String withMandates; + +} diff --git a/modules/eidas_proxy-sevice/src/main/java/at/asitplus/eidas/specific/modules/msproxyservice/dto/attributes/Type.java b/modules/eidas_proxy-sevice/src/main/java/at/asitplus/eidas/specific/modules/msproxyservice/dto/attributes/Type.java new file mode 100644 index 00000000..86ca49fa --- /dev/null +++ b/modules/eidas_proxy-sevice/src/main/java/at/asitplus/eidas/specific/modules/msproxyservice/dto/attributes/Type.java @@ -0,0 +1,79 @@ + +package at.asitplus.eidas.specific.modules.msproxyservice.dto.attributes; + +import java.util.HashMap; +import java.util.Map; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonPropertyOrder; +import com.fasterxml.jackson.annotation.JsonValue; + +import lombok.Data; + +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonPropertyOrder({ + "mds", + "mandator" +}) +@Data +public class Type { + + /** + * true if this attribute is part of MDS, otherwise + * false + */ + @JsonProperty("mds") + private Boolean mds; + + /** + * Classifie that attribute to specific mandate modes. + */ + @JsonProperty("mandator") + private Type.Mandator mandator; + + /** + * Mandate type in case of a mandate attriute. + */ + public enum Mandator { + BOTH("both"), + LEGAL("legal"), + NATURAL("natural"), + NONE("none"); + + private final String value; + private final static Map CONSTANTS = new HashMap<>(); + + static { + for (final Type.Mandator c : values()) { + CONSTANTS.put(c.value, c); + } + } + + Mandator(String value) { + this.value = value; + } + + @Override + public String toString() { + return this.value; + } + + @JsonValue + public String value() { + return this.value; + } + + @JsonCreator + public static Type.Mandator fromValue(String value) { + final Type.Mandator constant = CONSTANTS.get(value); + if (constant == null) { + throw new IllegalArgumentException(value); + } else { + return constant; + } + } + + } +} diff --git a/modules/eidas_proxy-sevice/src/main/java/at/asitplus/eidas/specific/modules/msproxyservice/service/ProxyEidasAttributeRegistry.java b/modules/eidas_proxy-sevice/src/main/java/at/asitplus/eidas/specific/modules/msproxyservice/service/ProxyEidasAttributeRegistry.java new file mode 100644 index 00000000..ea561c1d --- /dev/null +++ b/modules/eidas_proxy-sevice/src/main/java/at/asitplus/eidas/specific/modules/msproxyservice/service/ProxyEidasAttributeRegistry.java @@ -0,0 +1,176 @@ +package at.asitplus.eidas.specific.modules.msproxyservice.service; + +import java.io.IOException; +import java.io.InputStream; +import java.net.URISyntaxException; +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import java.util.Objects; +import java.util.Set; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import javax.annotation.PostConstruct; + +import org.apache.commons.io.IOUtils; +import org.apache.commons.lang3.StringUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.core.io.Resource; +import org.springframework.core.io.ResourceLoader; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.type.CollectionType; +import com.google.common.collect.Sets; + +import at.asitplus.eidas.specific.modules.core.eidas.service.EidasAttributeRegistry; +import at.asitplus.eidas.specific.modules.msproxyservice.MsProxyServiceConstants; +import at.asitplus.eidas.specific.modules.msproxyservice.dto.attributes.AttrMappingElement; +import at.asitplus.eidas.specific.modules.msproxyservice.dto.attributes.IdaAttribute; +import at.gv.egiz.eaaf.core.api.idp.IConfiguration; +import at.gv.egiz.eaaf.core.exceptions.EaafConfigurationException; +import at.gv.egiz.eaaf.core.impl.utils.FileUtils; +import lombok.Getter; +import lombok.NonNull; +import lombok.extern.slf4j.Slf4j; + +@Slf4j +public class ProxyEidasAttributeRegistry { + + private static final String ATTR_CONFIG_ALL = "*"; + + private static ObjectMapper mapper = new ObjectMapper(); + + @Autowired IConfiguration basicConfig; + @Autowired ResourceLoader resourceLoader; + + + @Getter + private EidasAttributeRegistry coreRegistry; + + private Set attributeConfiguration; + + + /** + * Attribute Registry for eIDAS Proxy-Service implementation. + * @param registry Core attribute registry + */ + public ProxyEidasAttributeRegistry(@Autowired EidasAttributeRegistry registry) { + this.coreRegistry = registry; + + } + + + /** + * Get all attributes that requested from IDA by default. + * + * @param withMandates true if mandates are supported, otherwise false + * @return {@link Stream} of IDA specific attribute names + */ + @NonNull + public Stream getAlwaysRequestedAttributes(boolean withMandates) { + return attributeConfiguration.stream() + .filter(el -> ATTR_CONFIG_ALL.equals(el.getEidasAttributeName())) + .map(el -> getReleadedIdaAttribute(el.getIdaAttribute(), withMandates)) + .flatMap(Collection::stream) + .filter(Objects::nonNull); + + } + + /** + * Get IDA attributes for a specific eIDAS attribute. + * + * @param eidasAttributeName Name of the eIDAS attribute. + * @param withMandates true if mandates are supported, otherwise false + * @return {@link Set} of IDA specific attribute names + */ + @NonNull + public Set getIdaAttributesForEidasAttribute(String eidasAttributeName, boolean withMandates) { + return attributeConfiguration.stream() + .filter(el -> el.getEidasAttributeName().equals(eidasAttributeName)) + .findFirst() + .map(el -> getReleadedIdaAttribute(el.getIdaAttribute(), withMandates)) + .orElse(Collections.emptySet()) + .stream() + .filter(Objects::nonNull) + .collect(Collectors.toSet()); + + } + + + @PostConstruct + private void initialize() throws EaafConfigurationException { + final String attrConfPath = basicConfig.getBasicConfiguration( + MsProxyServiceConstants.CONIG_PROPS_EIDAS_PROXY_ATTIBUTE_CONFIGURATION); + + log.debug("Initializing eIDAS <--> IDA attribute mapping from: {} ... ", attrConfPath); + + if (StringUtils.isEmpty(attrConfPath)) { + log.error("Error: Path to attribute-mapping config is unknown"); + throw new EaafConfigurationException("internal.configuration.00", + new Object[]{MsProxyServiceConstants.CONIG_PROPS_EIDAS_PROXY_ATTIBUTE_CONFIGURATION}); + + } + + try { + // reading attribute-configuration file + final CollectionType javaType = + mapper.getTypeFactory().constructCollectionType(List.class, AttrMappingElement.class); + List internalAttributeConfiguration = + mapper.readValue(readFromFile(attrConfPath), javaType); + log.debug("Found #{} eIDAS <--> IDA attribute-mappings . Starting import process ... ", + internalAttributeConfiguration.size()); + + // post-validation of attribute configuration + attributeConfiguration = internalAttributeConfiguration.stream() + .filter(el -> checkEidasAttributeName(el)) + .collect(Collectors.toSet()); + log.info("Load {} eIDAS <--> IDA attribute-mappings into attribute-registry", attributeConfiguration.size()); + + } catch (Exception e) { + log.error("Error reading eIDAS <--> IDA attribute-mapping configuration file", e); + throw new EaafConfigurationException("internal.configuration.01", + new Object[]{MsProxyServiceConstants.CONIG_PROPS_EIDAS_PROXY_ATTIBUTE_CONFIGURATION, + "Error reading Configurations file"}, e); + + } + } + + private Set getReleadedIdaAttribute(IdaAttribute el, boolean withMandates) { + return withMandates + ? Sets.newHashSet(el.getBasic(), el.getWithMandates()) + : Sets.newHashSet(el.getBasic()); + + } + + private boolean checkEidasAttributeName(AttrMappingElement el) { + if (StringUtils.isNotEmpty(el.getEidasAttributeName())) { + if (ATTR_CONFIG_ALL.equals(el.getEidasAttributeName()) + || coreRegistry.getCoreAttributeRegistry().getByName(el.getEidasAttributeName()) != null) { + return true; + + } else { + log.warn("eIDAS attribute: {} is UNKNOWN by eIDAS node. Ignore it!", el.getEidasAttributeName()); + + } + + } else { + log.warn("Find attribute-mapping element WITHOUT eIDAS attribute-name. Ignore it!"); + + } + + return false; + } + + private byte[] readFromFile(final String filePath) throws URISyntaxException, IOException { + final String fullFilePath = FileUtils.makeAbsoluteUrl(filePath, basicConfig.getConfigurationRootDirectory()); + final Resource ressource = resourceLoader.getResource(fullFilePath); + final InputStream is = ressource.getInputStream(); + final byte[] result = IOUtils.toByteArray(is); + is.close(); + return result; + + } + + +} diff --git a/modules/eidas_proxy-sevice/src/main/resources/spring/eidas_proxy-service.beans.xml b/modules/eidas_proxy-sevice/src/main/resources/spring/eidas_proxy-service.beans.xml index 1eb33e93..78b7640a 100644 --- a/modules/eidas_proxy-sevice/src/main/resources/spring/eidas_proxy-service.beans.xml +++ b/modules/eidas_proxy-sevice/src/main/resources/spring/eidas_proxy-service.beans.xml @@ -32,4 +32,7 @@ ref="specificConnectorAdditionalAttributesFileWithPath" /> + + \ No newline at end of file -- cgit v1.2.3 From 9c9463d593014292a4b19fbad2fca779e56e33cf Mon Sep 17 00:00:00 2001 From: Thomas <> Date: Tue, 7 Jun 2022 13:47:42 +0200 Subject: feat(eidas): update proxy-service attribute registry to request releated attributes --- .../dto/attributes/AttrMappingElement.java | 6 +++++ .../service/ProxyEidasAttributeRegistry.java | 27 +++++++++++++++------- 2 files changed, 25 insertions(+), 8 deletions(-) (limited to 'modules/eidas_proxy-sevice/src/main') diff --git a/modules/eidas_proxy-sevice/src/main/java/at/asitplus/eidas/specific/modules/msproxyservice/dto/attributes/AttrMappingElement.java b/modules/eidas_proxy-sevice/src/main/java/at/asitplus/eidas/specific/modules/msproxyservice/dto/attributes/AttrMappingElement.java index d6ed1147..cf106bad 100644 --- a/modules/eidas_proxy-sevice/src/main/java/at/asitplus/eidas/specific/modules/msproxyservice/dto/attributes/AttrMappingElement.java +++ b/modules/eidas_proxy-sevice/src/main/java/at/asitplus/eidas/specific/modules/msproxyservice/dto/attributes/AttrMappingElement.java @@ -1,6 +1,8 @@ package at.asitplus.eidas.specific.modules.msproxyservice.dto.attributes; +import java.util.List; + import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.annotation.JsonPropertyOrder; @@ -28,6 +30,10 @@ public class AttrMappingElement { @JsonProperty("idaAttribute") private IdaAttribute idaAttribute; + + @JsonProperty("addionalRequiredAttributes") + private List addionalRequiredAttributes; + /** * attribute characteristics. */ diff --git a/modules/eidas_proxy-sevice/src/main/java/at/asitplus/eidas/specific/modules/msproxyservice/service/ProxyEidasAttributeRegistry.java b/modules/eidas_proxy-sevice/src/main/java/at/asitplus/eidas/specific/modules/msproxyservice/service/ProxyEidasAttributeRegistry.java index ea561c1d..b9e0c488 100644 --- a/modules/eidas_proxy-sevice/src/main/java/at/asitplus/eidas/specific/modules/msproxyservice/service/ProxyEidasAttributeRegistry.java +++ b/modules/eidas_proxy-sevice/src/main/java/at/asitplus/eidas/specific/modules/msproxyservice/service/ProxyEidasAttributeRegistry.java @@ -26,7 +26,6 @@ import com.google.common.collect.Sets; import at.asitplus.eidas.specific.modules.core.eidas.service.EidasAttributeRegistry; import at.asitplus.eidas.specific.modules.msproxyservice.MsProxyServiceConstants; import at.asitplus.eidas.specific.modules.msproxyservice.dto.attributes.AttrMappingElement; -import at.asitplus.eidas.specific.modules.msproxyservice.dto.attributes.IdaAttribute; import at.gv.egiz.eaaf.core.api.idp.IConfiguration; import at.gv.egiz.eaaf.core.exceptions.EaafConfigurationException; import at.gv.egiz.eaaf.core.impl.utils.FileUtils; @@ -71,7 +70,7 @@ public class ProxyEidasAttributeRegistry { public Stream getAlwaysRequestedAttributes(boolean withMandates) { return attributeConfiguration.stream() .filter(el -> ATTR_CONFIG_ALL.equals(el.getEidasAttributeName())) - .map(el -> getReleadedIdaAttribute(el.getIdaAttribute(), withMandates)) + .map(el -> getReleadedIdaAttribute(el, withMandates)) .flatMap(Collection::stream) .filter(Objects::nonNull); @@ -89,7 +88,7 @@ public class ProxyEidasAttributeRegistry { return attributeConfiguration.stream() .filter(el -> el.getEidasAttributeName().equals(eidasAttributeName)) .findFirst() - .map(el -> getReleadedIdaAttribute(el.getIdaAttribute(), withMandates)) + .map(el -> getReleadedIdaAttribute(el, withMandates)) .orElse(Collections.emptySet()) .stream() .filter(Objects::nonNull) @@ -136,11 +135,23 @@ public class ProxyEidasAttributeRegistry { } } - private Set getReleadedIdaAttribute(IdaAttribute el, boolean withMandates) { - return withMandates - ? Sets.newHashSet(el.getBasic(), el.getWithMandates()) - : Sets.newHashSet(el.getBasic()); - + private Set getReleadedIdaAttribute(AttrMappingElement el, boolean withMandates) { + if (el.getIdaAttribute() != null) { + Set directMapping = withMandates + ? Sets.newHashSet(el.getIdaAttribute().getBasic(), el.getIdaAttribute().getWithMandates()) + : Sets.newHashSet(el.getIdaAttribute().getBasic()); + + if (el.getAddionalRequiredAttributes() != null) { + el.getAddionalRequiredAttributes().forEach( + attr -> directMapping.add(attr)); + + } + return directMapping; + + } else { + return Collections.emptySet(); + + } } private boolean checkEidasAttributeName(AttrMappingElement el) { -- cgit v1.2.3 From db3af28b79296b6f5650a85c5a41ad5015c57222 Mon Sep 17 00:00:00 2001 From: Thomas <> Date: Tue, 7 Jun 2022 13:48:34 +0200 Subject: feat(eidas): include IDA releated requested attributes into service-provider configuration --- .../protocol/EidasProxyServiceController.java | 35 +++++++++++++++++----- 1 file changed, 28 insertions(+), 7 deletions(-) (limited to 'modules/eidas_proxy-sevice/src/main') 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 cd404cee..26cc51ee 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 @@ -2,9 +2,11 @@ package at.asitplus.eidas.specific.modules.msproxyservice.protocol; import java.io.IOException; import java.text.MessageFormat; +import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.Map; +import java.util.Objects; import java.util.UUID; import java.util.stream.Collectors; @@ -22,13 +24,14 @@ import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import com.google.common.collect.ImmutableSortedSet; +import com.google.common.collect.Streams; import at.asitplus.eidas.specific.core.MsEidasNodeConstants; import at.asitplus.eidas.specific.core.config.ServiceProviderConfiguration; 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.msproxyservice.MsProxyServiceConstants; import at.asitplus.eidas.specific.modules.msproxyservice.exception.EidasProxyServiceException; +import at.asitplus.eidas.specific.modules.msproxyservice.service.ProxyEidasAttributeRegistry; import at.asitplus.eidas.specific.modules.msproxyservice.utils.EidasProxyServiceUtils; import at.gv.egiz.components.eventlog.api.EventConstants; import at.gv.egiz.eaaf.core.api.IRequest; @@ -76,7 +79,7 @@ public class EidasProxyServiceController extends AbstractController implements I public static final String PROTOCOL_ID = "eidasProxy"; - @Autowired EidasAttributeRegistry attrRegistry; + @Autowired ProxyEidasAttributeRegistry attrRegistry; @Autowired ProxyServiceAuthenticationAction responseAction; /** @@ -115,7 +118,7 @@ public class EidasProxyServiceController extends AbstractController implements I .toString()); final ILightRequest eidasRequest = specificProxyCommunicationService.getAndRemoveRequest( tokenBase64, - ImmutableSortedSet.copyOf(attrRegistry.getCoreAttributeRegistry().getAttributes())); + ImmutableSortedSet.copyOf(attrRegistry.getCoreRegistry().getCoreAttributeRegistry().getAttributes())); if (eidasRequest == null) { log.info("Find no eIDAS Authn. Request with stated token."); throw new EidasProxyServiceException(ERROR_11, null); @@ -317,9 +320,12 @@ public class EidasProxyServiceController extends AbstractController implements I spConfig.setRequiredLoA( eidasRequest.getLevelsOfAssurance().stream().map(el -> el.getValue()).collect(Collectors.toList())); - //build mandate profiles for this specific request + // build mandate profiles for this specific request buildMandateProfileConfiguration(spConfig, eidasRequest); - + + // map eIDAS attributes to national attributes + buildNationalRequestedAttributes(spConfig, eidasRequest); + return spConfig; } catch (EidasProxyServiceException e) { @@ -332,6 +338,22 @@ public class EidasProxyServiceController extends AbstractController implements I } + private void buildNationalRequestedAttributes( + ServiceProviderConfiguration spConfig, ILightRequest eidasRequest) { + boolean mandatesEnabled = !SpMandateModes.NONE.equals(spConfig.getMandateMode()); + spConfig.setRequestedAttributes( + Streams.concat( + eidasRequest.getRequestedAttributes().getAttributeMap().keySet().stream() + .map(el -> attrRegistry.getIdaAttributesForEidasAttribute( + el.getNameUri().toString(), mandatesEnabled)) + .flatMap(Collection::stream) + .filter(Objects::nonNull), + attrRegistry.getAlwaysRequestedAttributes(mandatesEnabled)) + .collect(Collectors.toSet())); + log.debug("Inject #{} attributes to request from IDA system", spConfig.getRequestedAttributes().size()); + + } + private Map extractRawConnectorConfiguration(ILightRequest eidasRequest) { Map allConnectorConfigs = authConfig.getBasicConfigurationWithPrefix( MsProxyServiceConstants.CONIG_PROPS_CONNECTOR_PREFIX); @@ -341,8 +363,7 @@ public class EidasProxyServiceController extends AbstractController implements I el -> log.trace("Key: {} -> Value: {}", el.getKey(), el.getValue())); } - - + Map connectorConfig = allConnectorConfigs.entrySet().stream() .filter(el -> el.getKey().endsWith(MsEidasNodeConstants.PROP_CONFIG_SP_UNIQUEIDENTIFIER) && el.getValue().equals(eidasRequest.getIssuer())) -- cgit v1.2.3 From 3d9d419a40b17de1f94d46cbc2f5b345a93bff00 Mon Sep 17 00:00:00 2001 From: Thomas <> Date: Wed, 8 Jun 2022 12:32:16 +0200 Subject: feat(eidas): perform mapping between IDA and eIDAS attributes based on external configuration --- .../msproxyservice/dto/attributes/Type.java | 7 + .../protocol/ProxyServiceAuthenticationAction.java | 297 ++++++++++----------- .../service/ProxyEidasAttributeRegistry.java | 34 ++- 3 files changed, 176 insertions(+), 162 deletions(-) (limited to 'modules/eidas_proxy-sevice/src/main') diff --git a/modules/eidas_proxy-sevice/src/main/java/at/asitplus/eidas/specific/modules/msproxyservice/dto/attributes/Type.java b/modules/eidas_proxy-sevice/src/main/java/at/asitplus/eidas/specific/modules/msproxyservice/dto/attributes/Type.java index 86ca49fa..f66bb799 100644 --- a/modules/eidas_proxy-sevice/src/main/java/at/asitplus/eidas/specific/modules/msproxyservice/dto/attributes/Type.java +++ b/modules/eidas_proxy-sevice/src/main/java/at/asitplus/eidas/specific/modules/msproxyservice/dto/attributes/Type.java @@ -15,6 +15,7 @@ import lombok.Data; @JsonInclude(JsonInclude.Include.NON_NULL) @JsonPropertyOrder({ "mds", + "autoIncludeWithMandates", "mandator" }) @Data @@ -27,6 +28,12 @@ public class Type { @JsonProperty("mds") private Boolean mds; + /** + * true if that attribute has to be included into eIDAS response in case of mandates. + */ + @JsonProperty("autoIncludeWithMandates") + private Boolean autoIncludeWithMandates; + /** * Classifie that attribute to specific mandate modes. */ diff --git a/modules/eidas_proxy-sevice/src/main/java/at/asitplus/eidas/specific/modules/msproxyservice/protocol/ProxyServiceAuthenticationAction.java b/modules/eidas_proxy-sevice/src/main/java/at/asitplus/eidas/specific/modules/msproxyservice/protocol/ProxyServiceAuthenticationAction.java index 92165412..bf1c5e5f 100644 --- a/modules/eidas_proxy-sevice/src/main/java/at/asitplus/eidas/specific/modules/msproxyservice/protocol/ProxyServiceAuthenticationAction.java +++ b/modules/eidas_proxy-sevice/src/main/java/at/asitplus/eidas/specific/modules/msproxyservice/protocol/ProxyServiceAuthenticationAction.java @@ -1,6 +1,7 @@ package at.asitplus.eidas.specific.modules.msproxyservice.protocol; import java.io.IOException; +import java.util.Optional; import java.util.UUID; import javax.annotation.PostConstruct; @@ -15,12 +16,11 @@ import org.springframework.context.ApplicationContext; import org.springframework.core.io.ResourceLoader; import org.springframework.web.util.UriComponentsBuilder; -import at.asitplus.eidas.specific.core.MsEidasNodeConstants; import at.asitplus.eidas.specific.core.gui.StaticGuiBuilderConfiguration; 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.msproxyservice.MsProxyServiceConstants; import at.asitplus.eidas.specific.modules.msproxyservice.exception.EidasProxyServiceException; +import at.asitplus.eidas.specific.modules.msproxyservice.service.ProxyEidasAttributeRegistry; import at.asitplus.eidas.specific.modules.msproxyservice.utils.EidasProxyServiceUtils; import at.gv.egiz.eaaf.core.api.IRequest; import at.gv.egiz.eaaf.core.api.data.PvpAttributeDefinitions; @@ -69,35 +69,35 @@ public class ProxyServiceAuthenticationAction implements IAction { @Autowired ISpringMvcGuiFormBuilder guiBuilder; @Autowired - EidasAttributeRegistry attrRegistry; + ProxyEidasAttributeRegistry attrRegistry; @Override public SloInformationInterface processRequest(IRequest pendingReq, HttpServletRequest httpReq, HttpServletResponse httpResp, IAuthData authData) throws EaafException { if (pendingReq instanceof ProxyServicePendingRequest) { - try { - ILightRequest eidasReq = ((ProxyServicePendingRequest) pendingReq).getEidasRequest(); - - //build eIDAS response - Builder lightRespBuilder = LightResponse.builder(); + try { + final ILightRequest eidasReq = ((ProxyServicePendingRequest) pendingReq).getEidasRequest(); + + // build eIDAS response + final Builder lightRespBuilder = LightResponse.builder(); lightRespBuilder.id(UUID.randomUUID().toString()); lightRespBuilder.inResponseToId(eidasReq.getId()); lightRespBuilder.relayState(eidasReq.getRelayState()); - + lightRespBuilder.status(ResponseStatus.builder() .statusCode(EidasConstants.SUCCESS_URI) .build()); - - //TODO: check if we can use transient subjectNameIds + + // TODO: check if we can use transient subjectNameIds lightRespBuilder.subject(UUID.randomUUID().toString()); lightRespBuilder.subjectNameIdFormat(NameIDType.TRANSIENT); - - //TODO: + + // TODO: lightRespBuilder.issuer(basicConfig.getBasicConfiguration( MsProxyServiceConstants.CONIG_PROPS_EIDAS_PROXY_NODE_ENTITYID)); - lightRespBuilder.levelOfAssurance(authData.getEidasQaaLevel()); + lightRespBuilder.levelOfAssurance(authData.getEidasQaaLevel()); lightRespBuilder.attributes(buildAttributesFromAuthData(authData, eidasReq)); - + // set SLO response object of EAAF framework final SloInformationImpl sloInformation = new SloInformationImpl(); sloInformation.setProtocolType(pendingReq.requestedModule()); @@ -121,7 +121,7 @@ public class ProxyServiceAuthenticationAction implements IAction { } } - + @Override public boolean needAuthentication(IRequest req, HttpServletRequest httpReq, HttpServletResponse httpResp) { return true; @@ -133,28 +133,29 @@ public class ProxyServiceAuthenticationAction implements IAction { return PROXYSERVICE_AUTH_ACTION_NAME; } - /** * Forward eIDAS Light response to eIDAS node. - * - * @param pendingReq Current pending request. - * @param httpReq Current HTTP request - * @param httpResp Current HTTP response + * + * @param pendingReq Current pending request. + * @param httpReq Current HTTP request + * @param httpResp Current HTTP response * @param lightResponse eIDAS LightResponse * @throws EaafConfigurationException In case of a configuration error - * @throws IOException In case of a general error - * @throws GuiBuildException In case of a GUI rendering error, if http POST binding is used - * @throws ServletException In case of a general error + * @throws IOException In case of a general error + * @throws GuiBuildException In case of a GUI rendering error, if http + * POST binding is used + * @throws ServletException In case of a general error */ public void forwardToEidasProxy(IRequest pendingReq, HttpServletRequest httpReq, - HttpServletResponse httpResp, LightResponse lightResponse) throws EaafConfigurationException, IOException, + HttpServletResponse httpResp, LightResponse lightResponse) throws EaafConfigurationException, + IOException, GuiBuildException, ServletException { // put request into shared cache final BinaryLightToken token = putResponseInCommunicationCache(lightResponse); final String tokenBase64 = BinaryLightTokenHelper.encodeBinaryLightTokenBase64(token); - + // select forward URL regarding the selected environment final String forwardUrl = basicConfig.getBasicConfiguration( MsProxyServiceConstants.CONIG_PROPS_EIDAS_PROXY_NODE_FORWARD_URL); @@ -196,148 +197,80 @@ public class ProxyServiceAuthenticationAction implements IAction { } } - - @PostConstruct + + @PostConstruct private void checkConfiguration() { - //TODO: validate configuration on start-up - + // TODO: validate configuration on start-up + } - - - private ImmutableAttributeMap buildAttributesFromAuthData(IAuthData authData, + + private ImmutableAttributeMap buildAttributesFromAuthData(IAuthData authData, ILightRequest eidasReq) { - IEidAuthData eidAuthData = (IEidAuthData) authData; + final IEidAuthData eidAuthData = (IEidAuthData) authData; + final ImmutableAttributeMap.Builder attributeMap = ImmutableAttributeMap.builder(); + + // inject all requested attributres + injectRequestedAttributes(attributeMap, eidasReq, eidAuthData); + if (eidAuthData.isUseMandate()) { log.debug("Building eIDAS Proxy-Service response with mandate ... "); - final ImmutableAttributeMap.Builder attributeMap = ImmutableAttributeMap.builder(); - injectRepesentativeInformation(attributeMap, eidAuthData); - injectMandatorInformation(attributeMap, eidAuthData); - - // work-around that injects nat. person subject to bypass validation on eIDAS Node + injectMdsRepesentativeInformation(attributeMap, eidAuthData, eidasReq.getRequestedAttributes()); + + // work-around that injects nat. person subject to bypass validation on eIDAS + // Node injectJurPersonWorkaroundIfRequired(attributeMap, eidasReq, authData); - - return attributeMap.build(); - - } else { - log.debug("Building eIDAS Proxy-Service response without mandates ... "); - return buildAttributesWithoutMandate(eidAuthData); - - } - } - - private void injectMandatorInformation( - ImmutableAttributeMap.Builder attributeMap, IEidAuthData eidAuthData) { - String natMandatorId = eidAuthData.getGenericData( - MsEidasNodeConstants.ATTR_EIDAS_NAT_MANDATOR_PERSONAL_IDENTIFIER, String.class); - - if (StringUtils.isNotEmpty(natMandatorId)) { - log.debug("Injecting natural mandator informations ... "); - final AttributeDefinition attrDefPersonalId = attrRegistry.getCoreAttributeRegistry().getByFriendlyName( - EidasConstants.eIDAS_ATTR_PERSONALIDENTIFIER).first(); - final AttributeDefinition attrDefFamilyName = attrRegistry.getCoreAttributeRegistry().getByFriendlyName( - EidasConstants.eIDAS_ATTR_CURRENTFAMILYNAME).first(); - final AttributeDefinition attrDefGivenName = attrRegistry.getCoreAttributeRegistry().getByFriendlyName( - EidasConstants.eIDAS_ATTR_CURRENTGIVENNAME).first(); - final AttributeDefinition attrDefDateOfBirth = attrRegistry.getCoreAttributeRegistry().getByFriendlyName( - EidasConstants.eIDAS_ATTR_DATEOFBIRTH).first(); - - attributeMap.put(attrDefPersonalId, natMandatorId); - attributeMap.put(attrDefFamilyName, eidAuthData.getGenericData( - PvpAttributeDefinitions.MANDATE_NAT_PER_FAMILY_NAME_NAME, String.class)); - attributeMap.put(attrDefGivenName, eidAuthData.getGenericData( - PvpAttributeDefinitions.MANDATE_NAT_PER_GIVEN_NAME_NAME, String.class)); - attributeMap.put(attrDefDateOfBirth, eidAuthData.getGenericData( - PvpAttributeDefinitions.MANDATE_NAT_PER_BIRTHDATE_NAME, String.class)); - - } else { - log.debug("Injecting legal mandator informations ... "); - final AttributeDefinition commonName = attrRegistry.getCoreAttributeRegistry().getByFriendlyName( - EidasConstants.eIDAS_ATTR_LEGALNAME).first(); - final AttributeDefinition legalPersonId = attrRegistry.getCoreAttributeRegistry().getByFriendlyName( - EidasConstants.eIDAS_ATTR_LEGALPERSONIDENTIFIER).first(); - - attributeMap.put(commonName, eidAuthData.getGenericData( - PvpAttributeDefinitions.MANDATE_LEG_PER_FULL_NAME_NAME, String.class)); - attributeMap.put(legalPersonId, eidAuthData.getGenericData( - MsEidasNodeConstants.ATTR_EIDAS_JUR_MANDATOR_PERSONAL_IDENTIFIER, String.class)); - - } - } - private void injectRepesentativeInformation( - ImmutableAttributeMap.Builder attributeMap, IEidAuthData eidAuthData) { - final AttributeDefinition attrDefPersonalId = attrRegistry.getCoreAttributeRegistry().getByFriendlyName( - EidasConstants.eIDAS_ATTR_REPRESENTATIVE_PERSONALIDENTIFIER).first(); - final AttributeDefinition attrDefFamilyName = attrRegistry.getCoreAttributeRegistry().getByFriendlyName( - EidasConstants.eIDAS_ATTR_REPRESENTATIVE_CURRENTFAMILYNAME).first(); - final AttributeDefinition attrDefGivenName = attrRegistry.getCoreAttributeRegistry().getByFriendlyName( - EidasConstants.eIDAS_ATTR_REPRESENTATIVE_CURRENTGIVENNAME).first(); - final AttributeDefinition attrDefDateOfBirth = attrRegistry.getCoreAttributeRegistry().getByFriendlyName( - EidasConstants.eIDAS_ATTR_REPRESENTATIVE_DATEOFBIRTH).first(); - - attributeMap.put(attrDefPersonalId, - eidAuthData.getGenericData(MsEidasNodeConstants.ATTR_EIDAS_PERSONAL_IDENTIFIER, String.class)); - attributeMap.put(attrDefFamilyName, eidAuthData.getFamilyName()); - attributeMap.put(attrDefGivenName, eidAuthData.getGivenName()); - - //TODO: throw an error in case of SZR Date with month or day = "00" - attributeMap.put(attrDefDateOfBirth, eidAuthData.getDateOfBirth()); - + } + + return attributeMap.build(); + } - /** - * Work-around to inject representative information as nat. person subject to bypass eIDAS Node validation. - * - *

Injection will only be done if this work-around is enabled by configuration, - * the mandator is a legal person, and both legal and natural person subject's is requested.

- * - * @param attributeMap Attribute set for eIDAS response - * @param eidasReq Incoming eIDAS request - * @param authData Authentication data - */ - private void injectJurPersonWorkaroundIfRequired( - ImmutableAttributeMap.Builder attributeMap, ILightRequest eidasReq, IAuthData authData) { - if (isLegalPersonWorkaroundActive() && isLegalPersonMandateAvailable(authData) - && EidasProxyServiceUtils.isNaturalPersonRequested(eidasReq) - && EidasProxyServiceUtils.isLegalPersonRequested(eidasReq)) { - log.debug("Injecting representative information as nat. person subject to bypass eIDAS Node validation"); - attributeMap.putAll(buildAttributesWithoutMandate(authData)); - - } + private void injectRequestedAttributes(ImmutableAttributeMap.Builder attributeMap, ILightRequest eidasReq, + IEidAuthData eidAuthData) { + eidasReq.getRequestedAttributes().getAttributeMap().keySet().stream() + .forEach(el -> injectEidasAttribute(attributeMap, eidAuthData, + el.getNameUri().toString(), eidAuthData.isUseMandate())); + } - - private ImmutableAttributeMap buildAttributesWithoutMandate(IAuthData eidAuthData) { - //TODO: throw an error in case of SZR Date with month or day = "00" - return buildAttributesWithoutMandate( - eidAuthData.getGenericData(MsEidasNodeConstants.ATTR_EIDAS_PERSONAL_IDENTIFIER, String.class), - eidAuthData.getFamilyName(), - eidAuthData.getGivenName(), - eidAuthData.getDateOfBirth()); - + + private void injectMdsRepesentativeInformation( + ImmutableAttributeMap.Builder attributeMap, IEidAuthData eidAuthData, + ImmutableAttributeMap requestedAttributes) { + attrRegistry.getRepresentativeAttributesToAddByDefault() + .filter(el -> requestedAttributes.getAttributeValuesByNameUri(el) == null) + .forEach(el -> injectEidasAttribute(attributeMap, eidAuthData, el, true)); + } - private ImmutableAttributeMap buildAttributesWithoutMandate(String personalIdentifier, String familyName, - String givenName, String dateOfBirth) { - final AttributeDefinition attrDefPersonalId = attrRegistry.getCoreAttributeRegistry().getByFriendlyName( - EidasConstants.eIDAS_ATTR_PERSONALIDENTIFIER).first(); - final AttributeDefinition attrDefFamilyName = attrRegistry.getCoreAttributeRegistry().getByFriendlyName( - EidasConstants.eIDAS_ATTR_CURRENTFAMILYNAME).first(); - final AttributeDefinition attrDefGivenName = attrRegistry.getCoreAttributeRegistry().getByFriendlyName( - EidasConstants.eIDAS_ATTR_CURRENTGIVENNAME).first(); - final AttributeDefinition attrDefDateOfBirth = attrRegistry.getCoreAttributeRegistry().getByFriendlyName( - EidasConstants.eIDAS_ATTR_DATEOFBIRTH).first(); - - final ImmutableAttributeMap.Builder attributeMap = - ImmutableAttributeMap.builder() - .put(attrDefPersonalId, personalIdentifier) - .put(attrDefFamilyName, familyName) - .put(attrDefGivenName, givenName) - .put(attrDefDateOfBirth, dateOfBirth); - - return attributeMap.build(); - + private void injectEidasAttribute(ImmutableAttributeMap.Builder attributeMap, IEidAuthData eidAuthData, + String eidasAttrName, boolean mandatesUsed) { + final Optional releatedIdaAttribute = + attrRegistry.mapEidasAttributeToSpecificIdaAttribute(eidasAttrName, mandatesUsed); + if (releatedIdaAttribute.isPresent()) { + log.trace("Mapping IDA attribute: {} to eIDAS attribute: {}", releatedIdaAttribute.get(), + eidasAttrName); + final String idaAttrValue = eidAuthData.getGenericData(releatedIdaAttribute.get(), String.class); + if (StringUtils.isNotEmpty(idaAttrValue)) { + log.debug("Build eIDAS attribute: {} from IDA attribute: {}", eidasAttrName, releatedIdaAttribute + .get()); + attributeMap.put( + attrRegistry.getCoreRegistry().getCoreAttributeRegistry().getByName(eidasAttrName), + idaAttrValue); + + } else { + log.info("No IDA attribute: {}, eIDAS attribute: {} will be ignored", releatedIdaAttribute.get(), + eidasAttrName); + + } + + } else { + log.warn("Can not build eIDAS attribute: {}, because there is not corresponding IDA attribute defined", + eidasAttrName); + + } } - + private BinaryLightToken putResponseInCommunicationCache(ILightResponse lightResponse) throws ServletException { final BinaryLightToken binaryLightToken; @@ -358,17 +291,61 @@ public class ProxyServiceAuthenticationAction implements IAction { return binaryLightToken; } + /** + * Work-around to inject representative information as nat. person subject to + * bypass eIDAS Node validation. + * + *

+ * Injection will only be done if this work-around is enabled by + * configuration, the mandator is a legal person, and both legal and natural + * person subject's is requested. + *

+ * + * @param attributeMap Attribute set for eIDAS response + * @param eidasReq Incoming eIDAS request + * @param authData Authentication data + */ + private void injectJurPersonWorkaroundIfRequired( + ImmutableAttributeMap.Builder attributeMap, ILightRequest eidasReq, IAuthData authData) { + if (isLegalPersonWorkaroundActive() && isLegalPersonMandateAvailable(authData) + && EidasProxyServiceUtils.isNaturalPersonRequested(eidasReq) + && EidasProxyServiceUtils.isLegalPersonRequested(eidasReq)) { + log.debug( + "Injecting representative information as nat. person subject to bypass eIDAS Node validation"); + + final AttributeDefinition attrDefPersonalId = + attrRegistry.getCoreRegistry().getCoreAttributeRegistry().getByFriendlyName( + EidasConstants.eIDAS_ATTR_PERSONALIDENTIFIER).first(); + final AttributeDefinition attrDefFamilyName = + attrRegistry.getCoreRegistry().getCoreAttributeRegistry().getByFriendlyName( + EidasConstants.eIDAS_ATTR_CURRENTFAMILYNAME).first(); + final AttributeDefinition attrDefGivenName = + attrRegistry.getCoreRegistry().getCoreAttributeRegistry().getByFriendlyName( + EidasConstants.eIDAS_ATTR_CURRENTGIVENNAME).first(); + final AttributeDefinition attrDefDateOfBirth = + attrRegistry.getCoreRegistry().getCoreAttributeRegistry().getByFriendlyName( + EidasConstants.eIDAS_ATTR_DATEOFBIRTH).first(); + + attributeMap.put(attrDefPersonalId, authData.getGenericData(PvpAttributeDefinitions.BPK_NAME, + String.class)); + attributeMap.put(attrDefFamilyName, authData.getFamilyName()); + attributeMap.put(attrDefGivenName, authData.getGivenName()); + attributeMap.put(attrDefDateOfBirth, authData.getDateOfBirth()); + + } + } + private boolean isLegalPersonWorkaroundActive() { return basicConfig.getBasicConfigurationBoolean( - MsProxyServiceConstants.CONIG_PROPS_EIDAS_PROXY_WORKAROUND_MANDATES_LEGAL_PERSON, + MsProxyServiceConstants.CONIG_PROPS_EIDAS_PROXY_WORKAROUND_MANDATES_LEGAL_PERSON, false); - + } - + private boolean isLegalPersonMandateAvailable(IAuthData authData) { return StringUtils.isNoneEmpty(authData.getGenericData( - MsEidasNodeConstants.ATTR_EIDAS_JUR_MANDATOR_PERSONAL_IDENTIFIER, String.class)); - + PvpAttributeDefinitions.MANDATE_LEG_PER_SOURCE_PIN_NAME, String.class)); + } } diff --git a/modules/eidas_proxy-sevice/src/main/java/at/asitplus/eidas/specific/modules/msproxyservice/service/ProxyEidasAttributeRegistry.java b/modules/eidas_proxy-sevice/src/main/java/at/asitplus/eidas/specific/modules/msproxyservice/service/ProxyEidasAttributeRegistry.java index b9e0c488..a6a50100 100644 --- a/modules/eidas_proxy-sevice/src/main/java/at/asitplus/eidas/specific/modules/msproxyservice/service/ProxyEidasAttributeRegistry.java +++ b/modules/eidas_proxy-sevice/src/main/java/at/asitplus/eidas/specific/modules/msproxyservice/service/ProxyEidasAttributeRegistry.java @@ -7,6 +7,7 @@ import java.util.Collection; import java.util.Collections; import java.util.List; import java.util.Objects; +import java.util.Optional; import java.util.Set; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -76,6 +77,19 @@ public class ProxyEidasAttributeRegistry { } + /** + * Get all eIDAS attributes that are added by default in case of mandates. + * + * @return {@link Stream} of eIDAS attributes + */ + @NonNull + public Stream getRepresentativeAttributesToAddByDefault() { + return attributeConfiguration.stream() + .filter(el -> el.getType() != null && el.getType().getAutoIncludeWithMandates()) + .map(el -> el.getEidasAttributeName()); + + } + /** * Get IDA attributes for a specific eIDAS attribute. * @@ -95,8 +109,24 @@ public class ProxyEidasAttributeRegistry { .collect(Collectors.toSet()); } - - + + /** + * Get eIDAS related IDA attribute. + * + * @param eidasAttributeName Name of the eIDAS attribute. + * @param withMandates true if mandates are supported, otherwise false + * @return Name of the related IDA attribute if available + */ + public Optional mapEidasAttributeToSpecificIdaAttribute( + String eidasAttributeName, boolean withMandates) { + return attributeConfiguration.stream() + .filter(el -> el.getEidasAttributeName().equals(eidasAttributeName)) + .findFirst() + .map(el -> withMandates ? el.getIdaAttribute().getWithMandates() : el.getIdaAttribute().getBasic()) + .filter(el -> StringUtils.isNotEmpty(el)); + + } + @PostConstruct private void initialize() throws EaafConfigurationException { final String attrConfPath = basicConfig.getBasicConfiguration( -- cgit v1.2.3 From cab2ab4ddb85b305d77798073b868cf42a7e0111 Mon Sep 17 00:00:00 2001 From: Thomas <> Date: Wed, 8 Jun 2022 14:56:42 +0200 Subject: chore(core): minory style, test and validation fixes --- .../specific/modules/msproxyservice/dto/attributes/Type.java | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) (limited to 'modules/eidas_proxy-sevice/src/main') diff --git a/modules/eidas_proxy-sevice/src/main/java/at/asitplus/eidas/specific/modules/msproxyservice/dto/attributes/Type.java b/modules/eidas_proxy-sevice/src/main/java/at/asitplus/eidas/specific/modules/msproxyservice/dto/attributes/Type.java index f66bb799..6a06a5b5 100644 --- a/modules/eidas_proxy-sevice/src/main/java/at/asitplus/eidas/specific/modules/msproxyservice/dto/attributes/Type.java +++ b/modules/eidas_proxy-sevice/src/main/java/at/asitplus/eidas/specific/modules/msproxyservice/dto/attributes/Type.java @@ -22,8 +22,7 @@ import lombok.Data; public class Type { /** - * true if this attribute is part of MDS, otherwise - * false + * true if this attribute is part of MDS, otherwise false. */ @JsonProperty("mds") private Boolean mds; @@ -50,7 +49,7 @@ public class Type { NONE("none"); private final String value; - private final static Map CONSTANTS = new HashMap<>(); + private static final Map CONSTANTS = new HashMap<>(); static { for (final Type.Mandator c : values()) { @@ -72,6 +71,12 @@ public class Type { return this.value; } + /** + * Build {@link Mandator} from textual representation. + * + * @param value textual representation + * @return Type of the mandator + */ @JsonCreator public static Type.Mandator fromValue(String value) { final Type.Mandator constant = CONSTANTS.get(value); -- cgit v1.2.3 From 669e1d6571b4bddcf5955597bcfe90e1523a6714 Mon Sep 17 00:00:00 2001 From: Thomas <> Date: Fri, 5 Aug 2022 13:10:37 +0200 Subject: feat(proxy-service): add work-around to support mandates for legal- and natural-persons in parallel --- .../protocol/EidasProxyServiceController.java | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) (limited to 'modules/eidas_proxy-sevice/src/main') 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 26cc51ee..9a0331bd 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 @@ -14,6 +14,7 @@ import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; +import org.apache.commons.collections4.ListUtils; import org.apache.commons.lang.StringEscapeUtils; import org.apache.commons.lang3.StringUtils; import org.opensaml.saml.saml2.core.NameIDType; @@ -429,8 +430,18 @@ public class EidasProxyServiceController extends AbstractController implements I log.trace("eIDAS Proxy-Service allows mandates for Connector: {}. Selecting profiles ... ", spConfig.getUniqueIdentifier()); - //check if legal person is requested - if (EidasProxyServiceUtils.isLegalPersonRequested(eidasRequest)) { + if (EidasProxyServiceUtils.isLegalPersonRequested(eidasRequest) + && EidasProxyServiceUtils.isNaturalPersonRequested(eidasRequest)) { + log.debug("Find requested attributes for legal and natural persons. Injecting mandate-profiles for both ... "); + spConfig.setMandateProfiles(ListUtils.union( + KeyValueUtils.getListOfCsvValues( + spConfig.getConfigurationValue(MsProxyServiceConstants.CONIG_PROPS_CONNECTOR_MANDATES_PROFILE_NATURAL)), + KeyValueUtils.getListOfCsvValues( + spConfig.getConfigurationValue(MsProxyServiceConstants.CONIG_PROPS_CONNECTOR_MANDATES_PROFILE_LEGAL)))); + spConfig.setMandateMode(SpMandateModes.BOTH); + + } else if (EidasProxyServiceUtils.isLegalPersonRequested(eidasRequest)) { + //check if legal person is requested spConfig.setMandateProfiles(KeyValueUtils.getListOfCsvValues( spConfig.getConfigurationValue(MsProxyServiceConstants.CONIG_PROPS_CONNECTOR_MANDATES_PROFILE_LEGAL))); spConfig.setMandateMode(SpMandateModes.LEGAL_FORCE); -- cgit v1.2.3 From b54e3424a7ac9a661aae771f13cdc94086b04132 Mon Sep 17 00:00:00 2001 From: Thomas <> Date: Mon, 8 Aug 2022 15:00:16 +0200 Subject: feat(proxy): optimize mandate support by selection mandate-mode based on requested attributes and profiles from configuration Now it's possible to deactivte legal- or natural-person mandates by mandate-profile configuration on eIDAS-Connector level --- .../protocol/EidasProxyServiceController.java | 231 +++++++++++---------- .../protocol/ProxyServiceAuthenticationAction.java | 2 + 2 files changed, 124 insertions(+), 109 deletions(-) (limited to 'modules/eidas_proxy-sevice/src/main') 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 9a0331bd..32be0e22 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 @@ -5,6 +5,7 @@ import java.text.MessageFormat; import java.util.Collection; import java.util.Collections; import java.util.HashMap; +import java.util.List; import java.util.Map; import java.util.Objects; import java.util.UUID; @@ -77,11 +78,13 @@ public class EidasProxyServiceController extends AbstractController implements I private static final String ERROR_09 = "eidas.proxyservice.09"; private static final String ERROR_10 = "eidas.proxyservice.10"; private static final String ERROR_11 = "eidas.proxyservice.11"; - + public static final String PROTOCOL_ID = "eidasProxy"; - @Autowired ProxyEidasAttributeRegistry attrRegistry; - @Autowired ProxyServiceAuthenticationAction responseAction; + @Autowired + ProxyEidasAttributeRegistry attrRegistry; + @Autowired + ProxyServiceAuthenticationAction responseAction; /** * End-point that receives authentication requests from eIDAS Node. @@ -92,8 +95,8 @@ public class EidasProxyServiceController extends AbstractController implements I * @throws EaafException In case of a validation or processing error */ @RequestMapping(value = { - MsProxyServiceConstants.EIDAS_HTTP_ENDPOINT_IDP_POST, - MsProxyServiceConstants.EIDAS_HTTP_ENDPOINT_IDP_REDIRECT + MsProxyServiceConstants.EIDAS_HTTP_ENDPOINT_IDP_POST, + MsProxyServiceConstants.EIDAS_HTTP_ENDPOINT_IDP_REDIRECT }, method = { RequestMethod.POST, RequestMethod.GET }) public void receiveEidasAuthnRequest(HttpServletRequest httpReq, HttpServletResponse httpResp) @@ -119,17 +122,18 @@ public class EidasProxyServiceController extends AbstractController implements I .toString()); final ILightRequest eidasRequest = specificProxyCommunicationService.getAndRemoveRequest( tokenBase64, - ImmutableSortedSet.copyOf(attrRegistry.getCoreRegistry().getCoreAttributeRegistry().getAttributes())); + ImmutableSortedSet.copyOf(attrRegistry.getCoreRegistry().getCoreAttributeRegistry() + .getAttributes())); if (eidasRequest == null) { log.info("Find no eIDAS Authn. Request with stated token."); throw new EidasProxyServiceException(ERROR_11, null); - - } - + + } + 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); @@ -141,7 +145,7 @@ public class EidasProxyServiceController extends AbstractController implements I revisionsLogger.logEvent(pendingReq.getUniqueSessionIdentifier(), pendingReq.getUniqueTransactionIdentifier(), EventConstants.TRANSACTION_IP, httpReq.getRemoteAddr()); - + // validate eIDAS Authn. request and set into pending-request validateEidasAuthnRequest(eidasRequest); pendingReq.setEidasRequest(eidasRequest); @@ -149,9 +153,9 @@ public class EidasProxyServiceController extends AbstractController implements I // generate Service-Provider configuration from eIDAS request final ISpConfiguration spConfig = generateSpConfigurationFromEidasRequest(eidasRequest); - // validate eIDAS Authn. request by using eIDAS Connector specifc parameters + // validate eIDAS Authn. request by using eIDAS Connector specifc parameters validateEidasAuthnRequest(spConfig, eidasRequest); - + // populate pendingRequest with parameters pendingReq.setOnlineApplicationConfiguration(spConfig); pendingReq.setSpEntityId(spConfig.getUniqueIdentifier()); @@ -190,16 +194,16 @@ public class EidasProxyServiceController extends AbstractController implements I 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(); + try { + final ILightRequest eidasReq = ((ProxyServicePendingRequest) pendingReq).getEidasRequest(); + + // build eIDAS response + final 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)); + MsProxyServiceConstants.CONIG_PROPS_EIDAS_PROXY_NODE_ENTITYID)); lightRespBuilder.subject(UUID.randomUUID().toString()); lightRespBuilder.subjectNameIdFormat(NameIDType.TRANSIENT); lightRespBuilder.status(ResponseStatus.builder() @@ -223,7 +227,7 @@ public class EidasProxyServiceController extends AbstractController implements I ProxyServicePendingRequest.class.getName()); } - + return false; } @@ -253,11 +257,11 @@ 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.getIssuer())) { + if (StringUtils.isEmpty(eidasRequest.getIssuer())) { throw new EidasProxyServiceException(ERROR_05, null); } - + // TODO: validate some other stuff } @@ -266,23 +270,23 @@ public class EidasProxyServiceController extends AbstractController implements I * eIDAS Connector specific validation of incoming eIDAS request. * * @param eidasRequest Incoming eIDAS authentication request - * @param spConfig eIDAS Connector configuration + * @param spConfig eIDAS Connector configuration * @throws EidasProxyServiceException In case of a validation error */ - private void validateEidasAuthnRequest(ISpConfiguration spConfig, ILightRequest eidasRequest) - throws EidasProxyServiceException { + private void validateEidasAuthnRequest(ISpConfiguration spConfig, ILightRequest eidasRequest) + throws EidasProxyServiceException { // check if natural-person and legal-person attributes requested in parallel - if (spConfig.isConfigurationValue(MsProxyServiceConstants.CONIG_PROPS_CONNECTOR_VALIDATION_ATTR_MDS, true) - && EidasProxyServiceUtils.isLegalPersonRequested(eidasRequest) + if (spConfig.isConfigurationValue(MsProxyServiceConstants.CONIG_PROPS_CONNECTOR_VALIDATION_ATTR_MDS, true) + && EidasProxyServiceUtils.isLegalPersonRequested(eidasRequest) && EidasProxyServiceUtils.isNaturalPersonRequested(eidasRequest)) { throw new EidasProxyServiceException(ERROR_08, null); - + } - + // TODO: validate some other stuff } - + /** * Generate a dummy Service-Provider configuration for processing. * @@ -293,55 +297,57 @@ public class EidasProxyServiceController extends AbstractController implements I private ISpConfiguration generateSpConfigurationFromEidasRequest(ILightRequest eidasRequest) throws EidasProxyServiceException { try { - - Map connectorConfigMap = extractRawConnectorConfiguration(eidasRequest); - + + final Map connectorConfigMap = extractRawConnectorConfiguration(eidasRequest); + // check if country-code is available - String spCountry = connectorConfigMap.get(MsProxyServiceConstants.CONIG_PROPS_CONNECTOR_COUNTRYCODE); - if (StringUtils.isEmpty(spCountry)) { + final String spCountry = connectorConfigMap.get( + MsProxyServiceConstants.CONIG_PROPS_CONNECTOR_COUNTRYCODE); + if (StringUtils.isEmpty(spCountry)) { throw new EidasProxyServiceException(ERROR_07, null); } - - // build FriendyName from CountryCode and SPType + + // build FriendyName from CountryCode and SPType connectorConfigMap.put(MsEidasNodeConstants.PROP_CONFIG_SP_FRIENDLYNAME, MessageFormat.format(MsProxyServiceConstants.TEMPLATE_SP_UNIQUE_ID, spCountry, eidasRequest.getSpType())); - // build Service-Provider configuration object - final ServiceProviderConfiguration spConfig = new ServiceProviderConfiguration(connectorConfigMap, authConfig); + // build Service-Provider configuration object + final ServiceProviderConfiguration spConfig = new ServiceProviderConfiguration(connectorConfigMap, + authConfig); - // build bPK target from Country-Code - final String ccCountry = authConfig.getBasicConfiguration(EidasConstants.CONIG_PROPS_EIDAS_NODE_COUNTRYCODE, + // build bPK target from Country-Code + final String ccCountry = authConfig.getBasicConfiguration( + EidasConstants.CONIG_PROPS_EIDAS_NODE_COUNTRYCODE, EidasConstants.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())); // build mandate profiles for this specific request buildMandateProfileConfiguration(spConfig, eidasRequest); - + // map eIDAS attributes to national attributes buildNationalRequestedAttributes(spConfig, eidasRequest); - + return spConfig; - } catch (EidasProxyServiceException e) { + } catch (final EidasProxyServiceException e) { throw e; - + } catch (final EaafException e) { throw new EidasProxyServiceException(ERROR_04, new Object[] { e.getMessage() }, e); } } - private void buildNationalRequestedAttributes( - ServiceProviderConfiguration spConfig, ILightRequest eidasRequest) { - boolean mandatesEnabled = !SpMandateModes.NONE.equals(spConfig.getMandateMode()); + ServiceProviderConfiguration spConfig, ILightRequest eidasRequest) { + final boolean mandatesEnabled = !SpMandateModes.NONE.equals(spConfig.getMandateMode()); spConfig.setRequestedAttributes( Streams.concat( eidasRequest.getRequestedAttributes().getAttributeMap().keySet().stream() @@ -350,126 +356,133 @@ public class EidasProxyServiceController extends AbstractController implements I .flatMap(Collection::stream) .filter(Objects::nonNull), attrRegistry.getAlwaysRequestedAttributes(mandatesEnabled)) - .collect(Collectors.toSet())); + .collect(Collectors.toSet())); log.debug("Inject #{} attributes to request from IDA system", spConfig.getRequestedAttributes().size()); - + } - private Map extractRawConnectorConfiguration(ILightRequest eidasRequest) { - Map allConnectorConfigs = authConfig.getBasicConfigurationWithPrefix( + private Map extractRawConnectorConfiguration(ILightRequest eidasRequest) { + final Map 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 connectorConfig = allConnectorConfigs.entrySet().stream() - .filter(el -> el.getKey().endsWith(MsEidasNodeConstants.PROP_CONFIG_SP_UNIQUEIDENTIFIER) + + final Map 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, + .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 ... ", + 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, + connectorConfig.put(MsProxyServiceConstants.CONIG_PROPS_CONNECTOR_COUNTRYCODE, eidasRequest.getSpCountryCode()); - + // set default mandate configuration - connectorConfig.put(MsProxyServiceConstants.CONIG_PROPS_CONNECTOR_MANDATES_ENABLED, + 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, + 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, + 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) + + private void buildMandateProfileConfiguration(ServiceProviderConfiguration spConfig, + ILightRequest eidasRequest) throws EidasProxyServiceException { // check if mandates are enabled - if (spConfig.isConfigurationValue(MsProxyServiceConstants.CONIG_PROPS_CONNECTOR_MANDATES_ENABLED, false)) { + if (spConfig.isConfigurationValue(MsProxyServiceConstants.CONIG_PROPS_CONNECTOR_MANDATES_ENABLED, + false)) { injectMandateInfosIntoSpConfig(spConfig, eidasRequest); - - } else { + + } else { if (EidasProxyServiceUtils.isLegalPersonRequested(eidasRequest)) { throw new EidasProxyServiceException(ERROR_09, null); - - } - + + } + spConfig.setMandateProfiles(Collections.emptyList()); - spConfig.setMandateMode(SpMandateModes.NONE); - + spConfig.setMandateMode(SpMandateModes.NONE); + } } private void injectMandateInfosIntoSpConfig(ServiceProviderConfiguration spConfig, ILightRequest eidasRequest) throws EidasProxyServiceException { - log.trace("eIDAS Proxy-Service allows mandates for Connector: {}. Selecting profiles ... ", + log.trace("eIDAS Proxy-Service allows mandates for Connector: {}. Selecting profiles ... ", spConfig.getUniqueIdentifier()); - if (EidasProxyServiceUtils.isLegalPersonRequested(eidasRequest) + final List legalPersonProfiles = KeyValueUtils.getListOfCsvValues(spConfig.getConfigurationValue( + MsProxyServiceConstants.CONIG_PROPS_CONNECTOR_MANDATES_PROFILE_LEGAL)); + final List natPersonProfiles = KeyValueUtils.getListOfCsvValues(spConfig.getConfigurationValue( + MsProxyServiceConstants.CONIG_PROPS_CONNECTOR_MANDATES_PROFILE_NATURAL)); + + if (EidasProxyServiceUtils.isLegalPersonRequested(eidasRequest) && EidasProxyServiceUtils.isNaturalPersonRequested(eidasRequest)) { - log.debug("Find requested attributes for legal and natural persons. Injecting mandate-profiles for both ... "); - spConfig.setMandateProfiles(ListUtils.union( - KeyValueUtils.getListOfCsvValues( - spConfig.getConfigurationValue(MsProxyServiceConstants.CONIG_PROPS_CONNECTOR_MANDATES_PROFILE_NATURAL)), - KeyValueUtils.getListOfCsvValues( - spConfig.getConfigurationValue(MsProxyServiceConstants.CONIG_PROPS_CONNECTOR_MANDATES_PROFILE_LEGAL)))); - spConfig.setMandateMode(SpMandateModes.BOTH); - + log.debug( + "Find requested attributes for legal and natural persons. Injecting mandate-profiles for both ... "); + spConfig.setMandateProfiles(ListUtils.union(natPersonProfiles, legalPersonProfiles)); + + // set Mandate-Mode based on SP configuration + final boolean isLegalPersonProfile = !legalPersonProfiles.isEmpty(); + final boolean isNaturalPersonProfile = !natPersonProfiles.isEmpty(); + spConfig.setMandateMode( + isLegalPersonProfile && isNaturalPersonProfile + ? SpMandateModes.BOTH + : isLegalPersonProfile + ? SpMandateModes.LEGAL_FORCE + : SpMandateModes.NATURAL); + } else if (EidasProxyServiceUtils.isLegalPersonRequested(eidasRequest)) { - //check if legal person is requested - spConfig.setMandateProfiles(KeyValueUtils.getListOfCsvValues( - spConfig.getConfigurationValue(MsProxyServiceConstants.CONIG_PROPS_CONNECTOR_MANDATES_PROFILE_LEGAL))); + // check if legal person is requested + spConfig.setMandateProfiles(legalPersonProfiles); spConfig.setMandateMode(SpMandateModes.LEGAL_FORCE); - + if (spConfig.getMandateProfiles().isEmpty()) { throw new EidasProxyServiceException(ERROR_10, null); - + } - + } else if (EidasProxyServiceUtils.isNaturalPersonRequested(eidasRequest)) { - spConfig.setMandateProfiles(KeyValueUtils.getListOfCsvValues( - spConfig.getConfigurationValue(MsProxyServiceConstants.CONIG_PROPS_CONNECTOR_MANDATES_PROFILE_NATURAL))); - + spConfig.setMandateProfiles(natPersonProfiles); spConfig.setMandateMode(SpMandateModes.NATURAL); - + } - - + if (spConfig.getMandateProfiles().isEmpty()) { - log.debug("No mandate-profiles for issure: {}. Set mandate-mode to 'none'", + log.debug("No mandate-profiles for issure: {}. Set mandate-mode to 'none'", spConfig.getUniqueIdentifier()); spConfig.setMandateMode(SpMandateModes.NONE); - + } else { log.debug("Set mandate-profiles: {} to request from issuer: {}", spConfig.getMandateProfiles(), spConfig.getUniqueIdentifier()); - + } - + } } diff --git a/modules/eidas_proxy-sevice/src/main/java/at/asitplus/eidas/specific/modules/msproxyservice/protocol/ProxyServiceAuthenticationAction.java b/modules/eidas_proxy-sevice/src/main/java/at/asitplus/eidas/specific/modules/msproxyservice/protocol/ProxyServiceAuthenticationAction.java index bf1c5e5f..8348558c 100644 --- a/modules/eidas_proxy-sevice/src/main/java/at/asitplus/eidas/specific/modules/msproxyservice/protocol/ProxyServiceAuthenticationAction.java +++ b/modules/eidas_proxy-sevice/src/main/java/at/asitplus/eidas/specific/modules/msproxyservice/protocol/ProxyServiceAuthenticationAction.java @@ -307,6 +307,8 @@ public class ProxyServiceAuthenticationAction implements IAction { */ private void injectJurPersonWorkaroundIfRequired( ImmutableAttributeMap.Builder attributeMap, ILightRequest eidasReq, IAuthData authData) { + + //TODO: maybe we have update that check, because we need an activation on Connector level if (isLegalPersonWorkaroundActive() && isLegalPersonMandateAvailable(authData) && EidasProxyServiceUtils.isNaturalPersonRequested(eidasReq) && EidasProxyServiceUtils.isLegalPersonRequested(eidasReq)) { -- cgit v1.2.3 From ca50cb8dda0a24b5a4589db126bfab8d0d885b00 Mon Sep 17 00:00:00 2001 From: Thomas <> Date: Tue, 16 Aug 2022 10:56:54 +0200 Subject: feat(proxy): add support for custom eIDAS attribute-handler into ProxyEidasAttributeRegistry This allow more sopisticated attribute-processing than simple mapping to IDA attributes --- .../dto/attributes/AttrMappingElement.java | 6 ++++++ .../service/ProxyEidasAttributeRegistry.java | 18 +++++++++++++++++- 2 files changed, 23 insertions(+), 1 deletion(-) (limited to 'modules/eidas_proxy-sevice/src/main') diff --git a/modules/eidas_proxy-sevice/src/main/java/at/asitplus/eidas/specific/modules/msproxyservice/dto/attributes/AttrMappingElement.java b/modules/eidas_proxy-sevice/src/main/java/at/asitplus/eidas/specific/modules/msproxyservice/dto/attributes/AttrMappingElement.java index cf106bad..2dffbc2d 100644 --- a/modules/eidas_proxy-sevice/src/main/java/at/asitplus/eidas/specific/modules/msproxyservice/dto/attributes/AttrMappingElement.java +++ b/modules/eidas_proxy-sevice/src/main/java/at/asitplus/eidas/specific/modules/msproxyservice/dto/attributes/AttrMappingElement.java @@ -13,6 +13,8 @@ import lombok.Data; @JsonPropertyOrder({ "eidasAttribute", "idaAttribute", + "addionalRequiredAttributes", + "specificAttributeHandlerClass", "type" }) @Data @@ -34,6 +36,10 @@ public class AttrMappingElement { @JsonProperty("addionalRequiredAttributes") private List addionalRequiredAttributes; + + @JsonProperty("specificAttributeHandlerClass") + private String specificAttributeHandlerClass; + /** * attribute characteristics. */ diff --git a/modules/eidas_proxy-sevice/src/main/java/at/asitplus/eidas/specific/modules/msproxyservice/service/ProxyEidasAttributeRegistry.java b/modules/eidas_proxy-sevice/src/main/java/at/asitplus/eidas/specific/modules/msproxyservice/service/ProxyEidasAttributeRegistry.java index a6a50100..a0c99019 100644 --- a/modules/eidas_proxy-sevice/src/main/java/at/asitplus/eidas/specific/modules/msproxyservice/service/ProxyEidasAttributeRegistry.java +++ b/modules/eidas_proxy-sevice/src/main/java/at/asitplus/eidas/specific/modules/msproxyservice/service/ProxyEidasAttributeRegistry.java @@ -111,7 +111,7 @@ public class ProxyEidasAttributeRegistry { } /** - * Get eIDAS related IDA attribute. + * Get eIDAS related IDA attribute for a specific mode-operation. * * @param eidasAttributeName Name of the eIDAS attribute. * @param withMandates true if mandates are supported, otherwise false @@ -127,6 +127,22 @@ public class ProxyEidasAttributeRegistry { } + /** + * Get eIDAS related custom attribute-handler. + * + * @param eidasAttributeName Name of the eIDAS attribute. + * @return full classname of the handler implementation if available + */ + public Optional mapEidasAttributeToAttributeHandler(String eidasAttributeName) { + return attributeConfiguration.stream() + .filter(el -> el.getEidasAttributeName().equals(eidasAttributeName)) + .filter(el -> StringUtils.isNotEmpty(el.getSpecificAttributeHandlerClass())) + .findFirst() + .map(el -> el.getSpecificAttributeHandlerClass()); + + } + + @PostConstruct private void initialize() throws EaafConfigurationException { final String attrConfPath = basicConfig.getBasicConfiguration( -- cgit v1.2.3 From 72e8da84f3ff8cd36d6f62d0d0690ad3f9a19efd Mon Sep 17 00:00:00 2001 From: Thomas <> Date: Tue, 16 Aug 2022 11:21:04 +0200 Subject: chore(core): check if custom attribute-handler implementations are available on start-up --- .../msproxyservice/handler/EJusticePersonRoleHandler.java | 13 +++++++++++++ .../msproxyservice/handler/IEidasAttributeHandler.java | 13 +++++++++++++ .../service/ProxyEidasAttributeRegistry.java | 15 +++++++++++++++ .../main/resources/spring/eidas_proxy-service.beans.xml | 3 +++ 4 files changed, 44 insertions(+) create mode 100644 modules/eidas_proxy-sevice/src/main/java/at/asitplus/eidas/specific/modules/msproxyservice/handler/EJusticePersonRoleHandler.java create mode 100644 modules/eidas_proxy-sevice/src/main/java/at/asitplus/eidas/specific/modules/msproxyservice/handler/IEidasAttributeHandler.java (limited to 'modules/eidas_proxy-sevice/src/main') 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 new file mode 100644 index 00000000..f42a7172 --- /dev/null +++ b/modules/eidas_proxy-sevice/src/main/java/at/asitplus/eidas/specific/modules/msproxyservice/handler/EJusticePersonRoleHandler.java @@ -0,0 +1,13 @@ +package at.asitplus.eidas.specific.modules.msproxyservice.handler; + +/** + * Attribute handling to integrate BORIS attributes without full IDA support for sector-specific attributes. + * + *

This attribute-handler maps a specific mandate-profile to an eIDAS attribute.

+ * + * @author tlenz + * + */ +public class EJusticePersonRoleHandler implements IEidasAttributeHandler { + +} 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 new file mode 100644 index 00000000..153cf262 --- /dev/null +++ b/modules/eidas_proxy-sevice/src/main/java/at/asitplus/eidas/specific/modules/msproxyservice/handler/IEidasAttributeHandler.java @@ -0,0 +1,13 @@ +package at.asitplus.eidas.specific.modules.msproxyservice.handler; + +/** + * Handlers for attribute-processing that requires more features than a simple mapping. + * + * @author tlenz + * + */ +public interface IEidasAttributeHandler { + + + +} diff --git a/modules/eidas_proxy-sevice/src/main/java/at/asitplus/eidas/specific/modules/msproxyservice/service/ProxyEidasAttributeRegistry.java b/modules/eidas_proxy-sevice/src/main/java/at/asitplus/eidas/specific/modules/msproxyservice/service/ProxyEidasAttributeRegistry.java index a0c99019..747c808c 100644 --- a/modules/eidas_proxy-sevice/src/main/java/at/asitplus/eidas/specific/modules/msproxyservice/service/ProxyEidasAttributeRegistry.java +++ b/modules/eidas_proxy-sevice/src/main/java/at/asitplus/eidas/specific/modules/msproxyservice/service/ProxyEidasAttributeRegistry.java @@ -17,6 +17,7 @@ import javax.annotation.PostConstruct; import org.apache.commons.io.IOUtils; import org.apache.commons.lang3.StringUtils; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.ApplicationContext; import org.springframework.core.io.Resource; import org.springframework.core.io.ResourceLoader; @@ -27,6 +28,7 @@ import com.google.common.collect.Sets; import at.asitplus.eidas.specific.modules.core.eidas.service.EidasAttributeRegistry; import at.asitplus.eidas.specific.modules.msproxyservice.MsProxyServiceConstants; import at.asitplus.eidas.specific.modules.msproxyservice.dto.attributes.AttrMappingElement; +import at.asitplus.eidas.specific.modules.msproxyservice.handler.IEidasAttributeHandler; import at.gv.egiz.eaaf.core.api.idp.IConfiguration; import at.gv.egiz.eaaf.core.exceptions.EaafConfigurationException; import at.gv.egiz.eaaf.core.impl.utils.FileUtils; @@ -41,6 +43,7 @@ public class ProxyEidasAttributeRegistry { private static ObjectMapper mapper = new ObjectMapper(); + @Autowired ApplicationContext context; @Autowired IConfiguration basicConfig; @Autowired ResourceLoader resourceLoader; @@ -204,6 +207,18 @@ public class ProxyEidasAttributeRegistry { if (StringUtils.isNotEmpty(el.getEidasAttributeName())) { if (ATTR_CONFIG_ALL.equals(el.getEidasAttributeName()) || coreRegistry.getCoreAttributeRegistry().getByName(el.getEidasAttributeName()) != null) { + + // check if custom attribute-handler implementation is available + if (StringUtils.isNotEmpty(el.getSpecificAttributeHandlerClass())) { + try { + context.getBean(el.getSpecificAttributeHandlerClass(), IEidasAttributeHandler.class); + + } catch (Exception e) { + log.error("No custom attribute-handler implementation for: {}", el.getSpecificAttributeHandlerClass(), e); + return false; + } + } + return true; } else { diff --git a/modules/eidas_proxy-sevice/src/main/resources/spring/eidas_proxy-service.beans.xml b/modules/eidas_proxy-sevice/src/main/resources/spring/eidas_proxy-service.beans.xml index 78b7640a..38bd44da 100644 --- a/modules/eidas_proxy-sevice/src/main/resources/spring/eidas_proxy-service.beans.xml +++ b/modules/eidas_proxy-sevice/src/main/resources/spring/eidas_proxy-service.beans.xml @@ -35,4 +35,7 @@ + + \ No newline at end of file -- cgit v1.2.3 From 68c46a22406af910838b3ee6bbea5a4e9807ddaa Mon Sep 17 00:00:00 2001 From: Thomas <> Date: Tue, 16 Aug 2022 13:20:02 +0200 Subject: feat(eidas): add advanced SP config post-processing based on requested attributes --- .../handler/EJusticePersonRoleHandler.java | 53 ++++++++++++++++++++++ .../handler/IEidasAttributeHandler.java | 9 ++++ .../protocol/EidasProxyServiceController.java | 38 +++++++++++++++- 3 files changed, 99 insertions(+), 1 deletion(-) (limited to 'modules/eidas_proxy-sevice/src/main') 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 f42a7172..52a69944 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 @@ -1,5 +1,17 @@ package at.asitplus.eidas.specific.modules.msproxyservice.handler; +import javax.annotation.PostConstruct; + +import org.apache.commons.lang3.StringUtils; +import org.springframework.beans.factory.annotation.Autowired; + +import at.asitplus.eidas.specific.core.config.ServiceProviderConfiguration; +import at.gv.egiz.eaaf.core.api.data.ExtendedPvpAttributeDefinitions.SpMandateModes; +import at.gv.egiz.eaaf.core.api.idp.IConfiguration; +import at.gv.egiz.eaaf.core.exceptions.EaafConfigurationException; +import at.gv.egiz.eaaf.core.impl.utils.KeyValueUtils; +import lombok.extern.slf4j.Slf4j; + /** * Attribute handling to integrate BORIS attributes without full IDA support for sector-specific attributes. * @@ -8,6 +20,47 @@ package at.asitplus.eidas.specific.modules.msproxyservice.handler; * @author tlenz * */ +@Slf4j public class EJusticePersonRoleHandler implements IEidasAttributeHandler { + public static final String CONFIG_PROP_IDA_MANDATE_PROFILE = "advanced.atributes.ejusticerole.mandate.profiles"; + public static final String CONFIG_PROP_IDA_MANDATE_MODE = "advanced.atributes.ejusticerole.mandate.mode"; + + @Autowired IConfiguration config; + + private SpMandateModes mandateMode; + private String mandateProfiles; + + @Override + public void performSpConfigPostprocessing(ServiceProviderConfiguration spConfig) { + spConfig.setMandateMode(mandateMode); + spConfig.setMandateProfiles(KeyValueUtils.getListOfCsvValues(mandateProfiles)); + log.info("Enforcing mandate-mode: {} with profile: {}", mandateMode, mandateProfiles); + + } + + + + @PostConstruct + private void initialize() throws EaafConfigurationException { + mandateMode = SpMandateModes.fromString(loadConfigValue(CONFIG_PROP_IDA_MANDATE_MODE)); + mandateProfiles = loadConfigValue(CONFIG_PROP_IDA_MANDATE_PROFILE); + + log.info("Initialize: {} with mandate-profile: {} mandate-mode: {}", + EJusticePersonRoleHandler.class.getSimpleName(), mandateProfiles, mandateMode); + + } + + private String loadConfigValue(String configProp) throws EaafConfigurationException { + String value = config.getBasicConfiguration(configProp); + if (StringUtils.isEmpty(value)) { + throw new EaafConfigurationException("internal.configuration.00", + new Object[]{configProp}); + + } + + return value; + + } + } 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 153cf262..02e091ef 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 @@ -1,5 +1,7 @@ package at.asitplus.eidas.specific.modules.msproxyservice.handler; +import at.asitplus.eidas.specific.core.config.ServiceProviderConfiguration; + /** * Handlers for attribute-processing that requires more features than a simple mapping. * @@ -8,6 +10,13 @@ package at.asitplus.eidas.specific.modules.msproxyservice.handler; */ public interface IEidasAttributeHandler { + /** + * Perform attribute-releated post-processing of internal Service-Provider configuration. + * + * @param spConfig SP configuration that was build from incoming eIDAS Authn. request. + */ + void performSpConfigPostprocessing(ServiceProviderConfiguration spConfig); + } 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 32be0e22..d0e3d1ba 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 @@ -8,6 +8,7 @@ import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Objects; +import java.util.Set; import java.util.UUID; import java.util.stream.Collectors; @@ -33,6 +34,7 @@ import at.asitplus.eidas.specific.core.config.ServiceProviderConfiguration; import at.asitplus.eidas.specific.modules.core.eidas.EidasConstants; import at.asitplus.eidas.specific.modules.msproxyservice.MsProxyServiceConstants; import at.asitplus.eidas.specific.modules.msproxyservice.exception.EidasProxyServiceException; +import at.asitplus.eidas.specific.modules.msproxyservice.handler.IEidasAttributeHandler; import at.asitplus.eidas.specific.modules.msproxyservice.service.ProxyEidasAttributeRegistry; import at.asitplus.eidas.specific.modules.msproxyservice.utils.EidasProxyServiceUtils; import at.gv.egiz.components.eventlog.api.EventConstants; @@ -333,7 +335,10 @@ public class EidasProxyServiceController extends AbstractController implements I // map eIDAS attributes to national attributes buildNationalRequestedAttributes(spConfig, eidasRequest); - + + // execute custom attribute-handler + advancedAttributeHandler(spConfig, eidasRequest); + return spConfig; } catch (final EidasProxyServiceException e) { @@ -344,6 +349,37 @@ public class EidasProxyServiceController extends AbstractController implements I } } + + private void advancedAttributeHandler(ServiceProviderConfiguration spConfig, ILightRequest eidasRequest) { + 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-handling ... ", requiredHandlers.size()); + requiredHandlers.forEach(el -> executeAttributeHandler(el, spConfig)); + + } else { + log.debug("No advanced eIDAS attribute-handling required."); + + } + } + + private void executeAttributeHandler(String handlerClass, ServiceProviderConfiguration spConfig) { + try { + IEidasAttributeHandler handler = applicationContext.getBean(handlerClass, IEidasAttributeHandler.class); + + log.trace("Perfom SP config post-processing by using: {}", handler.getClass().getName()); + handler.performSpConfigPostprocessing(spConfig); + + } catch (Exception e) { + log.error("No custom attribute-handler implementation for: {}. Operation can NOT be performed", handlerClass, e); + + } + } private void buildNationalRequestedAttributes( ServiceProviderConfiguration spConfig, ILightRequest eidasRequest) { -- cgit v1.2.3 From d5cb2ae3d5bf3f04646cc23d7d59cd10822349c6 Mon Sep 17 00:00:00 2001 From: Thomas <> Date: Tue, 16 Aug 2022 15:09:07 +0200 Subject: feat(eidas): generate advanced attributes in response-processing too --- .../handler/EJusticePersonRoleHandler.java | 78 +++++++++++++++++++--- .../handler/IEidasAttributeHandler.java | 16 ++++- .../protocol/ProxyServiceAuthenticationAction.java | 28 ++++++-- .../service/ProxyEidasAttributeRegistry.java | 1 + 4 files changed, 109 insertions(+), 14 deletions(-) (limited to 'modules/eidas_proxy-sevice/src/main') 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 52a69944..ec161b1a 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 @@ -1,5 +1,10 @@ package at.asitplus.eidas.specific.modules.msproxyservice.handler; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.stream.Collectors; + import javax.annotation.PostConstruct; import org.apache.commons.lang3.StringUtils; @@ -7,9 +12,12 @@ import org.springframework.beans.factory.annotation.Autowired; import at.asitplus.eidas.specific.core.config.ServiceProviderConfiguration; import at.gv.egiz.eaaf.core.api.data.ExtendedPvpAttributeDefinitions.SpMandateModes; -import at.gv.egiz.eaaf.core.api.idp.IConfiguration; +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 lombok.NonNull; import lombok.extern.slf4j.Slf4j; /** @@ -25,32 +33,84 @@ public class EJusticePersonRoleHandler implements IEidasAttributeHandler { public static final String CONFIG_PROP_IDA_MANDATE_PROFILE = "advanced.atributes.ejusticerole.mandate.profiles"; public static final String CONFIG_PROP_IDA_MANDATE_MODE = "advanced.atributes.ejusticerole.mandate.mode"; - - @Autowired IConfiguration config; + public static final String CONFIG_PROP_RESULT_PREFIX = "advanced.atributes.ejusticerole.value."; + public static final String CONFIG_PROP_RESULT_VALUE_DELIMITER = "="; + + + @Autowired IExtendedConfiguration config; private SpMandateModes mandateMode; - private String mandateProfiles; + private List mandateProfiles; + private Map resultMapper; @Override public void performSpConfigPostprocessing(ServiceProviderConfiguration spConfig) { spConfig.setMandateMode(mandateMode); - spConfig.setMandateProfiles(KeyValueUtils.getListOfCsvValues(mandateProfiles)); + spConfig.setMandateProfiles(mandateProfiles); log.info("Enforcing mandate-mode: {} with profile: {}", mandateMode, mandateProfiles); } + @Override + public String buildAttributeValue(@NonNull IEidAuthData eidAuthData) { + final String mandateType = eidAuthData.getGenericData( + PvpAttributeDefinitions.MANDATE_TYPE_NAME, String.class); + if (StringUtils.isNotEmpty(mandateType)) { + String attrValue = resultMapper.get(mandateType); + if (StringUtils.isNotEmpty(attrValue)) { + log.debug("Mapping mandate-type: {} to EJusticePersonRole: {}", mandateType, attrValue); + return attrValue; + + } else { + log.info("Ignore mandate-type: {}, because it is not mapped to a EJusticePersonRole", mandateType); + + } + + } else { + log.warn("Can not build: EJusticePersonRole, because IDA response contains no attribute: ", + PvpAttributeDefinitions.MANDATE_TYPE_NAME); + + } + + + return null; + + } @PostConstruct private void initialize() throws EaafConfigurationException { mandateMode = SpMandateModes.fromString(loadConfigValue(CONFIG_PROP_IDA_MANDATE_MODE)); - mandateProfiles = loadConfigValue(CONFIG_PROP_IDA_MANDATE_PROFILE); - - log.info("Initialize: {} with mandate-profile: {} mandate-mode: {}", + mandateProfiles = KeyValueUtils.getListOfCsvValues(loadConfigValue(CONFIG_PROP_IDA_MANDATE_PROFILE)); + resultMapper = config.getBasicConfigurationWithPrefix(CONFIG_PROP_RESULT_PREFIX).values().stream() + .filter(el -> el.contains(CONFIG_PROP_RESULT_VALUE_DELIMITER)) + .collect(Collectors.toMap(x -> split(x, 0), x -> split(x, 1))); + + // validate requested profiles to result map + Optional missingConfig = mandateProfiles.stream() + .filter(el -> !resultMapper.containsKey(el)) + .findFirst(); + if (missingConfig.isPresent()) { + log.error("Missing mandate-profile: {} in result mapping", missingConfig.get()); + throw new EaafConfigurationException("internal.configuration.00", + new Object[]{CONFIG_PROP_RESULT_PREFIX}); + + } + + log.info("Initialize: {} with mandate-profile: {} mandate-mode: {} and result-map:", EJusticePersonRoleHandler.class.getSimpleName(), mandateProfiles, mandateMode); + resultMapper.entrySet().stream().forEach(el -> + log.info("Profile: {} --> Attribute-Value: {}", el.getKey(), el.getValue())); + } + private String split(String value, int i) { + return value.split(CONFIG_PROP_RESULT_VALUE_DELIMITER, 2)[i]; + + } + + private String loadConfigValue(String configProp) throws EaafConfigurationException { String value = config.getBasicConfiguration(configProp); if (StringUtils.isEmpty(value)) { @@ -62,5 +122,5 @@ public class EJusticePersonRoleHandler implements IEidasAttributeHandler { return value; } - + } 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 02e091ef..5a9c8d8c 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 @@ -1,6 +1,10 @@ package at.asitplus.eidas.specific.modules.msproxyservice.handler; +import javax.annotation.Nullable; + import at.asitplus.eidas.specific.core.config.ServiceProviderConfiguration; +import at.gv.egiz.eaaf.core.api.idp.IEidAuthData; +import lombok.NonNull; /** * Handlers for attribute-processing that requires more features than a simple mapping. @@ -15,7 +19,17 @@ public interface IEidasAttributeHandler { * * @param spConfig SP configuration that was build from incoming eIDAS Authn. request. */ - void performSpConfigPostprocessing(ServiceProviderConfiguration spConfig); + void performSpConfigPostprocessing(@NonNull ServiceProviderConfiguration spConfig); + + + /** + * Build eIDAS attribute-value from authentication data. + * + * @param eidAuthData Authentication data for current process + * @return attribute-value if attribute is available, otherwise null + */ + @Nullable + String buildAttributeValue(@NonNull IEidAuthData eidAuthData); diff --git a/modules/eidas_proxy-sevice/src/main/java/at/asitplus/eidas/specific/modules/msproxyservice/protocol/ProxyServiceAuthenticationAction.java b/modules/eidas_proxy-sevice/src/main/java/at/asitplus/eidas/specific/modules/msproxyservice/protocol/ProxyServiceAuthenticationAction.java index 8348558c..f1cb8f0b 100644 --- a/modules/eidas_proxy-sevice/src/main/java/at/asitplus/eidas/specific/modules/msproxyservice/protocol/ProxyServiceAuthenticationAction.java +++ b/modules/eidas_proxy-sevice/src/main/java/at/asitplus/eidas/specific/modules/msproxyservice/protocol/ProxyServiceAuthenticationAction.java @@ -20,6 +20,7 @@ import at.asitplus.eidas.specific.core.gui.StaticGuiBuilderConfiguration; import at.asitplus.eidas.specific.modules.core.eidas.EidasConstants; import at.asitplus.eidas.specific.modules.msproxyservice.MsProxyServiceConstants; import at.asitplus.eidas.specific.modules.msproxyservice.exception.EidasProxyServiceException; +import at.asitplus.eidas.specific.modules.msproxyservice.handler.IEidasAttributeHandler; import at.asitplus.eidas.specific.modules.msproxyservice.service.ProxyEidasAttributeRegistry; import at.asitplus.eidas.specific.modules.msproxyservice.utils.EidasProxyServiceUtils; import at.gv.egiz.eaaf.core.api.IRequest; @@ -264,10 +265,29 @@ public class ProxyServiceAuthenticationAction implements IAction { } - } else { - log.warn("Can not build eIDAS attribute: {}, because there is not corresponding IDA attribute defined", - eidasAttrName); - + } else { + Optional advancedAttributeHandler = attrRegistry.mapEidasAttributeToAttributeHandler(eidasAttrName); + if (advancedAttributeHandler.isPresent()) { + final String idaAttrValue = context.getBean(advancedAttributeHandler.get(), IEidasAttributeHandler.class) + .buildAttributeValue(eidAuthData); + if (StringUtils.isNotEmpty(idaAttrValue)) { + log.debug("Build eIDAS attribute: {} by advanced attribute-handler: {}", + eidasAttrName, advancedAttributeHandler.get()); + attributeMap.put( + attrRegistry.getCoreRegistry().getCoreAttributeRegistry().getByName(eidasAttrName), + idaAttrValue); + + } else { + log.info("Empty attribte-value returned by advanced attribute-handler, eIDAS attribute: {} will be ignored", + eidasAttrName); + + } + + } else { + log.warn("Can not build eIDAS attribute: {}, because there is not corresponding IDA attribute defined", + eidasAttrName); + + } } } diff --git a/modules/eidas_proxy-sevice/src/main/java/at/asitplus/eidas/specific/modules/msproxyservice/service/ProxyEidasAttributeRegistry.java b/modules/eidas_proxy-sevice/src/main/java/at/asitplus/eidas/specific/modules/msproxyservice/service/ProxyEidasAttributeRegistry.java index 747c808c..edb21722 100644 --- a/modules/eidas_proxy-sevice/src/main/java/at/asitplus/eidas/specific/modules/msproxyservice/service/ProxyEidasAttributeRegistry.java +++ b/modules/eidas_proxy-sevice/src/main/java/at/asitplus/eidas/specific/modules/msproxyservice/service/ProxyEidasAttributeRegistry.java @@ -124,6 +124,7 @@ public class ProxyEidasAttributeRegistry { String eidasAttributeName, boolean withMandates) { return attributeConfiguration.stream() .filter(el -> el.getEidasAttributeName().equals(eidasAttributeName)) + .filter(el -> el.getIdaAttribute() != null) .findFirst() .map(el -> withMandates ? el.getIdaAttribute().getWithMandates() : el.getIdaAttribute().getBasic()) .filter(el -> StringUtils.isNotEmpty(el)); -- cgit v1.2.3 From ee60dcbde9210e6ecf417af9fd7e4f13e8d95bbd Mon Sep 17 00:00:00 2001 From: Thomas <> Date: Tue, 16 Aug 2022 15:46:31 +0200 Subject: style(eidas): fix typo in configuration properties --- .../modules/msproxyservice/handler/EJusticePersonRoleHandler.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'modules/eidas_proxy-sevice/src/main') 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 ec161b1a..87a033eb 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 @@ -31,9 +31,9 @@ import lombok.extern.slf4j.Slf4j; @Slf4j public class EJusticePersonRoleHandler implements IEidasAttributeHandler { - public static final String CONFIG_PROP_IDA_MANDATE_PROFILE = "advanced.atributes.ejusticerole.mandate.profiles"; - public static final String CONFIG_PROP_IDA_MANDATE_MODE = "advanced.atributes.ejusticerole.mandate.mode"; - public static final String CONFIG_PROP_RESULT_PREFIX = "advanced.atributes.ejusticerole.value."; + 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_RESULT_PREFIX = "advanced.attributes.ejusticerole.value"; public static final String CONFIG_PROP_RESULT_VALUE_DELIMITER = "="; -- cgit v1.2.3 From 920d33465e5ab1a71d81cc280e41de10cd8b5247 Mon Sep 17 00:00:00 2001 From: Thomas <> Date: Wed, 17 Aug 2022 09:53:46 +0200 Subject: feat(eidas): extend EJusticePersonRoleHandler to include additional requested attributes --- .../handler/EJusticePersonRoleHandler.java | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) (limited to 'modules/eidas_proxy-sevice/src/main') 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 87a033eb..6a5e4967 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 @@ -33,6 +33,9 @@ public class EJusticePersonRoleHandler implements IEidasAttributeHandler { 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 = + "advanced.attributes.ejusticerole.additional.ida.attributes"; + public static final String CONFIG_PROP_RESULT_PREFIX = "advanced.attributes.ejusticerole.value"; public static final String CONFIG_PROP_RESULT_VALUE_DELIMITER = "="; @@ -41,14 +44,21 @@ public class EJusticePersonRoleHandler implements IEidasAttributeHandler { private SpMandateModes mandateMode; private List mandateProfiles; + private List additionalReqAttributes; private Map resultMapper; @Override public void performSpConfigPostprocessing(ServiceProviderConfiguration spConfig) { spConfig.setMandateMode(mandateMode); - spConfig.setMandateProfiles(mandateProfiles); + spConfig.setMandateProfiles(mandateProfiles); log.info("Enforcing mandate-mode: {} with profile: {}", mandateMode, mandateProfiles); + if (!additionalReqAttributes.isEmpty()) { + spConfig.getRequestedAttributes().addAll(additionalReqAttributes); + log.info("Add additional requested attributes: {}", additionalReqAttributes); + + } + } @Override @@ -80,8 +90,10 @@ public class EJusticePersonRoleHandler implements IEidasAttributeHandler { @PostConstruct private void initialize() throws EaafConfigurationException { - mandateMode = SpMandateModes.fromString(loadConfigValue(CONFIG_PROP_IDA_MANDATE_MODE)); - mandateProfiles = KeyValueUtils.getListOfCsvValues(loadConfigValue(CONFIG_PROP_IDA_MANDATE_PROFILE)); + mandateMode = SpMandateModes.fromString(loadConfigValue(CONFIG_PROP_IDA_MANDATE_MODE, true)); + mandateProfiles = KeyValueUtils.getListOfCsvValues(loadConfigValue(CONFIG_PROP_IDA_MANDATE_PROFILE, true)); + additionalReqAttributes = KeyValueUtils.getListOfCsvValues( + loadConfigValue(CONFIG_PROP_IDA_ADDITIONAL_ATTRIBUTES, false)); resultMapper = config.getBasicConfigurationWithPrefix(CONFIG_PROP_RESULT_PREFIX).values().stream() .filter(el -> el.contains(CONFIG_PROP_RESULT_VALUE_DELIMITER)) .collect(Collectors.toMap(x -> split(x, 0), x -> split(x, 1))); @@ -111,9 +123,9 @@ public class EJusticePersonRoleHandler implements IEidasAttributeHandler { } - private String loadConfigValue(String configProp) throws EaafConfigurationException { + private String loadConfigValue(String configProp, boolean isRequired) throws EaafConfigurationException { String value = config.getBasicConfiguration(configProp); - if (StringUtils.isEmpty(value)) { + if (StringUtils.isEmpty(value) && isRequired) { throw new EaafConfigurationException("internal.configuration.00", new Object[]{configProp}); -- cgit v1.2.3