org.nuxeo.ecm.platform.signature.web.sign.SignActions.java Source code

Java tutorial

Introduction

Here is the source code for org.nuxeo.ecm.platform.signature.web.sign.SignActions.java

Source

/*
 * (C) Copyright 2011-2012 Nuxeo SA (http://nuxeo.com/) and contributors.
 *
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the GNU Lesser General Public License
 * (LGPL) version 2.1 which accompanies this distribution, and is available at
 * http://www.gnu.org/licenses/lgpl.html
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * Lesser General Public License for more details.
 *
 * Contributors:
 *     Wojciech Sulejman
 *     Florent Guillaume
 */
package org.nuxeo.ecm.platform.signature.web.sign;

import static org.jboss.seam.international.StatusMessage.Severity.ERROR;
import static org.jboss.seam.international.StatusMessage.Severity.INFO;
import static org.jboss.seam.international.StatusMessage.Severity.WARN;

import java.io.Serializable;
import java.security.Principal;
import java.security.cert.X509Certificate;
import java.text.SimpleDateFormat;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.commons.io.FilenameUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.jboss.seam.ScopeType;
import org.jboss.seam.annotations.In;
import org.jboss.seam.annotations.Name;
import org.jboss.seam.annotations.Scope;
import org.jboss.seam.faces.FacesMessages;
import org.nuxeo.ecm.core.api.Blob;
import org.nuxeo.ecm.core.api.ClientException;
import org.nuxeo.ecm.core.api.DocumentModel;
import org.nuxeo.ecm.core.api.blobholder.BlobHolder;
import org.nuxeo.ecm.core.api.event.DocumentEventCategories;
import org.nuxeo.ecm.core.event.EventProducer;
import org.nuxeo.ecm.core.event.impl.DocumentEventContext;
import org.nuxeo.ecm.platform.signature.api.exception.CertException;
import org.nuxeo.ecm.platform.signature.api.exception.SignException;
import org.nuxeo.ecm.platform.signature.api.pki.CertService;
import org.nuxeo.ecm.platform.signature.api.sign.SignatureService;
import org.nuxeo.ecm.platform.signature.api.sign.SignatureService.SigningDisposition;
import org.nuxeo.ecm.platform.signature.api.sign.SignatureService.StatusWithBlob;
import org.nuxeo.ecm.platform.ui.web.api.NavigationContext;
import org.nuxeo.ecm.platform.usermanager.UserManager;
import org.nuxeo.ecm.webapp.helpers.ResourcesAccessor;
import org.nuxeo.runtime.api.Framework;

/**
 * Document signing actions
 */
@Name("signActions")
@Scope(ScopeType.CONVERSATION)
public class SignActions implements Serializable {

    private static final long serialVersionUID = 1L;

    private static final Log log = LogFactory.getLog(SignActions.class);

    /**
     * If this system property is set to "true", then signature will use PDF/A.
     */
    public static final String SIGNATURE_USE_PDFA_PROP = "org.nuxeo.ecm.signature.pdfa";

    /**
     * Signature disposition for PDF files. Can be "replace", "archive" or "attach".
     */
    public static final String SIGNATURE_DISPOSITION_PDF = "org.nuxeo.ecm.signature.disposition.pdf";

    /**
     * Signature disposition for non-PDF files. Can be "replace", "archive" or "attach".
     */
    public static final String SIGNATURE_DISPOSITION_NOTPDF = "org.nuxeo.ecm.signature.disposition.notpdf";

    public static final String SIGNATURE_ARCHIVE_FILENAME_FORMAT_PROP = "org.nuxeo.ecm.signature.archive.filename.format";

    /** Used with {@link SimpleDateFormat}. */
    public static final String DEFAULT_ARCHIVE_FORMAT = " ('archive' yyyy-MM-dd HH:mm:ss)";

    protected static final String LABEL_SIGN_DOCUMENT_MISSING = "label.sign.document.missing";

    protected static final String NOTIFICATION_SIGN_PROBLEM = "notification.sign.problem";

    protected static final String NOTIFICATION_SIGN_CERTIFICATE_ACCESS_PROBLEM = "notification.sign.certificate.access.problem";

    protected static final String NOTIFICATION_SIGN_SIGNED = "notification.sign.signed";

    public static final String MIME_TYPE_PDF = "application/pdf";

    public static final String DOCUMENT_SIGNED = "documentSigned";

    public static final String DOCUMENT_SIGNED_COMMENT = "PDF signed";

    @In(create = true)
    protected transient SignatureService signatureService;

    @In(create = true)
    protected transient CertService certService;

    @In(create = true)
    protected transient NavigationContext navigationContext;

    @In(create = true)
    protected ResourcesAccessor resourcesAccessor;

    @In(create = true, required = false)
    protected FacesMessages facesMessages;

    @In(create = true)
    protected transient UserManager userManager;

    @In(create = true)
    protected Principal currentUser;

    protected void info(String msg) {
        facesMessages.add(INFO, getMessage(msg));
    }

    protected void warn(String msg) {
        facesMessages.add(WARN, getMessage(msg));
    }

    protected void error(String msg) {
        facesMessages.add(ERROR, getMessage(msg));
    }

    protected String getMessage(String msg) {
        return resourcesAccessor.getMessages().get(msg);
    }

    protected DocumentModel getCurrentUserModel() throws ClientException {
        return userManager.getUserModel(currentUser.getName());
    }

    /**
     * Signs digitally a PDF blob contained in the current document, modifies the document status and updates UI &
     * auditing messages related to signing
     *
     * @param signingReason
     * @param password
     * @throws SignException
     * @throws ClientException
     */
    public void signCurrentDoc(String signingReason, String password) throws ClientException {

        DocumentModel currentDoc = navigationContext.getCurrentDocument();
        DocumentModel currentUserModel = getCurrentUserModel();
        StatusWithBlob swb = signatureService.getSigningStatus(currentDoc, currentUserModel);
        if (swb.status == StatusWithBlob.UNSIGNABLE) {
            error(LABEL_SIGN_DOCUMENT_MISSING);
            return;
        }

        Blob originalBlob = currentDoc.getAdapter(BlobHolder.class).getBlob();
        boolean originalIsPdf = MIME_TYPE_PDF.equals(originalBlob.getMimeType());

        // decide if we want PDF/A
        boolean pdfa = getPDFA();

        // decide disposition
        SigningDisposition disposition = getDisposition(originalIsPdf);

        // decide archive filename
        String filename = originalBlob.getFilename();
        String archiveFilename = getArchiveFilename(filename);

        try {
            signatureService.signDocument(currentDoc, currentUserModel, password, signingReason, pdfa, disposition,
                    archiveFilename);
        } catch (CertException e) {
            log.debug("Signing problem: " + e.getMessage(), e);
            error(NOTIFICATION_SIGN_CERTIFICATE_ACCESS_PROBLEM);
            return;
        } catch (SignException e) {
            log.debug("Signing problem: " + e.getMessage(), e);
            error(NOTIFICATION_SIGN_PROBLEM);
            facesMessages.add(ERROR, e.getMessage());
            return;
        }

        // important to save doc now
        navigationContext.saveCurrentDocument();

        // write to the audit log
        Map<String, Serializable> properties = new HashMap<String, Serializable>();
        String comment = DOCUMENT_SIGNED_COMMENT;
        notifyEvent(DOCUMENT_SIGNED, currentDoc, properties, comment);

        // display a signing message
        facesMessages.add(INFO, filename + " " + getMessage(NOTIFICATION_SIGN_SIGNED));
    }

    protected boolean getPDFA() {
        return Framework.isBooleanPropertyTrue(SIGNATURE_USE_PDFA_PROP);
    }

    protected SigningDisposition getDisposition(boolean originalIsPdf) {
        String disp;
        if (originalIsPdf) {
            disp = Framework.getProperty(SIGNATURE_DISPOSITION_PDF, SigningDisposition.ARCHIVE.name());
        } else {
            disp = Framework.getProperty(SIGNATURE_DISPOSITION_NOTPDF, SigningDisposition.ATTACH.name());
        }
        try {
            return Enum.valueOf(SigningDisposition.class, disp.toUpperCase());
        } catch (RuntimeException e) {
            log.warn("Invalid signing disposition: " + disp);
            return SigningDisposition.ATTACH;
        }
    }

    protected String getArchiveFilename(String filename) {
        String format = Framework.getProperty(SIGNATURE_ARCHIVE_FILENAME_FORMAT_PROP, DEFAULT_ARCHIVE_FORMAT);
        return FilenameUtils.getBaseName(filename) + new SimpleDateFormat(format).format(new Date()) + "."
                + FilenameUtils.getExtension(filename);
    }

    /**
     * Gets the signing status for the current document.
     *
     * @return the signing status
     * @throws ClientException
     */
    public StatusWithBlob getSigningStatus() throws ClientException {
        DocumentModel currentDoc = navigationContext.getCurrentDocument();
        return signatureService.getSigningStatus(currentDoc, getCurrentUserModel());
    }

    /**
     * Returns info about the certificates contained in the current document.
     */
    public List<X509Certificate> getCertificateList() throws SignException, ClientException {

        DocumentModel currentDoc = navigationContext.getCurrentDocument();
        if (currentDoc == null) {
            error(LABEL_SIGN_DOCUMENT_MISSING);
            return Collections.emptyList();
        }

        return signatureService.getCertificates(currentDoc);
        // certificate.getSubjectDN()
        // certificate.getIssuerDN()
        // certificate.getNotAfter()
    }

    protected void notifyEvent(String eventId, DocumentModel source, Map<String, Serializable> properties,
            String comment) throws ClientException {
        properties.put(DocumentEventContext.COMMENT_PROPERTY_KEY, comment);
        properties.put(DocumentEventContext.CATEGORY_PROPERTY_KEY, DocumentEventCategories.EVENT_DOCUMENT_CATEGORY);

        DocumentEventContext eventContext = new DocumentEventContext(source.getCoreSession(),
                source.getCoreSession().getPrincipal(), source);

        eventContext.setProperties(properties);

        Framework.getLocalService(EventProducer.class).fireEvent(eventContext.newEvent(eventId));
    }

}