/******************************************************************************* * 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.authmodule_eIDASv2.handler; import java.util.Map; import java.util.regex.Matcher; import java.util.regex.Pattern; import org.apache.commons.lang3.StringUtils; import org.joda.time.DateTime; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import com.google.common.collect.ImmutableSortedSet; import at.asitplus.eidas.specific.modules.authmodule_eIDASv2.Constants; import at.asitplus.eidas.specific.modules.authmodule_eIDASv2.DAO.ERnBeIDData; import at.asitplus.eidas.specific.modules.authmodule_eIDASv2.exception.eIDASAttributeException; import at.asitplus.eidas.specific.modules.authmodule_eIDASv2.exception.eIDPostProcessingException; import at.asitplus.eidas.specific.modules.authmodule_eIDASv2.service.eIDASAttributeRegistry; import at.asitplus.eidas.specific.modules.authmodule_eIDASv2.utils.eIDASResponseUtils; import at.gv.e_government.reference.namespace.persondata._20020228.PostalAddressType; import at.gv.egiz.eaaf.core.api.IRequest; import at.gv.egiz.eaaf.core.api.idp.IConfigurationWithSP; import at.gv.egiz.eaaf.core.api.idp.ISPConfiguration; import at.gv.egiz.eaaf.core.impl.data.Trible; import edu.umd.cs.findbugs.annotations.NonNull; import eu.eidas.auth.commons.attribute.AttributeDefinition; import eu.eidas.auth.commons.attribute.ImmutableAttributeMap; import eu.eidas.auth.commons.light.impl.LightRequest.Builder; import eu.eidas.auth.commons.protocol.eidas.SpType; import eu.eidas.auth.commons.protocol.eidas.impl.PostalAddress; public abstract class AbstracteIDProcessor implements INationaleIDProcessor { private static final Logger log = LoggerFactory.getLogger(AbstracteIDProcessor.class); @Autowired protected eIDASAttributeRegistry attrRegistry; @Autowired protected IConfigurationWithSP basicConfig; @Override public final void preProcess(IRequest pendingReq, Builder authnRequestBuilder) { buildProviderNameAttribute(pendingReq, authnRequestBuilder); buildRequestedAttributes(pendingReq, authnRequestBuilder); } @Override public final ERnBeIDData postProcess(Map eIDASAttrMap) throws eIDPostProcessingException, eIDASAttributeException{ ERnBeIDData result = new ERnBeIDData(); Object eIdentifierObj = eIDASAttrMap.get(Constants.eIDAS_ATTR_PERSONALIDENTIFIER); Trible eIdentifier = eIDASResponseUtils.parseEidasPersonalIdentifier((String)eIdentifierObj); result.setCitizenCountryCode(eIdentifier.getFirst()); //MDS attributes result.setPseudonym(processPseudonym(eIDASAttrMap.get(Constants.eIDAS_ATTR_PERSONALIDENTIFIER))); result.setFamilyName(processFamilyName(eIDASAttrMap.get(Constants.eIDAS_ATTR_CURRENTFAMILYNAME))); result.setGivenName(processGivenName(eIDASAttrMap.get(Constants.eIDAS_ATTR_CURRENTGIVENNAME))); result.setDateOfBirth(processDateOfBirth(eIDASAttrMap.get(Constants.eIDAS_ATTR_DATEOFBIRTH))); //additional attributes result.setPlaceOfBirth(processPlaceOfBirth(eIDASAttrMap.get(Constants.eIDAS_ATTR_PLACEOFBIRTH))); result.setBirthName(processBirthName(eIDASAttrMap.get(Constants.eIDAS_ATTR_BIRTHNAME))); result.setAddress(processAddress(eIDASAttrMap.get(Constants.eIDAS_ATTR_CURRENTADDRESS))); return result; } @NonNull /** * Get a Map of country-specific requested attributes * * @return */ protected abstract Map getCountrySpecificRequestedAttributes(); /** * Post-Process the eIDAS CurrentAddress attribute * * @param currentAddressObj * @return current address or null if no attribute is available * @throws eIDPostProcessingException if post-processing fails * @throws eIDASAttributeException if eIDAS attribute is of a wrong type */ protected PostalAddressType processAddress(Object currentAddressObj) throws eIDPostProcessingException, eIDASAttributeException { if (currentAddressObj != null) { if ((currentAddressObj instanceof PostalAddress)) { PostalAddressType result = new PostalAddressType(); result.setPostalCode(((PostalAddress)currentAddressObj).getPostCode()); result.setMunicipality(((PostalAddress)currentAddressObj).getPostName()); //TODO: add more mappings } else { log.warn("eIDAS attr: " + Constants.eIDAS_ATTR_CURRENTADDRESS + " is of WRONG type"); throw new eIDASAttributeException(Constants.eIDAS_ATTR_CURRENTADDRESS); } } else log.debug("NO '" + Constants.eIDAS_ATTR_CURRENTADDRESS + "' attribute. Post-Processing skipped ... "); return null; } /** * Post-Process the eIDAS BirthName attribute * * @param birthNameObj * @return birthName or null if no attribute is available * @throws eIDPostProcessingException if post-processing fails * @throws eIDASAttributeException if eIDAS attribute is of a wrong type */ protected String processBirthName(Object birthNameObj) throws eIDPostProcessingException, eIDASAttributeException { if (birthNameObj != null) { if ((birthNameObj instanceof String)) { return (String)birthNameObj; } else { log.warn("eIDAS attr: " + Constants.eIDAS_ATTR_BIRTHNAME + " is of WRONG type"); throw new eIDASAttributeException(Constants.eIDAS_ATTR_BIRTHNAME); } } else log.debug("NO '" + Constants.eIDAS_ATTR_BIRTHNAME + "' attribute. Post-Processing skipped ... "); return null; } /** * Post-Process the eIDAS PlaceOfBirth attribute * * @param placeOfBirthObj * @return place of Birth or null if no attribute is available * @throws eIDPostProcessingException if post-processing fails * @throws eIDASAttributeException if eIDAS attribute is of a wrong type */ protected String processPlaceOfBirth(Object placeOfBirthObj) throws eIDPostProcessingException, eIDASAttributeException { if (placeOfBirthObj != null) { if ((placeOfBirthObj instanceof String)) { return (String)placeOfBirthObj; } else { log.warn("eIDAS attr: " + Constants.eIDAS_ATTR_PLACEOFBIRTH + " is of WRONG type"); throw new eIDASAttributeException(Constants.eIDAS_ATTR_PLACEOFBIRTH); } } else log.debug("NO '" + Constants.eIDAS_ATTR_PLACEOFBIRTH + "' attribute. Post-Processing skipped ... "); return null; } /** * Post-Process the eIDAS DateOfBirth attribute * * @param dateOfBirthObj * @return * @throws eIDASAttributeException if NO attribute is available * @throws eIDPostProcessingException if post-processing fails */ protected DateTime processDateOfBirth(Object dateOfBirthObj) throws eIDPostProcessingException, eIDASAttributeException { if (dateOfBirthObj == null || !(dateOfBirthObj instanceof DateTime)) throw new eIDASAttributeException(Constants.eIDAS_ATTR_DATEOFBIRTH); return (DateTime)dateOfBirthObj; } /** * Post-Process the eIDAS GivenName attribute * * @param givenNameObj * @return * @throws eIDASAttributeException if NO attribute is available * @throws eIDPostProcessingException if post-processing fails */ protected String processGivenName(Object givenNameObj) throws eIDPostProcessingException, eIDASAttributeException { if (givenNameObj == null || !(givenNameObj instanceof String)) throw new eIDASAttributeException(Constants.eIDAS_ATTR_CURRENTGIVENNAME); return (String)givenNameObj; } /** * Post-Process the eIDAS FamilyName attribute * * @param familyNameObj * @return * @throws eIDASAttributeException if NO attribute is available * @throws eIDPostProcessingException if post-processing fails */ protected String processFamilyName(Object familyNameObj) throws eIDPostProcessingException, eIDASAttributeException { if (familyNameObj == null || !(familyNameObj instanceof String)) throw new eIDASAttributeException(Constants.eIDAS_ATTR_CURRENTFAMILYNAME); return (String) familyNameObj; } /** * Post-Process the eIDAS pseudonym to ERnB unique identifier * * @param eIdentifierObj eIDAS PersonalIdentifierAttribute * @return * @throws eIDPostProcessingException * @throws eIDASAttributeException if NO attribute is available * @throws eIDPostProcessingException if post-processing fails */ protected String processPseudonym(Object eIdentifierObj) throws eIDPostProcessingException, eIDASAttributeException { if (eIdentifierObj == null || !(eIdentifierObj instanceof String)) throw new eIDASAttributeException(Constants.eIDAS_ATTR_PERSONALIDENTIFIER); Trible eIdentifier = eIDASResponseUtils.parseEidasPersonalIdentifier((String)eIdentifierObj); return eIdentifier.getThird(); } private void buildRequestedAttributes(IRequest pendingReq, Builder authnRequestBuilder) { //build and add requested attribute set Map ccSpecificReqAttr = getCountrySpecificRequestedAttributes(); log.debug("Get #{} country-specific requested attributes", ccSpecificReqAttr.size()); Map mdsReqAttr = attrRegistry.getDefaultAttributeSetFromConfiguration(); log.trace("Get #{} default requested attributes", mdsReqAttr.size()); //put it together ccSpecificReqAttr.putAll(mdsReqAttr); //convert it to eIDAS attributes ImmutableAttributeMap reqAttrMap = translateToEidasAttributes(ccSpecificReqAttr); authnRequestBuilder.requestedAttributes(reqAttrMap); } private ImmutableAttributeMap translateToEidasAttributes(final Map requiredAttributes) { ImmutableAttributeMap.Builder builder = ImmutableAttributeMap.builder(); for (Map.Entry attribute : requiredAttributes.entrySet()) { final String name = attribute.getKey(); final ImmutableSortedSet> byFriendlyName = attrRegistry.getCoreAttributeRegistry().getByFriendlyName(name); if (!byFriendlyName.isEmpty()) { final AttributeDefinition attributeDefinition = byFriendlyName.first(); builder.put(AttributeDefinition.builder(attributeDefinition).required(attribute.getValue()).build()); } else log.warn("Can NOT request UNKNOWN attribute: " + attribute.getKey() + " Ignore it!"); } return builder.build(); } private void buildProviderNameAttribute(IRequest pendingReq, Builder authnRequestBuilder) { ISPConfiguration spConfig = pendingReq.getServiceProviderConfiguration(); //set correct SPType for requested target sector String publicSectorTargetSelector = basicConfig.getBasicConfiguration( Constants.CONIG_PROPS_EIDAS_NODE_PUBLICSECTOR_TARGETS, Constants.POLICY_DEFAULT_ALLOWED_TARGETS); Pattern p = Pattern.compile(publicSectorTargetSelector); Matcher m = p.matcher(spConfig.getAreaSpecificTargetIdentifier()); if (m.matches()) { log.debug("Map " + spConfig.getAreaSpecificTargetIdentifier() + " to 'PublicSector'"); authnRequestBuilder.spType(SpType.PUBLIC.getValue()); if ( basicConfig.getBasicConfigurationBoolean( Constants.CONIG_PROPS_EIDAS_NODE_WORKAROUND_USE_STATIC_PROVIDERNAME_FOR_PUBLIC_SP, false) ) { authnRequestBuilder.providerName(basicConfig.getBasicConfiguration( Constants.CONIG_PROPS_EIDAS_NODE_STATIC_PROVIDERNAME_FOR_PUBLIC_SP, Constants.DEFAULT_PROPS_EIDAS_NODE_STATIC_PROVIDERNAME_FOR_PUBLIC_SP)); } else { //TODO: only for eIDAS ref. node 2.0 and 2.1 because it need 'Providername' for any SPType String providerName = pendingReq.getRawData(Constants.DATA_PROVIDERNAME, String.class); if ( StringUtils.isNotEmpty(providerName) && basicConfig.getBasicConfigurationBoolean( Constants.CONIG_PROPS_EIDAS_NODE_WORKAROUND_ADD_ALWAYS_PROVIDERNAME, false) ) { authnRequestBuilder.providerName(providerName); } } } else { log.debug("Map " + spConfig.getAreaSpecificTargetIdentifier() + " to 'PrivateSector'"); authnRequestBuilder.spType(SpType.PRIVATE.getValue()); //TODO: switch to RequesterId in further version //set provider name for private sector applications String providerName = pendingReq.getRawData(Constants.DATA_PROVIDERNAME, String.class); if (StringUtils.isNotEmpty(providerName)) authnRequestBuilder.providerName(providerName); } } }