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