Explorer
/proc/75/root/tmp/aa053-writer_policy.py
← Zurück ↓ Download
"""Fail-closed field ownership checks for persistence writers.

The YAML file is a baseline contract. It records domain owners and the concrete
legacy writer paths found during the AA-053 inventory. No writer is authorized
while a field remains in FIELD_AUDIT_REQUIRED.
"""

from __future__ import annotations

from dataclasses import dataclass
from pathlib import Path
from typing import Any

import yaml


_ALLOWED_STATUSES = frozenset({"ASSIGNED", "FIELD_AUDIT_REQUIRED", "OPEN", "UNASSIGNED"})


class WriteAuthorityError(PermissionError):
    """Raised when a writer is not the owner of every requested field."""


@dataclass(frozen=True, slots=True)
class FieldOwner:
    table: str
    field: str
    owner: str
    status: str
    known_writers: tuple[str, ...] = ()


@dataclass(frozen=True, slots=True)
class WriterPolicy:
    fields: dict[tuple[str, str], FieldOwner]
    writer_paths: frozenset[str] = frozenset()

    def owner_for(self, table: str, field: str) -> str:
        """Return the declared owner, including fields still locked for audit."""
        assignment = self.fields.get((table, field))
        if assignment is None:
            raise WriteAuthorityError(f"no owner declared for {table}.{field}")
        return assignment.owner

    def require(self, owner: str, table: str, fields: list[str] | tuple[str, ...]) -> None:
        """Require *owner* to own every field, failing closed for unknown fields."""
        denied: list[str] = []
        for field in fields:
            assignment = self.fields.get((table, field))
            if assignment is None or assignment.owner != owner or assignment.status != "ASSIGNED":
                denied.append(field)
        if denied:
            raise WriteAuthorityError(
                f"writer {owner!r} is not authorized for {table}: {', '.join(denied)}"
            )

    def require_writer(self, writer_path: str, table: str, fields: list[str] | tuple[str, ...]) -> None:
        """Require a concrete inventoried writer to be assigned to every field.

        Inventory membership is not authorization: FIELD_AUDIT_REQUIRED and all
        other non-ASSIGNED states remain denied, including for known legacy paths.
        """
        denied: list[str] = []
        for field in fields:
            assignment = self.fields.get((table, field))
            if (
                assignment is None
                or writer_path not in assignment.known_writers
                or assignment.status != "ASSIGNED"
            ):
                denied.append(field)
        if denied:
            raise WriteAuthorityError(
                f"writer path {writer_path!r} is not authorized for {table}: {', '.join(denied)}"
            )


def load_writer_policy(path: str | Path) -> WriterPolicy:
    """Load and validate the ownership baseline with concrete writer paths."""
    document: dict[str, Any] = yaml.safe_load(Path(path).read_text(encoding="utf-8")) or {}
    if not isinstance(document, dict):
        raise ValueError("writer policy must be a mapping")
    fields: dict[tuple[str, str], FieldOwner] = {}
    writer_paths: set[str] = set()
    groups = document.get("field_groups") or {}
    if not isinstance(groups, dict):
        raise ValueError("field_groups must be a mapping")
    for group_name, group in groups.items():
        if not isinstance(group, dict):
            raise ValueError(f"field group {group_name!r} must be a mapping")
        # The baseline's mixed group is explicitly a signals field group too.
        table = "signals" if group_name.startswith("signals_") or group_name == "mixed" else group.get("table")
        if not table:
            continue
        owner = str(group.get("owner", "UNASSIGNED"))
        status = str(group.get("status", "OPEN"))
        if status not in _ALLOWED_STATUSES:
            raise ValueError(f"invalid writer policy status {status!r}")
        raw_writers = group.get("known_writers") or []
        if not isinstance(raw_writers, list) or not all(isinstance(item, str) and item.strip() for item in raw_writers):
            raise ValueError(f"known_writers for {group_name!r} must be a list of non-empty strings")
        known_writers = tuple(dict.fromkeys(raw_writers))
        writer_paths.update(known_writers)
        raw_fields = group.get("examples") or []
        if not isinstance(raw_fields, list):
            raise ValueError(f"examples for {group_name!r} must be a list")
        for field in raw_fields:
            key = (str(table), str(field))
            if key in fields:
                raise ValueError(f"duplicate ownership assignment for {key[0]}.{key[1]}")
            fields[key] = FieldOwner(str(table), str(field), owner, status, known_writers)
    return WriterPolicy(fields, frozenset(writer_paths))