From bee5dd259a4438d45ecd1bcc26dfba12875236d6 Mon Sep 17 00:00:00 2001 From: Thomas Lenz Date: Tue, 26 Jun 2018 11:03:48 +0200 Subject: initial commit --- .../builder/AbstractAuthenticationDataBuilder.java | 467 +++++++++++++++++++++ 1 file changed, 467 insertions(+) create mode 100644 eaaf_core/src/main/java/at/gv/egiz/eaaf/core/impl/idp/auth/builder/AbstractAuthenticationDataBuilder.java (limited to 'eaaf_core/src/main/java/at/gv/egiz/eaaf/core/impl/idp/auth/builder/AbstractAuthenticationDataBuilder.java') diff --git a/eaaf_core/src/main/java/at/gv/egiz/eaaf/core/impl/idp/auth/builder/AbstractAuthenticationDataBuilder.java b/eaaf_core/src/main/java/at/gv/egiz/eaaf/core/impl/idp/auth/builder/AbstractAuthenticationDataBuilder.java new file mode 100644 index 00000000..561f6f32 --- /dev/null +++ b/eaaf_core/src/main/java/at/gv/egiz/eaaf/core/impl/idp/auth/builder/AbstractAuthenticationDataBuilder.java @@ -0,0 +1,467 @@ +/******************************************************************************* + *******************************************************************************/ +package at.gv.egiz.eaaf.core.impl.idp.auth.builder; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.util.ArrayList; +import java.util.Collection; + +import org.apache.commons.lang3.StringUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.util.Base64Utils; +import org.w3c.dom.DOMException; +import org.w3c.dom.Element; +import org.w3c.dom.Node; + +import at.gv.egiz.eaaf.core.api.IRequest; +import at.gv.egiz.eaaf.core.api.data.EAAFConstants; +import at.gv.egiz.eaaf.core.api.data.PVPAttributeDefinitions; +import at.gv.egiz.eaaf.core.api.idp.IAuthenticationDataBuilder; +import at.gv.egiz.eaaf.core.api.idp.IConfiguration; +import at.gv.egiz.eaaf.core.api.idp.ISPConfiguration; +import at.gv.egiz.eaaf.core.api.idp.auth.data.IAuthProcessDataContainer; +import at.gv.egiz.eaaf.core.api.idp.auth.data.IIdentityLink; +import at.gv.egiz.eaaf.core.exceptions.EAAFBuilderException; +import at.gv.egiz.eaaf.core.exceptions.EAAFConfigurationException; +import at.gv.egiz.eaaf.core.exceptions.EAAFParserException; +import at.gv.egiz.eaaf.core.exceptions.XPathException; +import at.gv.egiz.eaaf.core.impl.data.Pair; +import at.gv.egiz.eaaf.core.impl.idp.AuthenticationData; +import at.gv.egiz.eaaf.core.impl.idp.auth.data.SimpleIdentityLinkAssertionParser; +import at.gv.egiz.eaaf.core.impl.utils.XPathUtils; + + +public abstract class AbstractAuthenticationDataBuilder implements IAuthenticationDataBuilder { + private static final Logger log = LoggerFactory.getLogger(AbstractAuthenticationDataBuilder.class); + protected Collection includedToGenericAuthData = null; + @Autowired protected IConfiguration basicConfig; + + protected void generateBasicAuthData(AuthenticationData authData, IRequest pendingReq, + IAuthProcessDataContainer authProcessData) throws EAAFBuilderException, EAAFConfigurationException, XPathException, DOMException, EAAFParserException { + + if (authProcessData.getGenericSessionDataStorage() != null && + !authProcessData.getGenericSessionDataStorage().isEmpty()) + includedToGenericAuthData = authProcessData.getGenericSessionDataStorage().keySet(); + else + includedToGenericAuthData = new ArrayList(); + + //#################################################### + //set general authData info's + authData.setAuthenticationIssuer(pendingReq.getAuthURL()); + authData.setSsoSession(pendingReq.needSingleSignOnFunctionality()); + authData.setBaseIDTransferRestrication(pendingReq.getServiceProviderConfiguration().hasBaseIdTransferRestriction()); + + + //#################################################### + //parse user info's from identityLink + IIdentityLink idlFromPVPAttr = null; + IIdentityLink identityLink = authProcessData.getIdentityLink(); + if (identityLink != null) { + parseBasicUserInfosFromIDL(authData, identityLink, includedToGenericAuthData); + + } else { + // identityLink is not direct in MOASession + String pvpAttrIDL = authProcessData.getGenericDataFromSession(PVPAttributeDefinitions.EID_IDENTITY_LINK_NAME, String.class); + //find PVP-Attr. which contains the IdentityLink + if (StringUtils.isNotEmpty(pvpAttrIDL)) { + log.debug("Find PVP-Attr: " + PVPAttributeDefinitions.EID_IDENTITY_LINK_FRIENDLY_NAME + + " --> Parse basic user info's from that attribute."); + InputStream idlStream = null; + try { + idlStream = new ByteArrayInputStream(Base64Utils.decodeFromString(pvpAttrIDL)); + idlFromPVPAttr = new SimpleIdentityLinkAssertionParser(idlStream).parseIdentityLink(); + parseBasicUserInfosFromIDL(authData, idlFromPVPAttr, includedToGenericAuthData); + + //set identitylink into AuthProcessData + authProcessData.setIdentityLink(idlFromPVPAttr);; + + } catch (EAAFParserException e) { + log.warn("Received IdentityLink is not valid", e); + + } catch (Exception e) { + log.warn("Received IdentityLink is not valid", e); + + } finally { + try { + includedToGenericAuthData.remove(PVPAttributeDefinitions.EID_IDENTITY_LINK_NAME); + if (idlStream != null) + idlStream.close(); + + } catch (IOException e) { + log.warn("Close InputStream FAILED.", e); + + } + } + } + + //if no basic user info's are set yet, parse info's single PVP-Attributes + if (StringUtils.isEmpty(authData.getFamilyName())) { + log.debug("No IdentityLink found or not parseable --> Parse basic user info's from single PVP-Attributes."); + authData.setFamilyName(authProcessData.getGenericDataFromSession(PVPAttributeDefinitions.PRINCIPAL_NAME_NAME, String.class)); + authData.setGivenName(authProcessData.getGenericDataFromSession(PVPAttributeDefinitions.GIVEN_NAME_NAME, String.class)); + authData.setDateOfBirth(authProcessData.getGenericDataFromSession(PVPAttributeDefinitions.BIRTHDATE_NAME, String.class)); + authData.setIdentificationValue(authProcessData.getGenericDataFromSession(PVPAttributeDefinitions.EID_SOURCE_PIN_NAME, String.class)); + authData.setIdentificationType(authProcessData.getGenericDataFromSession(PVPAttributeDefinitions.EID_SOURCE_PIN_TYPE_NAME, String.class)); + + //remove corresponding keys from genericSessionData if exists + includedToGenericAuthData.remove(PVPAttributeDefinitions.PRINCIPAL_NAME_NAME); + includedToGenericAuthData.remove(PVPAttributeDefinitions.GIVEN_NAME_NAME); + includedToGenericAuthData.remove(PVPAttributeDefinitions.BIRTHDATE_NAME); + includedToGenericAuthData.remove(PVPAttributeDefinitions.EID_SOURCE_PIN_NAME); + includedToGenericAuthData.remove(PVPAttributeDefinitions.EID_SOURCE_PIN_TYPE_NAME); + } + + } + + if (authData.getIdentificationType() != null && + !authData.getIdentificationType().equals(EAAFConstants.URN_PREFIX_BASEID)) { + log.trace("IdentificationType is not a baseID --> clear it. "); + authData.setBPK(authData.getIdentificationValue()); + authData.setBPKType(authData.getIdentificationType()); + + authData.setIdentificationValue(null); + authData.setIdentificationType(null); + } + + //#################################################### + //set QAA level + includedToGenericAuthData.remove(PVPAttributeDefinitions.EID_CITIZEN_EIDAS_QAA_LEVEL_NAME); + String currentLoA = null; + if (StringUtils.isNotEmpty(authProcessData.getQAALevel())) + currentLoA = authProcessData.getQAALevel(); + else { + currentLoA = authProcessData.getGenericDataFromSession(PVPAttributeDefinitions.EID_CITIZEN_EIDAS_QAA_LEVEL_NAME, String.class); + if (StringUtils.isNotEmpty(currentLoA)) { + log.debug("Find PVP-Attr '" + PVPAttributeDefinitions.EID_CITIZEN_EIDAS_QAA_LEVEL_FRIENDLY_NAME + "':" + currentLoA + + " --> Parse QAA-Level from that attribute."); + + } + } + if (StringUtils.isNotEmpty(currentLoA)) { + if (currentLoA.startsWith(EAAFConstants.EIDAS_QAA_PREFIX)) { + authData.seteIDASLoA(currentLoA); + + } else + log.info("Only eIDAS LoAs are supported by this implementation"); + + } else { + log.info("No QAA level found. Set to default level " + EAAFConstants.EIDAS_QAA_LOW); + authData.seteIDASLoA(EAAFConstants.EIDAS_QAA_LOW); + + } + + //#################################################### + //set isForeigner flag + //TODO: change to new eIDAS-token attribute identifier + if (authProcessData.getGenericDataFromSession(PVPAttributeDefinitions.EID_STORK_TOKEN_NAME) != null) { + log.debug("Find PVP-Attr: " + PVPAttributeDefinitions.EID_STORK_TOKEN_FRIENDLY_NAME + + " --> Set 'isForeigner' flag to TRUE"); + authData.setForeigner(true); + + } else { + authData.setForeigner(authProcessData.isForeigner()); + + } + + //#################################################### + //set citizen country-code + includedToGenericAuthData.remove(PVPAttributeDefinitions.EID_ISSUING_NATION_NAME); + String pvpCCCAttr = authProcessData.getGenericDataFromSession(PVPAttributeDefinitions.EID_ISSUING_NATION_NAME, String.class); + if (StringUtils.isNotEmpty(pvpCCCAttr)) { + authData.setCiticenCountryCode(pvpCCCAttr); + log.debug("Find PVP-Attr: " + PVPAttributeDefinitions.EID_ISSUING_NATION_FRIENDLY_NAME); + + } else { + if (authData.isForeigner()) { + //TODO!!!! + + } else { + authData.setCiticenCountryCode(basicConfig.getBasicConfiguration( + IConfiguration.CONFIG_PROPS_AUTH_DEFAULT_COUNTRYCODE, + EAAFConstants.COUNTRYCODE_AUSTRIA)); + + } + } + + + //#################################################### + // set bPK and IdentityLink + String pvpbPKValue = getbPKValueFromPVPAttribute(authProcessData); + String pvpbPKTypeAttr = getbPKTypeFromPVPAttribute(authProcessData); + Pair pvpEncbPKAttr = getEncryptedbPKFromPVPAttribute(authProcessData, authData, pendingReq.getServiceProviderConfiguration()); + + //check if a unique ID for this citizen exists + if (StringUtils.isEmpty(authData.getIdentificationValue()) && + StringUtils.isEmpty(pvpbPKValue) && StringUtils.isEmpty(authData.getBPK()) && + pvpEncbPKAttr == null) { + log.info("Can not build authData, because moaSession include no bPK, encrypted bPK or baseID"); + throw new EAAFBuilderException("builder.08", new Object[]{"No " + PVPAttributeDefinitions.BPK_FRIENDLY_NAME + + " or " + PVPAttributeDefinitions.EID_SOURCE_PIN_FRIENDLY_NAME + + " or " + PVPAttributeDefinitions.ENC_BPK_LIST_FRIENDLY_NAME}, + "No " + PVPAttributeDefinitions.BPK_FRIENDLY_NAME + + " or " + PVPAttributeDefinitions.EID_SOURCE_PIN_FRIENDLY_NAME + + " or " + PVPAttributeDefinitions.ENC_BPK_LIST_FRIENDLY_NAME); + + } + + // baseID is in MOASesson --> calculate bPK directly + if (StringUtils.isNotEmpty(authData.getIdentificationValue())) { + log.debug("Citizen baseID is in MOASession --> calculate bPK from this."); + Pair result = buildOAspecificbPK(pendingReq, authData); + authData.setBPK(result.getFirst()); + authData.setBPKType(result.getSecond()); + + //check if bPK already added to AuthData matches OA + } else if (StringUtils.isNotEmpty(authData.getBPK()) + && matchsReceivedbPKToOnlineApplication(pendingReq.getServiceProviderConfiguration(), authData.getBPKType()) ) { + log.debug("Correct bPK is already included in AuthData."); + + //check if bPK received by PVP-Attribute matches OA + } else if (StringUtils.isNotEmpty(pvpbPKValue) && + matchsReceivedbPKToOnlineApplication(pendingReq.getServiceProviderConfiguration(), pvpbPKTypeAttr)) { + log.debug("Receive correct bPK from PVP-Attribute"); + authData.setBPK(pvpbPKValue); + authData.setBPKType(pvpbPKTypeAttr); + + //check if decrypted bPK exists + } else if (pvpEncbPKAttr != null) { + log.debug("Receive bPK as encrypted bPK and decryption was possible."); + authData.setBPK(pvpEncbPKAttr.getFirst()); + authData.setBPKType(pvpEncbPKAttr.getSecond()); + + //ask SZR to get bPK + } else { + String notValidbPK = authData.getBPK(); + String notValidbPKType = authData.getBPKType(); + if (StringUtils.isEmpty(notValidbPK) && + StringUtils.isEmpty(notValidbPKType)) { + notValidbPK = pvpbPKValue; + notValidbPKType = pvpbPKTypeAttr; + + if (StringUtils.isEmpty(notValidbPK) && + StringUtils.isEmpty(notValidbPKType)) { + log.error("No bPK in MOASession. THIS error should not occur any more."); + throw new NullPointerException("No bPK in MOASession. THIS error should not occur any more."); + } + } + + Pair baseIDFromSZR = getbaseIDFromSZR(authData, notValidbPK, notValidbPKType); + if (baseIDFromSZR != null) { + log.info("Receive citizen baseID from SRZ. Authentication can be completed"); + authData.setIdentificationValue(baseIDFromSZR.getFirst()); + authData.setIdentificationType(baseIDFromSZR.getSecond()); + Pair result = buildOAspecificbPK(pendingReq, authData); + authData.setBPK(result.getFirst()); + authData.setBPKType(result.getSecond()); + + } else { + log.warn("Can not build authData, because moaSession include no valid bPK, encrypted bPK or baseID"); + throw new EAAFBuilderException("builder.08", new Object[]{"No valid " + PVPAttributeDefinitions.BPK_FRIENDLY_NAME + + " or " + PVPAttributeDefinitions.EID_SOURCE_PIN_FRIENDLY_NAME + + " or " + PVPAttributeDefinitions.ENC_BPK_LIST_FRIENDLY_NAME}, + "No valid " + PVPAttributeDefinitions.BPK_FRIENDLY_NAME + + " or " + PVPAttributeDefinitions.EID_SOURCE_PIN_FRIENDLY_NAME + + " or " + PVPAttributeDefinitions.ENC_BPK_LIST_FRIENDLY_NAME); + + } + } + + //build IdentityLink + if (authProcessData.getIdentityLink() != null) + authData.setIdentityLink(buildOAspecificIdentityLink( + pendingReq.getServiceProviderConfiguration(), + authProcessData.getIdentityLink(), + authData.getBPK(), + authData.getBPKType())); + else + log.info("Can NOT set IdentityLink. Msg: No IdentityLink found"); + + } + + //extract a encrypted bPK from PVP attrobute + protected abstract Pair getEncryptedbPKFromPVPAttribute(IAuthProcessDataContainer authProcessDataContainer, + AuthenticationData authData, ISPConfiguration spConfig) throws EAAFBuilderException; + + //request baseId from SRZ + protected abstract Pair getbaseIDFromSZR(AuthenticationData authData, String notValidbPK, + String notValidbPKType); + + + protected Pair buildOAspecificbPK(IRequest pendingReq, AuthenticationData authData) throws EAAFBuilderException { + ISPConfiguration oaParam = pendingReq.getServiceProviderConfiguration(); + + String baseID = authData.getIdentificationValue(); + String baseIDType = authData.getIdentificationType(); + Pair sectorSpecId = null; + + if (EAAFConstants.URN_PREFIX_BASEID.equals(baseIDType)) { + //SAML1 legacy target parameter work-around + String spTargetId = oaParam.getAreaSpecificTargetIdentifier(); + log.debug("Use OA target identifier '" + spTargetId + "' from configuration"); + + //calculate sector specific unique identifier + sectorSpecId = new BPKBuilder().generateAreaSpecificPersonIdentifier(baseID, spTargetId); + + } else { + log.error("!!!baseID-element does not include a baseID. This should not be happen any more!!!"); + sectorSpecId = Pair.newInstance(baseID, baseIDType); + + } + + log.trace("Authenticate user with bPK:" + sectorSpecId.getFirst() + " Type:" + sectorSpecId.getSecond()); + return sectorSpecId; + + } + + protected IIdentityLink buildOAspecificIdentityLink(ISPConfiguration spConfig, IIdentityLink idl, String bPK, String bPKType) throws EAAFConfigurationException, XPathException, DOMException, EAAFParserException { + if (spConfig.hasBaseIdTransferRestriction()) { + log.debug("SP: " + spConfig.getUniqueIdentifier() + " has baseId transfer restriction. Remove baseId from IDL ..."); + Element idlassertion = idl.getSamlAssertion(); + //set bpk/wpbk; + Node prIdentification = XPathUtils.selectSingleNode(idlassertion, SimpleIdentityLinkAssertionParser.PERSON_IDENT_VALUE_XPATH); + prIdentification.getFirstChild().setNodeValue(bPK); + //set bkp/wpbk type + Node prIdentificationType = XPathUtils.selectSingleNode(idlassertion, SimpleIdentityLinkAssertionParser.PERSON_IDENT_TYPE_XPATH); + prIdentificationType.getFirstChild().setNodeValue(bPKType); + + SimpleIdentityLinkAssertionParser idlparser = new SimpleIdentityLinkAssertionParser(idlassertion); + return idlparser.parseIdentityLink(); + + } else + return idl; + + } + + /** + * Check a bPK-Type against a Service-Provider configuration
+ * If bPK-Type is null the result is false. + * + * @param oaParam Service-Provider configuration, never null + * @param bPKType bPK-Type to check + * @return true, if bPK-Type matchs to Service-Provider configuration, otherwise false + */ + private boolean matchsReceivedbPKToOnlineApplication(ISPConfiguration oaParam, String bPKType) { + return oaParam.getAreaSpecificTargetIdentifier().equals(bPKType); + + } + + /** + * Parse information from an IdentityLink into AuthData object + * + * @param authData + * @param identityLink + * @param includedGenericSessionData + */ + private void parseBasicUserInfosFromIDL(AuthenticationData authData, IIdentityLink identityLink, Collection includedGenericSessionData) { + authData.setIdentificationValue(identityLink.getIdentificationValue()); + authData.setIdentificationType(identityLink.getIdentificationType()); + + authData.setGivenName(identityLink.getGivenName()); + authData.setFamilyName(identityLink.getFamilyName()); + authData.setDateOfBirth(identityLink.getDateOfBirth()); + + //remove corresponding keys from genericSessionData if exists + includedGenericSessionData.remove(PVPAttributeDefinitions.PRINCIPAL_NAME_NAME); + includedGenericSessionData.remove(PVPAttributeDefinitions.GIVEN_NAME_NAME); + includedGenericSessionData.remove(PVPAttributeDefinitions.BIRTHDATE_NAME); + includedGenericSessionData.remove(PVPAttributeDefinitions.EID_SOURCE_PIN_NAME); + includedGenericSessionData.remove(PVPAttributeDefinitions.EID_SOURCE_PIN_TYPE_NAME); + + } + + /** + * Get bPK from PVP Attribute 'BPK_NAME', which could be exist in + * MOASession as 'GenericData'
session.getGenericDataFromSession(PVPConstants.BPK_NAME, String.class)
+ * + * @param session MOASession, but never null + * @return bPK, which was received by PVP-Attribute, or null if no attribute exists + */ + private String getbPKValueFromPVPAttribute(IAuthProcessDataContainer session) { + String pvpbPKValueAttr = session.getGenericDataFromSession(PVPAttributeDefinitions.BPK_NAME, String.class); + if (StringUtils.isNotEmpty(pvpbPKValueAttr)) { + + //fix a wrong bPK-value prefix, which was used in some PVP Standardportal implementations + if (pvpbPKValueAttr.startsWith("bPK:")) { + log.warn("Attribute " + PVPAttributeDefinitions.BPK_NAME + + " contains a not standardize prefix! Staring attribute value correction process ..."); + pvpbPKValueAttr = pvpbPKValueAttr.substring("bPK:".length()); + + } + + String[] spitted = pvpbPKValueAttr.split(":"); + if (spitted.length != 2) { + log.warn("Attribute " + PVPAttributeDefinitions.BPK_NAME + " has a wrong encoding and can NOT be USED!" + + " Value:" + pvpbPKValueAttr); + return null; + + } + log.debug("Find PVP-Attr: " + PVPAttributeDefinitions.BPK_FRIENDLY_NAME); + return spitted[1]; + + } + + return null; + } + + /** + * Get bPK-Type from PVP Attribute 'EID_SECTOR_FOR_IDENTIFIER_NAME', which could be exist in + * MOASession as 'GenericData'
session.getGenericDataFromSession(PVPConstants.EID_SECTOR_FOR_IDENTIFIER_NAME, String.class)
+ * + * @param session MOASession, but never null + * @return bPKType, which was received by PVP-Attribute, or null if no attribute exists + */ + private String getbPKTypeFromPVPAttribute(IAuthProcessDataContainer session) { + String pvpbPKTypeAttr = session.getGenericDataFromSession(PVPAttributeDefinitions.EID_SECTOR_FOR_IDENTIFIER_NAME, String.class); + if (StringUtils.isNotEmpty(pvpbPKTypeAttr)) { + + //fix a wrong bPK-Type encoding, which was used in some PVP Standardportal implementations + if (pvpbPKTypeAttr.startsWith(EAAFConstants.URN_PREFIX_CDID) && + !pvpbPKTypeAttr.substring(EAAFConstants.URN_PREFIX_CDID.length(), + EAAFConstants.URN_PREFIX_CDID.length() + 1).equals("+")) { + log.warn("Receive uncorrect encoded bBKType attribute " + pvpbPKTypeAttr + " Starting attribute value correction ... "); + pvpbPKTypeAttr = EAAFConstants.URN_PREFIX_CDID + "+" + pvpbPKTypeAttr.substring(EAAFConstants.URN_PREFIX_CDID.length() + 1); + + } + log.debug("Find PVP-Attr: " + PVPAttributeDefinitions.EID_SECTOR_FOR_IDENTIFIER_FRIENDLY_NAME); + return pvpbPKTypeAttr; + } + + return null; + + + /* + * INFO: This code could be used to extract the bPKType from 'PVPConstants.BPK_NAME', + * because the prefix of BPK_NAME attribute contains the postfix of the bPKType + * + * Now, all PVP Standardportals should be able to send 'EID_SECTOR_FOR_IDENTIFIER' + * PVP attributes + */ +// String pvpbPKValueAttr = session.getGenericDataFromSession(PVPConstants.BPK_NAME, String.class); +// String[] spitted = pvpbPKValueAttr.split(":"); +// if (MiscUtil.isEmpty(authData.getBPKType())) { +// Logger.debug("PVP assertion contains NO bPK/wbPK target attribute. " + +// "Starting target extraction from bPK/wbPK prefix ..."); +// //exract bPK/wbPK type from bpk attribute value prefix if type is +// //not transmitted as single attribute +// Pattern pattern = Pattern.compile("[a-zA-Z]{2}(-[a-zA-Z]+)?"); +// Matcher matcher = pattern.matcher(spitted[0]); +// if (matcher.matches()) { +// //find public service bPK +// authData.setBPKType(Constants.URN_PREFIX_CDID + "+" + spitted[0]); +// Logger.debug("Found bPK prefix. Set target to " + authData.getBPKType()); +// +// } else { +// //find business service wbPK +// authData.setBPKType(Constants.URN_PREFIX_WBPK+ "+" + spitted[0]); +// Logger.debug("Found wbPK prefix. Set target to " + authData.getBPKType()); +// +// } +// } + + } + +} -- cgit v1.2.3