package at.gv.egovernment.moa.id.protocols.stork2.attributeproviders; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.StringWriter; import java.io.UnsupportedEncodingException; import java.net.URL; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import javax.activation.DataSource; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.xml.namespace.QName; import javax.xml.transform.Source; import javax.xml.transform.TransformerConfigurationException; import javax.xml.transform.TransformerException; import javax.xml.transform.TransformerFactoryConfigurationError; import javax.xml.transform.stream.StreamSource; import javax.xml.ws.Service; import javax.xml.ws.soap.SOAPBinding; import javax.xml.ws.BindingProvider; import org.apache.commons.codec.binary.Base64; import org.apache.commons.io.IOUtils; import org.apache.commons.lang.NotImplementedException; import org.apache.velocity.Template; import org.apache.velocity.VelocityContext; import org.apache.velocity.app.VelocityEngine; import org.bouncycastle.util.encoders.UrlBase64; import at.gv.egovernment.moa.id.auth.exception.MOAIDException; import at.gv.egovernment.moa.id.config.auth.OAAuthParameter; import at.gv.egovernment.moa.id.data.IAuthData; import at.gv.egovernment.moa.id.protocols.stork2.ExternalAttributeRequestRequiredException; import at.gv.egovernment.moa.id.protocols.stork2.UnsupportedAttributeException; import at.gv.egovernment.moa.id.util.VelocityProvider; import at.gv.egovernment.moa.logging.Logger; import eu.stork.oasisdss.api.ApiUtils; import eu.stork.oasisdss.api.LightweightSourceResolver; import eu.stork.oasisdss.api.exceptions.ApiUtilsException; import eu.stork.oasisdss.api.exceptions.UtilsException; import eu.stork.oasisdss.profile.AnyType; import eu.stork.oasisdss.profile.Base64Data; import eu.stork.oasisdss.profile.DocumentType; import eu.stork.oasisdss.profile.DocumentWithSignature; import eu.stork.oasisdss.profile.IncludeObject; 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.PersonalAttributeList; import eu.stork.peps.auth.commons.STORKAttrQueryRequest; import eu.stork.peps.auth.engine.STORKSAMLEngine; import eu.stork.peps.exceptions.STORKSAMLEngineException; import eu.stork.documentservice.DocumentService; /** * Forwards a signedDoc attribute request to the oasis-dss service instance */ public class SignedDocAttributeRequestProvider extends AttributeProvider { private String dtlUrl = null; private PersonalAttribute requestedAttribute; /** * The URL of the service listening for the oasis dss webform post request */ private String oasisDssWebFormURL; /** * Instantiates a new signed doc attribute request provider. * * @param oasisDssWebFormURL * the AP location * @param attributes */ public SignedDocAttributeRequestProvider(String oasisDssWebFormURL, String attributes) { super(attributes); this.oasisDssWebFormURL = oasisDssWebFormURL; //TODO load dtlUrl from config dtlUrl = "http://mopsos.iaik.tugraz.at:8080/DocumentService/DocumentService"; } /* * (non-Javadoc) * * @see * at.gv.egovernment.moa.id.protocols.stork2.AttributeProvider#acquire(java * .lang.String) */ @Override protected IPersonalAttributeList acquire(PersonalAttribute attribute, String spCountyCode, IAuthData authData) throws UnsupportedAttributeException, ExternalAttributeRequestRequiredException { if(!attributes.contains(attribute.getName())) { throw new UnsupportedAttributeException(); } requestedAttribute = attribute; throw new ExternalAttributeRequestRequiredException(this); } /* * (non-Javadoc) * * @see * at.gv.egovernment.moa.id.protocols.stork2.AttributeProvider#parse(javax * .servlet.http.HttpServletRequest) */ public IPersonalAttributeList parse(HttpServletRequest httpReq) throws MOAIDException, UnsupportedAttributeException { Logger.debug("Beginning to extract OASIS-DSS response out of HTTP Request"); try { String signResponseString = new String(Base64.decodeBase64(httpReq.getParameter("signresponse")), "UTF8"); //create SignResponse object Source response = new StreamSource(new java.io.StringReader(signResponseString)); SignResponse signResponse = ApiUtils.unmarshal(response, SignResponse.class); //extract doc from signresponse DataSource dataSource = LightweightSourceResolver.getDataSource(signResponse); ByteArrayOutputStream baos = new ByteArrayOutputStream(); IOUtils.copy(dataSource.getInputStream(), baos); byte[] data = baos.toByteArray(); //update doc in DTL String docId, dssId = ""; docId = signResponse.getDocUI(); //For reference dssId equals docId dssId = docId; if (dssId != null && data!=null) { boolean success = false; try{ success = updateDocumentInDtl(data, docId, signResponseString); }catch(Exception e){//No document service used? Logger.info("No document service used?"); e.printStackTrace(); success = false; } if(success) { // set the url in the SignResponse DocumentWithSignature documentWithSignature = new DocumentWithSignature(); DocumentType value = new DocumentType(); value.setDocumentURL(dtlUrl); documentWithSignature.setDocument(value); if(signResponse.getOptionalOutputs()!=null) { //signResponse.getOptionalOutputs().getAny().add(documentWithSignature); for(Object o :signResponse.getOptionalOutputs().getAny()) { if(o instanceof DocumentWithSignature) { signResponse.getOptionalOutputs().getAny().remove(o); signResponse.getOptionalOutputs().getAny().add(documentWithSignature); break; } } } else { AnyType anytype = new AnyType(); anytype.getAny().add(documentWithSignature); signResponse.setOptionalOutputs(anytype ); } // System.out.println("overwriting:"+signResponse.getResult().getResultMessage()+" with DTL url:"+dtlUrl); InputStream istr = ApiUtils.marshalToInputStream(signResponse); StringWriter writer = new StringWriter(); IOUtils.copy(istr, writer, "UTF-8"); signResponseString = writer.toString(); Logger.info("SignResponse overwritten:"+signResponseString); } else { //No document service used? // do nothing.... } } else throw new Exception("No DSS id found."); //alter signresponse //done List values = new ArrayList(); values.add(signResponseString); Logger.debug("Assembling signedDoc attribute"); PersonalAttribute signedDocAttribute = new PersonalAttribute("signedDoc", false, values, "Available"); // pack and return the result PersonalAttributeList result = new PersonalAttributeList(); result.add(signedDocAttribute); return result; } catch (UnsupportedEncodingException e) { Logger.error("Failed to assemble signedDoc attribute"); throw new MOAIDException("stork.05", null); } catch (ApiUtilsException e) { e.printStackTrace(); Logger.error("Failed to assemble signedDoc attribute"); throw new MOAIDException("stork.05", null); } catch (IOException e) { e.printStackTrace(); Logger.error("Failed to assemble signedDoc attribute"); throw new MOAIDException("stork.05", null); } catch (Exception e) { e.printStackTrace(); Logger.error("Failed to assemble signedDoc attribute"); throw new MOAIDException("stork.05", null); } } /* * (non-Javadoc) * * @see * at.gv.egovernment.moa.id.protocols.stork2.AttributeProvider#performRedirect * (java.lang.String) */ public void performRedirect(String url, HttpServletRequest req, HttpServletResponse resp, OAAuthParameter oaParam) 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(); //Parse SignRequest String signRequestString = requestedAttribute.getValue().get(0); Logger.debug("performRedirect, signrequest:"+signRequestString); Source signDoc = new StreamSource(new java.io.StringReader(signRequestString)); SignRequest signRequest = ApiUtils.unmarshal(signDoc, SignRequest.class); try{ //search for DTL link String dtlURL = getDtlUrlFromRequest(signRequest); String docId = signRequest.getDocUI(); if(dtlURL!=null) { String docRequest = getDocTransferRequest(docId, dtlURL);//dtlUrl byte[] data = getDocumentFromDtl(docRequest, dtlURL);//dtlUrl //load doc from DTL Logger.debug("data:"+data); String mime = getDocumentMimeFromDtl(docId, dtlURL);//dtlUrl Logger.debug("mime:"+mime); //add doc as base64* to signrequest => post doc to oasis try{ List includeObjects = ApiUtils.findNamedElement( signRequest.getOptionalInputs(), "IncludeObject", IncludeObject.class); signRequest.getOptionalInputs().getAny().removeAll(includeObjects); DocumentType document = new DocumentType(); Base64Data b64data = new Base64Data(); b64data.setValue(data); b64data.setMimeType(mime); document.setBase64Data(b64data); signRequest.setInputDocuments(ApiUtils.createInputDocuments(document)); //override old signRequestString InputStream istr = ApiUtils.marshalToInputStream(signRequest); StringWriter writer = new StringWriter(); IOUtils.copy(istr, writer, "UTF-8"); signRequestString = writer.toString(); Logger.info("Signrequest overwritten"); } catch (Exception e) { e.printStackTrace(); throw new Exception("Could not marshall sign request", e); } } else//Do not modify signRequest, document is already included { } }catch(Exception e) { Logger.info("No documentservice used?"); e.printStackTrace(); } 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()); } catch (Exception e) { Logger.error("Error sending DSS signrequest.", e); throw new MOAIDException("stork.11", null); } } /* (non-Javadoc) * @see at.gv.egovernment.moa.id.protocols.stork2.AttributeProvider#getSupportedAttributeNames() */ @Override public List getSupportedAttributeNames() throws MOAIDException { ArrayList supportedAttributeNames = new ArrayList(); for (String attributeName : this.attributes.split(",")) { supportedAttributeNames.add(attributeName); } return supportedAttributeNames; } //From DTLPEPSUTIL /** * Get DTL uril from the oasis sign request * @param signRequest The signature request * @return The URL of DTL service * @throws SimpleException */ private String getDtlUrlFromRequest(SignRequest signRequest) throws Exception { if (signRequest == null) throw new Exception("Signature request is empty"); else { try { Object objDoc = signRequest.getInputDocuments().getDocumentOrTransformedDataOrDocumentHash().get(0); if (objDoc instanceof DocumentType) { DocumentType document = (DocumentType)objDoc; if (document.getDocumentURL() != null) return document.getDocumentURL(); else return null;//throw new Exception("No document url found"); } else throw new Exception("No input document found"); } catch (Exception ex) { throw new Exception("Unable to parse xml.", ex); } } } /** * Get document from DTL * @param transferRequest The transfer request (attribute query) * @param eDtlUrl The DTL url of external DTL * @return the document data * @throws SimpleException */ private byte[] getDocumentFromDtl(String transferRequest, String eDtlUrl) throws Exception { URL url = null; try { url = new URL(dtlUrl); QName qname = new QName("http://stork.eu", "DocumentService"); Service service = Service.create(url, qname); DocumentService docservice = service.getPort(DocumentService.class); BindingProvider bp = (BindingProvider) docservice; SOAPBinding binding = (SOAPBinding) bp.getBinding(); binding.setMTOMEnabled(true); if (eDtlUrl.equalsIgnoreCase(dtlUrl)) return docservice.getDocument(transferRequest, ""); else return docservice.getDocument(transferRequest, eDtlUrl); } catch (Exception e) { e.printStackTrace(); throw new Exception("Error in getDocumentFromDtl", e); } } /** * Get a document transfer request (attribute query) * @param docId * @return * @throws SimpleException */ private String getDocTransferRequest(String docId, String destinationUrl) throws Exception { final STORKSAMLEngine engine = STORKSAMLEngine.getInstance("VIDP"); STORKAttrQueryRequest req = new STORKAttrQueryRequest(); req.setAssertionConsumerServiceURL(dtlUrl); req.setDestination(destinationUrl); req.setSpCountry("IS");//FIXME req.setQaa(3);//TODO PersonalAttributeList pal = new PersonalAttributeList(); PersonalAttribute attr = new PersonalAttribute(); attr.setName("docRequest"); attr.setIsRequired(true); attr.setValue(Arrays.asList(docId)); pal.add(attr); req.setPersonalAttributeList(pal); STORKAttrQueryRequest req1; try { req1 = engine.generateSTORKAttrQueryRequest(req); return PEPSUtil.encodeSAMLTokenUrlSafe(req1.getTokenSaml()); } catch (STORKSAMLEngineException e) { e.printStackTrace(); throw new Exception("Error in doc request attribute query generation", e); } } /** * Get mime type of document from DTL * @param docId The document id * @param dtlUrl The url of dtl * @return The mime type */ private String getDocumentMimeFromDtl(String docId, String eDtlUrl) throws Exception { URL url = null; try { url = new URL(dtlUrl); QName qname = new QName("http://stork.eu", "DocumentService"); Service service = Service.create(url, qname); DocumentService docservice = service.getPort(DocumentService.class); BindingProvider bp = (BindingProvider) docservice; SOAPBinding binding = (SOAPBinding) bp.getBinding(); binding.setMTOMEnabled(true); if (eDtlUrl.equalsIgnoreCase(dtlUrl)) return docservice.getDocumentMime(docId, ""); else return docservice.getDocumentMime(docId, eDtlUrl); } catch (Exception e) { e.printStackTrace(); throw new Exception("Error in getDocumentFromDtl", e); } } /** * Add document to DTL service * @param docData the document data * @param mime the mime type of data * @param signRequest the sign request * @return the document id * @throws SimpleException */ private String addDocumentToDtl(byte[] docData, String mime, String signRequest, String destCountry, String spId) throws Exception { throw new NotImplementedException(); // URL url = null; // String docID = null; // try // { // url = new URL(dtlUrl); // QName qname = new QName("http://stork.eu", // "DocumentService"); // // Service service = Service.create(url, qname); // DocumentService docservice = service.getPort(DocumentService.class); // // BindingProvider bp = (BindingProvider) docservice; // SOAPBinding binding = (SOAPBinding) bp.getBinding(); // binding.setMTOMEnabled(true); // // docID = docservice.addDocument(docData, signRequest, destCountry, spId, mime, ""); // } // catch (Exception e) // { // e.printStackTrace(); // throw new Exception("Error in addDocumentToDtl", e); // } // // return docID; } /** * Update document in DTL * @param docData The docment data * @param docId The document ID * @param signResponse The signature response * @return True if successful * @throws SimpleException */ private boolean updateDocumentInDtl(byte[] docData, String docId, String signResponse) throws Exception { boolean success = false; URL url = null; try { url = new URL(dtlUrl); QName qname = new QName("http://stork.eu", "DocumentService"); Service service = Service.create(url, qname); DocumentService docservice = service.getPort(DocumentService.class); BindingProvider bp = (BindingProvider) docservice; SOAPBinding binding = (SOAPBinding) bp.getBinding(); binding.setMTOMEnabled(true); success = docservice.updateDocument(docId, signResponse, docData); } catch (Exception e) { e.printStackTrace(); throw new Exception("Error in addDocumentToDtl", e); } return success; } }