summaryrefslogtreecommitdiffstats
path: root/packages/meshbay-node/tests/test_roster_pairing.py
diff options
context:
space:
mode:
Diffstat (limited to 'packages/meshbay-node/tests/test_roster_pairing.py')
-rw-r--r--packages/meshbay-node/tests/test_roster_pairing.py41
1 files changed, 27 insertions, 14 deletions
diff --git a/packages/meshbay-node/tests/test_roster_pairing.py b/packages/meshbay-node/tests/test_roster_pairing.py
index 435cc76..9e45dbc 100644
--- a/packages/meshbay-node/tests/test_roster_pairing.py
+++ b/packages/meshbay-node/tests/test_roster_pairing.py
@@ -22,6 +22,7 @@ from cryptography.hazmat.primitives.asymmetric.x25519 import X25519PrivateKey
from meshbay_common.crypto import generate_gek, pk_to_b64, unwrap_gek_aes
from meshbay_common.join import ROLE_MEMBER, ROLE_OPERATOR, join_transcript
from meshbay_node.indexer.group_index import GroupIndex
+from conftest import one_root
from meshbay_node.roster import Roster, hash_code, normalize_code
from meshbay_node.transport.webrtc_server import WebRTCPeerSession
@@ -65,7 +66,7 @@ def _session(tmp_path: Path, roster, user_id: str = "grenet",
session = WebRTCPeerSession.__new__(WebRTCPeerSession)
session._ctx = {
- "shared_root": shared_root,
+ "roots": one_root(shared_root),
"index": index,
"sk_node": index.sk_node,
"roster": roster,
@@ -74,7 +75,7 @@ def _session(tmp_path: Path, roster, user_id: str = "grenet",
session._ctx["groups"] = {
group_id: {
"gek": gek,
- "shared_root": shared_root,
+ "roots": one_root(shared_root),
"index": index,
"join_policy": join_policy,
},
@@ -599,7 +600,7 @@ async def test_revoke_endpoint_stops_authorization(tmp_path, roster):
resp = client.post(f"/api/members/bob/revoke?group_id={GROUP}&t=tok")
assert resp.status_code == 200
- assert "gek-init" in resp.json()["reminder"], (
+ assert "gek rotate" in resp.json()["reminder"], (
"revocation must remind the operator to rotate the key they still hold")
assert not await roster.is_authorized(GROUP, "bob")
@@ -823,7 +824,7 @@ async def test_a_directory_with_anything_in_it_is_refused(tmp_path, roster):
full.mkdir()
(full / "keep.txt").write_text("still here")
- session._do_dir_delete({"dir": "full"})
+ session._do_dir_delete({"dir": "shared/full"})
assert _last(session).get("detail") == "Directory is not empty"
assert full.exists() and (full / "keep.txt").exists()
@@ -835,15 +836,23 @@ async def test_no_challenge_is_issued_without_an_operator(tmp_path, roster):
session._ctx["has_admin_authority"] = False
(tmp_path / "shared" / "empty").mkdir()
- session._do_dir_delete({"dir": "empty"})
+ session._do_dir_delete({"dir": "shared/empty"})
assert _last(session).get("detail") == "No authorized key for deletion"
assert (tmp_path / "shared" / "empty").exists()
-async def test_the_shared_root_itself_is_not_a_target(tmp_path, roster):
+async def test_a_root_itself_is_not_a_target(tmp_path, roster):
+ """
+ Neither the virtual root nor a root directory can be removed this way.
+
+ Removing a root is a configuration change: doing it through a file operation
+ would leave the group config naming a directory nobody can reach. And the
+ virtual root is not a directory on anyone's disk at all — it belongs to no
+ volume.
+ """
session = await _dir_session(tmp_path, roster)
- for attempt in ("", ".", "/", "../shared"):
+ for attempt in ("", ".", "/", "../shared", "shared", "shared/", "SHARED"):
session._do_dir_delete({"dir": attempt})
assert _last(session).get("type") == "error", f"{attempt!r} was accepted"
assert (tmp_path / "shared").is_dir()
@@ -854,7 +863,11 @@ async def test_escaping_the_shared_root_is_refused(tmp_path, roster):
outside = tmp_path / "outside"
outside.mkdir()
- for attempt in ("../outside", "../../outside", "sub/../../outside"):
+ # Both shapes: a path that names no root at all, and one that starts inside
+ # a real root and then climbs out of it.
+ for attempt in ("../outside", "../../outside", "sub/../../outside",
+ "shared/../outside", "shared/../../outside",
+ "shared/sub/../../outside"):
session._do_dir_delete({"dir": attempt})
assert _last(session).get("type") == "error", f"{attempt!r} was accepted"
assert outside.is_dir(), "a path leaving the shared root removed a directory"
@@ -871,19 +884,19 @@ async def test_an_empty_directory_needs_a_signature_and_then_goes(tmp_path, rost
session = await _dir_session(tmp_path, roster)
(tmp_path / "shared" / "gone").mkdir()
- session._do_dir_delete({"dir": "gone"})
+ session._do_dir_delete({"dir": "shared/gone"})
challenge = _last(session)
assert challenge["type"] == "admin_challenge"
assert challenge["op"] == "dir_delete"
- assert challenge["subject"] == "gone"
+ assert challenge["subject"] == "shared/gone"
transcript = admin_transcript(
op="dir_delete", node_pk_b64=session._node_pk_b64(), group_id="g1",
- subject="gone", nonce=base64.b64decode(challenge["nonce"]),
+ subject="shared/gone", nonce=base64.b64decode(challenge["nonce"]),
ts=challenge["ts"])
await session._admin_exec_dir_delete(
session._admin_ops.pop(challenge["op_id"]) if session._admin_ops
- else {"op": "dir_delete", "subject": "gone"},
+ else {"op": "dir_delete", "subject": "shared/gone"},
transcript, sk_ed.sign(transcript))
assert _last(session)["type"] == "dir_delete_ack"
@@ -906,9 +919,9 @@ async def test_someone_elses_signature_does_not_remove_it(tmp_path, roster):
transcript = admin_transcript(
op="dir_delete", node_pk_b64=session._node_pk_b64(), group_id="g1",
- subject="theirs", nonce=b"\x22" * 32, ts=int(time.time()))
+ subject="shared/theirs", nonce=b"\x22" * 32, ts=int(time.time()))
await session._admin_exec_dir_delete(
- {"op": "dir_delete", "subject": "theirs"},
+ {"op": "dir_delete", "subject": "shared/theirs"},
transcript, sk_member.sign(transcript))
assert _last(session).get("detail") == "Signature verification failed"