# HG changeset patch # User Goffi # Date 1577559407 -3600 # Node ID 7945930865178d67b4b5a9b8df5afc9ef6afe7b5 # Parent 89736353f6be5cec31147d8e3c57014594ac0451 backend: publish-options implementation: - removed some old code - new ConstraintFailed exception - publishing options implementation, following XEP-0060 §7.1.5 - first use of async/await syntax, used to simplify "publish" method diff -r 89736353f6be -r 794593086517 sat_pubsub/backend.py --- a/sat_pubsub/backend.py Mon Nov 18 20:49:38 2019 +0100 +++ b/sat_pubsub/backend.py Sat Dec 28 19:56:47 2019 +0100 @@ -1,5 +1,4 @@ #!/usr/bin/env python3 -#-*- coding: utf-8 -*- # # Copyright (c) 2012-2019 Jérôme Poisson # Copyright (c) 2013-2016 Adrien Cossa @@ -70,7 +69,6 @@ from twisted.python import components, log from twisted.internet import defer, reactor from twisted.words.protocols.jabber.error import StanzaError -# from twisted.words.protocols.jabber.jid import JID, InvalidFormat from twisted.words.xish import domish, utility from wokkel import disco @@ -209,6 +207,7 @@ def supportsPublishOptions(self): return True + def supportsPublisherAffiliation(self): return True @@ -284,10 +283,11 @@ raise error.Forbidden() return (affiliation, node) - d.addCallback(lambda ignore: node.isSubscribed(requestor)) + d.addCallback(lambda __: node.isSubscribed(requestor)) d.addCallback(checkSubscription) elif publish_model != const.VAL_PMODEL_OPEN: - raise ValueError('Unexpected value') # publish_model must be publishers (default), subscribers or open. + # publish_model must be publishers (default), subscribers or open. + raise ValueError('Unexpected value') return d @@ -395,17 +395,10 @@ d.addCallback(doCheck) return d - def publish(self, nodeIdentifier, items, requestor, pep, recipient): - d = self.storage.getNode(nodeIdentifier, pep, recipient) - d.addCallback(self._checkAuth, requestor) - #FIXME: owner and publisher are not necessarly the same. So far we use only owner to get roster. - #FIXME: in addition, there can be several owners: that is not managed yet - d.addCallback(self._doPublish, items, requestor, pep, recipient) - return d + async def publish(self, nodeIdentifier, items, requestor, options, pep, recipient): + node = await self.storage.getNode(nodeIdentifier, pep, recipient) + affiliation, node = await self._checkAuth(node, requestor) - @defer.inlineCallbacks - def _doPublish(self, result, items, requestor, pep, recipient): - affiliation, node = result if node.nodeType == 'collection': raise error.NoPublishing() @@ -413,6 +406,20 @@ persistItems = configuration[const.OPT_PERSIST_ITEMS] deliverPayloads = configuration[const.OPT_DELIVER_PAYLOADS] + # we check that publish-options are satisfied + for field, value in options.items(): + try: + current_value = configuration[field] + except KeyError: + raise error.ConstraintFailed( + f"publish-options {field!r} doesn't exist in node configuration" + ) + if current_value != value: + raise error.ConstraintFailed( + f"configuration field {field!r} has a value of {current_value!r} " + f"which doesn't fit publish-options expected {value!r}" + ) + if items and not persistItems and not deliverPayloads: raise error.ItemForbidden() elif not items and (persistItems or deliverPayloads): @@ -428,7 +435,7 @@ item.uri = None item.defaultUri = None if not item.getAttribute("id"): - item["id"] = yield node.getNextId() + item["id"] = await node.getNextId() new_item = True if ret_payload is None: ret_pubsub_elt = domish.Element((pubsub.NS_PUBSUB, 'pubsub')) @@ -456,7 +463,7 @@ if affiliation == 'owner' or self.isAdmin(requestor): if configuration[const.OPT_CONSISTENT_PUBLISHER]: - pub_map = yield node.getItemsPublishers(itemIdentifiers) + pub_map = await node.getItemsPublishers(itemIdentifiers) if pub_map: # if we have existing items, we replace publishers with # original one to stay consistent @@ -476,13 +483,13 @@ else: # we don't want a publisher to overwrite the item # of an other publisher - yield self._checkOverwrite(node, itemIdentifiers, requestor) + await self._checkOverwrite(node, itemIdentifiers, requestor) # TODO: check conflict and recalculate max id if serial_ids is set - yield node.storeItems(items_data, requestor) + await node.storeItems(items_data, requestor) - yield self._doNotify(node, items_data, deliverPayloads, pep, recipient) - defer.returnValue(ret_payload) + self._doNotify(node, items_data, deliverPayloads, pep, recipient) + return ret_payload def _doNotify(self, node, items_data, deliverPayloads, pep, recipient): if items_data and not deliverPayloads: @@ -624,33 +631,16 @@ def supportsAutoCreate(self): return True - def supportsCreatorCheck(self): - return True - def supportsInstantNodes(self): return True - def createNode(self, nodeIdentifier, requestor, options = None, pep=False, recipient=None): + def createNode(self, nodeIdentifier, requestor, options=None, pep=False, recipient=None): if not nodeIdentifier: nodeIdentifier = 'generic/%s' % uuid.uuid4() if not options: options = {} - # if self.supportsCreatorCheck(): - # groupblog = nodeIdentifier.startswith(const.NS_GROUPBLOG_PREFIX) - # try: - # nodeIdentifierJID = JID(nodeIdentifier[len(const.NS_GROUPBLOG_PREFIX):] if groupblog else nodeIdentifier) - # except InvalidFormat: - # is_user_jid = False - # else: - # is_user_jid = bool(nodeIdentifierJID.user) - - # if is_user_jid and nodeIdentifierJID.userhostJID() != requestor.userhostJID(): - # #we have an user jid node, but not created by the owner of this jid - # print "Wrong creator" - # raise error.Forbidden() - nodeType = 'leaf' config = self.storage.getDefaultConfiguration(nodeType) config['pubsub#node_type'] = nodeType @@ -1309,6 +1299,7 @@ error.NoPublishing: ('feature-not-implemented', 'unsupported', 'publish'), + error.ConstraintFailed: ('conflict', 'precondition-not-met', None), } def __init__(self, backend): @@ -1321,11 +1312,6 @@ self.backend.registerRetractNotifier(self._notifyRetract) self.backend.registerPreDelete(self._preDelete) - # FIXME: to be removed, it's not useful anymore as PEP is now used - # if self.backend.supportsCreatorCheck(): - # self.features.append("creator-jid-check") #SàT custom feature: Check that a node (which correspond to - # a jid in this server) is created by the right jid - if self.backend.supportsAutoCreate(): self.features.append("auto-create") @@ -1347,9 +1333,10 @@ if self.backend.supportsGroupBlog(): self.features.append("groupblog") - - # if self.backend.supportsPublishModel(): #XXX: this feature is not really described in XEP-0060, we just can see it in examples - # self.features.append("publish_model") # but it's necessary for microblogging comments (see XEP-0277) + # XXX: this feature is not really described in XEP-0060, we just can see it in + # examples but it's necessary for microblogging comments (see XEP-0277) + if self.backend.supportsPublishModel(): + self.features.append("publish_model") def getFullItem(self, item_data): """ Attach item configuration to this item @@ -1607,16 +1594,20 @@ print("Auto-creating node %s" % (request.nodeIdentifier,)) d = self.backend.createNode(request.nodeIdentifier, request.sender, + request.options, pep=self._isPep(request), recipient=request.recipient) - d.addCallback(lambda ignore, - request: self.backend.publish(request.nodeIdentifier, - request.items, - request.sender, - self._isPep(request), - request.recipient, - ), - request) + d.addCallback( + lambda __, request: defer.ensureDeferred(self.backend.publish( + request.nodeIdentifier, + request.items, + request.sender, + request.options, + self._isPep(request), + request.recipient, + )), + request, + ) return d return failure @@ -1628,11 +1619,14 @@ return False def publish(self, request): - d = self.backend.publish(request.nodeIdentifier, - request.items, - request.sender, - self._isPep(request), - request.recipient) + d = defer.ensureDeferred(self.backend.publish( + request.nodeIdentifier, + request.items, + request.sender, + request.options, + self._isPep(request), + request.recipient + )) d.addErrback(self._publish_errb, request) return d.addErrback(self._mapErrors) diff -r 89736353f6be -r 794593086517 sat_pubsub/error.py --- a/sat_pubsub/error.py Mon Nov 18 20:49:38 2019 +0100 +++ b/sat_pubsub/error.py Sat Dec 28 19:56:47 2019 +0100 @@ -148,5 +148,12 @@ This node does not support publishing. """ + class BadAccessTypeError(Error): pass + + +class ConstraintFailed(Error): + """ + A requirement is not fulfilled + """