aboutsummaryrefslogtreecommitdiff
path: root/id/server/modules/module-stork/src/main/java/at/gv/egovernment/moa/id/auth/modules/stork/tasks/PepsConnectorHandleResponseWithoutSignatureTask.java
diff options
context:
space:
mode:
Diffstat (limited to 'id/server/modules/module-stork/src/main/java/at/gv/egovernment/moa/id/auth/modules/stork/tasks/PepsConnectorHandleResponseWithoutSignatureTask.java')
-rw-r--r--id/server/modules/module-stork/src/main/java/at/gv/egovernment/moa/id/auth/modules/stork/tasks/PepsConnectorHandleResponseWithoutSignatureTask.java439
1 files changed, 439 insertions, 0 deletions
diff --git a/id/server/modules/module-stork/src/main/java/at/gv/egovernment/moa/id/auth/modules/stork/tasks/PepsConnectorHandleResponseWithoutSignatureTask.java b/id/server/modules/module-stork/src/main/java/at/gv/egovernment/moa/id/auth/modules/stork/tasks/PepsConnectorHandleResponseWithoutSignatureTask.java
new file mode 100644
index 000000000..3338804b4
--- /dev/null
+++ b/id/server/modules/module-stork/src/main/java/at/gv/egovernment/moa/id/auth/modules/stork/tasks/PepsConnectorHandleResponseWithoutSignatureTask.java
@@ -0,0 +1,439 @@
+package at.gv.egovernment.moa.id.auth.modules.stork.tasks;
+
+import iaik.x509.X509Certificate;
+
+import java.io.IOException;
+import java.io.StringWriter;
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import javax.xml.transform.Source;
+import javax.xml.transform.stream.StreamSource;
+
+import org.apache.commons.codec.binary.Base64;
+import org.apache.commons.io.IOUtils;
+import org.apache.commons.lang.StringEscapeUtils;
+import org.apache.velocity.Template;
+import org.apache.velocity.VelocityContext;
+import org.apache.velocity.app.VelocityEngine;
+import org.opensaml.saml2.core.StatusCode;
+
+import at.gv.egovernment.moa.id.auth.AuthenticationServer;
+import at.gv.egovernment.moa.id.auth.builder.DataURLBuilder;
+import at.gv.egovernment.moa.id.auth.data.AuthenticationSession;
+import at.gv.egovernment.moa.id.auth.exception.AuthenticationException;
+import at.gv.egovernment.moa.id.auth.exception.MOAIDException;
+import at.gv.egovernment.moa.id.auth.servlet.PEPSConnectorWithLocalSigningServlet;
+import at.gv.egovernment.moa.id.auth.stork.STORKException;
+import at.gv.egovernment.moa.id.auth.stork.STORKResponseProcessor;
+import at.gv.egovernment.moa.id.commons.db.ConfigurationDBUtils;
+import at.gv.egovernment.moa.id.commons.db.dao.config.AttributeProviderPlugin;
+import at.gv.egovernment.moa.id.config.auth.AuthConfigurationProvider;
+import at.gv.egovernment.moa.id.moduls.ModulUtils;
+import at.gv.egovernment.moa.id.process.api.ExecutionContext;
+import at.gv.egovernment.moa.id.storage.AuthenticationSessionStoreage;
+import at.gv.egovernment.moa.id.util.HTTPUtils;
+import at.gv.egovernment.moa.id.util.VelocityProvider;
+import at.gv.egovernment.moa.logging.Logger;
+import at.gv.egovernment.moa.util.StringUtils;
+import eu.stork.oasisdss.api.ApiUtils;
+import eu.stork.oasisdss.profile.SignRequest;
+import eu.stork.oasisdss.profile.SignResponse;
+import eu.stork.peps.auth.commons.IPersonalAttributeList;
+import eu.stork.peps.auth.commons.PEPSUtil;
+import eu.stork.peps.auth.commons.PersonalAttribute;
+import eu.stork.peps.auth.commons.STORKAuthnRequest;
+import eu.stork.peps.auth.commons.STORKAuthnResponse;
+import eu.stork.peps.auth.engine.STORKSAMLEngine;
+import eu.stork.peps.exceptions.STORKSAMLEngineException;
+
+/**
+ * Validates the SAML response from C-PEPS.
+ * <p/>
+ * In detail:
+ * <ul>
+ * <li>Decodes and validates SAML response from C-PEPS.</li>
+ * <li>Retrieves the moa session using the session id provided by HttpServletRequest parameter {@code RelayState} or by {@code inResponseTo} attribute of the saml response.</li>
+ * <li>Store saml response in moa session.</li>
+ * <li>Change moa session id.</li>
+ * <li>Redirect to {@code /PEPSConnectorWithLocalSigning}, with providing the moa session id as request parameter.</li>
+ * </ul>
+ * Expects:
+ * <ul>
+ * <li>HttpServletRequest parameter {@code moaSessionID} <strong>to be {@code null}</strong></li>
+ * <li>HttpServletRequest parameter {@code signresponse} <strong>to be {@code null}</strong></li>
+ * <li>HttpServletRequest parameter {@code SAMLResponse}</li>
+ * <li>Either HttpServletRequest parameter {@code RelayState} or {@code inResponseTo} attribute within the saml response, both reflecting the moa session id.</li>
+ * </ul>
+ * Result:
+ * <ul>
+ * <li>Updated moa session (with saml response).</li>
+ * <li>Redirect to {@code /PEPSConnectorWithLocalSigning}, with providing the moa session id as request parameter.</li>
+ * </ul>
+ * Code taken from {@link at.gv.egovernment.moa.id.auth.servlet.PEPSConnectorWithLocalSigningServlet}.<br/>
+ *
+ * @see #execute(ExecutionContext, HttpServletRequest, HttpServletResponse)
+ */
+public class PepsConnectorHandleResponseWithoutSignatureTask extends AbstractPepsConnectorWithLocalSigningTask {
+
+ private String oasisDssWebFormURL = "https://testvidp.buergerkarte.at/oasis-dss/DSSWebFormServlet";
+ // load from config below
+
+ @Override
+ public void execute(ExecutionContext executionContext, HttpServletRequest request, HttpServletResponse response)
+ throws Exception {
+ String moaSessionID = request.getParameter("moaSessionID");
+ String signResponse = request.getParameter("signresponse");
+ Logger.info("moaSessionID:" + moaSessionID);
+ Logger.info("signResponse:" + signResponse);
+
+ if (moaSessionID == null && signResponse == null) {
+ // normal saml response
+ handleSAMLResponse(executionContext, request, response);
+
+ } else {
+ // should not occur
+ throw new IOException("should not occur");
+ }
+ return;
+ }
+
+ private void handleSAMLResponse(ExecutionContext executionContext, HttpServletRequest request,
+ HttpServletResponse response) {
+ Logger.info("handleSAMLResponse started");
+ String pendingRequestID = null;
+
+ setNoCachingHeaders(response);
+ try {
+ Logger.info("PEPSConnector Servlet invoked, expecting C-PEPS message.");
+ Logger.debug("This ACS endpoint is: " + HTTPUtils.getBaseURL(request));
+
+ Logger.trace("No Caching headers set for HTTP response");
+
+ // check if https or only http
+ super.checkIfHTTPisAllowed(request.getRequestURL().toString());
+
+ Logger.debug("Beginning to extract SAMLResponse out of HTTP Request");
+
+ // extract STORK Response from HTTP Request
+ // Decodes SAML Response
+ byte[] decSamlToken;
+ try {
+ decSamlToken = PEPSUtil.decodeSAMLToken(request.getParameter("SAMLResponse"));
+ Logger.debug("SAMLResponse: " + new String(decSamlToken));
+
+ } catch (NullPointerException e) {
+ Logger.error("Unable to retrieve STORK Response", e);
+ throw new MOAIDException("stork.04", null);
+ }
+
+ // Get SAMLEngine instance
+ STORKSAMLEngine engine = STORKSAMLEngine.getInstance("outgoing");
+
+ STORKAuthnResponse authnResponse = null;
+ try {
+ // validate SAML Token
+ Logger.debug("Starting validation of SAML response");
+ authnResponse = engine.validateSTORKAuthnResponse(decSamlToken, (String) request.getRemoteHost());
+ Logger.info("SAML response succesfully verified!");
+ } catch (STORKSAMLEngineException e) {
+ Logger.error("Failed to verify STORK SAML Response", e);
+ throw new MOAIDException("stork.05", null);
+ }
+
+ Logger.info("STORK SAML Response message succesfully extracted");
+ Logger.debug("STORK response: ");
+ Logger.debug(authnResponse.toString());
+
+ Logger.debug("Trying to find MOA Session-ID ...");
+ // String moaSessionID = request.getParameter(PARAM_SESSIONID);
+ // first use SAML2 relayState
+ String moaSessionID = request.getParameter("RelayState");
+
+ // escape parameter strings
+ moaSessionID = StringEscapeUtils.escapeHtml(moaSessionID);
+
+ // check if SAML2 relaystate includes a MOA sessionID
+ if (StringUtils.isEmpty(moaSessionID)) {
+ // if relaystate is emtpty, use SAML response -> inResponseTo element as session identifier
+
+ moaSessionID = authnResponse.getInResponseTo();
+ moaSessionID = StringEscapeUtils.escapeHtml(moaSessionID);
+
+ if (StringUtils.isEmpty(moaSessionID)) {
+ // No authentication session has been started before
+ Logger.error("MOA-SessionID was not found, no previous AuthnRequest had been started");
+ Logger.debug("PEPSConnectorURL was: " + request.getRequestURL());
+ throw new AuthenticationException("auth.02", new Object[] { moaSessionID });
+
+ } else
+ Logger.trace("Use MOA SessionID " + moaSessionID + " from AuthnResponse->inResponseTo attribute.");
+
+ } else
+ // Logger.trace("MOA SessionID " + moaSessionID + " is found in http GET parameter.");
+ Logger.trace("MOA SessionID " + moaSessionID + " is found in SAML2 relayState.");
+
+ /*
+ * INFO!!!! SAML message IDs has an different format then MOASessionIDs This is only a workaround because
+ * many PEPS does not support SAML2 relayState or MOASessionID as AttributConsumerServiceURL GET parameter
+ */
+ // if (!ParamValidatorUtils.isValidSessionID(moaSessionID))
+ // throw new WrongParametersException("VerifyAuthenticationBlock", PARAM_SESSIONID, "auth.12");
+
+ pendingRequestID = AuthenticationSessionStoreage.getPendingRequestID(moaSessionID);
+
+ // load MOASession from database
+ AuthenticationSession moaSession = AuthenticationServer.getSession(moaSessionID);
+ // change MOASessionID
+ moaSessionID = AuthenticationSessionStoreage.changeSessionID(moaSession);
+
+ Logger.info("Found MOA sessionID: " + moaSessionID);
+
+ String statusCodeValue = authnResponse.getStatusCode();
+
+ if (!statusCodeValue.equals(StatusCode.SUCCESS_URI)) {
+ Logger.error("Received ErrorResponse from PEPS: " + statusCodeValue);
+ throw new MOAIDException("stork.06", new Object[] { statusCodeValue });
+ }
+
+ Logger.info("Got SAML response with authentication success message.");
+
+ Logger.debug("MOA session is still valid");
+
+ STORKAuthnRequest storkAuthnRequest = moaSession.getStorkAuthnRequest();
+
+ if (storkAuthnRequest == null) {
+ Logger.error("Could not find any preceeding STORK AuthnRequest to this MOA session: " + moaSessionID);
+ throw new MOAIDException("stork.07", null);
+ }
+
+ Logger.debug("Found a preceeding STORK AuthnRequest to this MOA session: " + moaSessionID);
+
+ // //////////// incorporate gender from parameters if not in stork response
+
+ IPersonalAttributeList attributeList = authnResponse.getPersonalAttributeList();
+
+ // but first, check if we have a representation case
+ if (STORKResponseProcessor.hasAttribute("mandateContent", attributeList)
+ || STORKResponseProcessor.hasAttribute("representative", attributeList)
+ || STORKResponseProcessor.hasAttribute("represented", attributeList)) {
+ // in a representation case...
+ moaSession.setUseMandate("true");
+
+ // and check if we have the gender value
+ PersonalAttribute gender = attributeList.get("gender");
+ if (null == gender) {
+ String gendervalue = (String) request.getParameter("gender");
+ if (null != gendervalue) {
+ gender = new PersonalAttribute();
+ gender.setName("gender");
+ ArrayList<String> tmp = new ArrayList<String>();
+ tmp.add(gendervalue);
+ gender.setValue(tmp);
+
+ authnResponse.getPersonalAttributeList().add(gender);
+ }
+ }
+ }
+
+
+
+ // ////////////////////////////////////////////////////////////////////////
+
+ Logger.debug("Starting extraction of signedDoc attribute");
+ // extract signed doc element and citizen signature
+ String citizenSignature = null;
+ try {
+ PersonalAttribute signedDoc = authnResponse.getPersonalAttributeList().get("signedDoc");
+ String signatureInfo = null;
+ // FIXME: Remove nonsense code (signedDoc attribute... (throw Exception for "should not occur" situations)), adjust error messages in order to reflect the true problem...
+ if (signedDoc != null) {
+ signatureInfo = signedDoc.getValue().get(0);
+ // should not occur
+ } else {
+
+ // store SAMLResponse
+ moaSession.setSAMLResponse(request.getParameter("SAMLResponse"));
+ // store authnResponse
+
+ // moaSession.setAuthnResponse(authnResponse);//not serializable
+ moaSession.setAuthnResponseGetPersonalAttributeList(authnResponse.getPersonalAttributeList());
+
+ String authnContextClassRef = null;
+ try {
+ authnContextClassRef = authnResponse.getAssertions().get(0).getAuthnStatements().get(0)
+ .getAuthnContext().getAuthnContextClassRef().getAuthnContextClassRef();
+ } catch (Throwable e) {
+ Logger.warn("STORK QAA-Level is not found in AuthnResponse. Set QAA Level to requested level");
+ }
+
+ moaSession.setAuthnContextClassRef(authnContextClassRef);
+ moaSession.setReturnURL(request.getRequestURL());
+
+ // load signedDoc
+ String signRequest = moaSession.getSignedDoc();
+
+ // session is implicit stored in changeSessionID!!!!
+ String newMOASessionID = AuthenticationSessionStoreage.changeSessionID(moaSession);
+
+ // set return url to PEPSConnectorWithLocalSigningServlet and add newMOASessionID
+ // signRequest
+
+ String issuerValue = AuthConfigurationProvider.getInstance().getPublicURLPrefix();
+ String acsURL = issuerValue
+ + PEPSConnectorWithLocalSigningServlet.PEPSCONNECTOR_SERVLET_URL_PATTERN;
+
+ String url = acsURL + "?moaSessionID=" + newMOASessionID;
+ // redirect to OASIS module and sign there
+
+ boolean found = false;
+ try {
+ List<AttributeProviderPlugin> aps = AuthConfigurationProvider.getInstance()
+ .getOnlineApplicationParameter(moaSession.getPublicOAURLPrefix()).getStorkAPs();
+ Logger.info("Found AttributeProviderPlugins:" + aps.size());
+ for (AttributeProviderPlugin ap : aps) {
+ Logger.info("Found AttributeProviderPlugin attribute:" + ap.getAttributes());
+ if (ap.getAttributes().equalsIgnoreCase("signedDoc")) {
+ // FIXME: A servlet's class field is not thread safe!!!
+ oasisDssWebFormURL = ap.getUrl();
+ found = true;
+ Logger.info("Loaded signedDoc attribute provider url from config:" + oasisDssWebFormURL);
+ break;
+ }
+ }
+ } catch (Exception e) {
+ e.printStackTrace();
+ Logger.error("Loading the signedDoc attribute provider url from config failed");
+ }
+ if (!found) {
+ Logger.error("Failed to load the signedDoc attribute provider url from config");
+ }
+ performRedirect(url, request, response, signRequest);
+
+ return;
+ }
+
+ // FIXME: This servlet/task is intended to handle peps responses without signature, so why do we try to process that signature here?
+ SignResponse dssSignResponse = (SignResponse) ApiUtils.unmarshal(new StreamSource(
+ new java.io.StringReader(signatureInfo)));
+
+ citizenSignature = getCitizienSignatureFromSignResponse(dssSignResponse);
+
+ // memorize signature into authblock
+ moaSession.setAuthBlock(citizenSignature);
+
+ X509Certificate cert = getSignerCertificate(citizenSignature);
+ moaSession.setSignerCertificate(cert);
+ moaSession.setForeigner(true);
+
+ } catch (Throwable e) {
+ Logger.error("Could not extract citizen signature from C-PEPS", e);
+ throw new MOAIDException("stork.09", null);
+ }
+
+ // FIXME: Same here; we do not have the citizen's signature, so this code might be regarded as dead code.
+ try {
+ SZRGInsertion(moaSession, authnResponse.getPersonalAttributeList(), authnResponse.getAssertions()
+ .get(0).getAuthnStatements().get(0).getAuthnContext().getAuthnContextClassRef()
+ .getAuthnContextClassRef(), citizenSignature);
+ } catch (STORKException e) {
+ // this is really nasty but we work against the system here. We are supposed to get the gender attribute
+ // from
+ // stork. If we do not, we cannot register the person in the ERnP - we have to have the
+ // gender for the represented person. So here comes the dirty hack.
+ if (e.getCause() instanceof STORKException
+ && e.getCause().getMessage().equals("gender not found in response")) {
+ try {
+ Logger.trace("Initialize VelocityEngine...");
+
+ VelocityEngine velocityEngine = VelocityProvider.getClassPathVelocityEngine();
+ Template template = velocityEngine.getTemplate("/resources/templates/fetchGender.html");
+ VelocityContext context = new VelocityContext();
+ context.put("SAMLResponse", request.getParameter("SAMLResponse"));
+ context.put("action", request.getRequestURL());
+
+ StringWriter writer = new StringWriter();
+ template.merge(context, writer);
+
+ response.getOutputStream().write(writer.toString().getBytes("UTF-8"));
+ } catch (Exception e1) {
+ Logger.error("Error sending gender retrival form.", e1);
+ // httpSession.invalidate();
+ throw new MOAIDException("stork.10", null);
+ }
+
+ return;
+ }
+
+ Logger.error("Error connecting SZR Gateway", e);
+ throw new MOAIDException("stork.10", null);
+ }
+
+ Logger.debug("Add full STORK AuthnResponse to MOA session");
+ moaSession.setStorkAuthnResponse(request.getParameter("SAMLResponse"));// TODO ask Florian/Thomas
+ // authnResponse?
+
+ // session is implicit stored in changeSessionID!!!!
+ String newMOASessionID = AuthenticationSessionStoreage.changeSessionID(moaSession);
+
+ Logger.info("Changed MOASession " + moaSessionID + " to Session " + newMOASessionID);
+
+ // redirect
+ String redirectURL = null;
+ redirectURL = new DataURLBuilder().buildDataURL(moaSession.getAuthURL(),
+ ModulUtils.buildAuthURL(moaSession.getModul(), moaSession.getAction(), pendingRequestID),
+ newMOASessionID);
+ redirectURL = response.encodeRedirectURL(redirectURL);
+
+ response.setContentType("text/html");
+ response.setStatus(302);
+ response.addHeader("Location", redirectURL);
+ Logger.info("REDIRECT TO: " + redirectURL);
+
+ } catch (AuthenticationException e) {
+ handleError(null, e, request, response, pendingRequestID);
+
+ } catch (MOAIDException e) {
+ handleError(null, e, request, response, pendingRequestID);
+
+ } catch (Exception e) {
+ Logger.error("PEPSConnector has an interal Error.", e);
+ }
+
+ finally {
+ ConfigurationDBUtils.closeSession();
+ }
+
+ }
+
+ private void performRedirect(String url, HttpServletRequest req, HttpServletResponse resp, String signRequestString)
+ throws MOAIDException {
+
+ try {
+ Logger.trace("Initialize VelocityEngine...");
+
+ VelocityEngine velocityEngine = VelocityProvider.getClassPathVelocityEngine();
+ Template template = velocityEngine.getTemplate("/resources/templates/oasis_dss_webform_binding.vm");
+ VelocityContext context = new VelocityContext();
+
+ Logger.debug("performRedirect, signrequest:" + signRequestString);
+ Source signDoc = new StreamSource(new java.io.StringReader(signRequestString));
+ SignRequest signRequest = ApiUtils.unmarshal(signDoc, SignRequest.class);
+ signRequest.setReturnURL("TODO");
+ signRequestString = IOUtils.toString(ApiUtils.marshalToInputStream(signRequest));
+ context.put("signrequest", Base64.encodeBase64String(signRequestString.getBytes("UTF8")));
+ context.put("clienturl", url);
+ context.put("action", oasisDssWebFormURL);
+
+ StringWriter writer = new StringWriter();
+ template.merge(context, writer);
+
+ resp.getOutputStream().write(writer.toString().getBytes("UTF-8"));
+ } catch (Exception e) {
+ Logger.error("Error sending DSS signrequest.", e);
+ throw new MOAIDException("stork.11", null);
+ }
+ }
+}