changeset 459:cebcb7f56889

backend, delegation: update to XEP-0355 v0.5 (namespace bump) + disco: - delegation now uses namespace `urn:xmpp:delegation:2` - restored node metadata and made it work with PEP - `delegated` attribute is also set on recipient when available (needed to know when a disco query is delegated as the original stanza is lost by wokkel)
author Goffi <goffi@goffi.org>
date Fri, 15 Oct 2021 09:32:04 +0200
parents bc2e04a4d3c1
children 607616f9ef5b
files CHANGELOG sat_pubsub/backend.py sat_pubsub/delegation.py
diffstat 3 files changed, 81 insertions(+), 50 deletions(-) [+]
line wrap: on
line diff
--- a/CHANGELOG	Thu Oct 14 21:30:33 2021 +0200
+++ b/CHANGELOG	Fri Oct 15 09:32:04 2021 +0200
@@ -7,6 +7,7 @@
     - XEP-0346 (Form Discovery and Publishing) implementation (replacing the non standard node schema)
     - environment variables can now be used to set options
     - service name can now be specified with "service_name" parameter
+    - namespace delegation update to v0.5 ("urn:xmpp:delegation:2" is now used)
     - bug fixes
 
 v 0.3.0 (16/08/2019)
--- a/sat_pubsub/backend.py	Thu Oct 14 21:30:33 2021 +0200
+++ b/sat_pubsub/backend.py	Fri Oct 15 09:32:04 2021 +0200
@@ -67,7 +67,7 @@
 from zope.interface import implementer
 
 from twisted.application import service
-from twisted.python import components, log
+from twisted.python import components, failure, log
 from twisted.internet import defer, reactor
 from twisted.words.protocols.jabber.error import StanzaError
 from twisted.words.protocols.jabber import jid
@@ -1638,35 +1638,49 @@
 
         raise exc
 
-    def getInfo(self, requestor, service, nodeIdentifier, pep=None, recipient=None):
-        return [] # FIXME: disabled for now, need to manage PEP
+    async def _getInfo(
+        self,
+        requestor: jid.JID,
+        service: jid.JID,
+        nodeIdentifier: str,
+        pep: bool = False,
+        recipient: Optional[jid.JID] = None
+    ) -> Optional[dict]:
         if not requestor.resource:
-            # this avoid error when getting a disco request from server during namespace delegation
-            return []
+            # this avoid error when getting a disco request from server during namespace
+            # delegation
+            return None
         info = {}
-
-        def saveType(result):
-            info['type'] = result
-            return nodeIdentifier
-
-        def saveMetaData(result):
-            info['meta-data'] = result
+        try:
+            info["type"] = await self.backend.getNodeType(nodeIdentifier, pep, recipient)
+            info["meta-data"] = await self.backend.getNodeMetaData(
+                nodeIdentifier, pep, recipient
+            )
+        except error.NodeNotFound:
+            info["meta-data"] = None
+            return None
+        except failure.Failure as e:
+            self._mapErrors(e)
+        except Exception as e:
+            self._mapErrors(failure.Failure(e))
+        else:
             return info
 
-        def trapNotFound(failure):
-            failure.trap(error.NodeNotFound)
-            return info
+    def getInfo(self, requestor, service, nodeIdentifier):
+        try:
+            pep = service.delegated
+        except AttributeError:
+            pep = False
+            recipient = None
+        else:
+            recipient = service if pep else None
+        return defer.ensureDeferred(
+            self._getInfo(
+                requestor, service, nodeIdentifier, pep, recipient
+            )
+        )
 
-        d = defer.succeed(nodeIdentifier)
-        d.addCallback(self.backend.getNodeType)
-        d.addCallback(saveType)
-        d.addCallback(self.backend.getNodeMetaData)
-        d.addCallback(saveMetaData)
-        d.addErrback(trapNotFound)
-        d.addErrback(self._mapErrors)
-        return d
-
-    def getNodes(self, requestor, service, nodeIdentifier):
+    async def _getNodes(self, requestor, service, nodeIdentifier):
         """return nodes for disco#items
 
         Pubsub/PEP nodes will be returned if disco node is not specified
@@ -1679,25 +1693,35 @@
             pep = False
 
         if service.resource:
-            return defer.succeed([])
+            return []
 
         if nodeIdentifier:
-            d = self.backend.getItemsIds(nodeIdentifier,
-                                         requestor,
-                                         [],
-                                         requestor.userhostJID() == service,
-                                         None,
-                                         None,
-                                         pep,
-                                         service)
+            items = await self.backend.getItemsIds(
+                nodeIdentifier,
+                requestor,
+                [],
+                requestor.userhostJID() == service,
+                None,
+                None,
+                pep,
+                service
+            )
             # items must be set as name, not node
-            d.addCallback(lambda items: [(None, item) for item in items])
-
+            return [(None, item) for item in items]
         else:
-            d = self.backend.getNodes(requestor.userhostJID(),
-                                      pep,
-                                      service)
-        return d.addErrback(self._mapErrors)
+            try:
+                return await self.backend.getNodes(
+                    requestor.userhostJID(),
+                    pep,
+                    service
+                )
+            except failure.Failure as e:
+                self._mapErrors(e)
+            except Exception as e:
+                self._mapErrors(failure.Failure(e))
+
+    def getNodes(self, requestor, service, nodeIdentifier):
+        return defer.ensureDeferred(self._getNodes(requestor, service, nodeIdentifier))
 
     def getConfigurationOptions(self):
         return self.backend.nodeOptions
--- a/sat_pubsub/delegation.py	Thu Oct 14 21:30:33 2021 +0200
+++ b/sat_pubsub/delegation.py	Fri Oct 15 09:32:04 2021 +0200
@@ -1,7 +1,6 @@
 #!/usr/bin/env python3
-#-*- coding: utf-8 -*-
 #
-# Copyright (c) 2015 Jérôme Poisson
+# Copyright (c) 2015-2021 Jérôme Poisson
 
 
 # This program is free software: you can redistribute it and/or modify
@@ -24,16 +23,16 @@
 from wokkel.subprotocols import XMPPHandler
 from wokkel import pubsub
 from wokkel import data_form
-from wokkel import disco, iwokkel
+from wokkel import disco, iwokkel, generic
 from wokkel.iwokkel import IPubSubService
 from wokkel import mam
 from twisted.python import log
-from twisted.words.protocols.jabber import jid, error
+from twisted.words.protocols.jabber import ijabber, jid, error
 from twisted.words.protocols.jabber.xmlstream import toResponse
 from twisted.words.xish import domish
 from zope.interface import implementer
 
-DELEGATION_NS = 'urn:xmpp:delegation:1'
+DELEGATION_NS = 'urn:xmpp:delegation:2'
 FORWARDED_NS = 'urn:xmpp:forward:0'
 DELEGATION_ADV_XPATH = '/message/delegation[@xmlns="{}"]'.format(DELEGATION_NS)
 DELEGATION_FWD_XPATH = '/iq[@type="set"]/delegation[@xmlns="{}"]/forwarded[@xmlns="{}"]'.format(DELEGATION_NS, FORWARDED_NS)
@@ -42,7 +41,8 @@
 DELEGATION_BARE_SEP = ":bare:"
 
 TO_HACK = ((IPubSubService, pubsub, "PubSubRequest"),
-           (mam.IMAMService, mam, "MAMRequest"))
+           (mam.IMAMService, mam, "MAMRequest"),
+           (None, disco, "_DiscoRequest"))
 
 
 class InvalidStanza(Exception):
@@ -62,9 +62,10 @@
         #      As PubSubRequest from sat.tmp.wokkel.pubsub use _request_class while
         #      original wokkel.pubsub use directly pubsub.PubSubRequest, we need to
         #      check which version is used before monkeypatching
-        for handler in self.parent.handlers:
-            for service, module, default_base_cls in TO_HACK:
-                if service.providedBy(handler):
+        for service, module, default_base_cls in TO_HACK:
+            module_patched = False
+            for handler in self.parent.handlers:
+                if not service or service.providedBy(handler):
                     if hasattr(handler, '_request_class'):
                         request_base_class = handler._request_class
                     else:
@@ -87,12 +88,17 @@
                                 delegated = False
                             instance = cls.__base__.fromElement(element)
                             instance.delegated = delegated
+                            try:
+                                instance.recipient.delegated = delegated
+                            except (AttributeError, TypeError):
+                                pass
                             return instance
 
                     if hasattr(handler, '_request_class'):
                         handler._request_class = RequestWithDelegation
-                    else:
+                    elif not module_patched:
                         setattr(module, default_base_cls, RequestWithDelegation)
+                        module_patched = True
         DelegationsHandler._service_hacked = True
 
     def connectionInitialized(self):