Files
govoplan-core/src/govoplan_core/privacy/retention.py
T
2026-07-14 13:22:10 +02:00

181 lines
6.0 KiB
Python

"""Compatibility facade for privacy retention policy.
Policy-owned behavior is provided by ``govoplan-policy`` through the
``policy.privacyRetention`` capability. This module keeps legacy imports stable
without importing policy implementation code from core.
"""
from __future__ import annotations
from dataclasses import dataclass
from typing import Any, Mapping
from govoplan_core.core.policy import CAPABILITY_POLICY_PRIVACY_RETENTION, PrivacyRetentionService
from govoplan_core.core.runtime import get_registry
from govoplan_core.privacy.schemas import (
RETENTION_DAY_KEYS as RETENTION_DAY_KEYS,
RETENTION_POLICY_FIELD_KEYS,
default_allow_lower_level_limits as default_allow_lower_level_limits,
normalize_allow_lower_level_limits,
)
PRIVACY_POLICY_SETTINGS_KEY = "privacy_retention_policy"
_DEFAULT_POLICY = {
"store_raw_campaign_json": True,
"raw_campaign_json_retention_days": None,
"generated_eml_retention_days": None,
"stored_report_detail_retention_days": None,
"mock_mailbox_retention_days": None,
"audit_detail_retention_days": None,
"audit_detail_level": "full",
}
_EXPORTS = (
"PRIVACY_POLICY_SETTINGS_KEY",
"RETENTION_DAY_KEYS",
"RETENTION_POLICY_FIELD_KEYS",
"PrivacyRetentionPolicy",
"PrivacyRetentionPolicyPatch",
"PrivacyPolicyError",
"default_allow_lower_level_limits",
"privacy_policy_from_settings",
"privacy_policy_from_session",
"set_privacy_policy",
"effective_privacy_policy",
"parent_privacy_policy",
"effective_privacy_policy_sources",
"parent_privacy_policy_sources",
"get_privacy_policy_for_scope",
"set_privacy_policy_for_scope",
"sanitize_audit_details_for_policy",
"apply_retention_policy",
)
class PrivacyPolicyError(RuntimeError):
pass
class _UnavailablePolicyModel:
def __init__(self, *args: Any, **kwargs: Any) -> None:
del args, kwargs
_raise_unavailable()
@classmethod
def model_validate(cls, value: Any) -> Any:
del value
_raise_unavailable()
PrivacyRetentionPolicy = _UnavailablePolicyModel
PrivacyRetentionPolicyPatch = _UnavailablePolicyModel
@dataclass(frozen=True, slots=True)
class _SystemPrivacyPolicy:
payload: Mapping[str, Any]
def model_dump(self, *args: Any, **kwargs: Any) -> dict[str, Any]:
del args, kwargs
return dict(self.payload)
def _raise_unavailable() -> None:
raise PrivacyPolicyError("Privacy retention policy requires the active govoplan-policy module.")
def _runtime_service() -> PrivacyRetentionService | None:
registry = get_registry()
if registry is None or not hasattr(registry, "has_capability") or not registry.has_capability(CAPABILITY_POLICY_PRIVACY_RETENTION):
return None
capability = registry.require_capability(CAPABILITY_POLICY_PRIVACY_RETENTION)
if not isinstance(capability, PrivacyRetentionService):
raise PrivacyPolicyError("Privacy retention policy capability is invalid")
return capability
def _service() -> PrivacyRetentionService:
service = _runtime_service()
if service is None:
_raise_unavailable()
return service
def _settings_payload(item: object) -> dict[str, Any]:
settings = getattr(item, "settings", None)
return dict(settings or {}) if isinstance(settings, Mapping) else {}
def _policy_data(settings_payload: Mapping[str, Any] | None) -> dict[str, Any]:
raw = settings_payload.get(PRIVACY_POLICY_SETTINGS_KEY) if isinstance(settings_payload, Mapping) else None
if not isinstance(raw, Mapping):
raw = {}
payload = dict(_DEFAULT_POLICY)
for key in RETENTION_POLICY_FIELD_KEYS:
if key in raw:
payload[key] = raw[key]
payload["allow_lower_level_limits"] = normalize_allow_lower_level_limits(raw.get("allow_lower_level_limits"), fill_defaults=True)
return payload
def privacy_policy_from_settings(item: object) -> Any:
service = _runtime_service()
if service is not None:
return service.privacy_policy_from_settings(item)
return _SystemPrivacyPolicy(_policy_data(_settings_payload(item)))
def privacy_policy_from_session(*args: Any, **kwargs: Any) -> Any:
return _service().privacy_policy_from_session(*args, **kwargs)
def set_privacy_policy(item: object, policy: Any) -> Any:
service = _runtime_service()
if service is not None:
return service.set_privacy_policy(item, policy)
value = policy.model_dump(mode="json") if hasattr(policy, "model_dump") else dict(policy or {})
next_settings = _settings_payload(item)
next_settings[PRIVACY_POLICY_SETTINGS_KEY] = _policy_data({PRIVACY_POLICY_SETTINGS_KEY: value})
setattr(item, "settings", next_settings)
return _SystemPrivacyPolicy(next_settings[PRIVACY_POLICY_SETTINGS_KEY])
def effective_privacy_policy(*args: Any, **kwargs: Any) -> Any:
return _service().effective_privacy_policy(*args, **kwargs)
def parent_privacy_policy(*args: Any, **kwargs: Any) -> Any:
return _service().parent_privacy_policy(*args, **kwargs)
def effective_privacy_policy_sources(*args: Any, **kwargs: Any) -> Any:
return _service().effective_privacy_policy_sources(*args, **kwargs)
def parent_privacy_policy_sources(*args: Any, **kwargs: Any) -> Any:
return _service().parent_privacy_policy_sources(*args, **kwargs)
def get_privacy_policy_for_scope(*args: Any, **kwargs: Any) -> Any:
return _service().get_privacy_policy_for_scope(*args, **kwargs)
def set_privacy_policy_for_scope(*args: Any, **kwargs: Any) -> Any:
return _service().set_privacy_policy_for_scope(*args, **kwargs)
def sanitize_audit_details_for_policy(*args: Any, **kwargs: Any) -> dict[str, Any]:
service = _runtime_service()
if service is not None:
return dict(service.sanitize_audit_details_for_policy(*args, **kwargs))
details = args[1] if len(args) > 1 else kwargs.get("details")
return dict(details or {})
def apply_retention_policy(*args: Any, **kwargs: Any) -> Any:
return _service().apply_retention_policy(*args, **kwargs)
__all__ = list(_EXPORTS)