# Copyright (c) 2010-2024 fastpyxl
from fastpyxl.compat import safe_string
from fastpyxl.typed_serialisable.base import Serialisable
from fastpyxl.typed_serialisable.fields import AliasField, Field
from fastpyxl.typed_serialisable.parse import normalize_attrib
from fastpyxl.utils.protection import hash_password
[docs]
class WorkbookProtection(Serialisable):
_workbook_password, _revisions_password = None, None
tagname = "workbookPr"
workbook_password = AliasField("workbookPassword", default=None)
workbookPasswordCharacterSet: str | None = Field.attribute(expected_type=str, allow_none=True, default=None)
revision_password = AliasField("revisionsPassword", default=None)
revisionsPasswordCharacterSet: str | None = Field.attribute(expected_type=str, allow_none=True, default=None)
lockStructure: bool | None = Field.attribute(expected_type=bool, allow_none=True, default=None)
lock_structure = AliasField("lockStructure", default=None)
lockWindows: bool | None = Field.attribute(expected_type=bool, allow_none=True, default=None)
lock_windows = AliasField("lockWindows", default=None)
lockRevision: bool | None = Field.attribute(expected_type=bool, allow_none=True, default=None)
lock_revision = AliasField("lockRevision", default=None)
revisionsAlgorithmName: str | None = Field.attribute(expected_type=str, allow_none=True, default=None)
revisionsHashValue: str | None = Field.attribute(expected_type=str, allow_none=True, default=None)
revisionsSaltValue: str | None = Field.attribute(expected_type=str, allow_none=True, default=None)
revisionsSpinCount: int | None = Field.attribute(expected_type=int, allow_none=True, default=None)
workbookAlgorithmName: str | None = Field.attribute(expected_type=str, allow_none=True, default=None)
workbookHashValue: str | None = Field.attribute(expected_type=str, allow_none=True, default=None)
workbookSaltValue: str | None = Field.attribute(expected_type=str, allow_none=True, default=None)
workbookSpinCount: int | None = Field.attribute(expected_type=int, allow_none=True, default=None)
def __init__(self,
workbookPassword=None,
workbookPasswordCharacterSet=None,
revisionsPassword=None,
revisionsPasswordCharacterSet=None,
lockStructure=None,
lockWindows=None,
lockRevision=None,
revisionsAlgorithmName=None,
revisionsHashValue=None,
revisionsSaltValue=None,
revisionsSpinCount=None,
workbookAlgorithmName=None,
workbookHashValue=None,
workbookSaltValue=None,
workbookSpinCount=None,
):
if workbookPassword is not None:
self.workbookPassword = workbookPassword
self.workbookPasswordCharacterSet = workbookPasswordCharacterSet
if revisionsPassword is not None:
self.revisionsPassword = revisionsPassword
self.revisionsPasswordCharacterSet = revisionsPasswordCharacterSet
self.lockStructure = lockStructure
self.lockWindows = lockWindows
self.lockRevision = lockRevision
self.revisionsAlgorithmName = revisionsAlgorithmName
self.revisionsHashValue = revisionsHashValue
self.revisionsSaltValue = revisionsSaltValue
self.revisionsSpinCount = revisionsSpinCount
self.workbookAlgorithmName = workbookAlgorithmName
self.workbookHashValue = workbookHashValue
self.workbookSaltValue = workbookSaltValue
self.workbookSpinCount = workbookSpinCount
def __iter__(self):
yield from super().__iter__()
if self._workbook_password is not None:
yield "workbookPassword", safe_string(self._workbook_password)
if self._revisions_password is not None:
yield "revisionsPassword", safe_string(self._revisions_password)
[docs]
def set_workbook_password(self, value='', already_hashed=False):
"""Set a password on this workbook."""
if not already_hashed:
value = hash_password(value)
self._workbook_password = value
@property
def workbookPassword(self):
"""Return the workbook password value, regardless of hash."""
return self._workbook_password
@workbookPassword.setter
def workbookPassword(self, value):
"""Set a workbook password directly, forcing a hash step."""
self.set_workbook_password(value)
[docs]
def set_revisions_password(self, value='', already_hashed=False):
"""Set a revision password on this workbook."""
if not already_hashed:
value = hash_password(value)
self._revisions_password = value
@property
def revisionsPassword(self):
"""Return the revisions password value, regardless of hash."""
return self._revisions_password
@revisionsPassword.setter
def revisionsPassword(self, value):
"""Set a revisions password directly, forcing a hash step."""
self.set_revisions_password(value)
[docs]
@classmethod
def from_tree(cls, node):
"""Don't hash passwords when deserialising from XML"""
attrib = dict(node.attrib)
for key, ns_key in cls.__namespaced__:
if ns_key in attrib:
attrib[key] = attrib.pop(ns_key)
attrib = normalize_attrib(attrib)
for xml_name, py_name in cls.__attribute_xml_name_map__.items():
if xml_name in attrib and py_name not in attrib:
attrib[py_name] = attrib.pop(xml_name)
wb_raw = attrib.pop("workbookPassword", None)
rev_raw = attrib.pop("revisionsPassword", None)
self = cls(**attrib)
if wb_raw is not None:
self.set_workbook_password(wb_raw, already_hashed=True)
if rev_raw is not None:
self.set_revisions_password(rev_raw, already_hashed=True)
return self
# Backwards compatibility
DocumentSecurity = WorkbookProtection
[docs]
class FileSharing(Serialisable):
tagname = "fileSharing"
readOnlyRecommended: bool | None = Field.attribute(expected_type=bool, allow_none=True, default=None)
userName: str | None = Field.attribute(expected_type=str, allow_none=True, default=None)
reservationPassword: str | None = Field.attribute(expected_type=str, allow_none=True, default=None)
algorithmName: str | None = Field.attribute(expected_type=str, allow_none=True, default=None)
hashValue: str | None = Field.attribute(expected_type=str, allow_none=True, default=None)
saltValue: str | None = Field.attribute(expected_type=str, allow_none=True, default=None)
spinCount: int | None = Field.attribute(expected_type=int, allow_none=True, default=None)
def __init__(self,
readOnlyRecommended=None,
userName=None,
reservationPassword=None,
algorithmName=None,
hashValue=None,
saltValue=None,
spinCount=None,
):
self.readOnlyRecommended = readOnlyRecommended
self.userName = userName
self.reservationPassword = reservationPassword
self.algorithmName = algorithmName
self.hashValue = hashValue
self.saltValue = saltValue
self.spinCount = spinCount