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.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.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)); // put request into shared cache final BinaryLightToken token = putResponseInCommunicationCache(lightRespBuilder.build()); final String tokenBase64 = BinaryLightTokenHelper.encodeBinaryLightTokenBase64(token); // 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, tokenBase64); 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; } @PostConstruct private void checkConfiguration() { //TODO: validate configuration on start-up } private ImmutableAttributeMap buildAttributesFromAuthData(IAuthData authData) { 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, authData.getGenericData(MsProxyServiceConstants.ATTR_EIDAS_PERSONAL_IDENTIFIER, String.class)) .put(attrDefFamilyName, authData.getFamilyName()) .put(attrDefGivenName, authData.getGivenName()) .put(attrDefDateOfBirth, authData.getFormatedDateOfBirth()); 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; } private void forwardToEidasProxy(IRequest pendingReq, HttpServletRequest httpReq, HttpServletResponse httpResp, String tokenBase64) throws EaafConfigurationException, IOException, GuiBuildException { // 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"); } } }