/******************************************************************************* *******************************************************************************/ package at.gv.egiz.eidas.specific.modules.authmodule_eIDASv2.tasks; import java.io.StringWriter; import java.util.ArrayList; import java.util.Collection; import java.util.List; import java.util.UUID; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.apache.commons.lang3.BooleanUtils; import org.apache.velocity.Template; import org.apache.velocity.VelocityContext; import org.apache.velocity.app.VelocityEngine; import org.opensaml.common.xml.SAMLConstants; import org.opensaml.saml2.metadata.EntityDescriptor; import org.opensaml.saml2.metadata.SingleSignOnService; import org.opensaml.saml2.metadata.provider.MetadataProviderException; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import org.springframework.util.StringUtils; import com.google.common.net.MediaType; import at.gv.egiz.eidas.specific.modules.authmodule_eIDASv2.Constants; import at.gv.egovernment.moa.id.advancedlogging.MOAIDEventConstants; import at.gv.egovernment.moa.id.auth.exception.AuthenticationException; import at.gv.egovernment.moa.id.auth.frontend.velocity.VelocityProvider; import at.gv.egovernment.moa.id.auth.modules.AbstractAuthServletTask; import at.gv.egovernment.moa.id.auth.modules.TaskExecutionException; import at.gv.egovernment.moa.id.commons.MOAIDAuthConstants; import at.gv.egovernment.moa.id.commons.api.IOAAuthParameters; import at.gv.egovernment.moa.id.commons.api.IRequest; import at.gv.egovernment.moa.id.commons.api.data.CPEPS; import at.gv.egovernment.moa.id.commons.api.data.StorkAttribute; import at.gv.egovernment.moa.id.commons.api.exceptions.MOAIDException; import at.gv.egovernment.moa.id.process.api.ExecutionContext; import at.gv.egovernment.moa.id.protocols.pvp2x.utils.SAML2Utils; import at.gv.egovernment.moa.logging.Logger; import at.gv.egovernment.moa.util.MiscUtil; import eu.eidas.auth.commons.EidasStringUtil; import eu.eidas.auth.commons.attribute.AttributeDefinition; import eu.eidas.auth.commons.attribute.AttributeDefinition.Builder; import eu.eidas.auth.commons.light.impl.LightRequest; import eu.eidas.auth.commons.attribute.ImmutableAttributeMap; import eu.eidas.auth.commons.protocol.IRequestMessage; import eu.eidas.auth.commons.protocol.eidas.LevelOfAssurance; import eu.eidas.auth.commons.protocol.eidas.LevelOfAssuranceComparison; import eu.eidas.auth.commons.protocol.eidas.SpType; import eu.eidas.auth.commons.protocol.eidas.impl.EidasAuthenticationRequest; /** * @author tlenz * */ @Component("GenerateAuthnRequestTask") public class GenerateAuthnRequestTask extends AbstractAuthServletTask { /* (non-Javadoc) * @see at.gv.egovernment.moa.id.process.springweb.MoaIdTask#execute(at.gv.egovernment.moa.id.process.api.ExecutionContext, javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse) */ @Override public void execute(ExecutionContext executionContext, HttpServletRequest request, HttpServletResponse response) throws TaskExecutionException { try{ //get service-provider configuration IOAAuthParameters oaConfig = pendingReq.getOnlineApplicationConfiguration(); // get target and validate citizen countryCode String citizenCountryCode = (String) executionContext.get(MOAIDAuthConstants.PARAM_CCC); if (StringUtils.isEmpty(citizenCountryCode)) { // illegal state; task should not have been executed without a selected country throw new AuthenticationException("eIDAS.03", new Object[] { "" }); } CPEPS cpeps = authConfig.getStorkConfig().getCPEPSWithFullName(citizenCountryCode); if(null == cpeps) { Logger.error("PEPS unknown for country: " + citizenCountryCode); throw new AuthenticationException("eIDAS.04", new Object[] {citizenCountryCode}); } Logger.debug("Found eIDaS Node/C-PEPS configuration for citizen of country: " + citizenCountryCode); //TODO: load authnReq End-Point URL from configuration SingleSignOnService authnReqEndpoint = null; //TODO: switch to entityID and set new status codes // revisionsLogger.logEvent(oaConfig, pendingReq, // MOAIDEventConstants.AUTHPROCESS_PEPS_SELECTED, // metadataUrl); // assemble requested attributes Collection attributesFromConfig = oaConfig.getRequestedSTORKAttributes(); // - prepare attribute list // - fill container List> reqAttrList = new ArrayList>(); //TODO: update requested attribute builder // for (StorkAttribute current : attributesFromConfig) { // AttributeDefinition newAttribute = SAMLEngineUtils.getMapOfAllAvailableAttributes().get(current.getName()); // // if (newAttribute == null) { // Logger.warn("eIDAS attribute with friendlyName:" + current.getName() + " is not supported."); // // } else { // boolean globallyMandatory = false; // for (StorkAttribute currentGlobalAttribute : authConfig.getStorkConfig().getStorkAttributes()) // if (current.getName().equals(currentGlobalAttribute.getName())) { // globallyMandatory = BooleanUtils.isTrue(currentGlobalAttribute.getMandatory()); // break; // } // // Builder attrBuilder = AttributeDefinition.builder(newAttribute).required(current.getMandatory() || globallyMandatory); // reqAttrList.add(attrBuilder.build()); // // } // } //request // if (reqAttrList.isEmpty()) { // Logger.info("No attributes requested by OA:" + pendingReq.getOnlineApplicationConfiguration().getPublicURLPrefix() // + " --> Request attr:" + Constants.eIDAS_ATTR_PERSONALIDENTIFIER + " by default"); // AttributeDefinition newAttribute = SAMLEngineUtils.getMapOfAllAvailableAttributes().get(Constants.eIDAS_ATTR_PERSONALIDENTIFIER); // Builder attrBuilder = AttributeDefinition.builder(newAttribute).required(true); // reqAttrList.add(attrBuilder.build()); // // } //build requested attribute set ImmutableAttributeMap reqAttrMap = new ImmutableAttributeMap.Builder().putAll(reqAttrList).build(); //build eIDAS AuthnRequest LightRequest.Builder authnRequestBuilder = LightRequest.builder(); authnRequestBuilder.id(UUID.randomUUID().toString()); authnRequestBuilder.providerName(pendingReq.getAuthURL()); String issur = pendingReq.getAuthURL() + Constants.eIDAS_HTTP_ENDPOINT_METADATA; authnRequestBuilder.issuer(issur); //TODO: //authnRequestBuilder.destination(authnReqEndpoint.getLocation()); authnRequestBuilder.nameIdFormat(Constants.eIDAS_REQ_NAMEID_FORMAT); //set minimum required eIDAS LoA from OA config String LoA = oaConfig.getQaaLevel(); //TODO: // if (MiscUtil.isNotEmpty(LoA)) // authnRequestBuilder.levelOfAssurance(LevelOfAssurance.fromString(oaConfig.getQaaLevel())); // else authnRequestBuilder.levelOfAssurance(LevelOfAssurance.HIGH.getValue()); //TODO: check if required //authnRequestBuilder.levelOfAssuranceComparison(LevelOfAssuranceComparison.MINIMUM); //set correct SPType for this online application if (oaConfig.hasBaseIdTransferRestriction()) authnRequestBuilder.spType(SpType.PRIVATE.getValue()); else authnRequestBuilder.spType(SpType.PUBLIC.getValue()); //TODO //set service provider (eIDAS node) countryCode // authnRequestBuilder.serviceProviderCountryCode( // authConfig.getBasicMOAIDConfiguration(Constants.CONIG_PROPS_EIDAS_NODE_COUNTRYCODE, "AT")); //set citizen country code for foreign uses authnRequestBuilder.citizenCountryCode(cpeps.getCountryCode()); //add requested attributes authnRequestBuilder.requestedAttributes(reqAttrMap); LightRequest lightAuthnReq = authnRequestBuilder.build(); //IRequestMessage authnRequest = engine.generateRequestMessage(authnRequestBuilder.build(), issur); //encode AuthnRequest // byte[] token = authnRequest.getMessageBytes(); // String SAMLRequest = EidasStringUtil.encodeToBase64(token); // if (SAMLConstants.SAML2_POST_BINDING_URI.equals(authnReqEndpoint.getBinding())) // buildPostBindingRequest(pendingReq, authnReqEndpoint, SAMLRequest, authnRequest, response); // // //TODO: redirect Binding is not completely implemented // //else if (SAMLConstants.SAML2_REDIRECT_BINDING_URI.equals(authnReqEndpoint.getBinding())) // //buildRedirecttBindingRequest(pendingReq, authnReqEndpoint, token, authnRequest, response); // // else { // Logger.error("eIDAS-node use an unsupported binding (" // + authnReqEndpoint.getBinding() + "). Request eIDAS node not possible."); // throw new MOAIDException("eIDAS.02", new Object[]{"eIDAS-node use an unsupported binding"}); // // } // }catch (EIDASSAMLEngineException e){ // throw new TaskExecutionException(pendingReq, "eIDAS AuthnRequest generation FAILED.", // new EIDASEngineException("eIDAS.00", new Object[]{e.getMessage()}, e)); } catch (MOAIDException e) { throw new TaskExecutionException(pendingReq, "eIDAS AuthnRequest generation FAILED.", e); } catch (Exception e) { Logger.error("eIDAS AuthnRequest generation FAILED.", e); throw new TaskExecutionException(pendingReq, e.getMessage(), e); } } /** * Encode the eIDAS request with POST binding * * @param pendingReq * @param authnReqEndpoint * @param SAMLRequest * @param authnRequest * @param response * @throws MOAIDException */ private void buildPostBindingRequest(IRequest pendingReq, SingleSignOnService authnReqEndpoint, String SAMLRequest, IRequestMessage authnRequest, HttpServletResponse response) throws MOAIDException { //send try { VelocityEngine velocityEngine = VelocityProvider.getClassPathVelocityEngine(); Template template = velocityEngine.getTemplate("/resources/templates/eidas_postbinding_template.vm"); VelocityContext context = new VelocityContext(); String actionType = "SAMLRequest"; context.put(actionType, SAMLRequest); context.put("RelayState", pendingReq.getRequestID()); context.put("action", authnReqEndpoint.getLocation()); Logger.debug("Using SingleSignOnService url as action: " + authnReqEndpoint.getLocation()); Logger.debug("Encoded " + actionType + " original: " + SAMLRequest); Logger.trace("Starting template merge"); StringWriter writer = new StringWriter(); Logger.trace("Doing template merge"); template.merge(context, writer); Logger.trace("Template merge done"); Logger.trace("Sending html content: " + writer.getBuffer().toString()); byte[] content = writer.getBuffer().toString().getBytes("UTF-8"); response.setContentType(MediaType.HTML_UTF_8.toString()); response.setContentLength(content.length); response.getOutputStream().write(content); revisionsLogger.logEvent(pendingReq.getOnlineApplicationConfiguration(), pendingReq, MOAIDEventConstants.AUTHPROCESS_PEPS_REQUESTED, authnRequest.getRequest().getId()); } catch (Exception e) { Logger.error("Velocity general error: " + e.getMessage()); throw new MOAIDException("eIDAS.02", new Object[]{e.getMessage()}, e); } } /** * Select a SingleSignOnService endPoint from eIDAS node metadata. * This endPoint receives the Authn. request * * @param idpEntity * @return */ private SingleSignOnService selectSingleSignOnServiceFromMetadata(EntityDescriptor idpEntity) { //select SingleSignOn Service endpoint from IDP metadata SingleSignOnService endpoint = null; if (idpEntity.getIDPSSODescriptor(SAMLConstants.SAML20P_NS) == null) { return null; } for (SingleSignOnService sss : idpEntity.getIDPSSODescriptor(SAMLConstants.SAML20P_NS).getSingleSignOnServices()) { // use POST binding as default if it exists if (sss.getBinding().equals(SAMLConstants.SAML2_POST_BINDING_URI)) endpoint = sss; //TODO: redirect Binding is not completely implemented // use Redirect binding as backup // else if ( sss.getBinding().equals(SAMLConstants.SAML2_REDIRECT_BINDING_URI) // && endpoint == null ) // endpoint = sss; } return endpoint; } }