001/*
002 * (C) Copyright 2016-2018 Nuxeo (http://nuxeo.com/) and others.
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 *     http://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 *
016 * Contributors:
017 *     Thibaud Arguillere
018 *     Miguel Nixo
019 */
020package org.nuxeo.ecm.platform.pdf.operations;
021
022import java.util.Map.Entry;
023import java.util.stream.Collectors;
024
025import org.apache.commons.lang3.StringUtils;
026import org.apache.pdfbox.pdmodel.encryption.AccessPermission;
027import org.nuxeo.ecm.automation.core.Constants;
028import org.nuxeo.ecm.automation.core.annotations.Operation;
029import org.nuxeo.ecm.automation.core.annotations.OperationMethod;
030import org.nuxeo.ecm.automation.core.annotations.Param;
031import org.nuxeo.ecm.automation.core.util.BlobList;
032import org.nuxeo.ecm.automation.core.util.Properties;
033import org.nuxeo.ecm.core.api.Blob;
034import org.nuxeo.ecm.core.api.DocumentModel;
035import org.nuxeo.ecm.core.api.DocumentModelList;
036import org.nuxeo.ecm.platform.pdf.PDFEncryption;
037
038/**
039 * Encrypts the PDF with the given permissions.
040 *
041 * @since 8.10
042 */
043@Operation(id = PDFEncryptOperation.ID, category = Constants.CAT_CONVERSION, label = "PDF: Encrypt", //
044description = "Encrypts the PDF with the given permissions, returning a copy. Permissions are print, modify, "
045        + "copy, modifyAnnot, fillForms, extractForAccessibility, assemble and printDegraded. Any missing permission "
046        + "is set to false (values are true or false, assemble=true for example). originalOwnerPwd is used if the PDF "
047        + "was originally encrypted. If no keyLength is provided, use 128. If the operation is ran on Document(s), "
048        + "xpath lets you specificy where to get the blob from (default: file:content).")
049public class PDFEncryptOperation {
050
051    public static final String ID = "PDF.Encrypt";
052
053    @Param(name = "originalOwnerPwd")
054    private String originalOwnerPwd;
055
056    @Param(name = "ownerPwd")
057    private String ownerPwd;
058
059    @Param(name = "userPwd")
060    private String userPwd;
061
062    @Param(name = "keyLength", required = false, widget = Constants.W_OPTION, values = { "40", "128" })
063    private String keyLength = "128";
064
065    @Param(name = "xpath", required = false, values = { "file:content" })
066    protected String xpath = "file:content";
067
068    @Param(name = "permissions", required = false)
069    protected Properties permissions;
070
071    private AccessPermission computeAccessPermission(Properties properties) {
072        AccessPermission ap = new AccessPermission(0);
073        if (properties == null) {
074            return ap;
075        }
076        for (Entry<String, String> property : properties.entrySet()) {
077            boolean value = Boolean.parseBoolean(property.getValue());
078            switch (property.getKey().toLowerCase()) {
079            case "print":
080                ap.setCanPrint(value);
081                break;
082            case "modify":
083                ap.setCanModify(value);
084                break;
085            case "copy":
086                ap.setCanExtractContent(value);
087                break;
088            case "modifyannot":
089                ap.setCanModifyAnnotations(value);
090                break;
091            case "fillforms":
092                ap.setCanFillInForm(value);
093                break;
094            case "extractforaccessibility":
095                ap.setCanExtractForAccessibility(value);
096                break;
097            case "assemble":
098                ap.setCanAssembleDocument(value);
099                break;
100            case "printdegraded":
101                ap.setCanPrintDegraded(value);
102                break;
103            }
104        }
105        return ap;
106    }
107
108    @OperationMethod
109    public Blob run(Blob inBlob) {
110        PDFEncryption pdfe = new PDFEncryption(inBlob);
111        pdfe.setKeyLength(Integer.parseInt(keyLength));
112        pdfe.setOriginalOwnerPwd(originalOwnerPwd);
113        pdfe.setOwnerPwd(ownerPwd);
114        pdfe.setUserPwd(userPwd);
115        return pdfe.encrypt(computeAccessPermission(permissions));
116    }
117
118    @OperationMethod
119    public BlobList run(BlobList inBlobs) {
120        return inBlobs.stream().map(this::run).collect(Collectors.toCollection(BlobList::new));
121    }
122
123    @OperationMethod
124    public Blob run(DocumentModel inDoc) {
125        if (StringUtils.isBlank(xpath)) {
126            xpath = "file:content";
127        }
128        Blob content = (Blob) inDoc.getPropertyValue(xpath);
129        return (content != null) ? this.run(content) : null;
130    }
131
132    @OperationMethod
133    public BlobList run(DocumentModelList inDocs) {
134        if (StringUtils.isBlank(xpath)) {
135            xpath = "file:content";
136        }
137        return inDocs.stream().map(this::run).collect(Collectors.toCollection(BlobList::new));
138    }
139
140}