Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
import org.apache.pdfbox.pdmodel.common.filespecification.PDComplexFileSpecification;
import org.apache.pdfbox.pdmodel.common.filespecification.PDEmbeddedFile;
import org.apache.pdfbox.pdmodel.common.filespecification.PDFileSpecification;
import org.apache.pdfbox.pdmodel.encryption.AccessPermission;
import org.apache.pdfbox.pdmodel.encryption.PDEncryption;
import org.apache.pdfbox.pdmodel.interactive.annotation.PDAnnotation;
import org.apache.pdfbox.pdmodel.interactive.annotation.PDAnnotationFileAttachment;
Expand Down Expand Up @@ -67,6 +68,26 @@ public class PDFMatcher extends Matcher {

public static final String IS_ENCRYPTED_KEY = "is-encrypted";

public static final String PERMISSIONS_KEY = "permissions";

public static final String CAN_PRINT_KEY = "can-print";

public static final String CAN_PRINT_FAITHFUL_KEY = "can-print-faithful";

public static final String CAN_MODIFY_KEY = "can-modify";

public static final String CAN_MODIFY_ANNOTATIONS_KEY = "can-modify-annotations";

public static final String CAN_FILL_IN_FORM_KEY = "can-fill-in-form";

public static final String CAN_EXTRACT_CONTENT_KEY = "can-extract-content";

public static final String CAN_EXTRACT_FOR_ACCESSIBILITY_KEY = "can-extract-for-accessibility";

public static final String CAN_ASSEMBLE_DOCUMENT_KEY = "can-assemble-document";

public static final String IS_OWNER_PERMISSION_KEY = "is-owner-permission";

public static final String METADATA_KEY = "metadata";

public static final String VERSION_KEY = "version";
Expand Down Expand Up @@ -121,6 +142,7 @@ public boolean matches(final Context context) {
} else
pdfDetails.put(IS_ENCRYPTED_KEY, false);

providePermissionInfo(pdfDetails, document.getCurrentAccessPermission());

final List<String> filenames = new ArrayList<>();

Expand Down Expand Up @@ -165,6 +187,28 @@ private void provideDocumentInformation(final Map<String, Object> pdfDetails, fi
pdfDetails.put("trapped", info.getTrapped());
}

/**
* Adds the PDF's access permissions (print, copy, modify, fill-in-form, etc.) to the result map.
* These are available regardless of whether the document is encrypted: for unencrypted
* documents PDFBox reports the permissions the owner would have (i.e. everything allowed).
*
* @param pdfDetails map to which the results get added
* @param permission the document's current access permission
*/
private void providePermissionInfo(final Map<String, Object> pdfDetails, final AccessPermission permission) {
Map<String, Object> permissions = new HashMap<>();
permissions.put(CAN_PRINT_KEY, permission.canPrint());
permissions.put(CAN_PRINT_FAITHFUL_KEY, permission.canPrintFaithful());
permissions.put(CAN_MODIFY_KEY, permission.canModify());
permissions.put(CAN_MODIFY_ANNOTATIONS_KEY, permission.canModifyAnnotations());
permissions.put(CAN_FILL_IN_FORM_KEY, permission.canFillInForm());
permissions.put(CAN_EXTRACT_CONTENT_KEY, permission.canExtractContent());
permissions.put(CAN_EXTRACT_FOR_ACCESSIBILITY_KEY, permission.canExtractForAccessibility());
permissions.put(CAN_ASSEMBLE_DOCUMENT_KEY, permission.canAssembleDocument());
permissions.put(IS_OWNER_PERMISSION_KEY, permission.isOwnerPermission());
pdfDetails.put(PERMISSIONS_KEY, permissions);
}

private void provideXMPMetadata(final Map<String, Object> pdfDetails, final PDMetadata meta) throws IOException {
InputStream xmpMetadata = meta.exportXMPMetadata();

Expand Down
24 changes: 24 additions & 0 deletions analyzer/src/test/java/TestPDFMatcher.java
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ void testUnencryptedPDFs() throws IOException {
assertValidDetails(result);
assertFalse(isEncrypted(result), f + " is not recognized as unencrypted PDF");
assertFalse(hasEmbeddedDocuments(result), f + " has no embedded documents");
assertValidPermissions(result);
}
}

Expand Down Expand Up @@ -80,6 +81,7 @@ void testPDFPortfolios() throws IOException {
assertValidDetails(result);
assertFalse(isEncrypted(result), f + " is not recognized as unencrypted PDF");
assertTrue(hasEmbeddedDocuments(result), f + " has embedded documents, but none were found");
assertValidPermissions(result);
}
}

Expand All @@ -94,6 +96,7 @@ void testEncryptedPDFs() throws IOException {
assertValidDetails(result);
assertTrue(isEncrypted(result),f + " is not recognized as encrypted PDF");
assertFalse(hasEmbeddedDocuments(result), f + " has no embedded documents");
assertValidPermissions(result);
}
}

Expand Down Expand Up @@ -205,6 +208,27 @@ private static void assertValidDetails(final Map<String, Object> result) {
assertTrue(object instanceof Map, "PDF details are not a map, but " + object.getClass());
}

@SuppressWarnings("unchecked")
private static void assertValidPermissions(final Map<String, Object> result) {
final Map<String, Object> details = (Map<String, Object>) result.get(PDFMatcher.DETAILS_KEY);
assertThat(details, hasKey(PDFMatcher.PERMISSIONS_KEY));
final Object permissionsObject = details.get(PDFMatcher.PERMISSIONS_KEY);
assertTrue(permissionsObject instanceof Map, "PDF permissions are not a map, but " + permissionsObject.getClass());
final Map<String, Object> permissions = (Map<String, Object>) permissionsObject;
assertThat(permissions, hasKey(PDFMatcher.CAN_PRINT_KEY));
assertThat(permissions, hasKey(PDFMatcher.CAN_PRINT_FAITHFUL_KEY));
assertThat(permissions, hasKey(PDFMatcher.CAN_MODIFY_KEY));
assertThat(permissions, hasKey(PDFMatcher.CAN_MODIFY_ANNOTATIONS_KEY));
assertThat(permissions, hasKey(PDFMatcher.CAN_FILL_IN_FORM_KEY));
assertThat(permissions, hasKey(PDFMatcher.CAN_EXTRACT_CONTENT_KEY));
assertThat(permissions, hasKey(PDFMatcher.CAN_EXTRACT_FOR_ACCESSIBILITY_KEY));
assertThat(permissions, hasKey(PDFMatcher.CAN_ASSEMBLE_DOCUMENT_KEY));
assertThat(permissions, hasKey(PDFMatcher.IS_OWNER_PERMISSION_KEY));
for (Object value : permissions.values()) {
assertTrue(value instanceof Boolean, "Permission value is not a boolean, but " + value.getClass());
}
}

@SuppressWarnings("unchecked")
private static boolean isEncrypted(final Map<String, Object> result) {
final Map<String, Object> details = (Map<String, Object>) result.get(PDFMatcher.DETAILS_KEY);
Expand Down
Loading