oc_chat
Loading...
Searching...
No Matches
roles.py
Go to the documentation of this file.
1#!/usr/bin/env python3
2# -*- coding: utf-8 -*-
3# oc_chat/registry/auth/roles.py
4
5# OCT-LINT: disable=tests_exist reason="Covered by oc_chat/tests/registry/test_auth_roles.py"
6"""
7Runtime role guard for registry operations.
8
9Purpose
10-------
11Map registry operation namespaces to the actor slot kinds that may author
12them.
13
14Responsibilities
15----------------
16- Keep the allow table in one auditable location.
17- Raise typed role errors before any operation-log mutation starts.
18- Provide an explicit system-actor bypass for enumerated internal rows.
19
20Diagnostics
21-----------
22Domain: OC_CHAT.MAIN
23Levels:
24 L2 - lifecycle events
25 L3 - semantic details
26 L4 - deep tracing
27
28Contracts
29---------
30- A role mismatch is not an auth failure and does not write ``auth_events``.
31- Unknown test/private operation namespaces remain compatibility-allowed.
32
33Dependencies
34------------
35- oc_chat.registry.errors (ActorRoleMismatchError raised on a role mismatch)
36- oc_diagnostics (structured debug logging via _dbg)
37"""
38from __future__ import annotations
39
40from oc_diagnostics import _dbg
41
42from oc_chat.registry.errors import ActorRoleMismatchError
43
44
45_dbg("OC_CHAT.MAIN", "module", "loaded", 4)
46
47SlotKind = str
48
49ALL_SLOT_KINDS = frozenset({"operator", "orchestrator", "worker"})
50SYSTEM_OPERATION_PREFIXES = ("migrate.", "heartbeat.")
51SYSTEM_OPERATION_TYPES = frozenset({"doctor.diagnose"})
52
53ROLE_ALLOWSETS: dict[str, frozenset[SlotKind]] = {
54 "auth.register_key": frozenset({"operator"}),
55 "auth.rotate_start": frozenset({"operator"}),
56 "auth.rotate_finalize": frozenset({"operator"}),
57 "auth.reset": frozenset({"operator"}),
58 "audit.override": frozenset({"operator", "orchestrator"}),
59 "audit.verify": frozenset({"operator", "orchestrator"}),
60 "db.archive_log.start": frozenset({"operator"}),
61 "db.archive_log.complete": frozenset({"operator"}),
62 "doctor.repair": frozenset({"operator"}),
63 "handover.accept": frozenset({"orchestrator", "operator"}),
64 "handover.initiate": frozenset({"orchestrator", "operator"}),
65 "handover.timeout": frozenset({"operator"}),
66 "role.handover.complete": frozenset({"orchestrator", "operator"}),
67 "role.handover.start": frozenset({"orchestrator", "operator"}),
68}
69
70PREFIX_ALLOWSETS: tuple[tuple[str, frozenset[SlotKind]], ...] = (
71 ("plan.", frozenset({"operator", "orchestrator", "worker"})),
72 ("queue.", frozenset({"operator", "orchestrator"})),
73 ("roadmap_", frozenset({"operator", "orchestrator"})),
74 ("roadmap.", frozenset({"operator", "orchestrator"})),
75 ("round.", frozenset({"orchestrator"})),
76 ("migrate.", frozenset({"operator"})),
77 ("db.", frozenset({"operator"})),
78 ("doctor.", frozenset({"operator"})),
79 ("_test.", ALL_SLOT_KINDS),
80)
81
82
84 """Table-driven operation role checker."""
85
86 def check(
87 self,
88 operation_type: str,
89 actor_slot_kind: str,
90 *,
91 actor_slot: str,
92 system_actor: bool = False,
93 ) -> None:
94 """Raise if ``actor_slot_kind`` cannot author ``operation_type``."""
95
96 if system_actor and _is_system_operation(operation_type):
97 return
98 allowed = self.allowed_slot_kinds(operation_type)
99 if actor_slot_kind not in allowed:
101 operation_type=operation_type,
102 actor_slot=actor_slot,
103 slot_kind=actor_slot_kind,
104 allowed_slot_kinds=tuple(sorted(allowed)),
105 )
106
107 def allowed_slot_kinds(self, operation_type: str) -> frozenset[SlotKind]:
108 """Return allowed slot kinds for an operation type."""
109
110 exact = ROLE_ALLOWSETS.get(operation_type)
111 if exact is not None:
112 return exact
113 for prefix, allowed in PREFIX_ALLOWSETS:
114 if operation_type.startswith(prefix):
115 return allowed
116 return ALL_SLOT_KINDS
117
118
119def _is_system_operation(operation_type: str) -> bool:
120 return operation_type in SYSTEM_OPERATION_TYPES or operation_type.startswith(
121 SYSTEM_OPERATION_PREFIXES
122 )
None check(self, str operation_type, str actor_slot_kind, *, str actor_slot, bool system_actor=False)
Definition roles.py:93
frozenset[SlotKind] allowed_slot_kinds(self, str operation_type)
Definition roles.py:107
bool _is_system_operation(str operation_type)
Definition roles.py:119