Source code for icalendar_anonymizer._config
# SPDX-FileCopyrightText: 2025 icalendar-anonymizer contributors
# SPDX-License-Identifier: AGPL-3.0-or-later
"""Configuration types for anonymization."""
from enum import StrEnum
[docs]
class AnonymizeMode(StrEnum):
"""Anonymization mode for a configurable field."""
KEEP = "keep"
REMOVE = "remove"
RANDOMIZE = "randomize"
REPLACE = "replace"
CONFIGURABLE_FIELDS = frozenset(
{
"SUMMARY",
"DESCRIPTION",
"LOCATION",
"COMMENT",
"CONTACT",
"RESOURCES",
"CATEGORIES",
"ATTENDEE",
"ORGANIZER",
"UID",
}
)
"""Set of field names that can be configured with field_modes parameter.
Contains 10 fields: SUMMARY, DESCRIPTION, LOCATION, COMMENT, CONTACT,
RESOURCES, CATEGORIES, ATTENDEE, ORGANIZER, and UID.
"""
DEFAULT_PLACEHOLDERS = {
"SUMMARY": "[Redacted]",
"DESCRIPTION": "[Content removed]",
"LOCATION": "[Location removed]",
"COMMENT": "[Comment removed]",
"CONTACT": "[Contact removed]",
"RESOURCES": "[Resources removed]",
"CATEGORIES": "REDACTED",
"ATTENDEE": "mailto:redacted@example.local",
"ORGANIZER": "mailto:redacted@example.local",
}
"""Default placeholder values used in REPLACE mode.
Maps field names to their placeholder strings. UID uses a counter for
uniqueness: redacted-1@anonymous.local, redacted-2@anonymous.local, etc.
"""
[docs]
def validate_field_modes(
field_modes: dict[str, str] | None,
) -> dict[str, AnonymizeMode] | None:
"""Validate and normalize field_modes dict.
Args:
field_modes: Dict mapping field name to mode string
Returns:
Normalized dict with uppercase keys and AnonymizeMode values
Raises:
ValueError: If invalid field name, invalid mode, or UID set to remove
TypeError: If field_modes is not a dict or None
"""
if field_modes is None:
return None
if not isinstance(field_modes, dict):
raise TypeError(f"field_modes must be dict or None, got {type(field_modes).__name__}")
result = {}
for field, mode in field_modes.items():
upper_field = field.upper()
if upper_field not in CONFIGURABLE_FIELDS:
raise ValueError(f"Unknown field '{field}'. Valid: {sorted(CONFIGURABLE_FIELDS)}")
try:
mode_enum = AnonymizeMode(mode.lower())
except ValueError:
valid = [m.value for m in AnonymizeMode]
raise ValueError(f"Invalid mode '{mode}'. Valid: {valid}") from None
if upper_field == "UID" and mode_enum == AnonymizeMode.REMOVE:
raise ValueError("UID cannot be removed (would break recurring events)")
result[upper_field] = mode_enum
return result or None