From 0537b6bf727985bc9d5c075071b52999f01f1975 Mon Sep 17 00:00:00 2001 From: Thomas Lenz Date: Fri, 18 Nov 2016 13:04:17 +0100 Subject: add first parts to support Redirect-Binding for eIDAS Auth. interface --- .../eidas/tasks/GenerateAuthnRequestTask.java | 178 +++++++++++++++------ 1 file changed, 133 insertions(+), 45 deletions(-) diff --git a/id/server/modules/moa-id-module-eIDAS/src/main/java/at/gv/egovernment/moa/id/auth/modules/eidas/tasks/GenerateAuthnRequestTask.java b/id/server/modules/moa-id-module-eIDAS/src/main/java/at/gv/egovernment/moa/id/auth/modules/eidas/tasks/GenerateAuthnRequestTask.java index 3522a16fd..a9c4d5d3a 100644 --- a/id/server/modules/moa-id-module-eIDAS/src/main/java/at/gv/egovernment/moa/id/auth/modules/eidas/tasks/GenerateAuthnRequestTask.java +++ b/id/server/modules/moa-id-module-eIDAS/src/main/java/at/gv/egovernment/moa/id/auth/modules/eidas/tasks/GenerateAuthnRequestTask.java @@ -22,10 +22,13 @@ */ package at.gv.egovernment.moa.id.auth.modules.eidas.tasks; +import java.io.ByteArrayOutputStream; import java.io.StringWriter; import java.util.ArrayList; import java.util.Collection; import java.util.List; +import java.util.zip.Deflater; +import java.util.zip.DeflaterOutputStream; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; @@ -39,6 +42,7 @@ 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.opensaml.xml.util.Base64; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; @@ -55,10 +59,12 @@ import at.gv.egovernment.moa.id.auth.modules.eidas.exceptions.EIDASEngineExcepti import at.gv.egovernment.moa.id.auth.modules.eidas.utils.SAMLEngineUtils; 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; @@ -111,15 +117,15 @@ public class GenerateAuthnRequestTask extends AbstractAuthServletTask { // select SingleSignOnService Endpoint from eIDAS-node metadata - String destination = null; + SingleSignOnService authnReqEndpoint = null; String metadataUrl = cpeps.getPepsURL().toString().split(";")[0].trim(); try { EntityDescriptor eIDASNodeMetadata = eIDASMetadataProvider.getEntityDescriptor(metadataUrl); if (eIDASNodeMetadata != null) { SingleSignOnService ssoDescr = selectSingleSignOnServiceFromMetadata(eIDASNodeMetadata); if (ssoDescr != null) { - destination = ssoDescr.getLocation(); - Logger.debug("Use destination URL:" + destination + " from eIDAS metadata:" + metadataUrl); + authnReqEndpoint = ssoDescr; + Logger.debug("Use destination URL:" + authnReqEndpoint.getLocation() + " from eIDAS metadata:" + metadataUrl); } else Logger.warn("eIDAS metadata for node:" + metadataUrl + " has no IDPSSODescriptor or no SingleSignOnService information."); @@ -134,13 +140,21 @@ public class GenerateAuthnRequestTask extends AbstractAuthServletTask { // load SingleSignOnService Endpoint from configuration, if Metadata contains no information // FIXME convenience function for not standard conform metadata - if (MiscUtil.isEmpty(destination)) { + if (authnReqEndpoint == null) { + String destination = null; String[] splitString = cpeps.getPepsURL().toString().split(";"); if (splitString.length > 1) destination = cpeps.getPepsURL().toString().split(";")[1].trim(); - if (MiscUtil.isNotEmpty(destination)) + if (MiscUtil.isNotEmpty(destination)) { Logger.debug("Use eIDAS node destination URL:" + destination + " from configuration"); + + //set POST binding as default binding, if Authn. request endpoint from config is used + authnReqEndpoint = SAML2Utils.createSAMLObject(SingleSignOnService.class); + authnReqEndpoint.setLocation(destination); + authnReqEndpoint.setBinding(SAMLConstants.SAML2_POST_BINDING_URI); + + } else { Logger.error("No eIDAS-node destination URL FOUND. Request eIDAS node not possible."); @@ -193,7 +207,7 @@ public class GenerateAuthnRequestTask extends AbstractAuthServletTask { authnRequestBuilder.providerName(pendingReq.getAuthURL()); String issur = pendingReq.getAuthURL() + Constants.eIDAS_HTTP_ENDPOINT_METADATA; authnRequestBuilder.issuer(issur); - authnRequestBuilder.destination(destination); + authnRequestBuilder.destination(authnReqEndpoint.getLocation()); authnRequestBuilder.nameIdFormat(Constants.eIDAS_REQ_NAMEID_FORMAT); @@ -226,45 +240,21 @@ public class GenerateAuthnRequestTask extends AbstractAuthServletTask { String SAMLRequest = EidasStringUtil.encodeToBase64(token); - //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); - Logger.debug("Encoded " + actionType + " original: " + SAMLRequest); - - context.put("RelayState", pendingReq.getRequestID()); - - Logger.debug("Using assertion consumer url as action: " + destination); - context.put("action", destination); - - Logger.debug("Starting template merge"); - StringWriter writer = new StringWriter(); - - Logger.debug("Doing template merge"); - template.merge(context, writer); - Logger.debug("Template merge done"); - - Logger.debug("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); + 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"}); + + } + - revisionsLogger.logEvent(oaConfig, 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); - - } }catch (EIDASSAMLEngineException e){ throw new TaskExecutionException(pendingReq, "eIDAS AuthnRequest generation FAILED.", @@ -280,6 +270,102 @@ public class GenerateAuthnRequestTask extends AbstractAuthServletTask { } } + /** + * Encode the eIDAS request with Redirect binding + * + * @param pendingReq + * @param authnReqEndpoint + * @param token + * @param authnRequest + * @param response + * @throws MOAIDException + */ + private void buildRedirecttBindingRequest(IRequest pendingReq, SingleSignOnService authnReqEndpoint, + byte[] token, IRequestMessage authnRequest, HttpServletResponse response) + throws MOAIDException { + + //FIXME: implement correct deflat encoding accodring to SAML2 Redirect Binding specification + + try { + ByteArrayOutputStream bytesOut = new ByteArrayOutputStream(); + Deflater deflater = new Deflater(Deflater.DEFLATED, true); + DeflaterOutputStream deflaterStream = new DeflaterOutputStream(bytesOut, deflater); + deflaterStream.write(token); + deflaterStream.finish(); + String samlReqBase64 = Base64.encodeBytes(bytesOut.toByteArray(), Base64.DONT_BREAK_LINES); + + + + } catch (Exception e) { + Logger.error("eIDAS Redirect-Binding request encoding error: " + e.getMessage()); + throw new MOAIDException("eIDAS.02", new Object[]{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); + Logger.debug("Encoded " + actionType + " original: " + SAMLRequest); + + context.put("RelayState", pendingReq.getRequestID()); + + Logger.debug("Using assertion consumer url as action: " + authnReqEndpoint.getLocation()); + context.put("action", authnReqEndpoint.getLocation()); + + Logger.debug("Starting template merge"); + StringWriter writer = new StringWriter(); + + Logger.debug("Doing template merge"); + template.merge(context, writer); + Logger.debug("Template merge done"); + + Logger.debug("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; @@ -294,7 +380,9 @@ public class GenerateAuthnRequestTask extends AbstractAuthServletTask { // 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; -- cgit v1.2.3