Alfresco – Protéger un document PDF avec PDFBox
Bertrand Magnier novembre 14th, 2008
Autre fonctionnalité liée au format PDF pouvant s’avérer intéressante : la protection d’un fichier par un mot de passe, ainsi que la restriction des actions autorisées (extraction de texte, impression, …).
L’implémentation va encore une fois se faire par le biais d’une action Alfresco, mais utilisera la librairie PDFBox, embarquée par Alfresco.
PDFBox [en] est une librairie Java Open-Source (licence BSD) permettant de travailler avec des fichiers PDF (génération, modification et extraction de contenu). Elle comporte également plusieurs programmes utilisables en ligne de commande.
Détails de l’action
Pour cette action de protection, il va nous falloir comme paramètres :
- les mots de passe propriétaire et utilisateur du document,
- le détail des actions autorisées.
Dans PDFBox, ce type de protection est représenté par les objets ProtectionPolicy (pour la partie authentification) et AccessPermission (pour la partie détail); plus précisément nous utiliserons une StandardProtectionPolicy, une protection par mot de passe (par opposition à la PublicKeyProtectionPolicy, protection par certificat).
Notre action va donc ouvrir le document PDF, vérifier si il n’est pas déjà protégé, puis créer la protection à partir des paramètres utilisateurs et enfin l’appliquer.
package com.atolcd.repo.action.executer; import java.io.File; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.util.List; import org.alfresco.model.ContentModel; import org.alfresco.repo.action.ParameterDefinitionImpl; import org.alfresco.repo.action.executer.ActionExecuterAbstractBase; import org.alfresco.repo.content.MimetypeMap; 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.ContentReader; import org.alfresco.service.cmr.repository.ContentService; import org.alfresco.service.cmr.repository.ContentWriter; import org.alfresco.service.cmr.repository.NodeRef; import org.alfresco.util.TempFileProvider; import org.pdfbox.exceptions.COSVisitorException; import org.pdfbox.pdmodel.PDDocument; import org.pdfbox.pdmodel.encryption.AccessPermission; import org.pdfbox.pdmodel.encryption.BadSecurityHandlerException; import org.pdfbox.pdmodel.encryption.StandardProtectionPolicy; import org.springframework.beans.factory.InitializingBean; import org.springframework.util.Assert; public class PDFProtectActionExecuter extends ActionExecuterAbstractBase implements InitializingBean { public static final String NAME = "protect"; public final static String PARAM_OWNER_PASSWORD = "owner_password"; public final static String PARAM_USER_PASSWORD = "user_password"; public final static String PARAM_ASSEMBLY = "assembly"; public final static String PARAM_EXTRACT_CONTENT = "extract_content"; public final static String PARAM_EXTRACT_ACCESSIBILITY = "extract_accessibility"; public final static String PARAM_FILLING = "filling"; public final static String PARAM_MODIFY = "modify"; public final static String PARAM_MODIFY_ANNOTATIONS = "modify_annotations"; public final static String PARAM_PRINTING = "printing"; public final static String PARAM_DEGRADED_PRINTING = "degraded_printing"; private ContentService contentService; public void afterPropertiesSet() throws Exception { Assert.notNull(contentService); } public void setContentService(ContentService contentService) { this.contentService = contentService; } @Override protected void executeImpl(Action action, NodeRef actionedUponNodeRef) { ContentReader reader = contentService.getReader(actionedUponNodeRef, ContentModel.PROP_CONTENT); if (MimetypeMap.MIMETYPE_PDF.equals(reader.getMimetype())) { PDDocument doc = null; try { File newPdf = TempFileProvider.createTempFile("tmp", "protect"); FileOutputStream fos = new FileOutputStream(newPdf); doc = PDDocument.load(reader.getContentInputStream()); if (!doc.isEncrypted()) { Boolean allowAssembly = (Boolean) action .getParameterValue(PARAM_ASSEMBLY); Boolean allowExtraction = (Boolean) action .getParameterValue(PARAM_EXTRACT_CONTENT); Boolean allowExtractionAccessibility = (Boolean) action .getParameterValue(PARAM_EXTRACT_ACCESSIBILITY); Boolean allowFillingInForm = (Boolean) action .getParameterValue(PARAM_FILLING); Boolean allowModifications = (Boolean) action .getParameterValue(PARAM_MODIFY); Boolean allowAnnotationModification = (Boolean) action .getParameterValue(PARAM_MODIFY_ANNOTATIONS); Boolean allowPrinting = (Boolean) action .getParameterValue(PARAM_PRINTING); Boolean allowDegradedPrinting = (Boolean) action .getParameterValue(PARAM_DEGRADED_PRINTING); AccessPermission ap = new AccessPermission(); ap .setCanAssembleDocument((allowAssembly != null && allowAssembly .booleanValue())); ap .setCanExtractContent((allowExtraction != null && allowExtraction .booleanValue())); ap .setCanExtractForAccessibility((allowExtractionAccessibility != null && allowExtractionAccessibility .booleanValue())); ap .setCanFillInForm((allowFillingInForm != null && allowFillingInForm .booleanValue())); ap .setCanModify((allowModifications != null && allowModifications .booleanValue())); ap .setCanModifyAnnotations((allowAnnotationModification != null && allowAnnotationModification .booleanValue())); ap.setCanPrint((allowPrinting != null && allowPrinting .booleanValue())); ap .setCanPrintDegraded((allowDegradedPrinting != null && allowDegradedPrinting .booleanValue())); StandardProtectionPolicy policy = new StandardProtectionPolicy( (String) action .getParameterValue(PARAM_OWNER_PASSWORD), (String) action .getParameterValue(PARAM_USER_PASSWORD), ap); doc.protect(policy); doc.save(fos); fos.close(); doc.close(); ContentWriter writer = contentService.getWriter( actionedUponNodeRef, ContentModel.PROP_CONTENT, true); writer.putContent(newPdf); } else { doc.close(); throw new RuntimeException( "This document is already encrypted"); } } catch (FileNotFoundException fnfe) { fnfe.printStackTrace(); } catch (BadSecurityHandlerException bshe) { bshe.printStackTrace(); } catch (COSVisitorException cosve) { cosve.printStackTrace(); } catch (IOException ioe) { ioe.printStackTrace(); } } } @Override protected void addParameterDefinitions(List paramList) { paramList.add(new ParameterDefinitionImpl(PARAM_OWNER_PASSWORD, DataTypeDefinition.TEXT, true, getParamDisplayLabel(PARAM_OWNER_PASSWORD))); paramList.add(new ParameterDefinitionImpl(PARAM_USER_PASSWORD, DataTypeDefinition.TEXT, true, getParamDisplayLabel(PARAM_USER_PASSWORD))); paramList.add(new ParameterDefinitionImpl(PARAM_ASSEMBLY, DataTypeDefinition.BOOLEAN, false, getParamDisplayLabel(PARAM_ASSEMBLY))); paramList.add(new ParameterDefinitionImpl(PARAM_EXTRACT_CONTENT, DataTypeDefinition.BOOLEAN, false, getParamDisplayLabel(PARAM_EXTRACT_CONTENT))); paramList.add(new ParameterDefinitionImpl(PARAM_EXTRACT_ACCESSIBILITY, DataTypeDefinition.BOOLEAN, false, getParamDisplayLabel(PARAM_EXTRACT_ACCESSIBILITY))); paramList.add(new ParameterDefinitionImpl(PARAM_FILLING, DataTypeDefinition.BOOLEAN, false, getParamDisplayLabel(PARAM_FILLING))); paramList.add(new ParameterDefinitionImpl(PARAM_MODIFY, DataTypeDefinition.BOOLEAN, false, getParamDisplayLabel(PARAM_MODIFY))); paramList.add(new ParameterDefinitionImpl(PARAM_MODIFY_ANNOTATIONS, DataTypeDefinition.BOOLEAN, false, getParamDisplayLabel(PARAM_MODIFY_ANNOTATIONS))); paramList.add(new ParameterDefinitionImpl(PARAM_PRINTING, DataTypeDefinition.BOOLEAN, false, getParamDisplayLabel(PARAM_PRINTING))); paramList.add(new ParameterDefinitionImpl(PARAM_DEGRADED_PRINTING, DataTypeDefinition.BOOLEAN, false, getParamDisplayLabel(PARAM_DEGRADED_PRINTING))); } }
Module
Encore une fois, nous vous proposons ici l’action sous la forme d’un module Alfresco, les sources complètes étant disponibles ici.