Skip to content

Commit

Permalink
Document new permissions API more cleanly
Browse files Browse the repository at this point in the history
  • Loading branch information
MatthiasValvekens committed Apr 27, 2024
1 parent f0815f8 commit af57d0c
Show file tree
Hide file tree
Showing 2 changed files with 65 additions and 7 deletions.
8 changes: 8 additions & 0 deletions docs/api-docs/pyhanko.pdf_utils.crypt.rst
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,14 @@ pyhanko.pdf\_utils.crypt.filter\_mixins module
:undoc-members:
:show-inheritance:

pyhanko.pdf\_utils.crypt.permissions module
-----------------------------------

.. automodule:: pyhanko.pdf_utils.crypt.permissions
:members:
:undoc-members:
:show-inheritance:

pyhanko.pdf\_utils.crypt.pubkey module
--------------------------------------

Expand Down
64 changes: 57 additions & 7 deletions pyhanko/pdf_utils/crypt/permissions.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,33 @@
from enum import Flag
from functools import reduce

__all__ = ['PdfPermissions', 'StandardPermissions', 'PubKeyPermissions']


class PdfPermissions(Flag):
"""
Utility mixin for PDF permission flags.
"""

# We purposefully do not inherit from IntFlag since
# PDF uses 32-bit twos complement to treat flags as ints,
# which doesn't jive well with what IntFlag would do,
# so it's hard to detect backwards compatibility issues.

@classmethod
def allow_everything(cls):
"""
Set all permissions.
"""

return reduce(operator.or_, cls.__members__.values())

@classmethod
def from_uint(cls, uint_flags: int):
"""
Convert a 32-bit unsigned integer into PDF permission flags.
"""

result = cls(0)
for flag in cls:
if uint_flags & flag.value:
Expand All @@ -20,28 +38,54 @@ def from_uint(cls, uint_flags: int):

@classmethod
def from_bytes(cls, flags: bytes):
"""
Convert a string of 4 bytes into PDF permission flags.
"""

uint_flags = struct.unpack('>I', flags)[0]
return cls.from_uint(uint_flags)

@classmethod
def from_sint32(cls, sint32_flags: int):
"""
Convert a 32-bit signed integer into PDF permission flags.
"""

return cls.from_uint(sint32_flags & 0xFFFFFFFF)

def as_uint32(self):
def as_uint32(self) -> int:
"""
Convert a set of PDF permission flags to their 32-bit
unsigned integer representation.
This will already take into account some conventions
in the PDF specification, i.e. to set as-yet undefined
permission flags to 'Allow'.
"""

raise NotImplementedError

def as_bytes(self) -> bytes:
"""
Convert a set of PDF permission flags to their binary
representation.
"""
return struct.pack('>I', self.as_uint32())

def as_sint32(self) -> int:
"""
Convert a set of PDF permission flags to their
signed integer representation.
"""
return struct.unpack('>i', self.as_bytes())[0]


class StandardPermissions(PdfPermissions, Flag):
# We purposefully do not inherit from IntFlag since
# PDF uses 32-bit twos complement to treat flags as ints,
# which doesn't jive well with what IntFlag would do,
# so it's hard to detect backwards compatibility issues.
"""
Permission flags for the standard security handler.
See Table 22 in ISO 32000-2:2020.
"""

ALLOW_PRINTING = 4
ALLOW_MODIFICATION_GENERIC = 8
Expand All @@ -52,11 +96,17 @@ class StandardPermissions(PdfPermissions, Flag):
ALLOW_REASSEMBLY = 1024
ALLOW_HIGH_QUALITY_PRINTING = 2048

def as_uint32(self):
def as_uint32(self) -> int:
return sum(x.value for x in self.__class__ if x in self) | 0xFFFFF0C0


class PubKeyPermissions(PdfPermissions, Flag):
"""
Permission flags for the public-key security handler.
See Table 24 in ISO 32000-2:2020.
"""

ALLOW_ENCRYPTION_CHANGE = 2
ALLOW_PRINTING = 4
ALLOW_MODIFICATION_GENERIC = 8
Expand All @@ -67,6 +117,6 @@ class PubKeyPermissions(PdfPermissions, Flag):
ALLOW_REASSEMBLY = 1024
ALLOW_HIGH_QUALITY_PRINTING = 2048

def as_uint32(self):
def as_uint32(self) -> int:
# ensure the first bit is set for compatibility with Acrobat
return sum(x.value for x in self.__class__ if x in self) | 0xFFFFF0C1

0 comments on commit af57d0c

Please sign in to comment.