org.alfresco.extension.countersign.action.executer.PDFSignatureProviderActionExecuter.java Source code

Java tutorial

Introduction

Here is the source code for org.alfresco.extension.countersign.action.executer.PDFSignatureProviderActionExecuter.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.action.executer;

import java.awt.Color;
import java.awt.geom.AffineTransform;
import java.awt.image.AffineTransformOp;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.io.OutputStream;
import java.io.Serializable;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.UnrecoverableKeyException;
import java.security.cert.Certificate;
import java.util.Calendar;
import java.util.HashMap;
import java.util.List;

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.extension.countersign.signature.SignatureToImage;
import org.alfresco.model.ContentModel;
import org.alfresco.repo.action.ParameterDefinitionImpl;
import org.alfresco.repo.action.executer.ActionExecuterAbstractBase;
import org.alfresco.repo.security.authentication.AuthenticationUtil;
import org.alfresco.service.ServiceRegistry;
import org.alfresco.service.cmr.action.Action;
import org.alfresco.service.cmr.action.ParameterDefinition;
import org.alfresco.service.cmr.dictionary.DataTypeDefinition;
import org.alfresco.service.cmr.repository.ChildAssociationRef;
import org.alfresco.service.cmr.repository.ContentIOException;
import org.alfresco.service.cmr.repository.ContentReader;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.namespace.QName;
import org.alfresco.util.TempFileProvider;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.json.simple.JSONObject;
import org.json.simple.parser.JSONParser;
import org.json.simple.parser.ParseException;

import com.itextpdf.text.DocumentException;
import com.itextpdf.text.Image;
import com.itextpdf.text.Rectangle;
import com.itextpdf.text.pdf.PdfReader;
import com.itextpdf.text.pdf.PdfSignatureAppearance;
import com.itextpdf.text.pdf.PdfStamper;

public class PDFSignatureProviderActionExecuter extends AbstractSignatureActionExecuter {

    /**
     * The logger
     */
    private static Log logger = LogFactory.getLog(PDFSignatureProviderActionExecuter.class);

    private JSONParser parser = new JSONParser();

    /**
     * Constraints
     */
    public static HashMap<String, String> visibilityConstraint = new HashMap<String, String>();
    public static HashMap<String, String> positionConstraint = new HashMap<String, String>();

    /**
     * Action constants
     */
    public static final String NAME = "pdf-signature-provider-action";
    public static final String PARAM_VISIBLE = "visible";
    public static final String PARAM_ALIAS = "alias";
    public static final String PARAM_SIGNATURE_JSON = "signature-json";
    public static final String PARAM_POSITION = "position";
    public static final String PARAM_GRAPHIC = "graphic";

    public static final String KEY_TYPE_PKCS12 = "pkcs12";
    public static final String POSITION_DELIMITER = "#";

    public static final String POSITION_TYPE_DRAWN = "drawn";
    public static final String POSITION_TYPE_PREDEFINED = "predefined";
    public static final String POSITION_TYPE_FIELD = "field";

    /**
     * Add parameter definitions
     */
    @Override
    protected void addParameterDefinitions(List<ParameterDefinition> paramList) {
        super.addParameterDefinitions(paramList);

        paramList.add(new ParameterDefinitionImpl(PARAM_ALIAS, DataTypeDefinition.TEXT, false,
                getParamDisplayLabel(PARAM_ALIAS)));
        paramList.add(new ParameterDefinitionImpl(PARAM_SIGNATURE_JSON, DataTypeDefinition.TEXT, false,
                getParamDisplayLabel(PARAM_SIGNATURE_JSON)));
        paramList.add(new ParameterDefinitionImpl(PARAM_VISIBLE, DataTypeDefinition.BOOLEAN, false,
                getParamDisplayLabel(PARAM_VISIBLE)));
        paramList.add(new ParameterDefinitionImpl(PARAM_GRAPHIC, DataTypeDefinition.BOOLEAN, false,
                getParamDisplayLabel(PARAM_GRAPHIC)));
        paramList.add(new ParameterDefinitionImpl(PARAM_POSITION, DataTypeDefinition.TEXT, false,
                getParamDisplayLabel(PARAM_POSITION), false, "countersign.position"));
    }

    /**
     * @see org.alfresco.repo.action.executer.ActionExecuterAbstractBase#executeImpl(org.alfresco.service.cmr.repository.NodeRef,
     * org.alfresco.service.cmr.repository.NodeRef)
     */
    protected void executeImpl(Action ruleAction, NodeRef actionedUponNodeRef) {

        if (serviceRegistry.getNodeService().exists(actionedUponNodeRef) == false) {
            // node doesn't exist - can't do anything
            return;
        }

        String location = (String) ruleAction.getParameterValue(PARAM_LOCATION);
        String geolocation = (String) ruleAction.getParameterValue(PARAM_GEOLOCATION);
        String reason = (String) ruleAction.getParameterValue(PARAM_REASON);
        String position = (String) ruleAction.getParameterValue(PARAM_POSITION);
        String keyPassword = (String) ruleAction.getParameterValue(PARAM_KEY_PASSWORD);
        String signatureJson = (String) ruleAction.getParameterValue(PARAM_SIGNATURE_JSON);
        Boolean visible = (Boolean) ruleAction.getParameterValue(PARAM_VISIBLE);
        Boolean graphic = (Boolean) ruleAction.getParameterValue(PARAM_GRAPHIC);

        boolean useSignatureField = false;
        String user = AuthenticationUtil.getRunAsUser();
        String positionType = "predefined";
        String positionLoc = "center";
        JSONObject box;
        int page = -1;

        // parse out the position JSON
        JSONObject positionObj = null;

        try {
            positionObj = (JSONObject) parser.parse(position);
        } catch (ParseException e) {
            logger.error("Could not parse position JSON from Share");
            throw new AlfrescoRuntimeException("Could not parse position JSON from Share");
        }

        // get the page
        page = Integer.parseInt(String.valueOf(positionObj.get("page")));

        // get the positioning type
        positionType = String.valueOf(positionObj.get("type"));

        // get the position (field or predefined)
        positionLoc = String.valueOf(positionObj.get("position"));

        // get the box (if required)
        box = (JSONObject) positionObj.get("box");

        int width = 350;
        int height = 75;

        File tempDir = null;

        // current date, used for both signing the PDF and creating the
        // associated signature object
        Calendar now = Calendar.getInstance();

        try {
            // get the keystore, pk and cert chain
            SignatureProvider signatureProvider = signatureProviderFactory.getSignatureProvider(user);
            KeyStore keystore = signatureProvider.getUserKeyStore(keyPassword);
            PrivateKey key = (PrivateKey) keystore.getKey(alias, keyPassword.toCharArray());
            Certificate[] chain = keystore.getCertificateChain(alias);

            // open original pdf
            ContentReader pdfReader = getReader(actionedUponNodeRef);
            PdfReader reader = new PdfReader(pdfReader.getContentInputStream());

            // create temp dir to store file
            File alfTempDir = TempFileProvider.getTempDir();
            tempDir = new File(alfTempDir.getPath() + File.separatorChar + actionedUponNodeRef.getId());
            tempDir.mkdir();
            File file = new File(tempDir,
                    serviceRegistry.getFileFolderService().getFileInfo(actionedUponNodeRef).getName());
            OutputStream cos = serviceRegistry.getContentService()
                    .getWriter(actionedUponNodeRef, ContentModel.PROP_CONTENT, true).getContentOutputStream();

            PdfStamper stamp = PdfStamper.createSignature(reader, cos, '\0', file, true);
            PdfSignatureAppearance sap = stamp.getSignatureAppearance();
            sap.setCrypto(key, chain, null, PdfSignatureAppearance.SELF_SIGNED);

            // set reason for signature, location of signer, and date
            sap.setReason(reason);
            sap.setLocation(location);
            sap.setSignDate(now);

            // get the image for the signature
            BufferedImage sigImage = SignatureToImage.convertJsonToImage(signatureJson, width, height);
            // save the signature image back to the signatureProvider
            signatureProvider.saveSignatureImage(sigImage, signatureJson);

            if (visible) {
                //if this is a graphic sig, set the graphic here
                if (graphic) {
                    sap.setRenderingMode(PdfSignatureAppearance.RenderingMode.GRAPHIC);
                    sap.setSignatureGraphic(Image.getInstance(sigImage, Color.WHITE));
                } else {
                    sap.setRenderingMode(PdfSignatureAppearance.RenderingMode.NAME_AND_DESCRIPTION);
                }

                // either insert the sig at a defined field or at a defined position / drawn loc
                if (positionType.equalsIgnoreCase(POSITION_TYPE_PREDEFINED)) {
                    Rectangle pageRect = reader.getPageSizeWithRotation(page);
                    sap.setVisibleSignature(positionBlock(positionLoc, pageRect, width, height), page, null);
                } else if (positionType.equalsIgnoreCase(POSITION_TYPE_DRAWN)) {
                    Rectangle pageRect = reader.getPageSizeWithRotation(page);
                    sap.setVisibleSignature(positionBlock(pageRect, box), page, null);
                } else {
                    sap.setVisibleSignature(positionLoc);
                    useSignatureField = true;
                }
            }

            // close the stamp, applying the changes to the PDF
            stamp.close();
            reader.close();
            cos.close();

            //delete the temp file
            file.delete();

            // apply the "signed" aspect
            serviceRegistry.getNodeService().addAspect(actionedUponNodeRef, CounterSignSignatureModel.ASPECT_SIGNED,
                    new HashMap<QName, Serializable>());

            // create a "signature" node and associate it with the signed doc
            addSignatureNodeAssociation(actionedUponNodeRef, location, reason,
                    useSignatureField ? positionLoc : "none", now.getTime(), geolocation, page, positionLoc);

        } catch (IOException e) {
            throw new AlfrescoRuntimeException(e.getMessage(), e);
        } catch (ContentIOException e) {
            throw new AlfrescoRuntimeException(e.getMessage(), e);
        } catch (DocumentException e) {
            throw new AlfrescoRuntimeException(e.getMessage(), e);
        } catch (KeyStoreException e) {
            throw new AlfrescoRuntimeException(e.getMessage(), e);
        } catch (UnrecoverableKeyException e) {
            throw new AlfrescoRuntimeException(e.getMessage(), e);
        } catch (NoSuchAlgorithmException e) {
            throw new AlfrescoRuntimeException(e.getMessage(), e);
        } finally {
            if (tempDir != null) {
                try {
                    tempDir.delete();
                } catch (Exception ex) {
                    throw new AlfrescoRuntimeException(ex.getMessage(), ex);
                }
            }
        }
    }

    /**
     * Scales the signature image to fit the provided signature field dimensions,
     * preserving the aspect ratio
     * 
     * @param signatureImage
     * @param width
     * @param height
     * @return
     */
    private BufferedImage scaleSignature(BufferedImage signatureImage, int width, int height) {
        if (signatureImage.getHeight() > height) {
            BufferedImage scaled = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
            AffineTransform at = new AffineTransform();
            at.scale(2.0, 2.0);
            AffineTransformOp scaleOp = new AffineTransformOp(at, AffineTransformOp.TYPE_BILINEAR);
            scaled = scaleOp.filter(signatureImage, scaled);
            return scaled;
        } else {
            return signatureImage;
        }
    }

}