Files
rpa_vision_v3/tests/unit/test_fleet_enroll_lock_wpb.py
Dom f18de016d7 fix(wp-b): verrou d'enrôlement du parc (RPA_FLEET_ENROLL_LOCKED)
Ferme le contournement "poste révoqué + nouveau machine_id + token global" :
quand RPA_FLEET_ENROLL_LOCKED=true, l'enrôlement d'un machine_id INCONNU est refusé
(FleetEnrollLockedError). Les machines déjà connues conservent leur comportement :
active -> AlreadyEnrolled, désinstallé non-revoke -> réactivable, admin_revoke -> Revoked.

- agent_registry.py : _fleet_enroll_locked() + FleetEnrollLockedError + gate avant INSERT
- tests/unit/test_fleet_enroll_lock_wpb.py : 6 tests (verts)

NB : le handler HTTP 403 (api_stream.py /api/v1/agents/enroll) reste dans le WIP de la
branche (api_stream déjà modifié par le préflight non committé) — sera embarqué au commit
de consolidation api_stream. La logique de sécurité (gate) est dans agent_registry, committée.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-08 15:43:04 +02:00

82 lines
2.7 KiB
Python

"""WP-B — verrou d'enrôlement du parc (RPA_FLEET_ENROLL_LOCKED).
Ferme le contournement « poste révoqué + nouveau machine_id + token global » :
quand le parc est verrouillé, aucun NOUVEAU machine_id ne peut s'enrôler, tandis
que les machines déjà connues conservent leur comportement.
"""
from __future__ import annotations
import pytest
from agent_v0.server_v1.agent_registry import (
AgentRegistry,
AgentAlreadyEnrolledError,
AgentRevokedError,
FleetEnrollLockedError,
)
@pytest.fixture
def registry(tmp_path):
return AgentRegistry(db_path=tmp_path / "fleet.db")
def _lock(monkeypatch, on: bool):
if on:
monkeypatch.setenv("RPA_FLEET_ENROLL_LOCKED", "true")
else:
monkeypatch.delenv("RPA_FLEET_ENROLL_LOCKED", raising=False)
def test_unlocked_new_machine_enrolls(registry, monkeypatch):
_lock(monkeypatch, False)
res = registry.enroll(machine_id="PC-NEW-1")
assert res["created"] is True
def test_locked_new_machine_refused(registry, monkeypatch):
_lock(monkeypatch, True)
with pytest.raises(FleetEnrollLockedError):
registry.enroll(machine_id="PC-INTRUS")
def test_locked_known_active_keeps_behavior(registry, monkeypatch):
# machine déjà active : conserve AgentAlreadyEnrolledError, pas FleetEnrollLocked
_lock(monkeypatch, False)
registry.enroll(machine_id="PC-A")
_lock(monkeypatch, True)
with pytest.raises(AgentAlreadyEnrolledError):
registry.enroll(machine_id="PC-A")
def test_locked_known_uninstalled_can_reactivate(registry, monkeypatch):
# machine connue puis désinstallée (raison normale) : réactivation autorisée même locked
_lock(monkeypatch, False)
registry.enroll(machine_id="PC-B")
registry.uninstall(machine_id="PC-B", reason="user_uninstall")
_lock(monkeypatch, True)
res = registry.enroll(machine_id="PC-B")
assert res["reactivated"] is True
def test_revoked_stays_revoked_and_new_id_blocked(registry, monkeypatch):
# le contournement fermé : poste révoqué NE se réactive pas, et un nouveau
# machine_id est refusé quand le parc est verrouillé.
_lock(monkeypatch, False)
registry.enroll(machine_id="PC-C")
registry.uninstall(machine_id="PC-C", reason="admin_revoke")
_lock(monkeypatch, True)
with pytest.raises(AgentRevokedError):
registry.enroll(machine_id="PC-C")
with pytest.raises(FleetEnrollLockedError):
registry.enroll(machine_id="PC-C-BIS") # nouveau machine_id → bloqué
def test_error_message_has_no_token(registry, monkeypatch):
_lock(monkeypatch, True)
try:
registry.enroll(machine_id="PC-X")
except FleetEnrollLockedError as exc:
msg = str(exc).lower()
assert "token" not in msg and "bearer" not in msg