package at.gv.egovernment.moa.id.protocols.stork2; 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.stork.VelocityProvider; import at.gv.egovernment.moa.id.commons.db.ex.MOADatabaseException; import at.gv.egovernment.moa.id.config.auth.AuthConfigurationProvider; import at.gv.egovernment.moa.id.config.auth.OAAuthParameter; import at.gv.egovernment.moa.id.moduls.IAction; import at.gv.egovernment.moa.id.moduls.IRequest; import at.gv.egovernment.moa.id.storage.AssertionStorage; import at.gv.egovernment.moa.logging.Logger; import eu.stork.peps.auth.commons.IPersonalAttributeList; 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; import org.apache.commons.io.IOUtils; import org.apache.velocity.Template; import org.apache.velocity.VelocityContext; import org.apache.velocity.app.VelocityEngine; import org.opensaml.common.impl.SecureRandomIdentifierGenerator; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; import java.io.IOException; import java.io.StringWriter; import java.util.ArrayList; import java.util.List; /** * the AttributeCollector Action tries to get all requested attributes from a set of {@link AttributeProvider} Plugins. * The class is called whenever the {@link AuthenticationRequest} Action is invoked and checks for missing attributes. * Furthermore, the class can handle direct posts. That is when the class triggers an attribute query which needs user * interaction, redirect to another portal, etc. The redirect will hit here and the class can continue to fetch attributes. *

* TODO how do we treat mandatory and optional attributes? */ public class AttributeCollector implements IAction { /** * The Constant ARTIFACT_ID. */ private static final String ARTIFACT_ID = "artifactId"; private DataContainer container; private HttpServletResponse httpResp; /* (non-Javadoc) * @see at.gv.egovernment.moa.id.moduls.IAction#processRequest(at.gv.egovernment.moa.id.moduls.IRequest, javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse, at.gv.egovernment.moa.id.auth.data.AuthenticationSession) */ public String processRequest(IRequest req, HttpServletRequest httpReq, HttpServletResponse httpResp, AuthenticationSession moasession) throws MOAIDException { this.httpResp = httpResp; // read configuration parameters of OA OAAuthParameter oaParam = AuthConfigurationProvider.getInstance().getOnlineApplicationParameter(moasession.getPublicOAURLPrefix()); if (oaParam == null) throw new AuthenticationException("stork.12", new Object[]{moasession.getPublicOAURLPrefix()}); // find the attribute provider plugin that can handle the response IPersonalAttributeList newAttributes = null; for (AttributeProvider current : AttributeProviderFactory.getConfiguredPlugins(oaParam.getStorkAPs())) try { newAttributes = current.parse(httpReq); } catch (UnsupportedAttributeException e1) { // the current provider cannot find anything familiar within the // provided httpreq. Try the next one. // TODO check the loop } if (null == newAttributes) { // we do not have a provider which is capable of fetching something // from the received httpreq. // TODO should we continue with the next attribute? Logger.error("No attribute could be retrieved from the response the attribute provider gave us."); throw new MOAIDException("stork.11", null); } // - fetch the container String artifactId = (String) httpReq.getAttribute(ARTIFACT_ID); DataContainer container; try { container = AssertionStorage.getInstance().get(artifactId, DataContainer.class); } catch (MOADatabaseException e) { Logger.error("Error fetching incomplete Stork response from temporary storage. Most likely a timeout occured.", e); throw new MOAIDException("stork.11", null); } // - insert the embedded attribute(s) into the container for (PersonalAttribute current : newAttributes) container.getResponse().getPersonalAttributeList().add(current); // see if we need some more attributes return processRequest(container, httpReq, httpResp, moasession, oaParam); } /** * Checks if there are missing attributes and tries to fetch them. If there are no more attribute to fetch, * this very method creates and sends the protocol result to the asking S-PEPS. * * @param container the {@link DataContainer} representing the status of the overall query. * @return the string * @throws MOAIDException */ public String processRequest(DataContainer container, HttpServletRequest request, HttpServletResponse response, AuthenticationSession moasession, OAAuthParameter oaParam) throws MOAIDException { // check if there are attributes we need to fetch this.httpResp = response; this.container = container; IPersonalAttributeList requestAttributeList = container.getRequest().getPersonalAttributeList(); IPersonalAttributeList responseAttributeList = container.getResponse().getPersonalAttributeList(); List missingAttributes = new ArrayList(); for (PersonalAttribute current : requestAttributeList) if (!responseAttributeList.containsKey(current)) missingAttributes.add(current); // Try to get all missing attributes try { // for each attribute still missing for (PersonalAttribute currentAttribute : missingAttributes) { Logger.error("Checking missing attribute: " + currentAttribute.getName()); // - check if we can find a suitable AttributeProvider Plugin for (AttributeProvider currentProvider : AttributeProviderFactory.getConfiguredPlugins(oaParam.getStorkAPs())) { try { // - hand over control to the suitable plugin Logger.error("Going to acquire missing attribute: " + currentAttribute.getName() + " at provider: " + currentProvider.getClass().getName()); IPersonalAttributeList aquiredAttributes = currentProvider.acquire(currentAttribute, moasession); // - add the aquired attribute to the container try { for (PersonalAttribute current : aquiredAttributes) container.getResponse().getPersonalAttributeList().add(current); } catch (NullPointerException ex) { Logger.error ("Nothing found");} } catch (UnsupportedAttributeException e) { // ok, try the next attributeprovider } catch (MOAIDException e) { // the current plugin had an error. Try the next one. // TODO we might want to add the non-fetchable attribute as "NotAvailable" to prevent an infinite loop } } } // build response generateSTORKResponse(); // set new http response generateRedirectResponse(); response = httpResp; return "12345"; // AssertionId } catch (ExternalAttributeRequestRequiredException e) { // the attribute request is ongoing and requires an external service. Logger.error("EXTERNAL EXCEPTION CAUGHT"); try { // memorize the container again // - generate new key String newArtifactId = new SecureRandomIdentifierGenerator() .generateIdentifier(); // - put container in temporary store. AssertionStorage.getInstance().put(newArtifactId, container); // add container-key to redirect embedded within the return URL Logger.info("Performing redirect to gather attributes to: " + AuthConfigurationProvider.getInstance().getPublicURLPrefix()); e.getAp().performRedirect(AuthConfigurationProvider.getInstance().getPublicURLPrefix() + "?" + ARTIFACT_ID + "=" + newArtifactId, container.getRequest().getCitizenCountryCode(), request, response, oaParam); } catch (Exception e1) { // TODO should we return the response as is to the PEPS? Logger.error("Error putting incomplete Stork response into temporary storage", e); e1.printStackTrace(); throw new MOAIDException("stork.11", null); } return "12345"; // TODO what to do here? } } private void generateSTORKResponse() throws MOAIDException { STORKAuthnResponse authnResponse = container.getResponse(); STORKAuthnRequest authnRequest = container.getRequest(); try { //Get SAMLEngine instance STORKSAMLEngine engine = STORKSAMLEngine.getInstance("VIDP"); Logger.debug("Starting generation of SAML response"); authnResponse = engine.generateSTORKAuthnResponse(authnRequest, authnResponse, container.getRemoteAddress(), false); //generateSAML Token Logger.info("SAML response succesfully generated!"); } catch (STORKSAMLEngineException e) { Logger.error("Failed to generate STORK SAML Response", e); throw new MOAIDException("stork.05", null); } Logger.info("STORK SAML Response message succesfully generated "); String statusCodeValue = authnResponse.getStatusCode(); try { Logger.debug("authn saml plain:" + authnResponse.getTokenSaml()); Logger.debug("authn saml string:" + new String(authnResponse.getTokenSaml())); // works Logger.debug("authn saml encodedx: " + new String(org.bouncycastle.util.encoders.Base64.encode(IOUtils.toString(authnResponse.getTokenSaml()).getBytes()))); } catch (IOException e) { e.printStackTrace(); } container.setResponse(authnResponse); } private void generateRedirectResponse() { STORKAuthnResponse authnResponse = container.getResponse(); STORKAuthnRequest authnRequest = container.getRequest(); // preparing redirection for the client try { VelocityEngine velocityEngine = VelocityProvider.getClassPathVelocityEngine(); Template template = velocityEngine.getTemplate("/resources/templates/stork2_postbinding_template.html"); VelocityContext context = new VelocityContext(); context.put("SAMLResponse", new String(org.bouncycastle.util.encoders.Base64.encode(IOUtils.toString(authnResponse.getTokenSaml()).getBytes()))); Logger.debug("SAMLResponse original: " + new String(org.bouncycastle.util.encoders.Base64.encode(IOUtils.toString(authnResponse.getTokenSaml()).getBytes()))); Logger.debug("Putting assertion consumer url as action: " + authnRequest.getAssertionConsumerServiceURL()); context.put("action", authnRequest.getAssertionConsumerServiceURL()); 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()); Logger.debug("Sending html content2 : " + new String(writer.getBuffer())); httpResp.getOutputStream().write(writer.getBuffer().toString().getBytes()); } catch (Exception e) { Logger.error("Velocity error: " + e.getMessage()); } //HttpSession httpSession = this.httpResp.getSession(); //httpSession.setAttribute("STORKSessionID", "12345"); //Logger.info("Status code again: " + authnResponse.getStatusCode()); //return "12345"; // AssertionId } /* (non-Javadoc) * @see at.gv.egovernment.moa.id.moduls.IAction#needAuthentication(at.gv.egovernment.moa.id.moduls.IRequest, javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse) */ public boolean needAuthentication(IRequest req, HttpServletRequest httpReq, HttpServletResponse httpResp) { // this action does not need any authentication. The authentication is already done by the preceding AuthenticationRequest-Action. return false; } /* (non-Javadoc) * @see at.gv.egovernment.moa.id.moduls.IAction#getDefaultActionName() */ public String getDefaultActionName() { return STORKProtocol.ATTRIBUTE_COLLECTOR; } }