From 50ffc8c3b458979bde43c250bb898f0b89d4ebdf Mon Sep 17 00:00:00 2001 From: Goffi Date: Mon, 26 Oct 2026 12:13:42 +0100 Subject: [PATCH] core, memory (sqla), plugins XEP-0277, XEP-0346: various fixes: - `is_local`: now use `server_jid`. - `aio_func`: pass the missing `self` argument (`aio_fund` is unbound). - Declare `pubsub_service` in `core_types.SatXMPPEntity` - plugin XEP-0277: fix handling of `MbData.from_element` when node is not specified (so we use default node). - plugin XEP-0346: `IdentityData`: fix field access, following move to Pydantic model. --- libervia/backend/core/core_types.py | 4 ++- libervia/backend/core/xmpp.py | 6 ++--- libervia/backend/memory/sqla.py | 7 ++--- libervia/backend/plugins/plugin_xep_0277.py | 3 ++- libervia/backend/plugins/plugin_xep_0346.py | 2 +- tests/unit/test_ap-gateway.py | 29 ++++++++++----------- 6 files changed, 26 insertions(+), 25 deletions(-) diff --git a/libervia/backend/core/core_types.py b/libervia/backend/core/core_types.py index d105b451..e6a1c9df 100644 --- a/libervia/backend/core/core_types.py +++ b/libervia/backend/core/core_types.py @@ -110,6 +110,8 @@ class SatXMPPEntity(ABC): conn_deferred: defer.Deferred xmlstream: mod_xmlstream.XmlStream tor: "Tor | None" = None + # Default pubsub service (set by XEP-0060) + pubsub_service: "t_jid.JID | None" = None def __init__(self) -> None: self.plugin_data: dict[str, Any] = {} @@ -374,5 +376,5 @@ class SatXMPPClient(SatXMPPEntity): class SatXMPPComponent(SatXMPPEntity): def is_local(self, jid_: t_jid.JID) -> bool: - """Returns True if jid_ use a domain or subdomain of component's host""" + """Returns True if jid_ is an account on the local server""" raise NotImplementedError diff --git a/libervia/backend/core/xmpp.py b/libervia/backend/core/xmpp.py index 20a38578..d3f960aa 100644 --- a/libervia/backend/core/xmpp.py +++ b/libervia/backend/core/xmpp.py @@ -1322,10 +1322,8 @@ class SatXMPPComponent(SatXMPPEntity, core_types.SatXMPPComponent, component.Com return False def is_local(self, jid_: jid.JID) -> bool: - """Returns True if jid_ use a domain or subdomain of component's host""" - local_host = self.host.split(".") - assert local_host - return jid_.host.split(".")[-len(local_host) :] == local_host + """Return True if jid_ is an account on the local server.""" + return jid_.host == self.server_jid.host def _create_sub_protocols(self): self.messageProt = SatMessageProtocol() diff --git a/libervia/backend/memory/sqla.py b/libervia/backend/memory/sqla.py index 094e7bdd..e729d535 100644 --- a/libervia/backend/memory/sqla.py +++ b/libervia/backend/memory/sqla.py @@ -164,7 +164,7 @@ class Storage: This method is called on shutdown. """ if self.engine.dialect.name == "sqlite": - await self.wal_checkpoint.aio_func() + await self.wal_checkpoint.aio_func(self) await self.engine.dispose() async def migrate_apply(self, *args: str, log_output: bool = False) -> None: @@ -1464,7 +1464,7 @@ class Storage: create_kwargs = {} try: return await self.set_pubsub_node.aio_func( - client, service, name, **create_kwargs + self, client, service, name, **create_kwargs ) except IntegrityError as e: if "unique" in str(e.orig).lower(): @@ -1472,6 +1472,7 @@ class Storage: # get_pubsub_node above log.debug("ignoring UNIQUE constraint error") return await self.get_pubsub_node.aio_func( + self, client, service, name=name, @@ -2266,7 +2267,7 @@ class Storage: If affiliation is None, the entity's affiliation is removed. @raise exceptions.NotFound: Node doesn't exist. """ - node = await self.get_pubsub_node.aio_func(client, service, node_name) + node = await self.get_pubsub_node.aio_func(self, client, service, node_name) if node is None: raise exceptions.NotFound(f"Node {node_name!r} not found") diff --git a/libervia/backend/plugins/plugin_xep_0277.py b/libervia/backend/plugins/plugin_xep_0277.py index 3f2c33b9..0c17f9a0 100644 --- a/libervia/backend/plugins/plugin_xep_0277.py +++ b/libervia/backend/plugins/plugin_xep_0277.py @@ -214,8 +214,9 @@ class MbData(BaseModel): data = { "id": id_, "service": service, - "node": node, } + if node is not None: + data["node"] = node # Set URI if node is provided # FIXME: node should alway be set in the future, check FIXME in method signature. diff --git a/libervia/backend/plugins/plugin_xep_0346.py b/libervia/backend/plugins/plugin_xep_0346.py index 6b35f4db..164f1525 100644 --- a/libervia/backend/plugins/plugin_xep_0346.py +++ b/libervia/backend/plugins/plugin_xep_0346.py @@ -810,7 +810,7 @@ class PubsubSchema(object): if fill_author: if not values.get("author"): id_data = await self._i.get_identity(client, None, ["nicknames"]) - values["author"] = id_data["nicknames"][0] + values["author"] = id_data.nicknames[0] if not values.get("author_jid"): values["author_jid"] = client.jid.full() item_id = await self.send_data_form_item( diff --git a/tests/unit/test_ap-gateway.py b/tests/unit/test_ap-gateway.py index cbff4455..e9d6cbd4 100644 --- a/tests/unit/test_ap-gateway.py +++ b/tests/unit/test_ap-gateway.py @@ -380,18 +380,20 @@ async def mock_get_pubsub_node(client, service, node, with_subscriptions=False, return fake_cached_node -def mock_client(jid): +def mock_client(client_jid): client = MagicMock() - client.jid = jid + client.jid = client_jid client.host = "test.example" + # same derivation as SatXMPPComponent.server_jid + client.server_jid = jid.JID(client_jid.host.split(".", 1)[-1]) client._ap_storage.get = AsyncMock() client._ap_storage.aset = AsyncMock() client.is_local = lambda jid_: SatXMPPComponent.is_local(client, jid_) return client -def get_virtual_client(jid): - return mock_client(jid) +def get_virtual_client(client_jid): + return mock_client(client_jid) class FakeTReqPostResponse: @@ -450,12 +452,12 @@ class TestActivityPubGateway: == "some_other_node---simple@test.example" ) - # local pubsub node + # pubsub node on non local pubsub service (exact domain match) with patch.object(ap_gateway, "is_pubsub") as is_pubsub: is_pubsub.return_value = True assert ( await get_account(jid_=jid.JID("pubsub.test.example"), node="some_node") - == "some_node@pubsub.test.example" + == "___some_node.40pubsub.2etest.2eexample@ap.test.example" ) # non local pubsub node @@ -505,16 +507,13 @@ class TestActivityPubGateway: with patch.object(ap_gateway, "is_pubsub") as is_pubsub: is_pubsub.return_value = True - # simple local node - assert await get_jid_node("toto@pubsub.test.example") == ( - jid.JID("pubsub.test.example"), - "toto", - ) + # node on non local pubsub service is not allowed with local_only + with pytest.raises(exceptions.PermissionError): + await get_jid_node("toto@pubsub.test.example") - # encoded local node - assert await get_jid_node( - "___urn.3axmpp.3amicroblog.3a0@pubsub.test.example" - ) == (jid.JID("pubsub.test.example"), "urn:xmpp:microblog:0") + # same with an encoded node + with pytest.raises(exceptions.PermissionError): + await get_jid_node("___urn.3axmpp.3amicroblog.3a0@pubsub.test.example") @ed async def test_ap_to_pubsub_conversion(self, ap_gateway, monkeypatch):