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;
}
}