at.gv.egovernment.moa.id.protocols.stork2.attributeproviders.SignedDocAttributeRequestProvider.java Source code

Java tutorial

Introduction

Here is the source code for at.gv.egovernment.moa.id.protocols.stork2.attributeproviders.SignedDocAttributeRequestProvider.java

Source

/*******************************************************************************
 * Copyright 2014 Federal Chancellery Austria
 * MOA-ID has been developed in a cooperation between BRZ, the Federal
 * Chancellery Austria - ICT staff unit, and Graz University of Technology.
 *
 * Licensed under the EUPL, Version 1.1 or - as soon they will be approved by
 * the European Commission - subsequent versions of the EUPL (the "Licence");
 * You may not use this work except in compliance with the Licence.
 * You may obtain a copy of the Licence at:
 * http://www.osor.eu/eupl/
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the Licence is distributed on an "AS IS" basis,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the Licence for the specific language governing permissions and
 * limitations under the Licence.
 *
 * This product combines work with different licenses. See the "NOTICE" text
 * file for details on the various modules and licenses.
 * The "NOTICE" text file is part of the distribution. Any derivative works
 * that you distribute must include a readable copy of the "NOTICE" text file.
 *******************************************************************************/
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 java.util.Properties;

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 eu.stork.peps.complex.attributes.eu.stork.names.tc.stork._1_0.assertion.AttributeStatusType;
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.AuthConfigurationProvider;
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.MOASTORKRequest;
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.ResultMajor;
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;
import eu.stork.documentservice.data.DatabaseConnectorMySQLImpl;

/**
 * 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;

        try {
            AuthConfigurationProvider authConfigurationProvider = AuthConfigurationProvider.getInstance();
            dtlUrl = authConfigurationProvider.getDocumentServiceUrl();
            Logger.info("SignedDocAttributeRequestProvider, using dtlUrl:" + dtlUrl);
        } catch (Exception e) {
            dtlUrl = "http://testvidp.buergerkarte.at/DocumentService/DocumentService";
            e.printStackTrace();
            Logger.error("Loading documentservice url failed, using default value:" + dtlUrl);
        }

        //      Properties props = new Properties();
        //      try {
        //         props.load(DatabaseConnectorMySQLImpl.class.getResourceAsStream("docservice.properties"));
        //         dtlUrl = props.getProperty("docservice.url");
        //      } catch (IOException e) {
        //         dtlUrl = "http://testvidp.buergerkarte.at/DocumentService/DocumentService";
        //         Logger.error("Loading DTL config failed, using default value:"+dtlUrl);
        //         e.printStackTrace();
        //      }      
    }

    /*
     * (non-Javadoc)
     * 
     * @see
     * at.gv.egovernment.moa.id.protocols.stork2.AttributeProvider#acquire(java
     * .lang.String)
     */
    @Override
    protected IPersonalAttributeList acquire(PersonalAttribute attribute, MOASTORKRequest moastorkRequest,
            IAuthData authData) throws UnsupportedAttributeException, ExternalAttributeRequestRequiredException {
        if (!attributes.contains(attribute.getName())) {
            throw new UnsupportedAttributeException();
        }

        requestedAttribute = attribute;
        try {
            String tmp = requestedAttribute.getValue().get(0);
        } catch (Exception e) {
            Logger.info("SignedDocAttributeProvide failed:" + e.toString());
            throw new UnsupportedAttributeException();
        }

        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 base64 = httpReq.getParameter("signresponse");
            Logger.debug("signresponse url: " + httpReq.getRequestURI().toString());
            Logger.debug("signresponse querystring: " + httpReq.getQueryString());
            Logger.debug("signresponse method: " + httpReq.getMethod());
            Logger.debug("signresponse content type: " + httpReq.getContentType());
            Logger.debug("signresponse parameter:" + base64);
            String signResponseString = new String(Base64.decodeBase64(base64), "UTF8");
            Logger.debug("RECEIVED signresponse:" + signResponseString);
            //create SignResponse object
            Source response = new StreamSource(new java.io.StringReader(signResponseString));
            SignResponse signResponse = ApiUtils.unmarshal(response, SignResponse.class);
            //Check if Signing was successfully or not

            if (!signResponse.getResult().getResultMajor().equals(ResultMajor.RESULT_MAJOR_SUCCESS)) {
                //Pass unmodifed or unmarshal & marshal??
                InputStream istr = ApiUtils.marshalToInputStream(signResponse);
                StringWriter writer = new StringWriter();
                IOUtils.copy(istr, writer, "UTF-8");
                signResponseString = writer.toString();
                Logger.info("SignResponse with error (unmodified):" + signResponseString);
                istr.close();
            } else {
                //extract doc from signresponse
                DataSource dataSource = LightweightSourceResolver.getDataSource(signResponse);

                ByteArrayOutputStream baos = new ByteArrayOutputStream();
                IOUtils.copy(dataSource.getInputStream(), baos);
                byte[] data = baos.toByteArray();
                baos.close();

                //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();
                        if (dtlUrl.endsWith("?wsdl")) {
                            String tmp = dtlUrl.replace("?wsdl", "");
                            Logger.debug("DocumentUrl ends with ? wsdl, using " + tmp + " instead.");
                            value.setDocumentURL(tmp);
                        } else {
                            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);
                        istr.close();
                    } else {
                        //No document service used?
                        // do nothing....
                        //TODO temporary fix because document is deleted after fetching => SP can't download Doc
                        //Add doc to Signresponse

                        DocumentWithSignature documentWithSignature = new DocumentWithSignature();
                        DocumentType value = new DocumentType();
                        if (signResponse.getProfile().toLowerCase().contains("xades")) {
                            value.setBase64XML(data);
                        } else {
                            Base64Data base64data = new Base64Data();
                            base64data.setValue(data);
                            base64data.setMimeType(dataSource.getContentType());
                            value.setBase64Data(base64data);
                        }
                        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);
                        istr.close();
                    }
                } else
                    throw new Exception("No DSS id found.");
            }

            //alter signresponse
            //done
            List<String> values = new ArrayList<String>();
            values.add(signResponseString);

            Logger.debug("Assembling signedDoc attribute");
            PersonalAttribute signedDocAttribute = new PersonalAttribute("signedDoc", false, values,
                    AttributeStatusType.AVAILABLE.value());

            // 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);
            throw new UnsupportedAttributeException();
        }
    }

    /*
     * (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...");
            Logger.info("performRedirect url:" + url);
            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 + " " + data.length);
                    try {
                        Logger.trace("data:" + new String(data, "UTF-8"));
                    } catch (Exception e) {
                        Logger.trace("data: creating String failed:" + e);
                    }
                    String mime = getDocumentMimeFromDtl(docId, dtlURL);//dtlUrl
                    Logger.debug("mime:" + mime);

                    //add doc as base64* to signrequest => post doc to oasis
                    try {
                        List<IncludeObject> includeObjects = ApiUtils.findNamedElement(
                                signRequest.getOptionalInputs(), "IncludeObject", IncludeObject.class);
                        signRequest.getOptionalInputs().getAny().removeAll(includeObjects);

                        String documentId = null;
                        Object objDoc = signRequest.getInputDocuments().getDocumentOrTransformedDataOrDocumentHash()
                                .get(0);
                        if (objDoc != null && objDoc instanceof DocumentType) {
                            DocumentType document = (DocumentType) objDoc;
                            documentId = document.getID();
                        }
                        DocumentType document = new DocumentType();
                        if (documentId != null)
                            document.setID(documentId);
                        if (signRequest.getProfile().toLowerCase().contains("xades")) {
                            document.setBase64XML(data);
                        } else {
                            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");
                        Logger.debug("Signrequest overwritten:" + signRequestString);
                        istr.close();
                    } 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("UTF-8"));
        } 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<String> getSupportedAttributeNames() throws MOAIDException {
        ArrayList<String> supportedAttributeNames = new ArrayList<String>();
        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 {
            Logger.debug("getDocumentFromDtl:" + dtlUrl);
            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 {
        String spCountry = docId.substring(0, docId.indexOf("/"));
        final STORKSAMLEngine engine = STORKSAMLEngine.getInstance("VIDP");
        STORKAttrQueryRequest req = new STORKAttrQueryRequest();
        req.setAssertionConsumerServiceURL(dtlUrl);
        req.setDestination(destinationUrl);
        req.setSpCountry(spCountry);
        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 updateDocumentInDtl", e);
        }

        return success;
    }

    /* (non-Javadoc)
     * @see at.gv.egovernment.moa.id.protocols.stork2.attributeproviders.AttributeProvider#getPriority()
     */
    @Override
    public int getPriority() {
        return 99;
    }
}