aboutsummaryrefslogtreecommitdiff
path: root/id/server/modules/moa-id-module-eIDAS/src/main/java/at/gv/egovernment/moa/id/protocols/eidas/EIDASProtocol.java
diff options
context:
space:
mode:
Diffstat (limited to 'id/server/modules/moa-id-module-eIDAS/src/main/java/at/gv/egovernment/moa/id/protocols/eidas/EIDASProtocol.java')
-rw-r--r--id/server/modules/moa-id-module-eIDAS/src/main/java/at/gv/egovernment/moa/id/protocols/eidas/EIDASProtocol.java339
1 files changed, 339 insertions, 0 deletions
diff --git a/id/server/modules/moa-id-module-eIDAS/src/main/java/at/gv/egovernment/moa/id/protocols/eidas/EIDASProtocol.java b/id/server/modules/moa-id-module-eIDAS/src/main/java/at/gv/egovernment/moa/id/protocols/eidas/EIDASProtocol.java
new file mode 100644
index 000000000..24134f1d9
--- /dev/null
+++ b/id/server/modules/moa-id-module-eIDAS/src/main/java/at/gv/egovernment/moa/id/protocols/eidas/EIDASProtocol.java
@@ -0,0 +1,339 @@
+/*******************************************************************************
+ * 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.IOException;
+import java.io.StringWriter;
+import java.util.List;
+
+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.opensaml.saml2.metadata.AssertionConsumerService;
+import org.springframework.http.MediaType;
+import org.springframework.stereotype.Controller;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestMethod;
+
+import at.gv.egovernment.moa.id.advancedlogging.MOAIDEventConstants;
+import at.gv.egovernment.moa.id.auth.frontend.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.engine.MOAeIDASMetadataProviderDecorator;
+import at.gv.egovernment.moa.id.auth.modules.eidas.exceptions.eIDASAuthnRequestProcessingException;
+import at.gv.egovernment.moa.id.auth.modules.eidas.exceptions.eIDASAuthnRequestValidationException;
+import at.gv.egovernment.moa.id.auth.modules.eidas.exceptions.eIDASException;
+import at.gv.egovernment.moa.id.auth.modules.eidas.utils.MOAPersonalAttributeList;
+import at.gv.egovernment.moa.id.auth.modules.eidas.utils.SAMLEngineUtils;
+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.exceptions.MOAIDException;
+import at.gv.egovernment.moa.id.moduls.RequestImpl;
+import at.gv.egovernment.moa.id.protocols.AbstractAuthProtocolModulController;
+import at.gv.egovernment.moa.logging.Logger;
+import at.gv.egovernment.moa.util.MiscUtil;
+import eu.eidas.auth.commons.EIDASAuthnRequest;
+import eu.eidas.auth.commons.EIDASAuthnResponse;
+import eu.eidas.auth.commons.EIDASUtil;
+import eu.eidas.auth.engine.EIDASSAMLEngine;
+import eu.eidas.auth.engine.metadata.MetadataUtil;
+import eu.eidas.engine.exceptions.EIDASSAMLEngineException;
+
+/**
+ * eIDAS Protocol Support for outbound authentication and metadata generation
+ *
+ * @author tlenz
+ */
+@Controller
+public class EIDASProtocol extends AbstractAuthProtocolModulController {
+
+ public static final String NAME = EIDASProtocol.class.getName();
+ public static final String PATH = "eidas";
+
+ public EIDASProtocol() {
+ super();
+ Logger.debug("Registering servlet " + getClass().getName() +
+ " with mappings '" + Constants.eIDAS_HTTP_ENDPOINT_METADATA +
+ "' and '" + Constants.eIDAS_HTTP_ENDPOINT_IDP_COLLEAGUEREQUEST +
+ "' and '" + Constants.eIDAS_HTTP_ENDPOINT_IDP_POST +"'.");
+
+ }
+
+ public String getName() {
+ return NAME;
+ }
+
+ public String getPath() {
+ return PATH;
+ }
+
+ //eIDAS metadata end-point
+ @RequestMapping(value = "/eidas/metadata", method = {RequestMethod.GET})
+ public void eIDASMetadataRequest(HttpServletRequest req, HttpServletResponse resp) throws MOAIDException {
+
+ //create pendingRequest object
+ EIDASData pendingReq = applicationContext.getBean(EIDASData.class);
+ pendingReq.initialize(req);
+ pendingReq.setModule(NAME);
+ pendingReq.setNeedAuthentication(false);
+ pendingReq.setAuthenticated(false);
+
+ revisionsLogger.logEvent(
+ pendingReq.getUniqueSessionIdentifier(),
+ pendingReq.getUniqueTransactionIdentifier(),
+ MOAIDEventConstants.TRANSACTION_IP,
+ req.getRemoteAddr());
+
+
+ EidasMetaDataRequest metadataAction = applicationContext.getBean(EidasMetaDataRequest.class);
+ metadataAction.processRequest(pendingReq,
+ req, resp, null);
+
+ revisionsLogger.logEvent(
+ pendingReq.getUniqueSessionIdentifier(),
+ pendingReq.getUniqueTransactionIdentifier(),
+ Constants.eIDAS_REVERSIONSLOG_METADATA);
+ }
+
+
+ //PVP2.x IDP POST-Binding end-point
+ @RequestMapping(value = "/eidas/ColleagueRequest", method = {RequestMethod.POST})
+ public void PVPIDPPostRequest(HttpServletRequest req, HttpServletResponse resp) throws MOAIDException, IOException {
+
+ //create pending-request object
+ EIDASData pendingReq = applicationContext.getBean(EIDASData.class);
+ pendingReq.initialize(req);
+ pendingReq.setModule(NAME);
+
+ revisionsLogger.logEvent(MOAIDEventConstants.SESSION_CREATED, pendingReq.getUniqueSessionIdentifier());
+ revisionsLogger.logEvent(MOAIDEventConstants.TRANSACTION_CREATED, pendingReq.getUniqueTransactionIdentifier());
+ revisionsLogger.logEvent(
+ pendingReq.getUniqueSessionIdentifier(),
+ pendingReq.getUniqueTransactionIdentifier(),
+ MOAIDEventConstants.TRANSACTION_IP,
+ req.getRemoteAddr());
+
+ //preProcess eIDAS request
+ preProcess(req, resp, pendingReq);
+
+ revisionsLogger.logEvent(pendingReq, Constants.eIDAS_REVERSIONSLOG_IDP_AUTHREQUEST);
+
+ //AuthnRequest needs authentication
+ pendingReq.setNeedAuthentication(true);
+
+ //set protocol action, which should be executed after authentication
+ pendingReq.setAction(eIDASAuthenticationRequest.class.getName());
+
+ //switch to session authentication
+ performAuthentication(req, resp, pendingReq);
+ }
+
+ /*
+ First request step - send it to BKU selection for user authentication. After the user credentials
+ and other info are obtained, in the second step the request will be processed and the user redirected
+ */
+ private void preProcess(HttpServletRequest request, HttpServletResponse response, EIDASData pendingReq) throws MOAIDException {
+
+ Logger.info("received an eIDaS request");
+
+ //get SAML Response and decode it
+ String base64SamlToken = request.getParameter("SAMLRequest");
+ if (MiscUtil.isEmpty(base64SamlToken)) {
+ Logger.warn("No eIDAS SAMLRequest found in http request.");
+ throw new MOAIDException("HTTP request includes no eIDAS SAML-Request element.", null);
+ }
+ byte[] decSamlToken = EIDASUtil.decodeSAMLToken(base64SamlToken);
+
+ try {
+ //get eIDAS SAML-engine
+ EIDASSAMLEngine engine = SAMLEngineUtils.createSAMLEngine();
+
+ //validate SAML token
+ EIDASAuthnRequest samlReq = engine.validateEIDASAuthnRequest(decSamlToken);
+
+ // - memorize remote ip
+ pendingReq.setRemoteAddress(request.getRemoteAddr());
+
+ // - memorize relaystate
+ String relayState = request.getParameter("RelayState");
+ pendingReq.setRemoteRelayState(relayState);
+
+ // - memorize country code of target country
+ pendingReq.setGenericDataToSession(
+ RequestImpl.eIDAS_GENERIC_REQ_DATA_COUNTRY, samlReq.getCountry());
+
+ // - memorize requested attributes
+ pendingReq.setEidasRequestedAttributes(new MOAPersonalAttributeList(samlReq.getPersonalAttributeList()));
+
+ // - memorize whole request
+ samlReq.setPersonalAttributeList(pendingReq.getEidasRequestedAttributes()); // circumvent non-serializable eidas personal attribute list
+ pendingReq.setEidasRequest(samlReq);
+
+ //validate destination against metadata
+ String reqDestination = samlReq.getDestination();
+ if (MiscUtil.isNotEmpty(reqDestination)) {
+ boolean isValid = false;
+ List<AssertionConsumerService> allowedAssertionConsumerUrl = new MOAeIDASMetadataProviderDecorator(MOAeIDASChainingMetadataProvider.getInstance())
+ .getSPSSODescriptor(samlReq.getIssuer()).getAssertionConsumerServices();
+
+ for (AssertionConsumerService el : allowedAssertionConsumerUrl) {
+ if (reqDestination.equals(el.getLocation()))
+ isValid = true;
+
+ }
+
+ if (!isValid) {
+ Logger.info("eIDAS AuthnRequest contains a not valid 'Destination' attribute");
+ throw new eIDASAuthnRequestValidationException("stork.01",
+ new Object[]{"eIDAS AuthnRequest contains a not valid 'Destination' attribute"});
+ }
+
+ }
+
+
+ // - memorize OA url
+ pendingReq.setOAURL(samlReq.getIssuer());
+
+ // - memorize OA config
+ IOAAuthParameters oaConfig = authConfig.getOnlineApplicationParameter(pendingReq.getOAURL());
+ if (oaConfig == null)
+ throw new eIDASAuthnRequestProcessingException("eIDAS.08", new Object[]{pendingReq.getOAURL()});
+
+ pendingReq.setOnlineApplicationConfiguration(oaConfig);
+
+ String spType = samlReq.getSPType();
+ if (MiscUtil.isEmpty(spType)) {
+ Logger.info("Load SPType from metadata ... IS NOT IMPLEMENTED YET!!!");
+ //TODO: maybe implement this if required
+
+ }
+
+ Logger.debug("eIDAS request has SPType:" + spType);
+
+ } catch (MOAIDException e) {
+ Logger.info("eIDAS AuthnRequest preProcessing FAILED. Msg:" + e.getMessage());
+ throw e;
+
+ } catch (EIDASSAMLEngineException e) {
+ Logger.info("eIDAS AuthnRequest preProcessing FAILED. Msg:" + e.getMessage());
+ throw new eIDASAuthnRequestProcessingException("eIDAS.06", new Object[]{e.getMessage()}, e);
+
+ } catch(Exception e) {
+ Logger.warn("eIDAS AuthnRequest preProcessing FAILED. Msg:" + e.getMessage(), e);
+ throw new eIDASAuthnRequestProcessingException("eIDAS.06", new Object[]{e.getMessage()}, e);
+
+ }
+ }
+
+ public boolean generateErrorMessage(Throwable e, HttpServletRequest request, HttpServletResponse response, IRequest pendingReq) throws Throwable {
+ if (pendingReq != null && pendingReq instanceof EIDASData) {
+ EIDASData eidasReq = (EIDASData) pendingReq;
+ if (eidasReq.getEidasRequest() == null) {
+ Logger.info("Can not build eIDAS ErrorResponse. No eIDAS AuthnRequest found.");
+ return false;
+ }
+
+ try {
+ EIDASAuthnResponse eIDASResp = new EIDASAuthnResponse();
+ eIDASResp.setIssuer(pendingReq.getAuthURL() + Constants.eIDAS_HTTP_ENDPOINT_METADATA);
+
+ if (e instanceof eIDASException) {
+ eIDASResp.setStatusCode(((eIDASException) e).getStatusCodeFirstLevel());
+ eIDASResp.setSubStatusCode(((eIDASException) e).getStatusCodeSecondLevel());
+ eIDASResp.setMessage(e.getMessage());
+
+ } else if (e instanceof MOAIDException ) {
+ eIDASResp.setStatusCode(StatusCode.RESPONDER_URI);
+ eIDASResp.setSubStatusCode(StatusCode.AUTHN_FAILED_URI);
+ eIDASResp.setMessage(e.getMessage());
+
+ } else {
+ eIDASResp.setStatusCode(StatusCode.RESPONDER_URI);
+ eIDASResp.setSubStatusCode(StatusCode.AUTHN_FAILED_URI);
+ eIDASResp.setMessage(e.getMessage());
+
+ }
+
+
+ EIDASSAMLEngine engine = SAMLEngineUtils.createSAMLEngine();
+
+ if(null == eidasReq.getEidasRequest().getAssertionConsumerServiceURL()) {
+ String assertionConsumerUrl = MetadataUtil.getAssertionUrlFromMetadata(
+ new MOAeIDASMetadataProviderDecorator(MOAeIDASChainingMetadataProvider.getInstance()),
+ engine,
+ eidasReq.getEidasRequest());
+ eidasReq.getEidasRequest().setAssertionConsumerServiceURL(assertionConsumerUrl);
+
+ }
+ //get eIDAS SAML-engine
+
+ eIDASResp = engine.generateEIDASAuthnResponseFail(eidasReq.getEidasRequest(), eIDASResp,
+ eidasReq.getRemoteAddress(), true);
+
+ String token = EIDASUtil.encodeSAMLToken(eIDASResp.getTokenSaml());
+
+ VelocityEngine velocityEngine = VelocityProvider.getClassPathVelocityEngine();
+ Template template = velocityEngine.getTemplate("/resources/templates/stork2_postbinding_template.html");
+ VelocityContext context = new VelocityContext();
+
+ context.put("RelayState", eidasReq.getRemoteRelayState());
+
+ context.put("SAMLResponse", token);
+ Logger.debug("SAMLResponse original: " + token);
+
+ Logger.debug("Putting assertion consumer url as action: " + eidasReq.getEidasRequest().getAssertionConsumerServiceURL());
+ context.put("action", eidasReq.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");
+
+ Logger.trace("Sending html content : " + new String(writer.getBuffer()));
+
+ response.getOutputStream().write(writer.getBuffer().toString().getBytes("UTF-8"));
+ response.setContentType(MediaType.TEXT_HTML.getType());
+
+ return true;
+
+ } catch (Exception e1 ) {
+ Logger.error("Generate eIDAS Error-Response failed.", e);
+
+ }
+
+ }
+
+ return false;
+ }
+
+ public boolean validate(HttpServletRequest request, HttpServletResponse response, IRequest pending) {
+ return false;
+ }
+}
+
+