org.alfresco.extension.countersign.service.ScriptCounterSignService.java Source code

Java tutorial

Introduction

Here is the source code for org.alfresco.extension.countersign.service.ScriptCounterSignService.java

Source

/*
 * Copyright 2012-2013 Alfresco Software Limited.
 * 
 * Licensed under the GNU Affero General Public License, Version 3.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *     http://www.gnu.org/licenses/agpl-3.0.html
 * 
 * If you do not wish to be bound to the terms of the AGPL v3.0, 
 * A commercial license may be obtained by contacting the author.
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 * 
 * This file is part of an unsupported extension to Alfresco.
 * 
 */
package org.alfresco.extension.countersign.service;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.StringWriter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;

import org.alfresco.error.AlfrescoRuntimeException;
import org.alfresco.extension.countersign.model.CounterSignSignatureModel;
import org.alfresco.extension.countersign.signature.SignatureProvider;
import org.alfresco.extension.countersign.signature.SignatureProviderFactory;
import org.alfresco.model.ContentModel;
import org.alfresco.repo.jscript.ScriptNode;
import org.alfresco.repo.processor.BaseProcessorExtension;
import org.alfresco.repo.web.scripts.workflow.WorkflowModelBuilder;
import org.alfresco.service.ServiceRegistry;
import org.alfresco.service.cmr.repository.ContentReader;
import org.alfresco.service.cmr.repository.ContentService;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.repository.NodeService;
import org.alfresco.service.cmr.workflow.WorkflowInstance;
import org.alfresco.service.cmr.workflow.WorkflowService;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.json.simple.JSONObject;

import com.itextpdf.text.pdf.PdfReader;

public class ScriptCounterSignService extends BaseProcessorExtension {

    private static final Log logger = LogFactory.getLog(ScriptCounterSignService.class);
    private ServiceRegistry serviceRegistry;
    private SignatureProviderFactory signatureProviderFactory;
    private Properties config;
    private ArrayList<String> counterSignWorkflowIds = new ArrayList<String>();
    private CounterSignService counterSignService;

    public static final String signatureValidName = "signatureValid";
    public static final String hashValidName = "hashValid";

    public String getClassName() {
        return "CounterSignSignatureService";
    }

    /**
     * Gets a signature source for the provided user.  This is JSON in the default
     * implementation, but could be a URL to an external signature image
     * 
     * @param user
     * @return
     */
    public String getSignatureSource(String user) {
        return signatureProviderFactory.getSignatureProvider(user).getSignatureSource();
    }

    /**
     * Get a nodeRef string for the user's keystore node
     * 
     * @param user
     * @return
     */
    public ScriptNode getKeystoreNode(String user) {
        NodeRef keystore = counterSignService.getSignatureArtifact(user,
                CounterSignSignatureModel.ASSOC_SIGNERKEYSTORE);
        if (keystore != null) {
            return new ScriptNode(keystore, serviceRegistry);
        } else {
            return null;
        }
    }

    /**
     * Get a nodeRef string for the user's signature image
     * 
     * @param user
     * @return
     */
    public ScriptNode getSignatureImageNode(String user) {
        NodeRef image = counterSignService.getSignatureArtifact(user,
                CounterSignSignatureModel.ASSOC_SIGNERSIGNATUREIMAGE);
        if (image != null) {
            return new ScriptNode(image, serviceRegistry);
        } else {
            return null;
        }
    }

    /**
     * Get a nodeRef string for the user's signature image
     * 
     * @param user
     * @return
     */
    public ScriptNode getPublicKeyNode(String user) {
        NodeRef publicKey = counterSignService.getSignatureArtifact(user,
                CounterSignSignatureModel.ASSOC_SIGNERPUBLICKEY);
        if (publicKey != null) {
            return new ScriptNode(publicKey, serviceRegistry);
        } else {
            return null;
        }
    }

    /**
     * Gets the signature info embedded in the signed PDF document itself
     * 
     * @param nodeRef
     */
    public void getSignatures(String nodeRef) {

    }

    /**
     * Gets the page count for a PDF document
     * 
     * @param nodeRef
     * @return
     */
    public int getPageCount(String nodeRef) {
        try {
            ContentReader reader = serviceRegistry.getContentService().getReader(new NodeRef(nodeRef),
                    ContentModel.PROP_CONTENT);
            PdfReader pdfReader = new PdfReader(reader.getContentInputStream());
            int count = pdfReader.getNumberOfPages();
            pdfReader.close();
            return count;

        } catch (IOException ioex) {
            return -1;
        }
    }

    /**
     * Get the extension configuration (all properties)
     * @return
     */
    public HashMap<String, String> getConfig() {
        return new HashMap<String, String>((Map) config);
    }

    /**
     * Get a single named properties from the CounterSign config file.  Simply 
     * for convenience.
     * 
     * @param name
     * @return
     */
    public String getProperty(String name) {
        return config.getProperty(name);
    }

    /**
     * Returns "true" if this user has all of the required bits and pieces (keystore) to
     * apply a digital signature.
     * 
     * @param user
     */
    public boolean getSignatureAvailable(String user) {
        return signatureProviderFactory.getSignatureProvider(user).signatureAvailable();
    }

    /**
     * Gets all of the recorded signature workflow events for this node, both for
     * active and completed workflows
     * 
     * @param nodeRef
     */
    public List<Map<String, Object>> getSignatureWorkflowHistory(String nodeRef) {
        NodeService ns = serviceRegistry.getNodeService();
        WorkflowService wfs = serviceRegistry.getWorkflowService();
        List<Map<String, Object>> results = new ArrayList<Map<String, Object>>();
        NodeRef node = new NodeRef(nodeRef);
        WorkflowModelBuilder modelBuilder = new WorkflowModelBuilder(serviceRegistry.getNamespaceService(),
                serviceRegistry.getNodeService(), serviceRegistry.getAuthenticationService(),
                serviceRegistry.getPersonService(), serviceRegistry.getWorkflowService(),
                serviceRegistry.getDictionaryService());
        if (ns.exists(node)) {
            List<WorkflowInstance> active = wfs.getWorkflowsForContent(node, true);
            List<WorkflowInstance> inactive = wfs.getWorkflowsForContent(node, false);

            // merge the lists
            ArrayList<WorkflowInstance> all = new ArrayList<WorkflowInstance>(active.size() + inactive.size());
            all.addAll(active);
            all.addAll(inactive);

            // we only need instances of the known CounterSign workflow types
            for (WorkflowInstance instance : all) {
                // if the instance definition name is in the list, get its tasks
                // and add them to the task list
                if (counterSignWorkflowIds.contains(instance.getDefinition().getName())) {
                    results.add(modelBuilder.buildDetailed(instance, true));
                }
            }
        } else {
            throw new AlfrescoRuntimeException("Node " + nodeRef + " does not exist");
        }

        return results;
    }

    /**
     * Validates a single signature, passed in as a nodeRef String
     * 
     * @param nodeRef
     * @return {signatureValid:[validity],hashValid:[validity]}
     */
    public JSONObject validateSignature(String nodeRef) {
        // get the node, make sure it exists
        NodeService ns = serviceRegistry.getNodeService();
        ContentService cs = serviceRegistry.getContentService();
        NodeRef sigNode = new NodeRef(nodeRef);
        boolean signatureValid = false;
        boolean hashValid = false;

        if (ns.exists(sigNode)) {
            try {
                // get the doc has from the time of the sig and the sig itself
                String docHash = String.valueOf(ns.getProperty(sigNode, CounterSignSignatureModel.PROP_DOCHASH));
                ContentReader sigReader = cs.getReader(sigNode, ContentModel.PROP_CONTENT);
                InputStream sigStream = sigReader.getContentInputStream();

                ByteArrayOutputStream baos = new ByteArrayOutputStream();
                byte[] buffer = new byte[1024];
                int read = 0;
                while ((read = sigStream.read(buffer, 0, buffer.length)) != -1) {
                    baos.write(buffer, 0, read);
                }
                baos.flush();

                // get the signing user's public key
                String person = String
                        .valueOf(ns.getProperty(sigNode, CounterSignSignatureModel.PROP_EXTERNALSIGNER));
                SignatureProvider prov = signatureProviderFactory.getSignatureProvider(person);

                // validate the sig using the public key
                signatureValid = prov.validateSignature(baos.toByteArray(), docHash.getBytes());

                // get the document associated with this sig, and compute the hash
                NodeRef signedDoc = ns.getParentAssocs(sigNode).get(0).getParentRef();
                ContentReader docReader = cs.getReader(signedDoc, ContentModel.PROP_CONTENT);
                String contentHash = new String(prov.computeHash(docReader.getContentInputStream()));
                if (docHash.equals(contentHash)) {
                    hashValid = true;
                } else {
                    signatureValid = false;
                }
            } catch (IOException ioex) {
                throw new AlfrescoRuntimeException("IOException reading signature: " + ioex.getMessage());
            }
        } else {
            throw new AlfrescoRuntimeException("Node: " + nodeRef + " does not exist");
        }

        // create the JSONObject to return
        JSONObject valid = new JSONObject();
        valid.put(signatureValidName, signatureValid);
        valid.put(hashValidName, hashValid);

        return valid;
    }

    // getters, setters and other boring stuff:

    public void setServiceRegistry(ServiceRegistry serviceRegistry) {
        this.serviceRegistry = serviceRegistry;
    }

    public void setSignatureProviderFactory(SignatureProviderFactory signatureProviderFactory) {
        this.signatureProviderFactory = signatureProviderFactory;
    }

    public void setExtensionConfig(Object config) {
        this.config = (Properties) config;
    }

    public void setCounterSignWorkflowIds(List ids) {
        this.counterSignWorkflowIds.addAll(ids);
    }

    public void setCounterSignService(CounterSignService counterSignService) {
        this.counterSignService = counterSignService;
    }
}