diff options
Diffstat (limited to 'packages/meshbay-common')
| -rw-r--r-- | packages/meshbay-common/src/meshbay_common/handshake.py | 6 | ||||
| -rw-r--r-- | packages/meshbay-common/tests/test_handshake.py | 34 |
2 files changed, 39 insertions, 1 deletions
diff --git a/packages/meshbay-common/src/meshbay_common/handshake.py b/packages/meshbay-common/src/meshbay_common/handshake.py index 65b4e85..4d2cac2 100644 --- a/packages/meshbay-common/src/meshbay_common/handshake.py +++ b/packages/meshbay-common/src/meshbay_common/handshake.py @@ -278,7 +278,11 @@ def authorize_token( raise HandshakeError("Not a member of this group", code="not_a_member") if hosted_groups is not None and group_id not in hosted_groups: - raise HandshakeError("Group not hosted on this node") + # Coded, because a client handed several nodes for one group has to tell + # "this node cannot serve it, try the next one" apart from "you, in this + # browser, must do something first". Uncoded it was neither, and a node + # wrongly registered for a group took that group down for everyone. + raise HandshakeError("Group not hosted on this node", code="not_hosted") return AuthorizedPeer( user_id=user_id, diff --git a/packages/meshbay-common/tests/test_handshake.py b/packages/meshbay-common/tests/test_handshake.py index 8981db8..d4f6d2b 100644 --- a/packages/meshbay-common/tests/test_handshake.py +++ b/packages/meshbay-common/tests/test_handshake.py @@ -223,3 +223,37 @@ def test_membership_refusal_carries_a_code_a_client_can_act_on(): with pytest.raises(HandshakeError) as excinfo: authorize_token(token, pem_pub, group_id="g" * 32) assert excinfo.value.code == "not_a_member" + + +def test_a_group_this_node_does_not_host_is_refused_with_a_code(): + """ + A client is handed every node the hub registered for a group, and only some + of them may be able to serve it. Telling "try the next node" apart from + "you, here, must do something first" is what this code is for: without it + the client either stopped at the first refusal — which is how a group went + dark on 2026-09-11 with its real host online — or had to match on wording. + """ + import jwt as _jwt + from cryptography.hazmat.primitives.asymmetric.ed25519 import Ed25519PrivateKey + from cryptography.hazmat.primitives import serialization + + sk = Ed25519PrivateKey.generate() + pem_priv = sk.private_bytes( + serialization.Encoding.PEM, serialization.PrivateFormat.PKCS8, + serialization.NoEncryption()) + pem_pub = sk.public_key().public_bytes( + serialization.Encoding.PEM, serialization.PublicFormat.SubjectPublicKeyInfo) + + group = "g" * 32 + token = _jwt.encode( + {"sub": "u1", "jti": "j1", "scope": "user", "groups": [group]}, + pem_priv, algorithm="EdDSA") + + # A member of the group, on a node that does not host it. + with pytest.raises(HandshakeError) as excinfo: + authorize_token(token, pem_pub, group_id=group, hosted_groups={"other"}) + assert excinfo.value.code == "not_hosted" + + # And the node that does host it still lets them in. + peer = authorize_token(token, pem_pub, group_id=group, hosted_groups={group}) + assert peer.group_id == group |