comparison src/iidavoll.py @ 369:dabee42494ac

config file + cleaning: - SàT Pubsub can now be configured using the same config file as SàT itself (i.e. sat.conf or .sat.conf), in the same locations (/etc, local dir, xdg dir). Its options must be in the "pubsub" section - options on command line override config options - removed tap and http files which are not used anymore - changed directory structure to put source in src, to be coherent with SàT and Libervia - changed options name, db* become db_*, secret become xmpp_pwd - an exception is raised if jid or xmpp_pwd is are not configured
author Goffi <goffi@goffi.org>
date Fri, 02 Mar 2018 12:59:38 +0100
parents sat_pubsub/iidavoll.py@618a92080812
children aa3a464df605
comparison
equal deleted inserted replaced
368:618a92080812 369:dabee42494ac
1 #!/usr/bin/python
2 #-*- coding: utf-8 -*-
3
4 # Copyright (c) 2003-2011 Ralph Meijer
5 # Copyright (c) 2012-2018 Jérôme Poisson
6
7
8 # This program is free software: you can redistribute it and/or modify
9 # it under the terms of the GNU Affero General Public License as published by
10 # the Free Software Foundation, either version 3 of the License, or
11 # (at your option) any later version.
12
13 # This program is distributed in the hope that it will be useful,
14 # but WITHOUT ANY WARRANTY; without even the implied warranty of
15 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 # GNU Affero General Public License for more details.
17
18 # You should have received a copy of the GNU Affero General Public License
19 # along with this program. If not, see <http://www.gnu.org/licenses/>.
20 # --
21
22 # This program is based on Idavoll (http://idavoll.ik.nu/),
23 # originaly written by Ralph Meijer (http://ralphm.net/blog/)
24 # It is sublicensed under AGPL v3 (or any later version) as allowed by the original
25 # license.
26
27 # --
28
29 # Here is a copy of the original license:
30
31 # Copyright (c) 2003-2011 Ralph Meijer
32
33 # Permission is hereby granted, free of charge, to any person obtaining
34 # a copy of this software and associated documentation files (the
35 # "Software"), to deal in the Software without restriction, including
36 # without limitation the rights to use, copy, modify, merge, publish,
37 # distribute, sublicense, and/or sell copies of the Software, and to
38 # permit persons to whom the Software is furnished to do so, subject to
39 # the following conditions:
40
41 # The above copyright notice and this permission notice shall be
42 # included in all copies or substantial portions of the Software.
43
44 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
45 # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
46 # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
47 # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
48 # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
49 # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
50 # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
51
52
53 """
54 Interfaces for idavoll.
55 """
56
57 from zope.interface import Attribute, Interface
58
59 class IBackendService(Interface):
60 """ Interface to a backend service of a pubsub service. """
61
62
63 def __init__(storage):
64 """
65 @param storage: Object providing L{IStorage}.
66 """
67
68
69 def supportsPublisherAffiliation():
70 """ Reports if the backend supports the publisher affiliation.
71
72 @rtype: C{bool}
73 """
74
75
76 def supportsOutcastAffiliation():
77 """ Reports if the backend supports the publisher affiliation.
78
79 @rtype: C{bool}
80 """
81
82
83 def supportsPersistentItems():
84 """ Reports if the backend supports persistent items.
85
86 @rtype: C{bool}
87 """
88
89
90 def getNodeType(nodeIdentifier):
91 """ Return type of a node.
92
93 @return: a deferred that returns either 'leaf' or 'collection'
94 """
95
96
97 def getNodes():
98 """ Returns list of all nodes.
99
100 @return: a deferred that returns a C{list} of node ids.
101 """
102
103
104 def getNodeMetaData(nodeIdentifier):
105 """ Return meta data for a node.
106
107 @return: a deferred that returns a C{list} of C{dict}s with the
108 metadata.
109 """
110
111
112 def createNode(nodeIdentifier, requestor):
113 """ Create a node.
114
115 @return: a deferred that fires when the node has been created.
116 """
117
118
119 def registerPreDelete(preDeleteFn):
120 """ Register a callback that is called just before a node deletion.
121
122 The function C{preDeletedFn} is added to a list of functions to be
123 called just before deletion of a node. The callback C{preDeleteFn} is
124 called with the C{nodeIdentifier} that is about to be deleted and
125 should return a deferred that returns a list of deferreds that are to
126 be fired after deletion. The backend collects the lists from all these
127 callbacks before actually deleting the node in question. After
128 deletion all collected deferreds are fired to do post-processing.
129
130 The idea is that you want to be able to collect data from the node
131 before deleting it, for example to get a list of subscribers that have
132 to be notified after the node has been deleted. To do this,
133 C{preDeleteFn} fetches the subscriber list and passes this list to a
134 callback attached to a deferred that it sets up. This deferred is
135 returned in the list of deferreds.
136 """
137
138
139 def deleteNode(nodeIdentifier, requestor):
140 """ Delete a node.
141
142 @return: a deferred that fires when the node has been deleted.
143 """
144
145
146 def purgeNode(nodeIdentifier, requestor):
147 """ Removes all items in node from persistent storage """
148
149
150 def subscribe(nodeIdentifier, subscriber, requestor):
151 """ Request the subscription of an entity to a pubsub node.
152
153 Depending on the node's configuration and possible business rules, the
154 C{subscriber} is added to the list of subscriptions of the node with id
155 C{nodeIdentifier}. The C{subscriber} might be different from the
156 C{requestor}, and if the C{requestor} is not allowed to subscribe this
157 entity an exception should be raised.
158
159 @return: a deferred that returns the subscription state
160 """
161
162
163 def unsubscribe(nodeIdentifier, subscriber, requestor):
164 """ Cancel the subscription of an entity to a pubsub node.
165
166 The subscription of C{subscriber} is removed from the list of
167 subscriptions of the node with id C{nodeIdentifier}. If the
168 C{requestor} is not allowed to unsubscribe C{subscriber}, an an
169 exception should be raised.
170
171 @return: a deferred that fires when unsubscription is complete.
172 """
173
174
175 def getSubscribers(nodeIdentifier):
176 """ Get node subscriber list.
177
178 @return: a deferred that fires with the list of subscribers.
179 """
180
181
182 def getSubscriptions(entity):
183 """ Report the list of current subscriptions with this pubsub service.
184
185 Report the list of the current subscriptions with all nodes within this
186 pubsub service, for the C{entity}.
187
188 @return: a deferred that returns the list of all current subscriptions
189 as tuples C{(nodeIdentifier, subscriber, subscription)}.
190 """
191
192
193 def getAffiliations(entity):
194 """ Report the list of current affiliations with this pubsub service.
195
196 Report the list of the current affiliations with all nodes within this
197 pubsub service, for the C{entity}.
198
199 @return: a deferred that returns the list of all current affiliations
200 as tuples C{(nodeIdentifier, affiliation)}.
201 """
202
203
204 def publish(nodeIdentifier, items, requestor):
205 """ Publish items to a pubsub node.
206
207 @return: a deferred that fires when the items have been published.
208 @rtype: L{Deferred<twisted.internet.defer.Deferred>}
209 """
210
211
212 def registerNotifier(observerfn, *args, **kwargs):
213 """ Register callback which is called for notification. """
214
215
216 def getNotifications(nodeIdentifier, items):
217 """
218 Get notification list.
219
220 This method is called to discover which entities should receive
221 notifications for the given items that have just been published to the
222 given node.
223
224 The notification list contains tuples (subscriber, subscriptions,
225 items) to result in one notification per tuple: the given subscriptions
226 yielded the given items to be notified to this subscriber. This
227 structure is needed allow for letting the subscriber know which
228 subscriptions yielded which notifications, while catering for
229 collection nodes and content-based subscriptions.
230
231 To minimize the amount of notifications per entity, implementers
232 should take care that if all items in C{items} were yielded
233 by the same set of subscriptions, exactly one tuple is for this
234 subscriber is returned, so that the subscriber would get exactly one
235 notification. Alternatively, one tuple per subscription combination.
236
237 @param nodeIdentifier: The identifier of the node the items were
238 published to.
239 @type nodeIdentifier: C{unicode}.
240 @param items: The list of published items as
241 L{Element<twisted.words.xish.domish.Element>}s.
242 @type items: C{list}
243 @return: The notification list as tuples of
244 (L{JID<twisted.words.protocols.jabber.jid.JID>},
245 C{list} of L{Subscription<wokkel.pubsub.Subscription>},
246 C{list} of L{Element<twisted.words.xish.domish.Element>}.
247 @rtype: C{list}
248 """
249
250
251 def getItems(nodeIdentifier, requestor, maxItems=None, itemIdentifiers=[]):
252 """ Retrieve items from persistent storage
253
254 If C{maxItems} is given, return the C{maxItems} last published
255 items, else if C{itemIdentifiers} is not empty, return the items
256 requested. If neither is given, return all items.
257
258 @return: a deferred that returns the requested items
259 """
260
261
262 def retractItem(nodeIdentifier, itemIdentifier, requestor):
263 """ Removes item in node from persistent storage """
264
265
266
267 class IStorage(Interface):
268 """
269 Storage interface.
270 """
271
272
273 def getNode(nodeIdentifier):
274 """
275 Get Node.
276
277 @param nodeIdentifier: NodeID of the desired node.
278 @type nodeIdentifier: C{str}
279 @return: deferred that returns a L{INode} providing object.
280 """
281
282
283 def getNodeIds():
284 """
285 Return all NodeIDs.
286
287 @return: deferred that returns a list of NodeIDs (C{unicode}).
288 """
289
290
291 def createNode(nodeIdentifier, owner, config):
292 """
293 Create new node.
294
295 The implementation should make sure, the passed owner JID is stripped
296 of the resource (e.g. using C{owner.userhostJID()}). The passed config
297 is expected to have values for the fields returned by
298 L{getDefaultConfiguration}, as well as a value for
299 C{'pubsub#node_type'}.
300
301 @param nodeIdentifier: NodeID of the new node.
302 @type nodeIdentifier: C{unicode}
303 @param owner: JID of the new nodes's owner.
304 @type owner: L{JID<twisted.words.protocols.jabber.jid.JID>}
305 @param config: Node configuration.
306 @type config: C{dict}
307 @return: deferred that fires on creation.
308 """
309
310
311 def deleteNode(nodeIdentifier):
312 """
313 Delete a node.
314
315 @param nodeIdentifier: NodeID of the new node.
316 @type nodeIdentifier: C{unicode}
317 @return: deferred that fires on deletion.
318 """
319
320
321 def getAffiliations(entity):
322 """
323 Get all affiliations for entity.
324
325 The implementation should make sure, the passed owner JID is stripped
326 of the resource (e.g. using C{owner.userhostJID()}).
327
328 @param entity: JID of the entity.
329 @type entity: L{JID<twisted.words.protocols.jabber.jid.JID>}
330 @return: deferred that returns a C{list} of tuples of the form
331 C{(nodeIdentifier, affiliation)}, where C{nodeIdentifier} is
332 of the type L{unicode} and C{affiliation} is one of
333 C{'owner'}, C{'publisher'} and C{'outcast'}.
334 """
335
336
337 def getSubscriptions(entity):
338 """
339 Get all subscriptions for an entity.
340
341 The implementation should make sure, the passed owner JID is stripped
342 of the resource (e.g. using C{owner.userhostJID()}).
343
344 @param entity: JID of the entity.
345 @type entity: L{JID<twisted.words.protocols.jabber.jid.JID>}
346 @return: deferred that returns a C{list} of tuples of the form
347 C{(nodeIdentifier, subscriber, state)}, where
348 C{nodeIdentifier} is of the type C{unicode}, C{subscriber} of
349 the type J{JID<twisted.words.protocols.jabber.jid.JID>}, and
350 C{state} is C{'subscribed'}, C{'pending'} or
351 C{'unconfigured'}.
352 """
353
354
355 def getDefaultConfiguration(nodeType):
356 """
357 Get the default configuration for the given node type.
358
359 @param nodeType: Either C{'leaf'} or C{'collection'}.
360 @type nodeType: C{str}
361 @return: The default configuration.
362 @rtype: C{dict}.
363 @raises: L{idavoll.error.NoCollections} if collections are not
364 supported.
365 """
366
367
368
369 class INode(Interface):
370 """
371 Interface to the class of objects that represent nodes.
372 """
373
374 nodeType = Attribute("""The type of this node. One of {'leaf'},
375 {'collection'}.""")
376 nodeIdentifier = Attribute("""The node identifer of this node""")
377
378
379 def getType():
380 """
381 Get node's type.
382
383 @return: C{'leaf'} or C{'collection'}.
384 """
385
386
387 def getConfiguration():
388 """
389 Get node's configuration.
390
391 The configuration must at least have two options:
392 C{pubsub#persist_items}, and C{pubsub#deliver_payloads}.
393
394 @return: C{dict} of configuration options.
395 """
396
397
398 def getMetaData():
399 """
400 Get node's meta data.
401
402 The meta data must be a superset of the configuration options, and
403 also at least should have a C{pubsub#node_type} entry.
404
405 @return: C{dict} of meta data.
406 """
407
408
409 def setConfiguration(options):
410 """
411 Set node's configuration.
412
413 The elements of {options} will set the new values for those
414 configuration items. This means that only changing items have to
415 be given.
416
417 @param options: a dictionary of configuration options.
418 @returns: a deferred that fires upon success.
419 """
420
421
422 def getAffiliation(entity):
423 """
424 Get affiliation of entity with this node.
425
426 @param entity: JID of entity.
427 @type entity: L{JID<twisted.words.protocols.jabber.jid.JID>}
428 @return: deferred that returns C{'owner'}, C{'publisher'}, C{'outcast'}
429 or C{None}.
430 """
431
432
433 def getSubscription(subscriber):
434 """
435 Get subscription to this node of subscriber.
436
437 @param subscriber: JID of the new subscriptions' entity.
438 @type subscriber: L{JID<twisted.words.protocols.jabber.jid.JID>}
439 @return: deferred that returns the subscription state (C{'subscribed'},
440 C{'pending'} or C{None}).
441 """
442
443
444 def getSubscriptions(state=None):
445 """
446 Get list of subscriptions to this node.
447
448 The optional C{state} argument filters the subscriptions to their
449 state.
450
451 @param state: Subscription state filter. One of C{'subscribed'},
452 C{'pending'}, C{'unconfigured'}.
453 @type state: C{str}
454 @return: a deferred that returns a C{list} of
455 L{wokkel.pubsub.Subscription}s.
456 """
457
458
459 def addSubscription(subscriber, state, config):
460 """
461 Add new subscription to this node with given state.
462
463 @param subscriber: JID of the new subscriptions' entity.
464 @type subscriber: L{JID<twisted.words.protocols.jabber.jid.JID>}
465 @param state: C{'subscribed'} or C{'pending'}
466 @type state: C{str}
467 @param config: Subscription configuration.
468 @param config: C{dict}
469 @return: deferred that fires on subscription.
470 """
471
472
473 def removeSubscription(subscriber):
474 """
475 Remove subscription to this node.
476
477 @param subscriber: JID of the subscriptions' entity.
478 @type subscriber: L{JID<twisted.words.protocols.jabber.jid.JID>}
479 @return: deferred that fires on removal.
480 """
481
482
483 def isSubscribed(entity):
484 """
485 Returns whether entity has any subscription to this node.
486
487 Only returns C{True} when the subscription state (if present) is
488 C{'subscribed'} for any subscription that matches the bare JID.
489
490 @param subscriber: bare JID of the subscriptions' entity.
491 @type subscriber: L{JID<twisted.words.protocols.jabber.jid.JID>}
492 @return: deferred that returns a C{bool}.
493 """
494
495
496 def getAffiliations():
497 """
498 Get affiliations of entities with this node.
499
500 @return: deferred that returns a C{list} of tuples (jid, affiliation),
501 where jid is a L(JID<twisted.words.protocols.jabber.jid.JID>)
502 and affiliation is one of C{'owner'},
503 C{'publisher'}, C{'outcast'}.
504 """
505
506
507
508 class ILeafNode(Interface):
509 """
510 Interface to the class of objects that represent leaf nodes.
511 """
512
513 def storeItems(items, publisher):
514 """
515 Store items in persistent storage for later retrieval.
516
517 @param items: The list of items to be stored. Each item is the
518 L{domish} representation of the XML fragment as defined
519 for C{<item/>} in the
520 C{http://jabber.org/protocol/pubsub} namespace.
521 @type items: C{list} of {domish.Element}
522 @param publisher: JID of the publishing entity.
523 @type publisher: L{JID<twisted.words.protocols.jabber.jid.JID>}
524 @return: deferred that fires upon success.
525 """
526
527
528 def removeItems(itemIdentifiers):
529 """
530 Remove items by id.
531
532 @param itemIdentifiers: C{list} of item ids.
533 @return: deferred that fires with a C{list} of ids of the items that
534 were deleted
535 """
536
537
538 def getItems(authorized_groups, unrestricted, maxItems=None):
539 """ Get all authorised items
540 If C{maxItems} is not given, all authorised items in the node are returned,
541 just like C{getItemsById}. Otherwise, C{maxItems} limits
542 the returned items to a maximum of that number of most recently
543 published and authorised items.
544
545 @param authorized_groups: we want to get items that these groups can access
546 @param unrestricted: if true, don't check permissions (i.e.: get all items)
547 @param maxItems: if given, a natural number (>0) that limits the
548 returned number of items.
549 @return: deferred that fires a C{list} of (item, access_model, id)
550 if unrestricted is True, else a C{list} of items.
551 """
552
553
554 def countItems(authorized_groups, unrestricted):
555 """ Count the accessible items.
556
557 @param authorized_groups: we want to get items that these groups can access.
558 @param unrestricted: if true, don't check permissions (i.e.: get all items).
559 @return: deferred that fires a C{int}.
560 """
561
562
563 def getIndex(authorized_groups, unrestricted, item):
564 """ Retrieve the index of the given item within the accessible window.
565
566 @param authorized_groups: we want to get items that these groups can access.
567 @param unrestricted: if true, don't check permissions (i.e.: get all items).
568 @param item: item identifier.
569 @return: deferred that fires a C{int}.
570 """
571
572 def getItemsById(authorized_groups, unrestricted, itemIdentifiers):
573 """
574 Get items by item id.
575
576 Each item in the returned list is a unicode string that
577 represent the XML of the item as it was published, including the
578 item wrapper with item id.
579
580 @param authorized_groups: we want to get items that these groups can access
581 @param unrestricted: if true, don't check permissions
582 @param itemIdentifiers: C{list} of item ids.
583 @return: deferred that fires a C{list} of (item, access_model, id)
584 if unrestricted is True, else a C{list} of items.
585 """
586
587
588 def purge():
589 """
590 Purge node of all items in persistent storage.
591
592 @return: deferred that fires when the node has been purged.
593 """
594
595
596 def filterItemsWithPublisher(itemIdentifiers, requestor):
597 """
598 Filter the given items by checking the items publisher against the requestor.
599
600 @param itemIdentifiers: C{list} of item ids.
601 @param requestor: JID of the requestor.
602 @type requestor: L{JID<twisted.words.protocols.jabber.jid.JID>}
603 @return: deferred that fires with a C{list} of item identifiers.
604 """
605
606 class IGatewayStorage(Interface):
607
608 def addCallback(service, nodeIdentifier, callback):
609 """
610 Register a callback URI.
611
612 The registered HTTP callback URI will have an Atom Entry documented
613 POSTed to it upon receiving a notification for the given pubsub node.
614
615 @param service: The XMPP entity that holds the node.
616 @type service: L{JID<twisted.words.protocols.jabber.jid.JID>}
617 @param nodeIdentifier: The identifier of the publish-subscribe node.
618 @type nodeIdentifier: C{unicode}.
619 @param callback: The callback URI to be registered.
620 @type callback: C{str}.
621 @rtype: L{Deferred<twisted.internet.defer.Deferred>}
622 """
623
624 def removeCallback(service, nodeIdentifier, callback):
625 """
626 Remove a registered callback URI.
627
628 The returned deferred will fire with a boolean that signals wether or
629 not this was the last callback unregistered for this node.
630
631 @param service: The XMPP entity that holds the node.
632 @type service: L{JID<twisted.words.protocols.jabber.jid.JID>}
633 @param nodeIdentifier: The identifier of the publish-subscribe node.
634 @type nodeIdentifier: C{unicode}.
635 @param callback: The callback URI to be unregistered.
636 @type callback: C{str}.
637 @rtype: L{Deferred<twisted.internet.defer.Deferred>}
638 """
639
640 def getCallbacks(service, nodeIdentifier):
641 """
642 Get the callbacks registered for this node.
643
644 Returns a deferred that fires with the set of HTTP callback URIs
645 registered for this node.
646
647 @param service: The XMPP entity that holds the node.
648 @type service: L{JID<twisted.words.protocols.jabber.jid.JID>}
649 @param nodeIdentifier: The identifier of the publish-subscribe node.
650 @type nodeIdentifier: C{unicode}.
651 @rtype: L{Deferred<twisted.internet.defer.Deferred>}
652 """
653
654
655 def hasCallbacks(service, nodeIdentifier):
656 """
657 Return wether there are callbacks registered for a node.
658
659 @param service: The XMPP entity that holds the node.
660 @type service: L{JID<twisted.words.protocols.jabber.jid.JID>}
661 @param nodeIdentifier: The identifier of the publish-subscribe node.
662 @type nodeIdentifier: C{unicode}.
663 @returns: Deferred that fires with a boolean.
664 @rtype: L{Deferred<twisted.internet.defer.Deferred>}
665 """