Java tutorial
/* * 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; } }