Mercurial > libervia-backend
diff sat/plugins/plugin_comp_ap_gateway/__init__.py @ 3869:c0bcbcf5b4b7
component AP gateway: handle `Like` and `Undo`/`Like` activities:
rel 370
author | Goffi <goffi@goffi.org> |
---|---|
date | Thu, 21 Jul 2022 18:07:35 +0200 |
parents | 59fbb66b2923 |
children | 03761f8ba8bb |
line wrap: on
line diff
--- a/sat/plugins/plugin_comp_ap_gateway/__init__.py Thu Jul 21 18:05:20 2022 +0200 +++ b/sat/plugins/plugin_comp_ap_gateway/__init__.py Thu Jul 21 18:07:35 2022 +0200 @@ -293,7 +293,7 @@ if self._pa.isAttachmentNode(itemsEvent.nodeIdentifier): await self.convertAndPostAttachments( client, ap_account, itemsEvent.sender, itemsEvent.nodeIdentifier, - itemsEvent.items, publisher=itemsEvent.sender + itemsEvent.items ) else: await self.convertAndPostItems( @@ -581,7 +581,7 @@ if self.client is None: raise exceptions.InternalError("Client is not set yet") - if jid_.host == self.client.jid.userhost(): + if self.isVirtualJID(jid_): # this is an proxy JID to an AP Actor return self._e.unescape(jid_.user) @@ -786,6 +786,10 @@ """ return url.startswith(self.base_ap_url) + def isVirtualJID(self, jid_: jid.JID) -> bool: + """Tell if a JID is an AP actor mapped through this gateway""" + return jid_.host == self.client.jid.userhost() + def buildSignatureHeader(self, values: Dict[str, str]) -> str: """Build key="<value>" signature header from signature data""" fields = [] @@ -983,12 +987,16 @@ for item in items: if item.name == "item": mb_data = await self._m.item2mbdata(client, item, service, node) - if subscribe_extra_nodes: + author_jid = jid.JID(mb_data["author_jid"]) + if subscribe_extra_nodes and not self.isVirtualJID(author_jid): # we subscribe automatically to comment nodes if any recipient_jid = self.getLocalJIDFromAccount(ap_account) recipient_client = self.client.getVirtualClient(recipient_jid) for comment_data in mb_data.get("comments", []): comment_service = jid.JID(comment_data["service"]) + if self.isVirtualJID(comment_service): + log.debug(f"ignoring virtual comment service: {comment_data}") + continue comment_node = comment_data["node"] await self._p.subscribe( recipient_client, comment_service, comment_node @@ -1019,7 +1027,7 @@ service: jid.JID, node: str, items: List[domish.Element], - publisher: Optional[jid.JID] + publisher: Optional[jid.JID] = None ) -> None: """Convert XMPP item attachments to AP activities and post them to actor inbox @@ -1027,8 +1035,14 @@ @param service: JID of the (virtual) pubsub service where the item has been published @param node: (virtual) node corresponding where the item has been published - @param subscribe_extra_nodes: if True, extra data nodes will be automatically - subscribed, that is comment nodes if present and attachments nodes. + subscribed, that is comment nodes if present and attachments nodes. + @param items: attachments items + @param publisher: publisher of the attachments item (it's NOT the PEP/Pubsub + service, it's the publisher of the item). To be filled only when the publisher + is known for sure, otherwise publisher will be determined either if + "publisher" attribute is set by pubsub service, or as a last resort, using + item's ID (which MUST be publisher bare JID according to pubsub-attachments + specification). """ if len(items) != 1: log.warning( @@ -1040,11 +1054,29 @@ inbox = await self.getAPInboxFromId(actor_id) item_elt = items[0] + item_id = item_elt["id"] + + if publisher is None: + item_pub_s = item_elt.getAttribute("publisher") + publisher = jid.JID(item_pub_s) if item_pub_s else jid.JID(item_id) + + if publisher.userhost() != item_id: + log.warning( + "attachments item ID must be publisher's bare JID, ignoring: " + f"{item_elt.toXml()}" + ) + return + + if self.isVirtualJID(publisher): + log.debug(f"ignoring item coming from local virtual JID {publisher}") + return + if publisher is not None: item_elt["publisher"] = publisher.userhost() + item_service, item_node, item_id = self._pa.attachmentNode2Item(node) item_account = await self.getAPAccountFromJidAndNode(item_service, item_node) - if item_service.host == self.client.jid.userhost(): + if self.isVirtualJID(item_service): # it's a virtual JID mapping to an external AP actor, we can use the # item_id directly item_url = item_id @@ -1072,11 +1104,11 @@ except IndexError: # no known element was present in attachments old_attachment = {} - sender_account = await self.getAPAccountFromJidAndNode( - client.jid, + publisher_account = await self.getAPAccountFromJidAndNode( + publisher, None ) - sender_actor_id = self.buildAPURL(TYPE_ACTOR, sender_account) + publisher_actor_id = self.buildAPURL(TYPE_ACTOR, publisher_account) try: attachments = self._pa.items2attachmentData(client, [item_elt])[0] except IndexError: @@ -1088,22 +1120,22 @@ # new "noticed" attachment, we translate to "Like" activity activity_id = self.buildAPURL("like", item_account, item_id) like = self.createActivity( - TYPE_LIKE, sender_actor_id, item_url, activity_id=activity_id + TYPE_LIKE, publisher_actor_id, item_url, activity_id=activity_id ) like["to"] = [NS_AP_PUBLIC] - await self.signAndPost(inbox, sender_actor_id, like) + await self.signAndPost(inbox, publisher_actor_id, like) else: if "noticed" in old_attachment: # "noticed" attachment has been removed, we undo the "Like" activity activity_id = self.buildAPURL("like", item_account, item_id) like = self.createActivity( - TYPE_LIKE, sender_actor_id, item_url, activity_id=activity_id + TYPE_LIKE, publisher_actor_id, item_url, activity_id=activity_id ) like["to"] = [NS_AP_PUBLIC] - undo = self.createActivity("Undo", sender_actor_id, like) - await self.signAndPost(inbox, sender_actor_id, undo) + undo = self.createActivity("Undo", publisher_actor_id, like) + await self.signAndPost(inbox, publisher_actor_id, undo) - if service.user and service.host == self.client.jid.userhost(): + if service.user and self.isVirtualJID(service): # the item is on a virtual service, we need to store it in cache log.debug("storing attachments item in cache") cached_node = await self.host.memory.storage.getPubsubNode( @@ -1720,7 +1752,7 @@ rep_item = parsed_url["item"] activity_id = self.buildAPURL("item", repeater.userhost(), mb_data["id"]) - if rep_service.host == self.client.jid.userhost(): + if self.isVirtualJID(rep_service): # it's an AP actor linked through this gateway # in this case we can simply use the item ID if not rep_item.startswith("https:"): @@ -1825,7 +1857,7 @@ target_ap_account = await self.getAPAccountFromJidAndNode( service, node ) - if service.host == self.client.jid.userhost(): + if self.isVirtualJID(service): # service is a proxy JID for AP account actor_data = await self.getAPActorDataFromAccount(target_ap_account) followers = actor_data.get("followers") @@ -1836,7 +1868,7 @@ ap_object["cc"] = [followers] if self._m.isCommentNode(node): parent_item = self._m.getParentItem(node) - if service.host == self.client.jid.userhost(): + if self.isVirtualJID(service): # the publication is on a virtual node (i.e. an XMPP node managed by # this gateway and linking to an ActivityPub actor) ap_object["inReplyTo"] = parent_item