Compat zope-2.12 : résolution des problèmes avec les interfaces.
[GroupUserFolder.git] / LDAPGroupFolder.py
1 # -*- coding: utf-8 -*-
2 ## GroupUserFolder
3 ## Copyright (C)2006 Ingeniweb
4
5 ## This program is free software; you can redistribute it and/or modify
6 ## it under the terms of the GNU General Public License as published by
7 ## the Free Software Foundation; either version 2 of the License, or
8 ## (at your option) any later version.
9
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 General Public License for more details.
14
15 ## You should have received a copy of the GNU General Public License
16 ## along with this program; see the file COPYING. If not, write to the
17 ## Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18 """
19
20 """
21 __version__ = "$Revision: $"
22 # $Source: $
23 # $Id: LDAPGroupFolder.py 587 2008-07-31 09:20:06Z pin $
24 __docformat__ = 'restructuredtext'
25
26 import time, traceback
27
28 # Zope imports
29 from Globals import DTMLFile, InitializeClass
30 from Acquisition import aq_base
31 from AccessControl import ClassSecurityInfo
32 from AccessControl.User import SimpleUser
33 from AccessControl.Permissions import view_management_screens, manage_users
34 from OFS.SimpleItem import SimpleItem
35 from DateTime import DateTime
36
37 from Products.GroupUserFolder import postonly
38 import GroupUserFolder
39
40 from global_symbols import *
41
42 # LDAPUserFolder package imports
43 from Products.LDAPUserFolder.SimpleCache import SimpleCache
44
45 addLDAPGroupFolderForm = DTMLFile('dtml/addLDAPGroupFolder', globals())
46
47
48 class LDAPGroupFolder(SimpleItem):
49 """ """
50 security = ClassSecurityInfo()
51
52 meta_type = 'LDAPGroupFolder'
53 id = 'acl_users'
54
55 isPrincipiaFolderish=1
56 isAUserFolder=1
57
58 manage_options=(
59 ({'label' : 'Groups', 'action' : 'manage_main',},)
60 + SimpleItem.manage_options
61 )
62
63 security.declareProtected(view_management_screens, 'manage_main')
64 manage_main = DTMLFile('dtml/groups', globals())
65
66
67 def __setstate__(self, v):
68 """ """
69 LDAPGroupFolder.inheritedAttribute('__setstate__')(self, v)
70 self._cache = SimpleCache()
71 self._cache.setTimeout(600)
72 self._cache.clear()
73
74 def __init__( self, title, luf=''):
75 """ """
76 self._luf = luf
77 self._cache = SimpleCache()
78 self._cache.setTimeout(600)
79 self._cache.clear()
80
81 security.declarePrivate(manage_users, 'getGRUF')
82 def getGRUF(self):
83 """ """
84 return self.aq_parent.aq_parent
85
86
87 security.declareProtected(manage_users, 'getLUF')
88 def getLUF(self):
89 """ """
90 s = self.getGRUF().getUserSource(self._luf)
91 if getattr(s, 'meta_type', None) != "LDAPUserFolder":
92 # whoops, we moved LDAPUF... let's try to find it back
93 Log(LOG_WARNING, "LDAPUserFolder moved. Trying to find it back.")
94 s = None
95 for src in self.getGRUF().listUserSources():
96 if src.meta_type == "LDAPUserFolder":
97 self._luf = src.getPhysicalPath()[-2]
98 s = src
99 break
100 if not s:
101 raise RuntimeError, "You must change your groups source in GRUF if you do not have a LDAPUserFolder as a users source."
102 return s
103
104
105 security.declareProtected(manage_users, 'getGroups')
106 def getGroups(self, dn='*', attr=None, pwd=''):
107 """ """
108 return self.getLUF().getGroups(dn, attr, pwd)
109
110
111 security.declareProtected(manage_users, 'getGroupType')
112 def getGroupType(self, group_dn):
113 """ """
114 return self.getLUF().getGroupType(group_dn)
115
116 security.declareProtected(manage_users, 'getGroupMappings')
117 def getGroupMappings(self):
118 """ """
119 return self.getLUF().getGroupMappings()
120
121 security.declareProtected(manage_users, 'manage_addGroupMapping')
122 def manage_addGroupMapping(self, group_name, role_name, REQUEST=None):
123 """ """
124 self._cache.remove(group_name)
125 self.getLUF().manage_addGroupMapping(group_name, role_name, None)
126
127 if REQUEST:
128 msg = 'Added LDAP group to Zope role mapping: %s -> %s' % (
129 group_name, role_name)
130 return self.manage_main(manage_tabs_message=msg)
131 manage_addGroupMapping = postonly(manage_addGroupMapping)
132
133 security.declareProtected(manage_users, 'manage_deleteGroupMappings')
134 def manage_deleteGroupMappings(self, group_names, REQUEST=None):
135 """ Delete mappings from LDAP group to Zope role """
136 self._cache.clear()
137 self.getLUF().manage_deleteGroupMappings(group_names, None)
138 if REQUEST:
139 msg = 'Deleted LDAP group to Zope role mapping for: %s' % (
140 ', '.join(group_names))
141 return self.manage_main(manage_tabs_message=msg)
142 manage_deleteGroupMappings = postonly(manage_deleteGroupMappings)
143
144 security.declareProtected(manage_users, 'manage_addGroup')
145 def manage_addGroup( self
146 , newgroup_name
147 , newgroup_type='groupOfUniqueNames'
148 , REQUEST=None
149 ):
150 """Add a new group in groups_base.
151 """
152 self.getLUF().manage_addGroup(newgroup_name, newgroup_type, None)
153
154 if REQUEST:
155 msg = 'Added new group %s' % (newgroup_name)
156 return self.manage_main(manage_tabs_message=msg)
157 manage_addGroup = postonly(manage_addGroup)
158
159 security.declareProtected(manage_users, 'manage_deleteGroups')
160 def manage_deleteGroups(self, dns=[], REQUEST=None):
161 """ Delete groups from groups_base """
162 self.getLUF().manage_deleteGroups(dns, None)
163 self._cache.clear()
164
165 if REQUEST:
166 msg = 'Deleted group(s):<br> %s' % '<br>'.join(dns)
167 return self.manage_main(manage_tabs_message=msg)
168 manage_deleteGroups = postonly(manage_deleteGroups)
169
170 security.declareProtected(manage_users, 'getUser')
171 def getUser(self, name):
172 """ """
173 # Prevent locally stored groups
174 luf = self.getLUF()
175 if luf._local_groups:
176 return []
177
178 # Get the group from the cache
179 user = self._cache.get(name, '')
180 if user:
181 return user
182
183 # Scan groups to find the proper user.
184 # THIS MAY BE EXPENSIVE AND HAS TO BE OPTIMIZED...
185 grps = self.getLUF().getGroups()
186 valid_roles = self.userFolderGetRoles()
187 dn = None
188 for n, g_dn in grps:
189 if n == name:
190 dn = g_dn
191 break
192 if not dn:
193 return None
194
195 # Current mapping
196 roles = self.getLUF()._mapRoles([name])
197
198 # Nested groups
199 groups = list(self.getLUF().getGroups(dn=dn, attr='cn', ))
200 roles.extend(self.getLUF()._mapRoles(groups))
201
202 # !!! start test
203 Log(LOG_DEBUG, name, "roles", groups, roles)
204 Log(LOG_DEBUG, name, "mapping", getattr(self.getLUF(), '_groups_mappings', {}))
205 # !!! end test
206
207 actual_roles = []
208 for r in roles:
209 if r in valid_roles:
210 actual_roles.append(r)
211 elif "%s%s" % (GROUP_PREFIX, r) in valid_roles:
212 actual_roles.append("%s%s" % (GROUP_PREFIX, r))
213 Log(LOG_DEBUG, name, "actual roles", actual_roles)
214 user = GroupUser(n, '', actual_roles, [])
215 self._cache.set(name, user)
216 return user
217
218 security.declareProtected(manage_users, 'getUserNames')
219 def getUserNames(self):
220 """ """
221 Log(LOG_DEBUG, "getUserNames", )
222 LogCallStack(LOG_DEBUG)
223 # Prevent locally stored groups
224 luf = self.getLUF()
225 if luf._local_groups:
226 return []
227 return [g[0] for g in luf.getGroups()]
228
229 security.declareProtected(manage_users, 'getUsers')
230 def getUsers(self, authenticated=1):
231 """ """
232 # Prevent locally stored groups
233 luf = self.getLUF()
234 if luf._local_groups:
235 return []
236
237 data = []
238
239 grps = self.getLUF().getGroups()
240 valid_roles = self.userFolderGetRoles()
241 for n, dn in grps:
242 # Group mapping
243 roles = self.getLUF()._mapRoles([n])
244
245 # computation
246 actual_roles = []
247 for r in roles:
248 if r in valid_roles:
249 actual_roles.append(r)
250 elif "%s%s" % (GROUP_PREFIX, r) in valid_roles:
251 actual_roles.append("%s%s" % (GROUP_PREFIX, r))
252 user = GroupUser(n, '', actual_roles, [])
253 data.append(user)
254
255 return data
256
257 security.declarePrivate('_doAddUser')
258 def _doAddUser(self, name, password, roles, domains, **kw):
259 """WARNING: If a role with exists with the same name as the group, we do not add
260 the group mapping for it, but we create it as if it were a Zope ROLE.
261 Ie. it's not possible to have a GRUF Group name = a Zope role name, BUT,
262 with this system, it's possible to differenciate between LDAP groups and LDAP roles.
263 """
264 self.getLUF().manage_addGroup(name)
265 self.manage_addGroupMapping(name, "group_" + name, None, )
266 self._doChangeUser(name, password, roles, domains, **kw)
267
268 security.declarePrivate('_doDelUsers')
269 def _doDelUsers(self, names):
270 dns = []
271 luf = self.getLUF()
272 for g_name, dn in luf.getGroups():
273 if g_name in names:
274 dns.append(dn)
275 self._cache.clear()
276 return luf.manage_deleteGroups(dns)
277
278 security.declarePrivate('_doChangeUser')
279 def _doChangeUser(self, name, password, roles, domains, **kw):
280 """
281 This is used to change the groups (especially their roles).
282
283 [ THIS TEXT IS OUTDATED :
284 WARNING: If a ZOPE role with the same name as the GRUF group exists,
285 we do not add the group mapping for it, but we create it as if it were a Zope ROLE.
286 Ie. it's not possible to have a GRUF Group name = a Zope role name, BUT,
287 with this system, it's possible to differenciate between LDAP groups and LDAP roles.
288 ]
289 """
290 luf = self.getLUF()
291 self._cache.remove(name)
292
293 # Get group DN
294 dn = None
295 for g_name, g_dn in luf.getGroups():
296 if g_name == name:
297 dn = g_dn
298 break
299 if not dn:
300 raise ValueError, "Invalid LDAP group: '%s'" % (name, )
301
302 # Edit group mappings
303 ## if name in self.aq_parent.valid_roles():
304 ## # This is, in fact, a role
305 ## self.getLUF().manage_addGroupMapping(name, name)
306 ## else:
307 ## # This is a group -> we set it as a group
308 ## self.getLUF().manage_addGroupMapping(name, self.getGroupPrefix() + name)
309
310 # Change roles
311 if luf._local_groups:
312 luf.manage_editUserRoles(dn, roles)
313 else:
314 # We have to transform roles into group dns: transform them as a dict
315 role_dns = []
316 all_groups = luf.getGroups()
317 all_roles = luf.valid_roles()
318 groups = {}
319 for g in all_groups:
320 groups[g[0]] = g[1]
321
322 # LDAPUF < 2.4Beta3 adds possibly invalid roles to the user roles
323 # (for example, adding the cn of a group additionnaly to the mapped zope role).
324 # So we must remove from our 'roles' list all roles which are prefixed by group prefix
325 # but are not actually groups.
326 # If a group has the same name as a role, we assume that it should be a _role_.
327 # We should check against group/role mapping here, but... well... XXX TODO !
328 # See "HERE IT IS" comment below.
329
330 # Scan roles we are asking for to manage groups correctly
331 for role in roles:
332 if not role in all_roles:
333 continue # Do not allow propagation of invalid roles
334 if role.startswith(GROUP_PREFIX):
335 role = role[GROUP_PREFIX_LEN:] # Remove group prefix : groups are stored WITHOUT prefix in LDAP
336 if role in all_roles:
337 continue # HERE IT IS
338 r = groups.get(role, None)
339 if not r:
340 Log(LOG_WARNING, "LDAP Server doesn't provide a '%s' group (asked for user '%s')." % (role, name, ))
341 continue
342 role_dns.append(r)
343
344 # Perform the change
345 luf.manage_editGroupRoles(dn, role_dns)
346
347
348
349 def manage_addLDAPGroupFolder( self, title = '', luf='', REQUEST=None):
350 """ """
351 this_folder = self.this()
352
353 if hasattr(aq_base(this_folder), 'acl_users') and REQUEST is not None:
354 msg = 'This+object+already+contains+a+User+Folder'
355
356 else:
357 # Try to guess where is LUF
358 if not luf:
359 for src in this_folder.listUserSources():
360 if src.meta_type == "LDAPUserFolder":
361 luf = src.aq_parent.getId()
362
363 # No LUF found : error
364 if not luf:
365 raise KeyError, "You must be within GRUF with a LDAPUserFolder as one of your user sources."
366
367 n = LDAPGroupFolder( title, luf )
368
369 this_folder._setObject('acl_users', n)
370 this_folder.__allow_groups__ = self.acl_users
371
372 msg = 'Added+LDAPGroupFolder'
373
374 # return to the parent object's manage_main
375 if REQUEST:
376 url = REQUEST['URL1']
377 qs = 'manage_tabs_message=%s' % msg
378 REQUEST.RESPONSE.redirect('%s/manage_main?%s' % (url, qs))
379
380
381 InitializeClass(LDAPGroupFolder)
382
383
384 class GroupUser(SimpleUser):
385 """ """
386
387 def __init__(self, name, password, roles, domains):
388 SimpleUser.__init__(self, name, password, roles, domains)
389 self._created = time.time()
390
391 def getCreationTime(self):
392 """ """
393 return DateTime(self._created)