aboutsummaryrefslogtreecommitdiffstats
path: root/packages/meshbay-node/tests
diff options
context:
space:
mode:
authorChristophe Besson <cbesson@gmail.com>2026-10-15 14:24:44 +0200
committerChristophe Besson <cbesson@gmail.com>2026-10-15 14:24:44 +0200
commit283771ff468bd9e1a60a4902a5ada4319c063073 (patch)
treef4a43f25def7621fe26ed57eee26413dcbe7cc6f /packages/meshbay-node/tests
parent2dbff20818d171242a15e3567e14292c0d63aea2 (diff)
downloadmeshbay-283771ff468bd9e1a60a4902a5ada4319c063073.tar.gz
fix(node): a removal made at the machine is in audit.db
The Node page and the CLI remove access through the loopback API, and revoke and unpin there wrote nothing to audit.db; only the signed MNP door did. A removed person's next connection then showed an auth_failed with nothing before it to explain it. Both routes now record member_revoke and member_unpin once the operation has succeeded, with the node's own account as the actor and 127.0.0.1 as the address, which tells the two doors apart. A refusal changes nothing and records nothing. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Diffstat (limited to 'packages/meshbay-node/tests')
-rw-r--r--packages/meshbay-node/tests/test_roster_pairing.py27
1 files changed, 27 insertions, 0 deletions
diff --git a/packages/meshbay-node/tests/test_roster_pairing.py b/packages/meshbay-node/tests/test_roster_pairing.py
index b2799e9..e2180ba 100644
--- a/packages/meshbay-node/tests/test_roster_pairing.py
+++ b/packages/meshbay-node/tests/test_roster_pairing.py
@@ -845,6 +845,33 @@ async def test_unpin_endpoint_allows_repairing(tmp_path, roster):
assert client.post("/api/members/bob/unpin?t=tok").status_code == 404
+async def test_a_removal_at_the_machine_is_in_the_audit_log(tmp_path, roster):
+ """The Node page and the CLI remove through here; the signed WebRTC door
+ already wrote its own row, this one wrote none."""
+ from meshbay_node.audit import AuditStore
+
+ audit = AuditStore(db_path=tmp_path / "audit.db")
+ await audit.open()
+ try:
+ client, _ = _ui_client(tmp_path, roster, audit_store=audit)
+ _, pk_ed_b64, pk_x_b64 = _keypair()
+ await roster.pin_identity("bob", "bob", pk_ed_b64, pk_x_b64, "code")
+ await roster.set_member(GROUP, "bob", ROLE_MEMBER, "active", "grenet")
+
+ assert client.post(f"/api/members/bob/revoke?group_id={GROUP}&t=tok").status_code == 200
+ assert client.post("/api/members/bob/unpin?t=tok").status_code == 200
+ # A refusal changed nothing, so it records nothing.
+ assert client.post("/api/members/bob/unpin?t=tok").status_code == 404
+
+ rows = sorted(await audit.get_entries(), key=lambda e: e.id)
+ assert [(e.event, e.user_id, e.ip, e.group_id, e.detail) for e in rows] == [
+ ("member_revoke", "grenet", "127.0.0.1", GROUP, "bob"),
+ ("member_unpin", "grenet", "127.0.0.1", "", "bob"),
+ ]
+ finally:
+ await audit.close()
+
+
async def test_operator_surface_needs_the_session_token(tmp_path, roster):
"""11.5.3 applies to every one of these: they change who may hold the key."""
client, _ = _ui_client(tmp_path, roster)