paula: reorganized into one paula repo
[paula.git] / paula.authutil / trunk / src / paula / authutil / utils.py
blob1f8206600e3b1d1899616c600b41fa984b48d2ac
1 # Copyright (c) 2008 by Florian Friesdorf
3 # GNU Affero General Public License (AGPL)
5 # This program is free software; you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License as
7 # published by the Free Software Foundation; either version 3 of the
8 # License, or (at your option) any later version.
10 # This program is distributed in the hope that it will be useful,
11 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 # GNU Affero General Public License for more details.
15 # You should have received a copy of the GNU Affero General Public
16 # License along with this program. If not, see
17 # <http://www.gnu.org/licenses/>.
18 """
19 """
20 __author__ = "Florian Friesdorf <flo@chaoflow.net>"
21 __docformat__ = "plaintext"
23 from BTrees import OOBTree
24 from persistent import Persistent
26 from zope.app.container.contained import Contained
28 from zope.component import queryUtility
29 from zope.component.interfaces import IFactory
31 from zope.interface import implements
33 from paula.authentication.interfaces import IAuthProvider
35 from paula.authutil.interfaces import IRWAuthProviders
36 from paula.authutil.interfaces import PrincipalIdAlreadyTaken
39 class RWAuthProviders(Persistent, Contained):
40 """Utility delivering IAuthProvider for principal ids
42 IAuthProvider objects are generated by dynamic adapter lookup from
43 IAuthProviderAdaptable objects, which are retrieved from an internal
44 mapping. Objects enter/leave the mapping through register/unregister.
46 Currently we do not support modification of principal id for registered
47 objects; before doing anything that modifies the prinicipal id of an object,
48 you need to unregister and reregister the object.
50 Currently, we maintain a two-way mapping, having a feeling that this comes
51 in handy, in case of ObjectModifiedEvents, which might overcome the
52 limitation about not changing registered objects. It is already used to
53 prevent unregistered objects with the same principal id as an registered
54 object, to remove the latter's mapping, when being passed to unregister.
56 This utility should be registered as a local utility, you probably want
57 the persistent version (see below).
59 This utility could be registered globally and works outside of an
60 application server. However, the adapter lookup for IAuthProvider needs
61 to be supported.
64 Mappings are empty, for a new utility
66 >>> apu = RWAuthProviders()
67 >>> len(apu._ids)
69 >>> len(apu._objs)
71 >>> len(apu)
74 Suitable object ends up in both mappings, strings work as id
76 >>> a = Mock(id = '1')
77 >>> alsoProvides(a, IAuthProvider)
78 >>> apu.register(a)
79 >>> len(apu._ids)
81 >>> len(apu._objs)
83 >>> len(apu)
85 >>> apu._ids[a] is '1'
86 True
87 >>> apu._objs['1'] is a
88 True
90 Object with same id as registered object triggers PrincipalIdAlreadyTaken
92 >>> b = Mock(id = '1')
93 >>> alsoProvides(b, IAuthProvider)
94 >>> apu.register(b)
95 Traceback (most recent call last):
96 blah
97 PrincipalIdAlreadyTaken: 1
99 Unsuitable object triggers TypeError
101 >>> b = Mock(id = '2')
102 >>> apu.register(b) # doctest: +IGNORE_EXCEPTION_DETAIL
103 Traceback (most recent call last):
104 blah
105 TypeError: blah
107 More than one object can be added
109 >>> b = Mock(id = '2')
110 >>> alsoProvides(b, IAuthProvider)
111 >>> apu.register(b)
112 >>> len(apu._ids)
114 >>> len(apu._objs)
117 Objects are retrieved correctly
119 >>> apu['1'] is a
120 True
121 >>> apu['2'] is b
122 True
124 implements(IRWAuthProviders)
126 def __init__(self):
129 # XXX:
130 # self might be stored in a persistent container
131 # _ids and _objs are our only attributes and they won't change
132 # I assume it is enough that the BTrees are persistent and that self
133 # does not need to be...
134 self._ids = queryUtility(IFactory, 'OOBTree', OOBTree.OOBTree)()
135 self._objs = queryUtility(IFactory, 'OOBTree', OOBTree.OOBTree)()
137 def __contains__(self, id):
138 return self.has_key(id)
140 def __getitem__(self, id):
141 """get IAuthProvider object for principal id
143 obj = self._objs[id]
144 ap = IAuthProvider(obj)
145 return ap
147 def __len__(self):
148 return self._ids.__len__()
150 def has_key(self, id):
151 # I currently consider this a broken BTree
152 return bool(self._objs.has_key(id))
154 def keys(self):
155 # I currently consider this a broken BTree
156 return list(self._objs.keys())
158 def register(self, obj):
159 """Register an object.
161 It is assumed that an adapter lookup for IAuthProvider succeeds.
163 Fails with PrincipalIdAlreadyTaken, in case an object with the same
164 principal id is registered already.
166 id = IAuthProvider(obj).id
167 if self._objs.has_key(id):
168 if self._objs[id] is not obj:
169 raise PrincipalIdAlreadyTaken, id
170 return
171 self._objs[id] = obj
172 self._ids[obj] = id
173 #notify(AuthProviderAdded(ob, event))
175 def unregister(self, obj):
176 """Unregister an object.
178 Fails with KeyError, if the object is not registered.
180 # get id for obj, implicit check that obj is registered
181 id = self._ids[obj]
182 del self._objs[id]
183 del self._ids[obj]
184 #notify(AuthProviderRemoved(ob, event))