Mercurial > libervia-pubsub
annotate idavoll/generic_backend.py @ 162:84cfe9fe38c5
Comply with the access model 'open'.
Currently, the only implemented access model is 'open', so we should not check
for the subscription of the requestor for item retrieval. We do reject
outcasts.
author | Ralph Meijer <ralphm@ik.nu> |
---|---|
date | Wed, 06 Sep 2006 12:57:53 +0000 |
parents | 6fe78048baf9 |
children |
rev | line source |
---|---|
155
5191ba7c4df8
Work towards first release 0.5.0.
Ralph Meijer <ralphm@ik.nu>
parents:
153
diff
changeset
|
1 # Copyright (c) 2003-2006 Ralph Meijer |
5191ba7c4df8
Work towards first release 0.5.0.
Ralph Meijer <ralphm@ik.nu>
parents:
153
diff
changeset
|
2 # See LICENSE for details. |
5191ba7c4df8
Work towards first release 0.5.0.
Ralph Meijer <ralphm@ik.nu>
parents:
153
diff
changeset
|
3 |
107 | 4 import sha |
5 import time | |
150
35977eb964e6
Use uuid.py module [1] for generating unique id's for nodes and items.
Ralph Meijer <ralphm@ik.nu>
parents:
144
diff
changeset
|
6 import uuid |
107 | 7 from twisted.words.protocols.jabber import jid |
152 | 8 from twisted.words.xish import utility |
107 | 9 from twisted.application import service |
10 from twisted.internet import defer | |
11 from zope.interface import implements | |
129 | 12 import backend, storage |
107 | 13 |
14 def _get_affiliation(node, entity): | |
15 d = node.get_affiliation(entity) | |
16 d.addCallback(lambda affiliation: (node, affiliation)) | |
17 return d | |
18 | |
19 class BackendService(service.MultiService, utility.EventDispatcher): | |
20 | |
21 implements(backend.IBackendService) | |
22 | |
23 options = {"pubsub#persist_items": | |
24 {"type": "boolean", | |
25 "label": "Persist items to storage"}, | |
26 "pubsub#deliver_payloads": | |
27 {"type": "boolean", | |
28 "label": "Deliver payloads with event notifications"}, | |
29 } | |
30 | |
31 default_config = {"pubsub#persist_items": True, | |
32 "pubsub#deliver_payloads": True, | |
33 } | |
34 | |
35 def __init__(self, storage): | |
36 service.MultiService.__init__(self) | |
37 utility.EventDispatcher.__init__(self) | |
38 self.storage = storage | |
39 | |
40 def supports_publisher_affiliation(self): | |
41 return True | |
42 | |
43 def supports_outcast_affiliation(self): | |
44 return True | |
45 | |
46 def supports_persistent_items(self): | |
47 return True | |
48 | |
49 def get_node_type(self, node_id): | |
50 d = self.storage.get_node(node_id) | |
51 d.addCallback(lambda node: node.get_type()) | |
52 return d | |
53 | |
54 def get_nodes(self): | |
55 return self.storage.get_node_ids() | |
56 | |
57 def get_node_meta_data(self, node_id): | |
58 d = self.storage.get_node(node_id) | |
59 d.addCallback(lambda node: node.get_meta_data()) | |
60 d.addCallback(self._make_meta_data) | |
61 return d | |
62 | |
63 def _make_meta_data(self, meta_data): | |
64 options = [] | |
65 for key, value in meta_data.iteritems(): | |
66 if self.options.has_key(key): | |
67 option = {"var": key} | |
68 option.update(self.options[key]) | |
69 option["value"] = value | |
70 options.append(option) | |
71 | |
72 return options | |
73 | |
74 class PublishService(service.Service): | |
75 | |
76 implements(backend.IPublishService) | |
77 | |
150
35977eb964e6
Use uuid.py module [1] for generating unique id's for nodes and items.
Ralph Meijer <ralphm@ik.nu>
parents:
144
diff
changeset
|
78 def _check_auth(self, node, requestor): |
35977eb964e6
Use uuid.py module [1] for generating unique id's for nodes and items.
Ralph Meijer <ralphm@ik.nu>
parents:
144
diff
changeset
|
79 def check(affiliation, node): |
35977eb964e6
Use uuid.py module [1] for generating unique id's for nodes and items.
Ralph Meijer <ralphm@ik.nu>
parents:
144
diff
changeset
|
80 if affiliation not in ['owner', 'publisher']: |
159
6fe78048baf9
Rework error handling, depend on Twisted Words 0.4.0.
Ralph Meijer <ralphm@ik.nu>
parents:
155
diff
changeset
|
81 raise backend.Forbidden |
150
35977eb964e6
Use uuid.py module [1] for generating unique id's for nodes and items.
Ralph Meijer <ralphm@ik.nu>
parents:
144
diff
changeset
|
82 return node |
35977eb964e6
Use uuid.py module [1] for generating unique id's for nodes and items.
Ralph Meijer <ralphm@ik.nu>
parents:
144
diff
changeset
|
83 |
35977eb964e6
Use uuid.py module [1] for generating unique id's for nodes and items.
Ralph Meijer <ralphm@ik.nu>
parents:
144
diff
changeset
|
84 d = node.get_affiliation(requestor) |
35977eb964e6
Use uuid.py module [1] for generating unique id's for nodes and items.
Ralph Meijer <ralphm@ik.nu>
parents:
144
diff
changeset
|
85 d.addCallback(check, node) |
35977eb964e6
Use uuid.py module [1] for generating unique id's for nodes and items.
Ralph Meijer <ralphm@ik.nu>
parents:
144
diff
changeset
|
86 return d |
35977eb964e6
Use uuid.py module [1] for generating unique id's for nodes and items.
Ralph Meijer <ralphm@ik.nu>
parents:
144
diff
changeset
|
87 |
107 | 88 def publish(self, node_id, items, requestor): |
89 d = self.parent.storage.get_node(node_id) | |
150
35977eb964e6
Use uuid.py module [1] for generating unique id's for nodes and items.
Ralph Meijer <ralphm@ik.nu>
parents:
144
diff
changeset
|
90 d.addCallback(self._check_auth, requestor) |
107 | 91 d.addCallback(self._do_publish, items, requestor) |
92 return d | |
93 | |
150
35977eb964e6
Use uuid.py module [1] for generating unique id's for nodes and items.
Ralph Meijer <ralphm@ik.nu>
parents:
144
diff
changeset
|
94 def _do_publish(self, node, items, requestor): |
107 | 95 configuration = node.get_configuration() |
96 persist_items = configuration["pubsub#persist_items"] | |
97 deliver_payloads = configuration["pubsub#deliver_payloads"] | |
98 | |
99 if items and not persist_items and not deliver_payloads: | |
159
6fe78048baf9
Rework error handling, depend on Twisted Words 0.4.0.
Ralph Meijer <ralphm@ik.nu>
parents:
155
diff
changeset
|
100 raise backend.ItemForbidden |
107 | 101 elif not items and (persist_items or deliver_payloads): |
159
6fe78048baf9
Rework error handling, depend on Twisted Words 0.4.0.
Ralph Meijer <ralphm@ik.nu>
parents:
155
diff
changeset
|
102 raise backend.ItemRequired |
107 | 103 |
104 if persist_items or deliver_payloads: | |
105 for item in items: | |
106 if not item.getAttribute("id"): | |
150
35977eb964e6
Use uuid.py module [1] for generating unique id's for nodes and items.
Ralph Meijer <ralphm@ik.nu>
parents:
144
diff
changeset
|
107 item["id"] = uuid.generate() |
107 | 108 |
109 if persist_items: | |
110 d = node.store_items(items, requestor) | |
111 else: | |
112 d = defer.succeed(None) | |
113 | |
114 d.addCallback(self._do_notify, node.id, items, deliver_payloads) | |
129 | 115 return d |
107 | 116 |
117 def _do_notify(self, result, node_id, items, deliver_payloads): | |
118 if items and not deliver_payloads: | |
119 for item in items: | |
120 item.children = [] | |
121 | |
122 self.parent.dispatch({ 'items': items, 'node_id': node_id }, | |
123 '//event/pubsub/notify') | |
124 | |
125 class NotificationService(service.Service): | |
126 | |
127 implements(backend.INotificationService) | |
128 | |
129 def get_notification_list(self, node_id, items): | |
130 d = self.parent.storage.get_node(node_id) | |
131 d.addCallback(lambda node: node.get_subscribers()) | |
132 d.addCallback(self._magic_filter, node_id, items) | |
133 return d | |
134 | |
135 def _magic_filter(self, subscribers, node_id, items): | |
136 list = [] | |
137 for subscriber in subscribers: | |
138 list.append((subscriber, items)) | |
139 return list | |
140 | |
141 def register_notifier(self, observerfn, *args, **kwargs): | |
142 self.parent.addObserver('//event/pubsub/notify', observerfn, | |
143 *args, **kwargs) | |
144 | |
145 class SubscriptionService(service.Service): | |
146 | |
147 implements(backend.ISubscriptionService) | |
148 | |
149 def subscribe(self, node_id, subscriber, requestor): | |
150 subscriber_entity = subscriber.userhostJID() | |
151 if subscriber_entity != requestor: | |
159
6fe78048baf9
Rework error handling, depend on Twisted Words 0.4.0.
Ralph Meijer <ralphm@ik.nu>
parents:
155
diff
changeset
|
152 return defer.fail(backend.Forbidden()) |
107 | 153 |
154 d = self.parent.storage.get_node(node_id) | |
155 d.addCallback(_get_affiliation, subscriber_entity) | |
156 d.addCallback(self._do_subscribe, subscriber) | |
157 return d | |
158 | |
159 def _do_subscribe(self, result, subscriber): | |
160 node, affiliation = result | |
161 | |
162 if affiliation == 'outcast': | |
159
6fe78048baf9
Rework error handling, depend on Twisted Words 0.4.0.
Ralph Meijer <ralphm@ik.nu>
parents:
155
diff
changeset
|
163 raise backend.Forbidden |
107 | 164 |
165 d = node.add_subscription(subscriber, 'subscribed') | |
118
7d83fe9bdb65
Change storage.INode.add_subscription() to return a Failure when a subscription
Ralph Meijer <ralphm@ik.nu>
parents:
107
diff
changeset
|
166 d.addCallback(lambda _: 'subscribed') |
129 | 167 d.addErrback(self._get_subscription, node, subscriber) |
153 | 168 d.addCallback(self._return_subscription, node.id) |
107 | 169 return d |
170 | |
129 | 171 def _get_subscription(self, failure, node, subscriber): |
172 failure.trap(storage.SubscriptionExists) | |
118
7d83fe9bdb65
Change storage.INode.add_subscription() to return a Failure when a subscription
Ralph Meijer <ralphm@ik.nu>
parents:
107
diff
changeset
|
173 return node.get_subscription(subscriber) |
7d83fe9bdb65
Change storage.INode.add_subscription() to return a Failure when a subscription
Ralph Meijer <ralphm@ik.nu>
parents:
107
diff
changeset
|
174 |
153 | 175 def _return_subscription(self, result, node_id): |
176 return node_id, result | |
107 | 177 |
178 def unsubscribe(self, node_id, subscriber, requestor): | |
179 if subscriber.userhostJID() != requestor: | |
159
6fe78048baf9
Rework error handling, depend on Twisted Words 0.4.0.
Ralph Meijer <ralphm@ik.nu>
parents:
155
diff
changeset
|
180 return defer.fail(backend.Forbidden()) |
107 | 181 |
182 d = self.parent.storage.get_node(node_id) | |
183 d.addCallback(lambda node: node.remove_subscription(subscriber)) | |
184 return d | |
185 | |
153 | 186 def get_subscriptions(self, entity): |
187 return self.parent.storage.get_subscriptions(entity) | |
188 | |
107 | 189 class NodeCreationService(service.Service): |
190 | |
191 implements(backend.INodeCreationService) | |
192 | |
193 def supports_instant_nodes(self): | |
194 return True | |
195 | |
196 def create_node(self, node_id, requestor): | |
197 if not node_id: | |
150
35977eb964e6
Use uuid.py module [1] for generating unique id's for nodes and items.
Ralph Meijer <ralphm@ik.nu>
parents:
144
diff
changeset
|
198 node_id = 'generic/%s' % uuid.generate() |
107 | 199 d = self.parent.storage.create_node(node_id, requestor) |
200 d.addCallback(lambda _: node_id) | |
201 return d | |
202 | |
159
6fe78048baf9
Rework error handling, depend on Twisted Words 0.4.0.
Ralph Meijer <ralphm@ik.nu>
parents:
155
diff
changeset
|
203 def get_default_configuration(self): |
6fe78048baf9
Rework error handling, depend on Twisted Words 0.4.0.
Ralph Meijer <ralphm@ik.nu>
parents:
155
diff
changeset
|
204 d = defer.succeed(self.parent.default_config) |
6fe78048baf9
Rework error handling, depend on Twisted Words 0.4.0.
Ralph Meijer <ralphm@ik.nu>
parents:
155
diff
changeset
|
205 d.addCallback(self._make_config) |
6fe78048baf9
Rework error handling, depend on Twisted Words 0.4.0.
Ralph Meijer <ralphm@ik.nu>
parents:
155
diff
changeset
|
206 return d |
6fe78048baf9
Rework error handling, depend on Twisted Words 0.4.0.
Ralph Meijer <ralphm@ik.nu>
parents:
155
diff
changeset
|
207 |
107 | 208 def get_node_configuration(self, node_id): |
159
6fe78048baf9
Rework error handling, depend on Twisted Words 0.4.0.
Ralph Meijer <ralphm@ik.nu>
parents:
155
diff
changeset
|
209 if not node_id: |
6fe78048baf9
Rework error handling, depend on Twisted Words 0.4.0.
Ralph Meijer <ralphm@ik.nu>
parents:
155
diff
changeset
|
210 raise backend.NoRootNode |
6fe78048baf9
Rework error handling, depend on Twisted Words 0.4.0.
Ralph Meijer <ralphm@ik.nu>
parents:
155
diff
changeset
|
211 |
6fe78048baf9
Rework error handling, depend on Twisted Words 0.4.0.
Ralph Meijer <ralphm@ik.nu>
parents:
155
diff
changeset
|
212 d = self.parent.storage.get_node(node_id) |
6fe78048baf9
Rework error handling, depend on Twisted Words 0.4.0.
Ralph Meijer <ralphm@ik.nu>
parents:
155
diff
changeset
|
213 d.addCallback(lambda node: node.get_configuration()) |
107 | 214 |
215 d.addCallback(self._make_config) | |
216 return d | |
217 | |
218 def _make_config(self, config): | |
219 options = [] | |
220 for key, value in self.parent.options.iteritems(): | |
221 option = {"var": key} | |
222 option.update(value) | |
223 if config.has_key(key): | |
224 option["value"] = config[key] | |
225 options.append(option) | |
226 | |
227 return options | |
228 | |
229 def set_node_configuration(self, node_id, options, requestor): | |
159
6fe78048baf9
Rework error handling, depend on Twisted Words 0.4.0.
Ralph Meijer <ralphm@ik.nu>
parents:
155
diff
changeset
|
230 if not node_id: |
6fe78048baf9
Rework error handling, depend on Twisted Words 0.4.0.
Ralph Meijer <ralphm@ik.nu>
parents:
155
diff
changeset
|
231 raise backend.NoRootNode |
6fe78048baf9
Rework error handling, depend on Twisted Words 0.4.0.
Ralph Meijer <ralphm@ik.nu>
parents:
155
diff
changeset
|
232 |
107 | 233 for key, value in options.iteritems(): |
234 if not self.parent.options.has_key(key): | |
235 raise backend.InvalidConfigurationOption | |
236 if self.parent.options[key]["type"] == 'boolean': | |
237 try: | |
238 options[key] = bool(int(value)) | |
239 except ValueError: | |
240 raise backend.InvalidConfigurationValue | |
241 | |
242 d = self.parent.storage.get_node(node_id) | |
243 d.addCallback(_get_affiliation, requestor) | |
244 d.addCallback(self._do_set_node_configuration, options) | |
245 return d | |
246 | |
247 def _do_set_node_configuration(self, result, options): | |
248 node, affiliation = result | |
249 | |
250 if affiliation != 'owner': | |
159
6fe78048baf9
Rework error handling, depend on Twisted Words 0.4.0.
Ralph Meijer <ralphm@ik.nu>
parents:
155
diff
changeset
|
251 raise backend.Forbidden |
107 | 252 |
253 return node.set_configuration(options) | |
254 | |
255 class AffiliationsService(service.Service): | |
256 | |
257 implements(backend.IAffiliationsService) | |
258 | |
259 def get_affiliations(self, entity): | |
153 | 260 return self.parent.storage.get_affiliations(entity) |
107 | 261 |
262 class ItemRetrievalService(service.Service): | |
263 | |
264 implements(backend.IItemRetrievalService) | |
265 | |
266 def get_items(self, node_id, requestor, max_items=None, item_ids=[]): | |
267 d = self.parent.storage.get_node(node_id) | |
162
84cfe9fe38c5
Comply with the access model 'open'.
Ralph Meijer <ralphm@ik.nu>
parents:
159
diff
changeset
|
268 d.addCallback(_get_affiliation, requestor) |
107 | 269 d.addCallback(self._do_get_items, max_items, item_ids) |
270 return d | |
271 | |
162
84cfe9fe38c5
Comply with the access model 'open'.
Ralph Meijer <ralphm@ik.nu>
parents:
159
diff
changeset
|
272 def _do_get_items(self, result, max_items, item_ids): |
84cfe9fe38c5
Comply with the access model 'open'.
Ralph Meijer <ralphm@ik.nu>
parents:
159
diff
changeset
|
273 node, affiliation = result |
107 | 274 |
162
84cfe9fe38c5
Comply with the access model 'open'.
Ralph Meijer <ralphm@ik.nu>
parents:
159
diff
changeset
|
275 if affiliation == 'outcast': |
84cfe9fe38c5
Comply with the access model 'open'.
Ralph Meijer <ralphm@ik.nu>
parents:
159
diff
changeset
|
276 raise backend.Forbidden |
107 | 277 |
278 if item_ids: | |
279 return node.get_items_by_id(item_ids) | |
280 else: | |
281 return node.get_items(max_items) | |
282 | |
283 class RetractionService(service.Service): | |
284 | |
285 implements(backend.IRetractionService) | |
286 | |
287 def retract_item(self, node_id, item_ids, requestor): | |
288 d = self.parent.storage.get_node(node_id) | |
289 d.addCallback(_get_affiliation, requestor) | |
290 d.addCallback(self._do_retract, item_ids) | |
291 return d | |
292 | |
293 def _do_retract(self, result, item_ids): | |
294 node, affiliation = result | |
295 persist_items = node.get_configuration()["pubsub#persist_items"] | |
296 | |
297 if affiliation not in ['owner', 'publisher']: | |
159
6fe78048baf9
Rework error handling, depend on Twisted Words 0.4.0.
Ralph Meijer <ralphm@ik.nu>
parents:
155
diff
changeset
|
298 raise backend.Forbidden |
107 | 299 |
300 if not persist_items: | |
301 raise backend.NodeNotPersistent | |
302 | |
303 d = node.remove_items(item_ids) | |
142
812300cdbc22
Changed behaviour of retraction of items so that only the actually deleted
Ralph Meijer <ralphm@ik.nu>
parents:
129
diff
changeset
|
304 d.addCallback(self._do_notify_retraction, node.id) |
107 | 305 return d |
306 | |
142
812300cdbc22
Changed behaviour of retraction of items so that only the actually deleted
Ralph Meijer <ralphm@ik.nu>
parents:
129
diff
changeset
|
307 def _do_notify_retraction(self, item_ids, node_id): |
129 | 308 self.parent.dispatch({ 'item_ids': item_ids, 'node_id': node_id }, |
107 | 309 '//event/pubsub/retract') |
310 | |
311 def purge_node(self, node_id, requestor): | |
312 d = self.parent.storage.get_node(node_id) | |
313 d.addCallback(_get_affiliation, requestor) | |
314 d.addCallback(self._do_purge) | |
315 return d | |
316 | |
317 def _do_purge(self, result): | |
318 node, affiliation = result | |
319 persist_items = node.get_configuration()["pubsub#persist_items"] | |
320 | |
321 if affiliation != 'owner': | |
159
6fe78048baf9
Rework error handling, depend on Twisted Words 0.4.0.
Ralph Meijer <ralphm@ik.nu>
parents:
155
diff
changeset
|
322 raise backend.Forbidden |
107 | 323 |
324 if not persist_items: | |
325 raise backend.NodeNotPersistent | |
326 | |
327 d = node.purge() | |
328 d.addCallback(self._do_notify_purge, node.id) | |
329 return d | |
330 | |
331 def _do_notify_purge(self, result, node_id): | |
332 self.parent.dispatch(node_id, '//event/pubsub/purge') | |
333 | |
334 class NodeDeletionService(service.Service): | |
335 | |
336 implements(backend.INodeDeletionService) | |
337 | |
338 def __init__(self): | |
339 self._callback_list = [] | |
340 | |
341 def register_pre_delete(self, pre_delete_fn): | |
342 self._callback_list.append(pre_delete_fn) | |
343 | |
344 def get_subscribers(self, node_id): | |
345 d = self.parent.storage.get_node(node_id) | |
346 d.addCallback(lambda node: node.get_subscribers()) | |
347 return d | |
348 | |
349 def delete_node(self, node_id, requestor): | |
350 d = self.parent.storage.get_node(node_id) | |
351 d.addCallback(_get_affiliation, requestor) | |
352 d.addCallback(self._do_pre_delete) | |
353 return d | |
354 | |
355 def _do_pre_delete(self, result): | |
356 node, affiliation = result | |
357 | |
358 if affiliation != 'owner': | |
159
6fe78048baf9
Rework error handling, depend on Twisted Words 0.4.0.
Ralph Meijer <ralphm@ik.nu>
parents:
155
diff
changeset
|
359 raise backend.Forbidden |
107 | 360 |
144
5b0b3f013ccc
Fixed typo. Reported by Herman Li.
Ralph Meijer <ralphm@ik.nu>
parents:
142
diff
changeset
|
361 d = defer.DeferredList([cb(node.id) for cb in self._callback_list], |
107 | 362 consumeErrors=1) |
363 d.addCallback(self._do_delete, node.id) | |
364 | |
365 def _do_delete(self, result, node_id): | |
366 dl = [] | |
367 for succeeded, r in result: | |
368 if succeeded and r: | |
369 dl.extend(r) | |
370 | |
371 d = self.parent.storage.delete_node(node_id) | |
372 d.addCallback(self._do_notify_delete, dl) | |
373 | |
374 return d | |
375 | |
376 def _do_notify_delete(self, result, dl): | |
377 for d in dl: | |
378 d.callback(None) |