/******************************************************************************* * Copyright 2014 Federal Chancellery Austria * MOA-ID has been developed in a cooperation between BRZ, the Federal * Chancellery Austria - ICT staff unit, and Graz University of Technology. * * Licensed under the EUPL, Version 1.1 or - as soon they will be approved by * the European Commission - subsequent versions of the EUPL (the "Licence"); * You may not use this work except in compliance with the Licence. * You may obtain a copy of the Licence at: * http://www.osor.eu/eupl/ * * Unless required by applicable law or agreed to in writing, software * distributed under the Licence is distributed on an "AS IS" basis, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the Licence for the specific language governing permissions and * limitations under the Licence. * * 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.gv.egovernment.moa.id.protocols.eidas; import java.io.StringWriter; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.apache.velocity.Template; import org.apache.velocity.VelocityContext; import org.apache.velocity.app.VelocityEngine; import org.opensaml.saml2.core.StatusCode; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import com.google.common.collect.ImmutableSet; import at.gv.egiz.eaaf.core.api.IRequest; 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.slo.SLOInformationInterface; import at.gv.egiz.eaaf.core.api.logging.IRevisionLogger; import at.gv.egiz.eaaf.core.impl.data.Pair; import at.gv.egiz.eaaf.core.impl.data.SLOInformationImpl; import at.gv.egiz.eaaf.core.impl.gui.velocity.VelocityProvider; import at.gv.egovernment.moa.id.auth.modules.eidas.Constants; import at.gv.egovernment.moa.id.auth.modules.eidas.engine.MOAeIDASChainingMetadataProvider; import at.gv.egovernment.moa.id.auth.modules.eidas.utils.eIDASAttributeBuilder; import at.gv.egovernment.moa.id.commons.MOAIDConstants; import at.gv.egovernment.moa.id.commons.api.exceptions.MOAIDException; import at.gv.egovernment.moa.id.data.IMOAAuthData; import at.gv.egovernment.moa.logging.Logger; import eu.eidas.auth.commons.EidasStringUtil; import eu.eidas.auth.commons.attribute.AttributeDefinition; import eu.eidas.auth.commons.attribute.AttributeValue; import eu.eidas.auth.commons.attribute.ImmutableAttributeMap; import eu.eidas.auth.commons.attribute.ImmutableAttributeMap.Builder; import eu.eidas.auth.commons.protocol.IResponseMessage; import eu.eidas.auth.commons.protocol.impl.AuthenticationResponse; import eu.eidas.auth.engine.ProtocolEngineI; import eu.eidas.auth.engine.core.eidas.spec.RepresentativeLegalPersonSpec; import eu.eidas.auth.engine.core.eidas.spec.RepresentativeNaturalPersonSpec; import eu.eidas.auth.engine.xml.opensaml.SAMLEngineUtils; /** * Second request step - after authentication of the user is done and moasession obtained, * process request and forward the user further to PEPS and/or other entities * * @author tlenz */ @Service("eIDASAuthenticationRequest") public class eIDASAuthenticationRequest implements IAction { @Autowired protected IRevisionLogger revisionsLogger; @Autowired(required=true) MOAeIDASChainingMetadataProvider eIDASMetadataProvider; @Override public SLOInformationInterface processRequest(IRequest req, HttpServletRequest httpReq, HttpServletResponse httpResp, IAuthData authData) throws MOAIDException { EIDASData eidasRequest; if(req instanceof EIDASData) eidasRequest = (EIDASData) req; else throw new MOAIDException("got wrong IRequest type. is: {}, should be: {}", new String[] {req.getClass().toString(), EIDASData.class.toString()}); String subjectNameID = null; //gather attributes ImmutableAttributeMap reqAttributeList = (ImmutableAttributeMap) eidasRequest.getEidasRequestedAttributes(); //add mandate attr. to requested attributes of eMandates are used an no mandate attr. are requested if (authData instanceof IMOAAuthData && ((IMOAAuthData)authData).isUseMandate()) { Logger.trace("eMandates are used. Starting eIDAS requsted attr. update process ...."); Builder reqAttrWithMandates = ImmutableAttributeMap.builder(reqAttributeList); //check if the exists a local builder for (AttributeDefinition el : RepresentativeNaturalPersonSpec.REGISTRY.getAttributes()) { if (eIDASAttributeBuilder.getAllProvideableeIDASAttributes().contains(el.getNameUri().toString())) { if (reqAttributeList.getDefinitionByNameUri(el.getNameUri()) == null) { Logger.debug("Add eIDAS attr: " + el.getNameUri().toString() + " to requested attributes"); reqAttrWithMandates.put(AttributeDefinition.builder(el).required(false).build()); } } else Logger.trace("eIDAS attribute: " + el.getNameUri().toString() + " is not providable by Austrian eIDAS node."); } for (AttributeDefinition el : RepresentativeLegalPersonSpec.REGISTRY.getAttributes()) { if (eIDASAttributeBuilder.getAllProvideableeIDASAttributes().contains(el.getNameUri().toString())) { if (reqAttributeList.getDefinitionByNameUri(el.getNameUri()) == null) { Logger.debug("Add eIDAS attr: " + el.getNameUri().toString() + " to requested attributes"); reqAttrWithMandates.put(AttributeDefinition.builder(el).required(false).build()); } } else Logger.trace("eIDAS attribute: " + el.getNameUri().toString() + " is not providable by Austrian eIDAS node."); } reqAttributeList = reqAttrWithMandates.build(); Logger.trace("eIDAS requsted attr. update process finished"); } Logger.trace("Starting eIDAS response generation ...."); //generate eIDAS attributes ImmutableAttributeMap.Builder attrMapBuilder = ImmutableAttributeMap.builder(); for(AttributeDefinition attr : reqAttributeList.getDefinitions()) buildAndAddAttribute(attrMapBuilder, attr, eidasRequest, authData); //build final attibute set ImmutableAttributeMap eIDASAttrbutMap = attrMapBuilder.build(); // construct eIDaS response AuthenticationResponse.Builder responseBuilder = new AuthenticationResponse.Builder(); responseBuilder.id(SAMLEngineUtils.generateNCName()); responseBuilder.inResponseTo(eidasRequest.getEidasRequest().getId()); String pubURLPrefix = req.getAuthURL(); String metadata_url = pubURLPrefix + Constants.eIDAS_HTTP_ENDPOINT_METADATA; responseBuilder.issuer(metadata_url); responseBuilder.levelOfAssurance(authData.getEIDASQAALevel()); //add attributes responseBuilder.attributes(eIDASAttrbutMap); //set success statuscode responseBuilder.statusCode(StatusCode.SUCCESS_URI); //build response AuthenticationResponse response = responseBuilder.build(); String token = null; IResponseMessage eIDASRespMsg = null; try { ProtocolEngineI engine = at.gv.egovernment.moa.id.auth.modules.eidas.utils.SAMLEngineUtils.createSAMLEngine(eIDASMetadataProvider); // encryption is done by the SamlEngine, i.e. by the module we provide in the config // but we need to set the appropriate request issuer //engine.setRequestIssuer(eidasRequest.getEidasRequest().getIssuer()); eIDASRespMsg = engine.generateResponseMessage(eidasRequest.getEidasRequest(), response, true, eidasRequest.getRemoteAddress()); token = EidasStringUtil.encodeToBase64(eIDASRespMsg.getMessageBytes()); } catch(Exception e) { Logger.error("eIDAS Response encoding error." , e); throw new MOAIDException("eIDAS.13", new Object[]{e.getMessage()}, e); } revisionsLogger.logEvent(req, Constants.eIDAS_REVERSIONSLOG_IDP_AUTHRESPONSE, eIDASRespMsg.getResponse().getId()); // send the response try { VelocityEngine velocityEngine = VelocityProvider.getClassPathVelocityEngine(); Template template = velocityEngine.getTemplate("/resources/templates/eidas_postbinding_template.vm"); VelocityContext context = new VelocityContext(); context.put("RelayState", eidasRequest.getRemoteRelayState()); context.put("SAMLResponse", token); Logger.debug("SAMLResponse original: " + token); Logger.debug("Putting assertion consumer url as action: " + eidasRequest.getEidasRequest().getAssertionConsumerServiceURL()); context.put("action", eidasRequest.getEidasRequest().getAssertionConsumerServiceURL()); Logger.trace("Starting template merge"); StringWriter writer = new StringWriter(); Logger.trace("Doing template merge"); template.merge(context, writer); Logger.trace("Template merge done"); byte[] content = writer.getBuffer().toString().getBytes("UTF-8"); httpResp.setContentType(MOAIDConstants.DEFAULT_CONTENT_TYPE_HTML_UTF8); httpResp.setContentLength(content.length); httpResp.getOutputStream().write(content); } catch (Exception e) { Logger.error("Velocity error: " + e.getMessage()); throw new MOAIDException("eIDAS.13", new Object[]{e.getMessage()}, e); } SLOInformationInterface ssoContainer = null; try { ssoContainer = new SLOInformationImpl( req.getAuthURL(), eidasRequest.getEidasRequest().getIssuer(), null, subjectNameID, eidasRequest.getEidasRequest().getNameIdFormat(), EIDASProtocol.NAME); } catch (Exception e) { Logger.error("Can not generate container with SSO information!", e); } return ssoContainer; } @Override public boolean needAuthentication(IRequest req, HttpServletRequest httpReq, HttpServletResponse httpResp) { return true; } @Override public String getDefaultActionName() { return "eIDAS_AuthnRequest"; } private void buildAndAddAttribute(ImmutableAttributeMap.Builder attrMapBuilder, AttributeDefinition attr, IRequest req, IAuthData authData) throws MOAIDException { Pair, ImmutableSet>> eIDASAttr = eIDASAttributeBuilder.buildAttribute( attr, req.getServiceProviderConfiguration(), authData); if(eIDASAttr == null) { if (attr.isRequired()) { Logger.info("eIDAS Attr:" + attr.getNameUri() + " is marked as 'Required' but not available."); throw new MOAIDException("eIDAS.15", new Object[]{attr.getFriendlyName()}); } else Logger.info("eIDAS Attr:" + attr.getNameUri() + " is not available."); } else { //add attribute to Map attrMapBuilder.put( (AttributeDefinition)eIDASAttr.getFirst(), (ImmutableSet)eIDASAttr.getSecond()); } } }