From 789ca3e743fe84936965b757c685385ce5751de7 Mon Sep 17 00:00:00 2001 From: Kierre Date: Wed, 10 Sep 2025 23:57:35 -0400 Subject: [PATCH] Allow Vona to receive invites, and add validation to make_join --- src/globals.py | 32 +++++++++++++--------- src/s2s.py | 73 ++++++++++++++++++++++++++++++++------------------ 2 files changed, 66 insertions(+), 39 deletions(-) diff --git a/src/globals.py b/src/globals.py index c3b60bd..22f89cd 100644 --- a/src/globals.py +++ b/src/globals.py @@ -6,7 +6,7 @@ import json import re import os -vona_version = "1.2.4" +vona_version = "1.2.5" def canonical_json(value): @@ -28,23 +28,29 @@ def sign_json(data): decoded_key = base64.b64decode(base64_key) signing_key = nacl.signing.SigningKey(decoded_key) - signed_message = signing_key.sign(canonical_json(data)) + unsigned_keys = {key: data[key] for key in list(data.keys()) if key == "unsigned"} + for key in unsigned_keys: + del data[key] + signed_message = signing_key.sign(canonical_json(data)) signature = signed_message.signature key_version = parts[1] signature_base64 = base64.b64encode(signature).decode("utf-8").rstrip("=") - signed_json = { - **data, - "signatures": { - config.server_name: { - f"ed25519:{key_version}": signature_base64, - }, - }, - } + new_signature = {f"ed25519:{key_version}": signature_base64} - return signed_json + if "signatures" in data: + data["signatures"][config.server_name] = { + **data["signatures"].get(config.server_name, {}), + **new_signature, + } + else: + data["signatures"] = {config.server_name: new_signature} + + data.update(unsigned_keys) + + return data def make_event_id(): @@ -91,7 +97,7 @@ def make_auth_header(destination, method, path, content=None) -> str: authorization_headers = [] - for key, sig in signed_json["signatures"][origin_name].items(): + for key, sig in signed_json["signatures"][config.server_name].items(): authorization_headers.append( bytes( 'X-Matrix origin="%s",destination="%s",key="%s",sig="%s"' @@ -104,4 +110,4 @@ def make_auth_header(destination, method, path, content=None) -> str: ) ) - return ("Authorization", authorization_headers[0]) + return authorization_headers[0] diff --git a/src/s2s.py b/src/s2s.py index 5c069d6..4b147db 100644 --- a/src/s2s.py +++ b/src/s2s.py @@ -139,8 +139,15 @@ def send_join_v1(roomId, eventId): def send_join_v2(roomId, eventId): return jsonify(send_join(request, roomId)) -@server.route('/_matrix/federation/v1/make_join//') +@server.route('/_matrix/federation/v1/make_join//') def make_join(roomId, userId): + if 2 not in request.args.getlist("ver"): + return jsonify({ + "errcode": "M_INCOMPATIBLE_ROOM_VERSION", + "error": "Your homeserver does not support the features required to join this room.", + "room_version": invite_data["room_version"] + }), 400 + return jsonify({ "event": { "content": { @@ -200,7 +207,7 @@ def user_devices(user): "devices": [], "stream_id": the_funny_number, "user_id": f"@vona:{server_name}" -}) + }) @server.route('/_matrix/federation/v1/user/keys/query', methods=['POST']) def user_keys(): @@ -210,35 +217,49 @@ def user_keys(): # https://spec.matrix.org/v1.15/server-server-api/#inviting-to-a-room @server.route('/_matrix/federation/v2/invite//', methods=['PUT']) def invite_user_v2(room, txnId): - # This endpoint is not allowed for room - # versions over v2 as per spec, so any - # invites received here can be discarded - # safely. - - return jsonify({ - "errcode": "M_INCOMPATIBLE_ROOM_VERSION", - "error": "Vona only supports room version 2.", - "room_version": str(the_funny_number) - }), 400 + return invite_user(request.data.decode('utf-8'), 2) @server.route('/_matrix/federation/v1/invite//', methods=['PUT']) -def invite_user(room, txnId): - # TODO: Sign provided JSON and return it. +def invite_user_v1(room, txnId): + return invite_user(request.data.decode('utf-8'), 1) - data = request.data.decode('utf-8') - invite_data = json.loads(data) +def invite_user(data, endpVer): + try: + invite_data = json.loads(data) + except: + return jsonify({"errcode":"M_NOT_JSON","error":"Content not JSON."}), 400 - if "room_version" in invite_data: - if invite_data["room_version"] != "2": - return jsonify({ - "errcode": "M_INCOMPATIBLE_ROOM_VERSION", - "error": "Vona only supports room version 2.", - "room_version": invite_data["room_version"] - }) - - # Placeholder - abort(500) + if "event" in invite_data: + if "room_version" in invite_data: + if invite_data["room_version"] != "2": + return jsonify({ + "errcode": "M_INCOMPATIBLE_ROOM_VERSION", + "error": "Vona only supports room version 2.", + "room_version": invite_data["room_version"] + }), 400 + else: + if "content" in invite_data["event"]: + if "membership" in invite_data["event"]["content"] and invite_data["event"]["content"]["membership"] == "invite": + if endpVer == 1: + return jsonify([ + 200, + {"event": globals.sign_json(invite_data["event"])} + ]) + else: + return jsonify({"event": globals.sign_json(invite_data["event"]), "room_version": "2"}) + else: + print("membership is NOT an invitation, or it is missing.") + else: + print("event does not have content") + else: + print("data does have room version") + else: + print("data does have event") + return jsonify({ + "errcode": "M_FORBIDDEN", + "error": "Invalid invitation PDU" + }), 403 @server.route('/_matrix/federation/v1/hierarchy/') def space_hierachy(roomId):