Mercurial > libervia-pubsub
comparison sat_pubsub/backend.py @ 353:7c5d85c6fb3a
backend: enforce schema on get/publish and notifications
author | Goffi <goffi@goffi.org> |
---|---|
date | Fri, 08 Sep 2017 08:02:05 +0200 |
parents | efbdca10f0fb |
children | 18b983fe9e1b |
comparison
equal
deleted
inserted
replaced
352:efbdca10f0fb | 353:7c5d85c6fb3a |
---|---|
161 def __init__(self, storage): | 161 def __init__(self, storage): |
162 utility.EventDispatcher.__init__(self) | 162 utility.EventDispatcher.__init__(self) |
163 self.storage = storage | 163 self.storage = storage |
164 self._callbackList = [] | 164 self._callbackList = [] |
165 | 165 |
166 | |
167 def supportsPublisherAffiliation(self): | 166 def supportsPublisherAffiliation(self): |
168 return True | 167 return True |
169 | 168 |
170 | |
171 def supportsGroupBlog(self): | 169 def supportsGroupBlog(self): |
172 return True | 170 return True |
173 | 171 |
174 | |
175 def supportsOutcastAffiliation(self): | 172 def supportsOutcastAffiliation(self): |
176 return True | 173 return True |
177 | 174 |
178 | |
179 def supportsPersistentItems(self): | 175 def supportsPersistentItems(self): |
180 return True | 176 return True |
181 | 177 |
182 | |
183 def supportsPublishModel(self): | 178 def supportsPublishModel(self): |
184 return True | 179 return True |
185 | |
186 | 180 |
187 def getNodeType(self, nodeIdentifier, pep, recipient=None): | 181 def getNodeType(self, nodeIdentifier, pep, recipient=None): |
188 # FIXME: manage pep and recipient | 182 # FIXME: manage pep and recipient |
189 d = self.storage.getNode(nodeIdentifier, pep, recipient) | 183 d = self.storage.getNode(nodeIdentifier, pep, recipient) |
190 d.addCallback(lambda node: node.getType()) | 184 d.addCallback(lambda node: node.getType()) |
204 d = self.privilege.isSubscribedFrom(requestor, recipient) | 198 d = self.privilege.isSubscribedFrom(requestor, recipient) |
205 d.addCallback(self._getNodesIds, pep, recipient) | 199 d.addCallback(self._getNodesIds, pep, recipient) |
206 return d | 200 return d |
207 return self.storage.getNodeIds(pep, recipient) | 201 return self.storage.getNodeIds(pep, recipient) |
208 | 202 |
209 | |
210 def getNodeMetaData(self, nodeIdentifier, pep, recipient=None): | 203 def getNodeMetaData(self, nodeIdentifier, pep, recipient=None): |
211 # FIXME: manage pep and recipient | 204 # FIXME: manage pep and recipient |
212 d = self.storage.getNode(nodeIdentifier, pep, recipient) | 205 d = self.storage.getNode(nodeIdentifier, pep, recipient) |
213 d.addCallback(lambda node: node.getMetaData()) | 206 d.addCallback(lambda node: node.getMetaData()) |
214 d.addCallback(self._makeMetaData) | 207 d.addCallback(self._makeMetaData) |
215 return d | 208 return d |
216 | |
217 | 209 |
218 def _makeMetaData(self, metaData): | 210 def _makeMetaData(self, metaData): |
219 options = [] | 211 options = [] |
220 for key, value in metaData.iteritems(): | 212 for key, value in metaData.iteritems(): |
221 if key in self.nodeOptions: | 213 if key in self.nodeOptions: |
223 option.update(self.nodeOptions[key]) | 215 option.update(self.nodeOptions[key]) |
224 option["value"] = value | 216 option["value"] = value |
225 options.append(option) | 217 options.append(option) |
226 | 218 |
227 return options | 219 return options |
228 | |
229 | 220 |
230 def _checkAuth(self, node, requestor): | 221 def _checkAuth(self, node, requestor): |
231 """ Check authorisation of publishing in node for requestor """ | 222 """ Check authorisation of publishing in node for requestor """ |
232 | 223 |
233 def check(affiliation): | 224 def check(affiliation): |
296 if category: | 287 if category: |
297 categories.append(category) | 288 categories.append(category) |
298 | 289 |
299 return categories | 290 return categories |
300 | 291 |
292 def enforceSchema(self, item_elt, schema, affiliation): | |
293 """modifify item according to element, or refuse publishing | |
294 | |
295 @param item_elt(domish.Element): item to check/modify | |
296 @param schema(domish.Eement): schema to enfore | |
297 @param affiliation(unicode): affiliation of the publisher | |
298 """ | |
299 try: | |
300 x_elt = next(item_elt.elements(data_form.NS_X_DATA, 'x')) | |
301 item_form = data_form.Form.fromElement(x_elt) | |
302 except (StopIteration, data_form.Error): | |
303 raise pubsub.BadRequest(text="node has a schema but item has no form") | |
304 else: | |
305 item_elt.children.remove(x_elt) | |
306 | |
307 schema_form = data_form.Form.fromElement(schema) | |
308 | |
309 # we enforce restrictions | |
310 for field_elt in schema.elements(data_form.NS_X_DATA, 'field'): | |
311 var = field_elt['var'] | |
312 for restrict_elt in field_elt.elements(const.NS_SCHEMA_RESTRICT, 'restrict'): | |
313 write_restriction = restrict_elt.attributes.get('write') | |
314 if write_restriction is not None: | |
315 if write_restriction == 'owner': | |
316 if affiliation != 'owner': | |
317 # write is not allowed on this field, we use default value | |
318 # we can safely use Field from schema_form because | |
319 # we have created this instance only for this method | |
320 try: | |
321 item_form.removeField(item_form.fields[var]) | |
322 except KeyError: | |
323 pass | |
324 item_form.addField(schema_form.fields[var]) | |
325 else: | |
326 raise StanzaError('feature-not-implemented', text='unknown write restriction {}'.format(write_restriction)) | |
327 | |
328 # we now remove every field which is not in data schema | |
329 to_remove = set() | |
330 for item_var, item_field in item_form.fields.iteritems(): | |
331 if item_var not in schema_form.fields: | |
332 to_remove.add(item_field) | |
333 | |
334 for field in to_remove: | |
335 item_form.removeField(field) | |
336 item_elt.addChild(item_form.toElement()) | |
337 | |
301 def _checkOverwrite(self, node, itemIdentifiers, publisher): | 338 def _checkOverwrite(self, node, itemIdentifiers, publisher): |
302 """Check that the itemIdentifiers correspond to items published | 339 """Check that the itemIdentifiers correspond to items published |
303 by the current publisher""" | 340 by the current publisher""" |
304 def doCheck(item_pub_map): | 341 def doCheck(item_pub_map): |
305 for item_publisher in item_pub_map.itervalues(): | 342 for item_publisher in item_pub_map.itervalues(): |
308 | 345 |
309 d = node.getItemsPublishers(itemIdentifiers) | 346 d = node.getItemsPublishers(itemIdentifiers) |
310 d.addCallback(doCheck) | 347 d.addCallback(doCheck) |
311 return d | 348 return d |
312 | 349 |
313 | |
314 def publish(self, nodeIdentifier, items, requestor, pep, recipient): | 350 def publish(self, nodeIdentifier, items, requestor, pep, recipient): |
315 d = self.storage.getNode(nodeIdentifier, pep, recipient) | 351 d = self.storage.getNode(nodeIdentifier, pep, recipient) |
316 d.addCallback(self._checkAuth, requestor) | 352 d.addCallback(self._checkAuth, requestor) |
317 #FIXME: owner and publisher are not necessarly the same. So far we use only owner to get roster. | 353 #FIXME: owner and publisher are not necessarly the same. So far we use only owner to get roster. |
318 #FIXME: in addition, there can be several owners: that is not managed yet | 354 #FIXME: in addition, there can be several owners: that is not managed yet |
319 d.addCallback(self._doPublish, items, requestor, pep, recipient) | 355 d.addCallback(self._doPublish, items, requestor, pep, recipient) |
320 return d | 356 return d |
321 | |
322 | 357 |
323 def _doPublish(self, result, items, requestor, pep, recipient): | 358 def _doPublish(self, result, items, requestor, pep, recipient): |
324 affiliation, node = result | 359 affiliation, node = result |
325 if node.nodeType == 'collection': | 360 if node.nodeType == 'collection': |
326 raise error.NoPublishing() | 361 raise error.NoPublishing() |
346 item["id"] = str(uuid.uuid4()) | 381 item["id"] = str(uuid.uuid4()) |
347 else: | 382 else: |
348 check_overwrite = True | 383 check_overwrite = True |
349 access_model, item_config = self.parseItemConfig(item) | 384 access_model, item_config = self.parseItemConfig(item) |
350 categories = self.parseCategories(item) | 385 categories = self.parseCategories(item) |
386 schema = node.getSchema() | |
387 if schema is not None: | |
388 self.enforceSchema(item, schema, affiliation) | |
351 items_data.append(container.ItemData(item, access_model, item_config, categories)) | 389 items_data.append(container.ItemData(item, access_model, item_config, categories)) |
352 | 390 |
353 if persistItems: | 391 if persistItems: |
354 if check_overwrite and affiliation != 'owner': | 392 if check_overwrite and affiliation != 'owner': |
355 # we don't want a publisher to overwrite the item | 393 # we don't want a publisher to overwrite the item |
363 | 401 |
364 d.addCallback(self._doNotify, node, items_data, | 402 d.addCallback(self._doNotify, node, items_data, |
365 deliverPayloads, pep, recipient) | 403 deliverPayloads, pep, recipient) |
366 return d | 404 return d |
367 | 405 |
368 | |
369 def _doNotify(self, result, node, items_data, deliverPayloads, pep, recipient): | 406 def _doNotify(self, result, node, items_data, deliverPayloads, pep, recipient): |
370 if items_data and not deliverPayloads: | 407 if items_data and not deliverPayloads: |
371 for item_data in items_data: | 408 for item_data in items_data: |
372 item_data.item.children = [] | 409 item_data.item.children = [] |
373 self.dispatch({'items_data': items_data, 'node': node, 'pep': pep, 'recipient': recipient}, | 410 self.dispatch({'items_data': items_data, 'node': node, 'pep': pep, 'recipient': recipient}, |
374 '//event/pubsub/notify') | 411 '//event/pubsub/notify') |
375 | |
376 | 412 |
377 def getNotifications(self, node, items_data): | 413 def getNotifications(self, node, items_data): |
378 """Build a list of subscriber to the node | 414 """Build a list of subscriber to the node |
379 | 415 |
380 subscribers will be associated with subscribed items, | 416 subscribers will be associated with subscribed items, |
424 d = self.storage.getNode(nodeIdentifier, pep, recipient) | 460 d = self.storage.getNode(nodeIdentifier, pep, recipient) |
425 d.addCallback(_getAffiliation, subscriberEntity) | 461 d.addCallback(_getAffiliation, subscriberEntity) |
426 d.addCallback(self._doSubscribe, subscriber) | 462 d.addCallback(self._doSubscribe, subscriber) |
427 return d | 463 return d |
428 | 464 |
429 | |
430 def _doSubscribe(self, result, subscriber): | 465 def _doSubscribe(self, result, subscriber): |
431 # TODO: implement other access models | 466 # TODO: implement other access models |
432 node, affiliation = result | 467 node, affiliation = result |
433 | 468 |
434 if affiliation == 'outcast': | 469 if affiliation == 'outcast': |
452 d = node.addSubscription(subscriber, 'subscribed', {}) | 487 d = node.addSubscription(subscriber, 'subscribed', {}) |
453 d.addCallbacks(lambda _: True, trapExists) | 488 d.addCallbacks(lambda _: True, trapExists) |
454 d.addCallback(cb) | 489 d.addCallback(cb) |
455 | 490 |
456 return d | 491 return d |
457 | |
458 | 492 |
459 def _sendLastPublished(self, subscription, node): | 493 def _sendLastPublished(self, subscription, node): |
460 | 494 |
461 def notifyItem(items): | 495 def notifyItem(items): |
462 if items: | 496 if items: |
476 d.addCallback(notifyItem) | 510 d.addCallback(notifyItem) |
477 d.addErrback(log.err) | 511 d.addErrback(log.err) |
478 | 512 |
479 return subscription | 513 return subscription |
480 | 514 |
481 | |
482 def unsubscribe(self, nodeIdentifier, subscriber, requestor, pep, recipient): | 515 def unsubscribe(self, nodeIdentifier, subscriber, requestor, pep, recipient): |
483 if subscriber.userhostJID() != requestor.userhostJID(): | 516 if subscriber.userhostJID() != requestor.userhostJID(): |
484 return defer.fail(error.Forbidden()) | 517 return defer.fail(error.Forbidden()) |
485 | 518 |
486 d = self.storage.getNode(nodeIdentifier, pep, recipient) | 519 d = self.storage.getNode(nodeIdentifier, pep, recipient) |
487 d.addCallback(lambda node: node.removeSubscription(subscriber)) | 520 d.addCallback(lambda node: node.removeSubscription(subscriber)) |
488 return d | 521 return d |
489 | 522 |
490 | |
491 def getSubscriptions(self, entity): | 523 def getSubscriptions(self, entity): |
492 return self.storage.getSubscriptions(entity) | 524 return self.storage.getSubscriptions(entity) |
493 | 525 |
494 def supportsAutoCreate(self): | 526 def supportsAutoCreate(self): |
495 return True | 527 return True |
497 def supportsCreatorCheck(self): | 529 def supportsCreatorCheck(self): |
498 return True | 530 return True |
499 | 531 |
500 def supportsInstantNodes(self): | 532 def supportsInstantNodes(self): |
501 return True | 533 return True |
502 | |
503 | 534 |
504 def createNode(self, nodeIdentifier, requestor, options = None, pep=False, recipient=None): | 535 def createNode(self, nodeIdentifier, requestor, options = None, pep=False, recipient=None): |
505 if not nodeIdentifier: | 536 if not nodeIdentifier: |
506 nodeIdentifier = 'generic/%s' % uuid.uuid4() | 537 nodeIdentifier = 'generic/%s' % uuid.uuid4() |
507 | 538 |
530 # TODO: handle schema on creation | 561 # TODO: handle schema on creation |
531 d = self.storage.createNode(nodeIdentifier, requestor, config, None, pep, recipient) | 562 d = self.storage.createNode(nodeIdentifier, requestor, config, None, pep, recipient) |
532 d.addCallback(lambda _: nodeIdentifier) | 563 d.addCallback(lambda _: nodeIdentifier) |
533 return d | 564 return d |
534 | 565 |
535 | |
536 def getDefaultConfiguration(self, nodeType): | 566 def getDefaultConfiguration(self, nodeType): |
537 d = defer.succeed(self.storage.getDefaultConfiguration(nodeType)) | 567 d = defer.succeed(self.storage.getDefaultConfiguration(nodeType)) |
538 return d | 568 return d |
539 | |
540 | 569 |
541 def getNodeConfiguration(self, nodeIdentifier, pep, recipient): | 570 def getNodeConfiguration(self, nodeIdentifier, pep, recipient): |
542 if not nodeIdentifier: | 571 if not nodeIdentifier: |
543 return defer.fail(error.NoRootNode()) | 572 return defer.fail(error.NoRootNode()) |
544 | 573 |
545 d = self.storage.getNode(nodeIdentifier, pep, recipient) | 574 d = self.storage.getNode(nodeIdentifier, pep, recipient) |
546 d.addCallback(lambda node: node.getConfiguration()) | 575 d.addCallback(lambda node: node.getConfiguration()) |
547 | 576 |
548 return d | 577 return d |
549 | 578 |
550 | |
551 def setNodeConfiguration(self, nodeIdentifier, options, requestor, pep, recipient): | 579 def setNodeConfiguration(self, nodeIdentifier, options, requestor, pep, recipient): |
552 if not nodeIdentifier: | 580 if not nodeIdentifier: |
553 return defer.fail(error.NoRootNode()) | 581 return defer.fail(error.NoRootNode()) |
554 | 582 |
555 d = self.storage.getNode(nodeIdentifier, pep, recipient) | 583 d = self.storage.getNode(nodeIdentifier, pep, recipient) |
556 d.addCallback(_getAffiliation, requestor) | 584 d.addCallback(_getAffiliation, requestor) |
557 d.addCallback(self._doSetNodeConfiguration, options) | 585 d.addCallback(self._doSetNodeConfiguration, options) |
558 return d | 586 return d |
559 | 587 |
560 | |
561 def _doSetNodeConfiguration(self, result, options): | 588 def _doSetNodeConfiguration(self, result, options): |
562 node, affiliation = result | 589 node, affiliation = result |
563 | 590 |
564 if affiliation != 'owner': | 591 if affiliation != 'owner': |
565 raise error.Forbidden() | 592 raise error.Forbidden() |
566 | 593 |
567 return node.setConfiguration(options) | 594 return node.setConfiguration(options) |
568 | |
569 | 595 |
570 def getNodeSchema(self, nodeIdentifier, pep, recipient): | 596 def getNodeSchema(self, nodeIdentifier, pep, recipient): |
571 if not nodeIdentifier: | 597 if not nodeIdentifier: |
572 return defer.fail(error.NoRootNode()) | 598 return defer.fail(error.NoRootNode()) |
573 | 599 |
590 d = self.storage.getNode(nodeIdentifier, pep, recipient) | 616 d = self.storage.getNode(nodeIdentifier, pep, recipient) |
591 d.addCallback(_getAffiliation, requestor) | 617 d.addCallback(_getAffiliation, requestor) |
592 d.addCallback(self._doSetNodeSchema, schema) | 618 d.addCallback(self._doSetNodeSchema, schema) |
593 return d | 619 return d |
594 | 620 |
595 | |
596 def _doSetNodeSchema(self, result, schema): | 621 def _doSetNodeSchema(self, result, schema): |
597 node, affiliation = result | 622 node, affiliation = result |
598 | 623 |
599 if affiliation != 'owner': | 624 if affiliation != 'owner': |
600 raise error.Forbidden() | 625 raise error.Forbidden() |
601 | 626 |
602 return node.setSchema(schema) | 627 return node.setSchema(schema) |
603 | 628 |
604 | |
605 def getAffiliations(self, entity, nodeIdentifier, pep, recipient): | 629 def getAffiliations(self, entity, nodeIdentifier, pep, recipient): |
606 return self.storage.getAffiliations(entity, nodeIdentifier, pep, recipient) | 630 return self.storage.getAffiliations(entity, nodeIdentifier, pep, recipient) |
607 | |
608 | 631 |
609 def getAffiliationsOwner(self, nodeIdentifier, requestor, pep, recipient): | 632 def getAffiliationsOwner(self, nodeIdentifier, requestor, pep, recipient): |
610 d = self.storage.getNode(nodeIdentifier, pep, recipient) | 633 d = self.storage.getNode(nodeIdentifier, pep, recipient) |
611 d.addCallback(_getAffiliation, requestor) | 634 d.addCallback(_getAffiliation, requestor) |
612 d.addCallback(self._doGetAffiliationsOwner) | 635 d.addCallback(self._doGetAffiliationsOwner) |
613 return d | 636 return d |
614 | 637 |
615 | |
616 def _doGetAffiliationsOwner(self, result): | 638 def _doGetAffiliationsOwner(self, result): |
617 node, affiliation = result | 639 node, affiliation = result |
618 | 640 |
619 if affiliation != 'owner': | 641 if affiliation != 'owner': |
620 raise error.Forbidden() | 642 raise error.Forbidden() |
621 return node.getAffiliations() | 643 return node.getAffiliations() |
622 | |
623 | 644 |
624 def setAffiliationsOwner(self, nodeIdentifier, requestor, affiliations, pep, recipient): | 645 def setAffiliationsOwner(self, nodeIdentifier, requestor, affiliations, pep, recipient): |
625 d = self.storage.getNode(nodeIdentifier, pep, recipient) | 646 d = self.storage.getNode(nodeIdentifier, pep, recipient) |
626 d.addCallback(_getAffiliation, requestor) | 647 d.addCallback(_getAffiliation, requestor) |
627 d.addCallback(self._doSetAffiliationsOwner, requestor, affiliations) | 648 d.addCallback(self._doSetAffiliationsOwner, requestor, affiliations) |
628 return d | 649 return d |
629 | |
630 | 650 |
631 def _doSetAffiliationsOwner(self, result, requestor, affiliations): | 651 def _doSetAffiliationsOwner(self, result, requestor, affiliations): |
632 # Check that requestor is allowed to set affiliations, and delete entities | 652 # Check that requestor is allowed to set affiliations, and delete entities |
633 # with "none" affiliation | 653 # with "none" affiliation |
634 | 654 |
656 else: | 676 else: |
657 d = node.setAffiliations(affiliations) | 677 d = node.setAffiliations(affiliations) |
658 | 678 |
659 return d | 679 return d |
660 | 680 |
661 | |
662 def getSubscriptionsOwner(self, nodeIdentifier, requestor, pep, recipient): | 681 def getSubscriptionsOwner(self, nodeIdentifier, requestor, pep, recipient): |
663 d = self.storage.getNode(nodeIdentifier, pep, recipient) | 682 d = self.storage.getNode(nodeIdentifier, pep, recipient) |
664 d.addCallback(_getAffiliation, requestor) | 683 d.addCallback(_getAffiliation, requestor) |
665 d.addCallback(self._doGetSubscriptionsOwner) | 684 d.addCallback(self._doGetSubscriptionsOwner) |
666 return d | 685 return d |
667 | 686 |
668 | |
669 def _doGetSubscriptionsOwner(self, result): | 687 def _doGetSubscriptionsOwner(self, result): |
670 node, affiliation = result | 688 node, affiliation = result |
671 | 689 |
672 if affiliation != 'owner': | 690 if affiliation != 'owner': |
673 raise error.Forbidden() | 691 raise error.Forbidden() |
674 return node.getSubscriptions() | 692 return node.getSubscriptions() |
675 | |
676 | 693 |
677 def setSubscriptionsOwner(self, nodeIdentifier, requestor, subscriptions, pep, recipient): | 694 def setSubscriptionsOwner(self, nodeIdentifier, requestor, subscriptions, pep, recipient): |
678 d = self.storage.getNode(nodeIdentifier, pep, recipient) | 695 d = self.storage.getNode(nodeIdentifier, pep, recipient) |
679 d.addCallback(_getAffiliation, requestor) | 696 d.addCallback(_getAffiliation, requestor) |
680 d.addCallback(self._doSetSubscriptionsOwner, requestor, subscriptions) | 697 d.addCallback(self._doSetSubscriptionsOwner, requestor, subscriptions) |
706 | 723 |
707 d = defer.gatherResults(d_list, consumeErrors=True) | 724 d = defer.gatherResults(d_list, consumeErrors=True) |
708 d.addCallback(lambda _: None) | 725 d.addCallback(lambda _: None) |
709 d.addErrback(self.unwrapFirstError) | 726 d.addErrback(self.unwrapFirstError) |
710 return d | 727 return d |
728 | |
729 def filterItemsWithSchema(self, items_data, schema, owner): | |
730 """check schema restriction and remove fields/items if they don't comply | |
731 | |
732 @param items_data(list[ItemData]): items to filter | |
733 items in this list will be modified | |
734 @param schema(domish.Element): node schema | |
735 @param owner(bool): True is requestor is a owner of the node | |
736 """ | |
737 fields_to_remove = set() | |
738 for field_elt in schema.elements(data_form.NS_X_DATA, 'field'): | |
739 for restrict_elt in field_elt.elements(const.NS_SCHEMA_RESTRICT, 'restrict'): | |
740 read_restriction = restrict_elt.attributes.get('read') | |
741 if read_restriction is not None: | |
742 if read_restriction == 'owner': | |
743 if not owner: | |
744 fields_to_remove.add(field_elt['var']) | |
745 else: | |
746 raise StanzaError('feature-not-implemented', text='unknown read restriction {}'.format(read_restriction)) | |
747 items_to_remove = [] | |
748 for idx, item_data in enumerate(items_data): | |
749 item_elt = item_data.item | |
750 try: | |
751 x_elt = next(item_elt.elements(data_form.NS_X_DATA, 'x')) | |
752 except StopIteration: | |
753 log.msg("WARNING, item {id} has a schema but no form, ignoring it") | |
754 items_to_remove.append(item_data) | |
755 continue | |
756 form = data_form.Form.fromElement(x_elt) | |
757 # we remove fields which are not visible for this user | |
758 for field in fields_to_remove: | |
759 try: | |
760 form.removeField(form.fields[field]) | |
761 except KeyError: | |
762 continue | |
763 item_elt.children.remove(x_elt) | |
764 item_elt.addChild(form.toElement()) | |
765 | |
766 for item_data in items_to_remove: | |
767 items_data.remove(item_data) | |
711 | 768 |
712 @defer.inlineCallbacks | 769 @defer.inlineCallbacks |
713 def checkNodeAccess(self, node, requestor): | 770 def checkNodeAccess(self, node, requestor): |
714 """check if a requestor can access data of a node | 771 """check if a requestor can access data of a node |
715 | 772 |
857 #FIXME | 914 #FIXME |
858 raise NotImplementedError | 915 raise NotImplementedError |
859 else: | 916 else: |
860 raise error.BadAccessTypeError(access_model) | 917 raise error.BadAccessTypeError(access_model) |
861 | 918 |
919 schema = node.getSchema() | |
920 if schema is not None: | |
921 self.filterItemsWithSchema(items_data, schema, owner) | |
922 | |
862 yield self._items_rsm(items_data, node, requestor_groups, owner, itemIdentifiers, ext_data) | 923 yield self._items_rsm(items_data, node, requestor_groups, owner, itemIdentifiers, ext_data) |
863 defer.returnValue(items_data) | 924 defer.returnValue(items_data) |
864 | 925 |
865 def _setCount(self, value, response): | 926 def _setCount(self, value, response): |
866 response.count = value | 927 response.count = value |
970 | 1031 |
971 if notify: | 1032 if notify: |
972 d.addCallback(self._doNotifyRetraction, node, pep, recipient) | 1033 d.addCallback(self._doNotifyRetraction, node, pep, recipient) |
973 return d | 1034 return d |
974 | 1035 |
975 | |
976 def _doNotifyRetraction(self, items_data, node, pep, recipient): | 1036 def _doNotifyRetraction(self, items_data, node, pep, recipient): |
977 self.dispatch({'items_data': items_data, | 1037 self.dispatch({'items_data': items_data, |
978 'node': node, | 1038 'node': node, |
979 'pep': pep, | 1039 'pep': pep, |
980 'recipient': recipient}, | 1040 'recipient': recipient}, |
981 '//event/pubsub/retract') | 1041 '//event/pubsub/retract') |
982 | 1042 |
983 | |
984 def purgeNode(self, nodeIdentifier, requestor, pep, recipient): | 1043 def purgeNode(self, nodeIdentifier, requestor, pep, recipient): |
985 d = self.storage.getNode(nodeIdentifier, pep, recipient) | 1044 d = self.storage.getNode(nodeIdentifier, pep, recipient) |
986 d.addCallback(_getAffiliation, requestor) | 1045 d.addCallback(_getAffiliation, requestor) |
987 d.addCallback(self._doPurge) | 1046 d.addCallback(self._doPurge) |
988 return d | 1047 return d |
989 | 1048 |
990 | |
991 def _doPurge(self, result): | 1049 def _doPurge(self, result): |
992 node, affiliation = result | 1050 node, affiliation = result |
993 persistItems = node.getConfiguration()[const.OPT_PERSIST_ITEMS] | 1051 persistItems = node.getConfiguration()[const.OPT_PERSIST_ITEMS] |
994 | 1052 |
995 if affiliation != 'owner': | 1053 if affiliation != 'owner': |
1000 | 1058 |
1001 d = node.purge() | 1059 d = node.purge() |
1002 d.addCallback(self._doNotifyPurge, node.nodeIdentifier) | 1060 d.addCallback(self._doNotifyPurge, node.nodeIdentifier) |
1003 return d | 1061 return d |
1004 | 1062 |
1005 | |
1006 def _doNotifyPurge(self, result, nodeIdentifier): | 1063 def _doNotifyPurge(self, result, nodeIdentifier): |
1007 self.dispatch(nodeIdentifier, '//event/pubsub/purge') | 1064 self.dispatch(nodeIdentifier, '//event/pubsub/purge') |
1008 | 1065 |
1009 | |
1010 def registerPreDelete(self, preDeleteFn): | 1066 def registerPreDelete(self, preDeleteFn): |
1011 self._callbackList.append(preDeleteFn) | 1067 self._callbackList.append(preDeleteFn) |
1012 | |
1013 | 1068 |
1014 def getSubscribers(self, nodeIdentifier, pep, recipient): | 1069 def getSubscribers(self, nodeIdentifier, pep, recipient): |
1015 def cb(subscriptions): | 1070 def cb(subscriptions): |
1016 return [subscription.subscriber for subscription in subscriptions] | 1071 return [subscription.subscriber for subscription in subscriptions] |
1017 | 1072 |
1018 d = self.storage.getNode(nodeIdentifier, pep, recipient) | 1073 d = self.storage.getNode(nodeIdentifier, pep, recipient) |
1019 d.addCallback(lambda node: node.getSubscriptions('subscribed')) | 1074 d.addCallback(lambda node: node.getSubscriptions('subscribed')) |
1020 d.addCallback(cb) | 1075 d.addCallback(cb) |
1021 return d | 1076 return d |
1022 | |
1023 | 1077 |
1024 def deleteNode(self, nodeIdentifier, requestor, pep, recipient, redirectURI=None): | 1078 def deleteNode(self, nodeIdentifier, requestor, pep, recipient, redirectURI=None): |
1025 d = self.storage.getNode(nodeIdentifier, pep, recipient) | 1079 d = self.storage.getNode(nodeIdentifier, pep, recipient) |
1026 d.addCallback(_getAffiliation, requestor) | 1080 d.addCallback(_getAffiliation, requestor) |
1027 d.addCallback(self._doPreDelete, redirectURI, pep, recipient) | 1081 d.addCallback(self._doPreDelete, redirectURI, pep, recipient) |
1028 return d | 1082 return d |
1029 | 1083 |
1030 | |
1031 def _doPreDelete(self, result, redirectURI, pep, recipient): | 1084 def _doPreDelete(self, result, redirectURI, pep, recipient): |
1032 node, affiliation = result | 1085 node, affiliation = result |
1033 | 1086 |
1034 if affiliation != 'owner': | 1087 if affiliation != 'owner': |
1035 raise error.Forbidden() | 1088 raise error.Forbidden() |
1039 | 1092 |
1040 d = defer.DeferredList([cb(data, pep, recipient) | 1093 d = defer.DeferredList([cb(data, pep, recipient) |
1041 for cb in self._callbackList], | 1094 for cb in self._callbackList], |
1042 consumeErrors=1) | 1095 consumeErrors=1) |
1043 d.addCallback(self._doDelete, node.nodeDbId) | 1096 d.addCallback(self._doDelete, node.nodeDbId) |
1044 | |
1045 | 1097 |
1046 def _doDelete(self, result, nodeDbId): | 1098 def _doDelete(self, result, nodeDbId): |
1047 dl = [] | 1099 dl = [] |
1048 for succeeded, r in result: | 1100 for succeeded, r in result: |
1049 if succeeded and r: | 1101 if succeeded and r: |
1052 d = self.storage.deleteNodeByDbId(nodeDbId) | 1104 d = self.storage.deleteNodeByDbId(nodeDbId) |
1053 d.addCallback(self._doNotifyDelete, dl) | 1105 d.addCallback(self._doNotifyDelete, dl) |
1054 | 1106 |
1055 return d | 1107 return d |
1056 | 1108 |
1057 | |
1058 def _doNotifyDelete(self, result, dl): | 1109 def _doNotifyDelete(self, result, dl): |
1059 for d in dl: | 1110 for d in dl: |
1060 d.callback(None) | 1111 d.callback(None) |
1061 | |
1062 | 1112 |
1063 | 1113 |
1064 class PubSubResourceFromBackend(pubsub.PubSubResource): | 1114 class PubSubResourceFromBackend(pubsub.PubSubResource): |
1065 """ | 1115 """ |
1066 Adapts a backend to an xmpp publish-subscribe service. | 1116 Adapts a backend to an xmpp publish-subscribe service. |
1149 self.features.append("groupblog") | 1199 self.features.append("groupblog") |
1150 | 1200 |
1151 # if self.backend.supportsPublishModel(): #XXX: this feature is not really described in XEP-0060, we just can see it in examples | 1201 # if self.backend.supportsPublishModel(): #XXX: this feature is not really described in XEP-0060, we just can see it in examples |
1152 # self.features.append("publish_model") # but it's necessary for microblogging comments (see XEP-0277) | 1202 # self.features.append("publish_model") # but it's necessary for microblogging comments (see XEP-0277) |
1153 | 1203 |
1154 | |
1155 def getFullItem(self, item_data): | 1204 def getFullItem(self, item_data): |
1156 """ Attach item configuration to this item | 1205 """ Attach item configuration to this item |
1157 | 1206 |
1158 Used to give item configuration back to node's owner (and *only* to owner) | 1207 Used to give item configuration back to node's owner (and *only* to owner) |
1159 """ | 1208 """ |
1230 notifications_filtered) | 1279 notifications_filtered) |
1231 | 1280 |
1232 d = self._prepareNotify(items_data, node, data.get('subscription'), pep, recipient) | 1281 d = self._prepareNotify(items_data, node, data.get('subscription'), pep, recipient) |
1233 d.addCallback(afterPrepare) | 1282 d.addCallback(afterPrepare) |
1234 return d | 1283 return d |
1235 | |
1236 | 1284 |
1237 @defer.inlineCallbacks | 1285 @defer.inlineCallbacks |
1238 def _prepareNotify(self, items_data, node, subscription=None, pep=None, recipient=None): | 1286 def _prepareNotify(self, items_data, node, subscription=None, pep=None, recipient=None): |
1239 """Do a bunch of permissions check and filter notifications | 1287 """Do a bunch of permissions check and filter notifications |
1240 | 1288 |
1270 | 1318 |
1271 # now we check access of subscriber for each item, and keep only allowed ones | 1319 # now we check access of subscriber for each item, and keep only allowed ones |
1272 | 1320 |
1273 #we filter items not allowed for the subscribers | 1321 #we filter items not allowed for the subscribers |
1274 notifications_filtered = [] | 1322 notifications_filtered = [] |
1323 schema = node.getSchema() | |
1275 | 1324 |
1276 for subscriber, subscriptions, items_data in notifications: | 1325 for subscriber, subscriptions, items_data in notifications: |
1277 subscriber_bare = subscriber.userhostJID() | 1326 subscriber_bare = subscriber.userhostJID() |
1278 if subscriber_bare in owners: | 1327 if subscriber_bare in owners: |
1279 # as notification is always sent to owner, | 1328 # as notification is always sent to owner, |
1280 # we ignore owner if he is here | 1329 # we ignore owner if he is here |
1281 continue | 1330 continue |
1282 allowed_items = [] #we keep only item which subscriber can access | 1331 allowed_items = [] #we keep only item which subscriber can access |
1332 | |
1333 if schema is not None: | |
1334 # we have to deepcopy items because different subscribers may receive | |
1335 # different items (e.g. read restriction in schema) | |
1336 items_data = deepcopy(items_data) | |
1337 self.backend.filterItemsWithSchema(items_data, schema, False) | |
1283 | 1338 |
1284 for item_data in items_data: | 1339 for item_data in items_data: |
1285 item, access_model = item_data.item, item_data.access_model | 1340 item, access_model = item_data.item, item_data.access_model |
1286 access_list = item_data.config | 1341 access_list = item_data.config |
1287 if access_model == const.VAL_AMODEL_OPEN: | 1342 if access_model == const.VAL_AMODEL_OPEN: |
1390 d = self.backend.getNodes(requestor.userhostJID(), | 1445 d = self.backend.getNodes(requestor.userhostJID(), |
1391 pep, | 1446 pep, |
1392 service) | 1447 service) |
1393 return d.addErrback(self._mapErrors) | 1448 return d.addErrback(self._mapErrors) |
1394 | 1449 |
1395 | |
1396 def getConfigurationOptions(self): | 1450 def getConfigurationOptions(self): |
1397 return self.backend.nodeOptions | 1451 return self.backend.nodeOptions |
1398 | 1452 |
1399 def _publish_errb(self, failure, request): | 1453 def _publish_errb(self, failure, request): |
1400 if failure.type == error.NodeNotFound and self.backend.supportsAutoCreate(): | 1454 if failure.type == error.NodeNotFound and self.backend.supportsAutoCreate(): |
1428 self._isPep(request), | 1482 self._isPep(request), |
1429 request.recipient) | 1483 request.recipient) |
1430 d.addErrback(self._publish_errb, request) | 1484 d.addErrback(self._publish_errb, request) |
1431 return d.addErrback(self._mapErrors) | 1485 return d.addErrback(self._mapErrors) |
1432 | 1486 |
1433 | |
1434 def subscribe(self, request): | 1487 def subscribe(self, request): |
1435 d = self.backend.subscribe(request.nodeIdentifier, | 1488 d = self.backend.subscribe(request.nodeIdentifier, |
1436 request.subscriber, | 1489 request.subscriber, |
1437 request.sender, | 1490 request.sender, |
1438 self._isPep(request), | 1491 self._isPep(request), |
1439 request.recipient) | 1492 request.recipient) |
1440 return d.addErrback(self._mapErrors) | 1493 return d.addErrback(self._mapErrors) |
1441 | 1494 |
1442 | |
1443 def unsubscribe(self, request): | 1495 def unsubscribe(self, request): |
1444 d = self.backend.unsubscribe(request.nodeIdentifier, | 1496 d = self.backend.unsubscribe(request.nodeIdentifier, |
1445 request.subscriber, | 1497 request.subscriber, |
1446 request.sender, | 1498 request.sender, |
1447 self._isPep(request), | 1499 self._isPep(request), |
1448 request.recipient) | 1500 request.recipient) |
1449 return d.addErrback(self._mapErrors) | 1501 return d.addErrback(self._mapErrors) |
1450 | 1502 |
1451 | |
1452 def subscriptions(self, request): | 1503 def subscriptions(self, request): |
1453 d = self.backend.getSubscriptions(self._isPep(request), | 1504 d = self.backend.getSubscriptions(self._isPep(request), |
1454 request.sender) | 1505 request.sender) |
1455 return d.addErrback(self._mapErrors) | 1506 return d.addErrback(self._mapErrors) |
1456 | |
1457 | 1507 |
1458 def affiliations(self, request): | 1508 def affiliations(self, request): |
1459 """Retrieve affiliation for normal entity (cf. XEP-0060 §5.7) | 1509 """Retrieve affiliation for normal entity (cf. XEP-0060 §5.7) |
1460 | 1510 |
1461 retrieve all node where this jid is affiliated | 1511 retrieve all node where this jid is affiliated |
1464 request.nodeIdentifier, | 1514 request.nodeIdentifier, |
1465 self._isPep(request), | 1515 self._isPep(request), |
1466 request.recipient) | 1516 request.recipient) |
1467 return d.addErrback(self._mapErrors) | 1517 return d.addErrback(self._mapErrors) |
1468 | 1518 |
1469 | |
1470 def create(self, request): | 1519 def create(self, request): |
1471 d = self.backend.createNode(request.nodeIdentifier, | 1520 d = self.backend.createNode(request.nodeIdentifier, |
1472 request.sender, request.options, | 1521 request.sender, request.options, |
1473 self._isPep(request), | 1522 self._isPep(request), |
1474 request.recipient) | 1523 request.recipient) |
1475 return d.addErrback(self._mapErrors) | 1524 return d.addErrback(self._mapErrors) |
1476 | 1525 |
1477 | |
1478 def default(self, request): | 1526 def default(self, request): |
1479 d = self.backend.getDefaultConfiguration(request.nodeType, | 1527 d = self.backend.getDefaultConfiguration(request.nodeType, |
1480 self._isPep(request), | 1528 self._isPep(request), |
1481 request.sender) | 1529 request.sender) |
1482 return d.addErrback(self._mapErrors) | 1530 return d.addErrback(self._mapErrors) |
1483 | 1531 |
1484 | |
1485 def configureGet(self, request): | 1532 def configureGet(self, request): |
1486 d = self.backend.getNodeConfiguration(request.nodeIdentifier, | 1533 d = self.backend.getNodeConfiguration(request.nodeIdentifier, |
1487 self._isPep(request), | 1534 self._isPep(request), |
1488 request.recipient) | 1535 request.recipient) |
1489 return d.addErrback(self._mapErrors) | 1536 return d.addErrback(self._mapErrors) |
1490 | |
1491 | 1537 |
1492 def configureSet(self, request): | 1538 def configureSet(self, request): |
1493 d = self.backend.setNodeConfiguration(request.nodeIdentifier, | 1539 d = self.backend.setNodeConfiguration(request.nodeIdentifier, |
1494 request.options, | 1540 request.options, |
1495 request.sender, | 1541 request.sender, |
1496 self._isPep(request), | 1542 self._isPep(request), |
1497 request.recipient) | 1543 request.recipient) |
1498 return d.addErrback(self._mapErrors) | 1544 return d.addErrback(self._mapErrors) |
1499 | 1545 |
1500 | |
1501 def affiliationsGet(self, request): | 1546 def affiliationsGet(self, request): |
1502 """Retrieve affiliations for owner (cf. XEP-0060 §8.9.1) | 1547 """Retrieve affiliations for owner (cf. XEP-0060 §8.9.1) |
1503 | 1548 |
1504 retrieve all affiliations for a node | 1549 retrieve all affiliations for a node |
1505 """ | 1550 """ |
1515 request.affiliations, | 1560 request.affiliations, |
1516 self._isPep(request), | 1561 self._isPep(request), |
1517 request.recipient) | 1562 request.recipient) |
1518 return d.addErrback(self._mapErrors) | 1563 return d.addErrback(self._mapErrors) |
1519 | 1564 |
1520 | |
1521 def subscriptionsGet(self, request): | 1565 def subscriptionsGet(self, request): |
1522 """Retrieve subscriptions for owner (cf. XEP-0060 §8.8.1) | 1566 """Retrieve subscriptions for owner (cf. XEP-0060 §8.8.1) |
1523 | 1567 |
1524 retrieve all affiliations for a node | 1568 retrieve all affiliations for a node |
1525 """ | 1569 """ |
1527 request.sender, | 1571 request.sender, |
1528 self._isPep(request), | 1572 self._isPep(request), |
1529 request.recipient) | 1573 request.recipient) |
1530 return d.addErrback(self._mapErrors) | 1574 return d.addErrback(self._mapErrors) |
1531 | 1575 |
1532 | |
1533 def subscriptionsSet(self, request): | 1576 def subscriptionsSet(self, request): |
1534 d = self.backend.setSubscriptionsOwner(request.nodeIdentifier, | 1577 d = self.backend.setSubscriptionsOwner(request.nodeIdentifier, |
1535 request.sender, | 1578 request.sender, |
1536 request.subscriptions, | 1579 request.subscriptions, |
1537 self._isPep(request), | 1580 self._isPep(request), |
1538 request.recipient) | 1581 request.recipient) |
1539 return d.addErrback(self._mapErrors) | 1582 return d.addErrback(self._mapErrors) |
1540 | |
1541 | 1583 |
1542 def items(self, request): | 1584 def items(self, request): |
1543 ext_data = {} | 1585 ext_data = {} |
1544 if const.FLAG_ENABLE_RSM and request.rsm is not None: | 1586 if const.FLAG_ENABLE_RSM and request.rsm is not None: |
1545 ext_data['rsm'] = request.rsm | 1587 ext_data['rsm'] = request.rsm |
1562 request.notify, | 1604 request.notify, |
1563 self._isPep(request), | 1605 self._isPep(request), |
1564 request.recipient) | 1606 request.recipient) |
1565 return d.addErrback(self._mapErrors) | 1607 return d.addErrback(self._mapErrors) |
1566 | 1608 |
1567 | |
1568 def purge(self, request): | 1609 def purge(self, request): |
1569 d = self.backend.purgeNode(request.nodeIdentifier, | 1610 d = self.backend.purgeNode(request.nodeIdentifier, |
1570 request.sender, | 1611 request.sender, |
1571 self._isPep(request), | 1612 self._isPep(request), |
1572 request.recipient) | 1613 request.recipient) |
1573 return d.addErrback(self._mapErrors) | 1614 return d.addErrback(self._mapErrors) |
1574 | 1615 |
1575 | |
1576 def delete(self, request): | 1616 def delete(self, request): |
1577 d = self.backend.deleteNode(request.nodeIdentifier, | 1617 d = self.backend.deleteNode(request.nodeIdentifier, |
1578 request.sender, | 1618 request.sender, |
1579 self._isPep(request), | 1619 self._isPep(request), |
1580 request.recipient) | 1620 request.recipient) |