Skip to content

Commit

Permalink
ENH : provide user permissions in human readable
Browse files Browse the repository at this point in the history
  • Loading branch information
pubpub-zz committed Jan 6, 2024
1 parent 8f53c26 commit 8bf9c98
Show file tree
Hide file tree
Showing 3 changed files with 47 additions and 5 deletions.
22 changes: 20 additions & 2 deletions pypdf/_reader.py
Original file line number Diff line number Diff line change
Expand Up @@ -1808,8 +1808,26 @@ def decrypt(self, password: Union[str, bytes]) -> PasswordType:
# TODO: raise Exception for wrong password
return self._encryption.verify(password)

def decode_permissions(self, permissions_code: int) -> Dict[str, bool]:
# Takes the permissions as an integer, returns the allowed access
def decode_permissions(self, permissions_code: Optional[int]) -> Dict[str, bool]:
"""
Decodes the permissions as a dictionary
Args:
permissions_code: integer providing bits from an integer
if None is provided, the value is retrieved from the default pdf fields
Individual permissions using PKCS#7 shall be extracted manually
Returns:
permissions as human readable structure in a dict
Can be used to feed permissions_flag within PdfWriter.encrypt
"""
if permissions_code is None:
permissions_code = cast(
int,
cast(DictionaryObject, self.trailer[TK.ENCRYPT]).get("/P", -1)
if TK.ENCRYPT in self.trailer
else -1,
)
permissions = {}
permissions["print"] = permissions_code & (1 << 3 - 1) != 0 # bit 3
permissions["modify"] = permissions_code & (1 << 4 - 1) != 0 # bit 4
Expand Down
28 changes: 26 additions & 2 deletions pypdf/_writer.py
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,7 @@
)

OPTIONAL_READ_WRITE_FIELD = FieldFlag(0)
ALL_DOCUMENT_PERMISSIONS = UserAccessPermissions((2**31 - 1) - 3)
ALL_DOCUMENT_PERMISSIONS = UserAccessPermissions((2**32 - 1) - 3)


class ObjectDeletionFlag(enum.IntFlag):
Expand Down Expand Up @@ -1065,7 +1065,9 @@ def encrypt(
user_password: str,
owner_password: Optional[str] = None,
use_128bit: bool = True,
permissions_flag: UserAccessPermissions = ALL_DOCUMENT_PERMISSIONS,
permissions_flag: Union[
UserAccessPermissions, Dict[str, bool]
] = ALL_DOCUMENT_PERMISSIONS,
*,
algorithm: Optional[str] = None,
) -> None:
Expand All @@ -1088,13 +1090,35 @@ def encrypt(
Bit position 3 is for printing, 4 is for modifying content,
5 and 6 control annotations, 9 for form fields,
10 for extraction of text and graphics.
permissions can be provided as an integer or as a dictionary
as provided by `PdfReader.decode_permissions()`
algorithm: encrypt algorithm. Values maybe one of "RC4-40", "RC4-128",
"AES-128", "AES-256-R5", "AES-256". If it's valid,
`use_128bit` will be ignored.
"""
if owner_password is None:
owner_password = user_password

if isinstance(permissions_flag, dict):
permissions = permissions_flag
_flag = cast(int, ALL_DOCUMENT_PERMISSIONS)
if not permissions.get("print", False):
_flag -= 1 << 3
if not permissions.get("modify", False):
_flag -= 1 << 4
if not permissions.get("copy", False):
_flag -= 1 << 5
if not permissions.get("annotations", False):
_flag -= 1 << 6
if not permissions.get("forms", False):
_flag -= 1 << 9
if not permissions.get("accessability", False):
_flag -= 1 << 10
if not permissions.get("assemble", False):
_flag -= 1 << 11
if not permissions.get("print_high_quality", False):
_flag -= 1 << 12
permissions_flag = cast(UserAccessPermissions, _flag)
if algorithm is not None:
try:
alg = getattr(EncryptAlgorithm, algorithm.replace("-", "_"))
Expand Down

0 comments on commit 8bf9c98

Please sign in to comment.