From e443168b481bb88fecbad73084147e7e8c882908 Mon Sep 17 00:00:00 2001 From: Thomas Lenz Date: Tue, 10 Dec 2019 07:39:27 +0100 Subject: refactoring to new EGIZ code requirements --- .../verification/AuthnRequestValidator.java | 446 +++++++++++---------- 1 file changed, 231 insertions(+), 215 deletions(-) (limited to 'connector/src/main/java/at/asitplus/eidas/specific/connector/verification/AuthnRequestValidator.java') diff --git a/connector/src/main/java/at/asitplus/eidas/specific/connector/verification/AuthnRequestValidator.java b/connector/src/main/java/at/asitplus/eidas/specific/connector/verification/AuthnRequestValidator.java index c49c0b28..0230521c 100644 --- a/connector/src/main/java/at/asitplus/eidas/specific/connector/verification/AuthnRequestValidator.java +++ b/connector/src/main/java/at/asitplus/eidas/specific/connector/verification/AuthnRequestValidator.java @@ -1,6 +1,6 @@ -/******************************************************************************* +/* * Copyright 2018 A-SIT Plus GmbH - * AT-specific eIDAS Connector has been developed in a cooperation between EGIZ, + * 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 @@ -19,7 +19,8 @@ * 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.connector.verification; import java.util.ArrayList; @@ -31,8 +32,8 @@ import org.apache.commons.lang3.StringUtils; import org.opensaml.saml2.core.AuthnContextClassRef; import org.opensaml.saml2.core.AuthnContextComparisonTypeEnumeration; import org.opensaml.saml2.core.AuthnRequest; -import org.opensaml.saml2.core.NameID; import org.opensaml.saml2.core.NameIDPolicy; +import org.opensaml.saml2.core.NameIDType; import org.opensaml.saml2.core.RequestedAuthnContext; import org.opensaml.saml2.core.Scoping; import org.opensaml.saml2.metadata.SPSSODescriptor; @@ -41,7 +42,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; -import at.asitplus.eidas.specific.connector.MSeIDASNodeConstants; +import at.asitplus.eidas.specific.connector.MsEidasNodeConstants; import at.asitplus.eidas.specific.connector.config.ServiceProviderConfiguration; import at.gv.egiz.eaaf.core.api.IRequest; import at.gv.egiz.eaaf.core.api.data.EaafConstants; @@ -58,215 +59,230 @@ import eu.eidas.auth.commons.protocol.eidas.LevelOfAssurance; public class AuthnRequestValidator implements IAuthnRequestValidator { - private static final Logger log = LoggerFactory.getLogger(AuthnRequestValidator.class); - - @Autowired(required=true) private IConfiguration basicConfig; - - @Override - public void validate(HttpServletRequest httpReq, IRequest pendingReq, AuthnRequest authnReq, - SPSSODescriptor spSSODescriptor) throws AuthnRequestValidatorException { - try { - //validate NameIDPolicy - NameIDPolicy nameIDPolicy = authnReq.getNameIDPolicy(); - if (nameIDPolicy != null) { - String nameIDFormat = nameIDPolicy.getFormat(); - if (nameIDFormat != null) { - if ( !(NameID.TRANSIENT.equals(nameIDFormat) || - NameID.PERSISTENT.equals(nameIDFormat)) ) { - - throw new NameIdFormatNotSupportedException(nameIDFormat); - - } - - } else - log.trace("Find NameIDPolicy, but NameIDFormat is 'null'"); - } else - log.trace("AuthnRequest includes no 'NameIDPolicy'"); - - - //post-process RequesterId - String spEntityId = extractScopeRequsterId(authnReq); - if (StringUtils.isEmpty(spEntityId)) { - log.info("NO service-provider entityID in Authn. request. Stop authn. process ... "); - throw new AuthnRequestValidatorException("pvp2.22", - new Object[] {"NO relaying-party entityID in Authn. request"}, pendingReq); - - } else - pendingReq.setRawDataToTransaction(MSeIDASNodeConstants.DATA_REQUESTERID, spEntityId); - - - //post-process ProviderName - String providerName = authnReq.getProviderName(); - if (StringUtils.isEmpty(providerName)) - log.info("Authn. request contains NO SP friendlyName"); - else - pendingReq.setRawDataToTransaction(MSeIDASNodeConstants.DATA_PROVIDERNAME, spEntityId); - - //post-process requested LoA - List reqLoA = extractLoA(authnReq); - - LevelOfAssurance minimumLoAFromConfig = LevelOfAssurance.fromString(basicConfig.getBasicConfiguration( - MSeIDASNodeConstants.PROP_EIDAS_REQUEST_LOA_MINIMUM_LEVEL, - EaafConstants.EIDAS_LOA_HIGH)); - if (minimumLoAFromConfig == null) { - log.warn("Can not load minimum LoA from configuration. Use LoA: {} as default", EaafConstants.EIDAS_LOA_HIGH); - minimumLoAFromConfig = LevelOfAssurance.HIGH; - - } - - log.trace("Validate requested LoA to connector configuration minimum LoA: {} ...", minimumLoAFromConfig); - List allowedLoA = new ArrayList<>(); - for (String loa : reqLoA) { - try { - LevelOfAssurance intLoa = LevelOfAssurance.fromString(loa); - String selectedLoA = EaafConstants.EIDAS_LOA_HIGH; - if (intLoa != null && - intLoa.numericValue() >= minimumLoAFromConfig.numericValue()) { - log.info("Client: {} requested LoA: {} will be upgraded to: {}", - pendingReq.getServiceProviderConfiguration().getUniqueIdentifier(), - loa, - minimumLoAFromConfig); - selectedLoA = intLoa.getValue(); - - } - - if (!allowedLoA.contains(selectedLoA)) { - log.debug("Allow LoA: {} for Client: {}", - selectedLoA, - pendingReq.getServiceProviderConfiguration().getUniqueIdentifier()); - allowedLoA.add(selectedLoA); - - } - - } catch (IllegalArgumentException e) { - log.warn("LoA: {} is currently NOT supported and it will be ignored.", loa); - - } - - } - - pendingReq.getServiceProviderConfiguration(ServiceProviderConfiguration.class).setRequiredLoA(allowedLoA); - - //post-process requested LoA comparison-level - String reqLoAComperison = extractComparisonLevel(authnReq); - pendingReq.getServiceProviderConfiguration(ServiceProviderConfiguration.class).setLoAMachtingMode(reqLoAComperison); - - //validate and process requested attributes - boolean sectorDetected = false; - List requestedAttributes = authnReq.getExtensions().getUnknownXMLObjects(); - for (XMLObject reqAttrObj : requestedAttributes) { - if (reqAttrObj instanceof EaafRequestedAttributes) { - EaafRequestedAttributes reqAttr = (EaafRequestedAttributes)reqAttrObj; - if (reqAttr.getAttributes() != null && reqAttr.getAttributes().size() != 0 ) { - for (EaafRequestedAttribute el : reqAttr.getAttributes()) { - log.trace("Processing req. attribute '" + el.getName() + "' ... "); - if (el.getName().equals(PvpAttributeDefinitions.EID_SECTOR_FOR_IDENTIFIER_NAME)) { - if (el.getAttributeValues() != null && el.getAttributeValues().size() == 1) { - String sectorId = el.getAttributeValues().get(0).getDOM().getTextContent(); - ServiceProviderConfiguration spConfig = pendingReq.getServiceProviderConfiguration(ServiceProviderConfiguration.class); - - try { - spConfig.setbPKTargetIdentifier(sectorId); - sectorDetected = true; - - } catch (EaafException e) { - log.info("Requested sector: " + sectorId + " DOES NOT match to allowed sectors for SP: " + spConfig.getUniqueIdentifier()); - } - - } else - log.info("Req. attribute '" + el.getName() + "' contains NO or MORE THEN ONE attribute-values. Ignore full req. attribute"); - - } else - log.debug("Ignore req. attribute: " + el.getName()); - - } - - } else - log.debug("No requested Attributes in Authn. Request"); - - } else - log.info("Ignore unknown requested attribute: " + reqAttrObj.getElementQName().toString()); - - } - - if (!sectorDetected) { - log.info("Authn.Req validation FAILED. Reason: Contains NO or NO VALID target-sector information."); - throw new AuthnRequestValidatorException("pvp2.22", new Object[] {"NO or NO VALID target-sector information"}); - - } - - } catch (EaafStorageException e) { - log.info("Can NOT store Authn. Req. data into pendingRequest." , e); - throw new AuthnRequestValidatorException("internal.02", null, e); - - } - - } - - private String extractComparisonLevel(AuthnRequest authnReq) { - if (authnReq.getRequestedAuthnContext() != null) { - RequestedAuthnContext authContext = authnReq.getRequestedAuthnContext(); - return authContext.getComparison().toString(); - - } - - return null; - } - - private List extractLoA(AuthnRequest authnReq) throws AuthnRequestValidatorException { - List result = new ArrayList(); - if (authnReq.getRequestedAuthnContext() != null) { - RequestedAuthnContext authContext = authnReq.getRequestedAuthnContext(); - if (authContext.getComparison().equals(AuthnContextComparisonTypeEnumeration.MINIMUM)) { - if (authContext.getAuthnContextClassRefs().isEmpty()) { - log.debug("Authn. Req. contains no requested LoA"); - - } else if (authContext.getAuthnContextClassRefs().size() > 1) { - log.info("Authn. Req. contains MORE THAN ONE requested LoA, but " - + AuthnContextComparisonTypeEnumeration.MINIMUM + " allows only one" ); - throw new AuthnRequestValidatorException("pvp2.22", - new Object[] {"Authn. Req. contains MORE THAN ONE requested LoA, but " - + AuthnContextComparisonTypeEnumeration.MINIMUM + " allows only one"}); - - } else - result.add(authContext.getAuthnContextClassRefs().get(0).getAuthnContextClassRef()); - - } else if (authContext.getComparison().equals(AuthnContextComparisonTypeEnumeration.EXACT)) { - for (AuthnContextClassRef el : authContext.getAuthnContextClassRefs()) - result.add(el.getAuthnContextClassRef()); - - } else { - log.info("Currently only '" + AuthnContextComparisonTypeEnumeration.MINIMUM + "' and '" - + AuthnContextComparisonTypeEnumeration.EXACT + "' are supported"); - throw new AuthnRequestValidatorException("pvp2.22", - new Object[] {"Currently only '" + AuthnContextComparisonTypeEnumeration.MINIMUM + "' and '" - + AuthnContextComparisonTypeEnumeration.EXACT + "' are supported"}); - - } - - } - - return result; - } - - private String extractScopeRequsterId(AuthnRequest authnReq) { - if (authnReq.getScoping() != null) { - Scoping scoping = authnReq.getScoping(); - if (scoping.getRequesterIDs() != null && - scoping.getRequesterIDs().size() > 0) { - if (scoping.getRequesterIDs().size() == 1) - return scoping.getRequesterIDs().get(0).getRequesterID(); - - else { - log.info("Authn. request contains more than on RequesterIDs! Only use first one"); - return scoping.getRequesterIDs().get(0).getRequesterID(); - - } - } - } - - return null; - } - + private static final Logger log = LoggerFactory.getLogger(AuthnRequestValidator.class); + + @Autowired(required = true) + private IConfiguration basicConfig; + + @Override + public void validate(HttpServletRequest httpReq, IRequest pendingReq, AuthnRequest authnReq, + SPSSODescriptor spSsoDescriptor) throws AuthnRequestValidatorException { + try { + // validate NameIDPolicy + final NameIDPolicy nameIdPolicy = authnReq.getNameIDPolicy(); + if (nameIdPolicy != null) { + final String nameIdFormat = nameIdPolicy.getFormat(); + if (nameIdFormat != null) { + if (!(NameIDType.TRANSIENT.equals(nameIdFormat) + || NameIDType.PERSISTENT.equals(nameIdFormat))) { + + throw new NameIdFormatNotSupportedException(nameIdFormat); + + } + + } else { + log.trace("Find NameIDPolicy, but NameIDFormat is 'null'"); + } + } else { + log.trace("AuthnRequest includes no 'NameIDPolicy'"); + } + + // post-process RequesterId + final String spEntityId = extractScopeRequsterId(authnReq); + if (StringUtils.isEmpty(spEntityId)) { + log.info("NO service-provider entityID in Authn. request. Stop authn. process ... "); + throw new AuthnRequestValidatorException("pvp2.22", + new Object[] { "NO relaying-party entityID in Authn. request" }, pendingReq); + + } else { + pendingReq.setRawDataToTransaction(MsEidasNodeConstants.DATA_REQUESTERID, spEntityId); + } + + // post-process ProviderName + final String providerName = authnReq.getProviderName(); + if (StringUtils.isEmpty(providerName)) { + log.info("Authn. request contains NO SP friendlyName"); + } else { + pendingReq.setRawDataToTransaction(MsEidasNodeConstants.DATA_PROVIDERNAME, spEntityId); + } + + // post-process requested LoA + final List reqLoA = extractLoA(authnReq); + + LevelOfAssurance minimumLoAFromConfig = LevelOfAssurance.fromString(basicConfig.getBasicConfiguration( + MsEidasNodeConstants.PROP_EIDAS_REQUEST_LOA_MINIMUM_LEVEL, + EaafConstants.EIDAS_LOA_HIGH)); + if (minimumLoAFromConfig == null) { + log.warn("Can not load minimum LoA from configuration. Use LoA: {} as default", + EaafConstants.EIDAS_LOA_HIGH); + minimumLoAFromConfig = LevelOfAssurance.HIGH; + + } + + log.trace("Validate requested LoA to connector configuration minimum LoA: {} ...", + minimumLoAFromConfig); + final List allowedLoA = new ArrayList<>(); + for (final String loa : reqLoA) { + try { + final LevelOfAssurance intLoa = LevelOfAssurance.fromString(loa); + String selectedLoA = EaafConstants.EIDAS_LOA_HIGH; + if (intLoa != null + && intLoa.numericValue() >= minimumLoAFromConfig.numericValue()) { + log.info("Client: {} requested LoA: {} will be upgraded to: {}", + pendingReq.getServiceProviderConfiguration().getUniqueIdentifier(), + loa, + minimumLoAFromConfig); + selectedLoA = intLoa.getValue(); + + } + + if (!allowedLoA.contains(selectedLoA)) { + log.debug("Allow LoA: {} for Client: {}", + selectedLoA, + pendingReq.getServiceProviderConfiguration().getUniqueIdentifier()); + allowedLoA.add(selectedLoA); + + } + + } catch (final IllegalArgumentException e) { + log.warn("LoA: {} is currently NOT supported and it will be ignored.", loa); + + } + + } + + pendingReq.getServiceProviderConfiguration(ServiceProviderConfiguration.class).setRequiredLoA( + allowedLoA); + + // post-process requested LoA comparison-level + final String reqLoAComperison = extractComparisonLevel(authnReq); + pendingReq.getServiceProviderConfiguration(ServiceProviderConfiguration.class).setLoAMachtingMode( + reqLoAComperison); + + // validate and process requested attributes + boolean sectorDetected = false; + final List requestedAttributes = authnReq.getExtensions().getUnknownXMLObjects(); + for (final XMLObject reqAttrObj : requestedAttributes) { + if (reqAttrObj instanceof EaafRequestedAttributes) { + final EaafRequestedAttributes reqAttr = (EaafRequestedAttributes) reqAttrObj; + if (reqAttr.getAttributes() != null && reqAttr.getAttributes().size() != 0) { + for (final EaafRequestedAttribute el : reqAttr.getAttributes()) { + log.trace("Processing req. attribute '" + el.getName() + "' ... "); + if (el.getName().equals(PvpAttributeDefinitions.EID_SECTOR_FOR_IDENTIFIER_NAME)) { + if (el.getAttributeValues() != null && el.getAttributeValues().size() == 1) { + final String sectorId = el.getAttributeValues().get(0).getDOM().getTextContent(); + final ServiceProviderConfiguration spConfig = pendingReq.getServiceProviderConfiguration( + ServiceProviderConfiguration.class); + + try { + spConfig.setBpkTargetIdentifier(sectorId); + sectorDetected = true; + + } catch (final EaafException e) { + log.info("Requested sector: " + sectorId + " DOES NOT match to allowed sectors for SP: " + + spConfig.getUniqueIdentifier()); + } + + } else { + log.info("Req. attribute '" + el.getName() + + "' contains NO or MORE THEN ONE attribute-values. Ignore full req. attribute"); + } + + } else { + log.debug("Ignore req. attribute: " + el.getName()); + } + + } + + } else { + log.debug("No requested Attributes in Authn. Request"); + } + + } else { + log.info("Ignore unknown requested attribute: " + reqAttrObj.getElementQName().toString()); + } + + } + + if (!sectorDetected) { + log.info("Authn.Req validation FAILED. Reason: Contains NO or NO VALID target-sector information."); + throw new AuthnRequestValidatorException("pvp2.22", new Object[] { + "NO or NO VALID target-sector information" }); + + } + + } catch (final EaafStorageException e) { + log.info("Can NOT store Authn. Req. data into pendingRequest.", e); + throw new AuthnRequestValidatorException("internal.02", null, e); + + } + + } + + private String extractComparisonLevel(AuthnRequest authnReq) { + if (authnReq.getRequestedAuthnContext() != null) { + final RequestedAuthnContext authContext = authnReq.getRequestedAuthnContext(); + return authContext.getComparison().toString(); + + } + + return null; + } + + private List extractLoA(AuthnRequest authnReq) throws AuthnRequestValidatorException { + final List result = new ArrayList<>(); + if (authnReq.getRequestedAuthnContext() != null) { + final RequestedAuthnContext authContext = authnReq.getRequestedAuthnContext(); + if (authContext.getComparison().equals(AuthnContextComparisonTypeEnumeration.MINIMUM)) { + if (authContext.getAuthnContextClassRefs().isEmpty()) { + log.debug("Authn. Req. contains no requested LoA"); + + } else if (authContext.getAuthnContextClassRefs().size() > 1) { + log.info("Authn. Req. contains MORE THAN ONE requested LoA, but " + + AuthnContextComparisonTypeEnumeration.MINIMUM + " allows only one"); + throw new AuthnRequestValidatorException("pvp2.22", + new Object[] { "Authn. Req. contains MORE THAN ONE requested LoA, but " + + AuthnContextComparisonTypeEnumeration.MINIMUM + " allows only one" }); + + } else { + result.add(authContext.getAuthnContextClassRefs().get(0).getAuthnContextClassRef()); + } + + } else if (authContext.getComparison().equals(AuthnContextComparisonTypeEnumeration.EXACT)) { + for (final AuthnContextClassRef el : authContext.getAuthnContextClassRefs()) { + result.add(el.getAuthnContextClassRef()); + } + + } else { + log.info("Currently only '" + AuthnContextComparisonTypeEnumeration.MINIMUM + "' and '" + + AuthnContextComparisonTypeEnumeration.EXACT + "' are supported"); + throw new AuthnRequestValidatorException("pvp2.22", + new Object[] { "Currently only '" + AuthnContextComparisonTypeEnumeration.MINIMUM + "' and '" + + AuthnContextComparisonTypeEnumeration.EXACT + "' are supported" }); + + } + + } + + return result; + } + + private String extractScopeRequsterId(AuthnRequest authnReq) { + if (authnReq.getScoping() != null) { + final Scoping scoping = authnReq.getScoping(); + if (scoping.getRequesterIDs() != null + && scoping.getRequesterIDs().size() > 0) { + if (scoping.getRequesterIDs().size() == 1) { + return scoping.getRequesterIDs().get(0).getRequesterID(); + } else { + log.info("Authn. request contains more than on RequesterIDs! Only use first one"); + return scoping.getRequesterIDs().get(0).getRequesterID(); + + } + } + } + + return null; + } } -- cgit v1.2.3