Mercurial > libervia-pubsub
comparison sat_pubsub/backend.py @ 250:eb14b8d30cba
fine tuning per-item permissions
author | Goffi <goffi@goffi.org> |
---|---|
date | Sun, 24 Jun 2012 19:35:49 +0200 |
parents | 50f6ee966da8 |
children | 0a7d43b3dad6 |
comparison
equal
deleted
inserted
replaced
249:aaf5e34ff765 | 250:eb14b8d30cba |
---|---|
71 from twisted.internet import defer, reactor | 71 from twisted.internet import defer, reactor |
72 from twisted.words.protocols.jabber.error import StanzaError | 72 from twisted.words.protocols.jabber.error import StanzaError |
73 from twisted.words.protocols.jabber.jid import JID, InvalidFormat | 73 from twisted.words.protocols.jabber.jid import JID, InvalidFormat |
74 from twisted.words.xish import utility | 74 from twisted.words.xish import utility |
75 | 75 |
76 from wokkel import disco | 76 from wokkel import disco, data_form |
77 from wokkel.iwokkel import IPubSubResource | 77 from wokkel.iwokkel import IPubSubResource |
78 from wokkel.pubsub import PubSubResource, PubSubError | 78 from wokkel.pubsub import PubSubResource, PubSubError |
79 | 79 |
80 from sat_pubsub import error, iidavoll | 80 from sat_pubsub import error, iidavoll, const |
81 from sat_pubsub.iidavoll import IBackendService, ILeafNode | 81 from sat_pubsub.iidavoll import IBackendService, ILeafNode |
82 | |
82 | 83 |
83 def _getAffiliation(node, entity): | 84 def _getAffiliation(node, entity): |
84 d = node.getAffiliation(entity) | 85 d = node.getAffiliation(entity) |
85 d.addCallback(lambda affiliation: (node, affiliation)) | 86 d.addCallback(lambda affiliation: (node, affiliation)) |
86 return d | 87 return d |
139 | 140 |
140 def supportsPublisherAffiliation(self): | 141 def supportsPublisherAffiliation(self): |
141 return True | 142 return True |
142 | 143 |
143 | 144 |
145 def supportsGroupBlog(self): | |
146 return True | |
147 | |
148 | |
144 def supportsOutcastAffiliation(self): | 149 def supportsOutcastAffiliation(self): |
145 return True | 150 return True |
146 | 151 |
147 | 152 |
148 def supportsPersistentItems(self): | 153 def supportsPersistentItems(self): |
186 | 191 |
187 d = node.getAffiliation(requestor) | 192 d = node.getAffiliation(requestor) |
188 d.addCallback(check, node) | 193 d.addCallback(check, node) |
189 return d | 194 return d |
190 | 195 |
196 def parseItemConfig(self, item): | |
197 """Get and remove item configuration information | |
198 @param item: | |
199 """ | |
200 item_config = None | |
201 access_model = const.VAL_DEFAULT | |
202 for i in range(len(item.children)): | |
203 elt = item.children[i] | |
204 if not (elt.uri,elt.name)==(data_form.NS_X_DATA,'x'): | |
205 continue | |
206 form = data_form.Form.fromElement(elt) | |
207 if (form.formNamespace == const.NS_ITEM_CONFIG): | |
208 item_config = form | |
209 del item.children[i] #we need to remove the config from item | |
210 break | |
211 | |
212 if item_config: | |
213 access_model = item_config.get(const.OPT_ACCESS_MODEL, const.VAL_DEFAULT) | |
214 | |
215 return (access_model, item_config) | |
216 | |
191 | 217 |
192 def publish(self, nodeIdentifier, items, requestor): | 218 def publish(self, nodeIdentifier, items, requestor): |
193 d = self.storage.getNode(nodeIdentifier) | 219 d = self.storage.getNode(nodeIdentifier) |
194 d.addCallback(self._checkAuth, requestor) | 220 d.addCallback(self._checkAuth, requestor) |
221 #FIXME: owner and publisher are not necessarly the same. So far we use only owner to get roster. | |
222 #FIXME: in addition, there can be several owners: that is not managed yet | |
195 d.addCallback(self._doPublish, items, requestor) | 223 d.addCallback(self._doPublish, items, requestor) |
196 return d | 224 return d |
197 | 225 |
198 | 226 |
199 def _doPublish(self, node, items, requestor): | 227 def _doPublish(self, node, items, requestor): |
207 if items and not persistItems and not deliverPayloads: | 235 if items and not persistItems and not deliverPayloads: |
208 raise error.ItemForbidden() | 236 raise error.ItemForbidden() |
209 elif not items and (persistItems or deliverPayloads): | 237 elif not items and (persistItems or deliverPayloads): |
210 raise error.ItemRequired() | 238 raise error.ItemRequired() |
211 | 239 |
212 if persistItems or deliverPayloads: | 240 parsed_items = [] |
213 for item in items: | 241 for item in items: |
242 if persistItems or deliverPayloads: | |
214 item.uri = None | 243 item.uri = None |
215 item.defaultUri = None | 244 item.defaultUri = None |
216 if not item.getAttribute("id"): | 245 if not item.getAttribute("id"): |
217 item["id"] = str(uuid.uuid4()) | 246 item["id"] = str(uuid.uuid4()) |
247 access_model, item_config = self.parseItemConfig(item) | |
248 parsed_items.append((access_model, item_config, item)) | |
218 | 249 |
219 if persistItems: | 250 if persistItems: |
220 d = node.storeItems(items, requestor) | 251 d = node.storeItems(parsed_items, requestor) |
221 else: | 252 else: |
222 d = defer.succeed(None) | 253 d = defer.succeed(None) |
223 | 254 |
224 d.addCallback(self._doNotify, node.nodeIdentifier, items, | 255 d.addCallback(self._doNotify, node, parsed_items, |
225 deliverPayloads) | 256 deliverPayloads) |
226 return d | 257 return d |
227 | 258 |
228 | 259 |
229 def _doNotify(self, result, nodeIdentifier, items, deliverPayloads): | 260 def _doNotify(self, result, node, items, deliverPayloads): |
230 if items and not deliverPayloads: | 261 if items and not deliverPayloads: |
231 for item in items: | 262 for access_model, item_config, item in items: |
232 item.children = [] | 263 item.children = [] |
233 | 264 |
234 self.dispatch({'items': items, 'nodeIdentifier': nodeIdentifier}, | 265 self.dispatch({'items': items, 'node': node}, |
235 '//event/pubsub/notify') | 266 '//event/pubsub/notify') |
236 | 267 |
237 | 268 |
238 def getNotifications(self, nodeIdentifier, items): | 269 def getNotifications(self, nodeIdentifier, items): |
239 | 270 |
282 return d | 313 return d |
283 | 314 |
284 | 315 |
285 def _doSubscribe(self, result, subscriber): | 316 def _doSubscribe(self, result, subscriber): |
286 node, affiliation = result | 317 node, affiliation = result |
318 #FIXME: must check node's access_model before subscribing | |
287 | 319 |
288 if affiliation == 'outcast': | 320 if affiliation == 'outcast': |
289 raise error.Forbidden() | 321 raise error.Forbidden() |
290 | 322 |
291 def trapExists(failure): | 323 def trapExists(failure): |
351 def createNode(self, nodeIdentifier, requestor): | 383 def createNode(self, nodeIdentifier, requestor): |
352 if not nodeIdentifier: | 384 if not nodeIdentifier: |
353 nodeIdentifier = 'generic/%s' % uuid.uuid4() | 385 nodeIdentifier = 'generic/%s' % uuid.uuid4() |
354 | 386 |
355 if self.supportsCreatorCheck(): | 387 if self.supportsCreatorCheck(): |
388 groupblog = nodeIdentifier.startswith(const.NS_GROUPBLOG_PREFIX) | |
356 try: | 389 try: |
357 nodeIdentifierJID = JID(nodeIdentifier) | 390 nodeIdentifierJID = JID(nodeIdentifier[len(const.NS_GROUPBLOG_PREFIX):] if groupblog else nodeIdentifier) |
358 except InvalidFormat: | 391 except InvalidFormat: |
359 is_user_jid = False | 392 is_user_jid = False |
360 else: | 393 else: |
361 is_user_jid = bool(nodeIdentifierJID.user) | 394 is_user_jid = bool(nodeIdentifierJID.user) |
362 | 395 |
665 self.features.append("persistent-items") | 698 self.features.append("persistent-items") |
666 | 699 |
667 if self.backend.supportsPublisherAffiliation(): | 700 if self.backend.supportsPublisherAffiliation(): |
668 self.features.append("publisher-affiliation") | 701 self.features.append("publisher-affiliation") |
669 | 702 |
703 if self.backend.supportsGroupBlog(): | |
704 self.features.append("groupblog") | |
705 | |
670 | 706 |
671 def _notify(self, data): | 707 def _notify(self, data): |
672 items = data['items'] | 708 items = data['items'] |
673 nodeIdentifier = data['nodeIdentifier'] | 709 node = data['node'] |
710 | |
711 def _notifyAllowed(result): | |
712 """Check access of subscriber for each item, | |
713 and notify only allowed ones""" | |
714 notifications, roster = result | |
715 | |
716 #we filter items not allowed for the subscribers | |
717 notifications_filtered = [] | |
718 | |
719 for subscriber, subscriptions, items in notifications: | |
720 allowed_items = [] #we keep only item which subscriber can access | |
721 | |
722 for access_model, item_config, item in items: | |
723 if access_model == 'open': | |
724 allowed_items.append(item) | |
725 elif access_model == 'roster': | |
726 _subscriber = subscriber.userhost() | |
727 if not _subscriber in roster: | |
728 continue | |
729 #the subscriber is known, is he in the right group ? | |
730 authorized_groups = item_config[const.OPT_ROSTER_GROUPS_ALLOWED] | |
731 if roster[_subscriber].groups.intersection(authorized_groups): | |
732 allowed_items.append(item) | |
733 | |
734 else: #unknown access_model | |
735 raise NotImplementedError | |
736 | |
737 notifications_filtered.append((subscriber, subscriptions, allowed_items)) | |
738 | |
739 return self.pubsubService.notifyPublish( | |
740 self.serviceJID, | |
741 node.nodeIdentifier, | |
742 notifications_filtered) | |
743 | |
744 | |
674 if 'subscription' not in data: | 745 if 'subscription' not in data: |
675 d = self.backend.getNotifications(nodeIdentifier, items) | 746 d1 = self.backend.getNotifications(node.nodeIdentifier, items) |
676 else: | 747 else: |
677 subscription = data['subscription'] | 748 subscription = data['subscription'] |
678 d = defer.succeed([(subscription.subscriber, [subscription], | 749 d1 = defer.succeed([(subscription.subscriber, [subscription], |
679 items)]) | 750 items)]) |
680 d.addCallback(lambda notifications: self.pubsubService.notifyPublish( | 751 |
681 self.serviceJID, | 752 d2 = node.getNodeOwner() |
682 nodeIdentifier, | 753 d2.addCallback(self.backend.roster.getRoster) |
683 notifications)) | 754 |
755 d = defer.gatherResults([d1, d2]) | |
756 d.addCallback(_notifyAllowed) | |
684 | 757 |
685 | 758 |
686 def _preDelete(self, data): | 759 def _preDelete(self, data): |
687 nodeIdentifier = data['nodeIdentifier'] | 760 nodeIdentifier = data['nodeIdentifier'] |
688 redirectURI = data.get('redirectURI', None) | 761 redirectURI = data.get('redirectURI', None) |