aboutsummaryrefslogtreecommitdiffstats
path: root/packages/meshbay-node/tests/test_webrtc_transport.py
diff options
context:
space:
mode:
authorChristophe Besson <cbesson@gmail.com>2026-08-15 02:34:19 +0200
committerChristophe Besson <cbesson@gmail.com>2026-08-15 02:34:19 +0200
commit84b032c65e17267d41e04605e79eea82a6f5a59f (patch)
treeeb7708a72944bb15540e103b4319242199af6fbb /packages/meshbay-node/tests/test_webrtc_transport.py
parent5338894f7fec9e1a60affb0e2ff3b9797bcbc968 (diff)
downloadmeshbay-84b032c65e17267d41e04605e79eea82a6f5a59f.tar.gz
feat(groups): editable description, and one source of operator authority
A description could only be set the moment a group was created, so every group made before anyone thought of one stayed blank for good. The owner can now edit it from the group's page, and PATCH /v1/groups/{id} takes it. That endpoint takes the description and nothing else, deliberately. The name, the visibility and the join policy are the terms members joined on; a private group that can quietly become public is not the group they agreed to be in. Changing those needs a decision about who gets told, not a field on a form — there is a test saying so. Separately, the legacy operator key is gone. `admin_pk_ed25519` in node.toml named the operator before the roster existed and was kept so that an existing deployment would keep working; nothing uses it, and a second source of node authority is not something to carry around out of politeness. Authority is the roster, read fresh on every check. It is removed rather than ignored: a config that still names the key gets a warning at startup pointing at the file. Dropping it in silence would refuse invites and file deletion with a signature error that looks like a bug somewhere else — which is exactly how finding M3 presented. Two tests were verifying admin operations by naming a key in the context, which was the legacy path. They now pair an operator into a roster, the way an operator does. The authority test anchored on the deleted function and passed vacuously once it disappeared; it states the invariant against the verifier and the daemon instead. Also defined .btn-secondary, used in four places and styled in none. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Diffstat (limited to 'packages/meshbay-node/tests/test_webrtc_transport.py')
-rw-r--r--packages/meshbay-node/tests/test_webrtc_transport.py34
1 files changed, 30 insertions, 4 deletions
diff --git a/packages/meshbay-node/tests/test_webrtc_transport.py b/packages/meshbay-node/tests/test_webrtc_transport.py
index 93cd3fd..cc0c6a2 100644
--- a/packages/meshbay-node/tests/test_webrtc_transport.py
+++ b/packages/meshbay-node/tests/test_webrtc_transport.py
@@ -823,8 +823,31 @@ async def test_webrtc_dtls_channel_binding_detects_mitm(sk_node, sk_hub, gek, sh
await transport.close_all()
+async def _paired_operator_roster(tmp_path, sk_admin):
+ """
+ A roster holding one operator, which is the only thing that authorizes an
+ admin operation now. It used to be enough to name a key in node.toml; that
+ path is gone, so these tests build the authority the way an operator does —
+ by pairing.
+ """
+ from meshbay_node.roster import Roster
+
+ roster = Roster(db_path=tmp_path / "roster.db")
+ await roster.open()
+ await roster.pin_identity(
+ user_id="user-001", username="operator",
+ pk_ed25519=base64.b64encode(sk_admin.public_key().public_bytes(
+ encoding=serialization.Encoding.Raw,
+ format=serialization.PublicFormat.Raw)).decode(),
+ pk_x25519="", via="test")
+ await roster.set_member(group_id="", user_id="user-001", role="operator",
+ status="active", approved_by="test")
+ return roster
+
+
@pytest.mark.asyncio
-async def test_webrtc_admin_challenge_response(sk_node, sk_hub, gek, shared_dir):
+async def test_webrtc_admin_challenge_response(sk_node, sk_hub, gek, shared_dir,
+ tmp_path):
"""WebRTC DataChannel: admin file delete requires Ed25519 challenge-response."""
hub_pk_pem = _hub_pk_pem(sk_hub)
indexer = DirectoryIndexer(root=shared_dir, group_id="g", sk_node=sk_node, gek=gek)
@@ -837,7 +860,8 @@ async def test_webrtc_admin_challenge_response(sk_node, sk_hub, gek, shared_dir)
shared_root=shared_dir, index=indexer.index,
stun_servers=[],
)
- transport._ctx["admin_pk_ed25519"] = sk_admin.public_key()
+ transport._ctx["roster"] = await _paired_operator_roster(tmp_path, sk_admin)
+ transport._ctx["has_admin_authority"] = True
transport._ctx["node_user_id"] = "user-001"
browser_pc, channel, received = await _setup_peer(
@@ -871,7 +895,8 @@ async def test_webrtc_admin_challenge_response(sk_node, sk_hub, gek, shared_dir)
@pytest.mark.asyncio
-async def test_webrtc_admin_bad_signature_rejected(sk_node, sk_hub, gek, shared_dir):
+async def test_webrtc_admin_bad_signature_rejected(sk_node, sk_hub, gek, shared_dir,
+ tmp_path):
"""WebRTC DataChannel: wrong Ed25519 signature is rejected — hub can't fake admin."""
hub_pk_pem = _hub_pk_pem(sk_hub)
indexer = DirectoryIndexer(root=shared_dir, group_id="g", sk_node=sk_node, gek=gek)
@@ -885,7 +910,8 @@ async def test_webrtc_admin_bad_signature_rejected(sk_node, sk_hub, gek, shared_
shared_root=shared_dir, index=indexer.index,
stun_servers=[],
)
- transport._ctx["admin_pk_ed25519"] = sk_admin.public_key()
+ transport._ctx["roster"] = await _paired_operator_roster(tmp_path, sk_admin)
+ transport._ctx["has_admin_authority"] = True
transport._ctx["node_user_id"] = "user-001"
browser_pc, channel, received = await _setup_peer(