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.connector.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.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)); // 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) { 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); 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( MsProxyServiceConstants.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( MsProxyServiceConstants.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(MsProxyServiceConstants.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()); } private ImmutableAttributeMap buildAttributesWithoutMandate(IEidAuthData eidAuthData) { 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, eidAuthData.getGenericData(MsProxyServiceConstants.ATTR_EIDAS_PERSONAL_IDENTIFIER, String.class)) .put(attrDefFamilyName, eidAuthData.getFamilyName()) .put(attrDefGivenName, eidAuthData.getGivenName()) //TODO: throw an error in case of SZR Date with month or day = "00" .put(attrDefDateOfBirth, eidAuthData.getDateOfBirth()); 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; } }