Lines 72-80
def disable_ad_restrictions(disable=True):
|
Link Here
|
---|
|
72 |
_prevent_to_change_ad_properties = disable |
72 |
_prevent_to_change_ad_properties = disable |
73 |
|
73 |
|
74 |
|
74 |
|
75 |
class base(object): |
75 |
class simpleLdap(object): |
76 |
|
76 |
|
77 |
def __init__(self, co, lo, position, dn='', superordinate = None ): |
77 |
def __init__(self, co, lo, position, dn='', superordinate=None, attributes=None): |
|
|
78 |
self._exists = False |
79 |
self.exceptions = [] |
78 |
self.co = co |
80 |
self.co = co |
79 |
self.lo = lo |
81 |
self.lo = lo |
80 |
self.dn = dn |
82 |
self.dn = dn |
Lines 107-114
def __init__(self, co, lo, position, dn='', superordinate = None ):
|
Link Here
|
---|
|
107 |
self.old_options = [] |
109 |
self.old_options = [] |
108 |
self.alloc = [] |
110 |
self.alloc = [] |
109 |
|
111 |
|
|
|
112 |
# s4connector_present is a global caching variable than can be |
113 |
# None ==> ldap has not been checked for servers with service "S4 Connector" |
114 |
# True ==> at least one server with IP address (aRecord) is present |
115 |
# False ==> no server is present |
116 |
global s4connector_present |
117 |
if s4connector_present is None: |
118 |
s4connector_present = False |
119 |
searchResult = self.lo.search('(&(|(objectClass=univentionDomainController)(objectClass=univentionMemberServer))(univentionService=S4 Connector))', attr=['aRecord']) |
120 |
s4connector_present = any(ddn for (ddn, attr) in searchResult if 'aRecord' in attr) |
121 |
self.s4connector_present = s4connector_present |
122 |
|
123 |
if not univention.admin.modules.modules: |
124 |
univention.debug.debug(univention.debug.ADMIN, univention.debug.WARN, 'univention.admin.modules.update() was not called') |
125 |
univention.admin.modules.update() |
126 |
|
127 |
m = univention.admin.modules.get(self.module) |
128 |
if not hasattr(self, 'mapping'): |
129 |
self.mapping = getattr(m, 'mapping', None) |
130 |
if not hasattr(self, 'descriptions'): |
131 |
self.descriptions = getattr(m, 'property_descriptions', None) |
132 |
|
133 |
self.info = {} |
134 |
self.oldattr = {} |
135 |
if attributes: |
136 |
self.oldattr = attributes |
137 |
elif self.dn: |
138 |
try: |
139 |
self.oldattr = self.lo.get(self.dn, required=True) |
140 |
except ldap.NO_SUCH_OBJECT: |
141 |
pass |
142 |
# raise univention.admin.uexceptions.noObject(self.dn) |
143 |
|
144 |
if self.oldattr: |
145 |
self._exists = True |
146 |
oldinfo = univention.admin.mapping.mapDict(self.mapping, self.oldattr) |
147 |
oldinfo = self._post_unmap(oldinfo, self.oldattr) |
148 |
self.info.update(oldinfo) |
149 |
|
150 |
self.policies = self.oldattr.get('univentionPolicyReference', []) |
151 |
self.__set_options() |
152 |
self.save() |
153 |
|
154 |
def exists( self ): |
155 |
return self._exists |
156 |
|
157 |
def call_udm_property_hook(self, hookname, module, changes = None): |
158 |
m = univention.admin.modules.get( module.module ) |
159 |
if hasattr(m, 'extended_udm_attributes'): |
160 |
for prop in m.extended_udm_attributes: |
161 |
if prop.hook != None: |
162 |
func = getattr(prop.hook, hookname, None) |
163 |
if changes == None: |
164 |
func(module) |
165 |
else: |
166 |
changes = func(module, changes) |
167 |
return changes |
168 |
|
110 |
def open(self): |
169 |
def open(self): |
111 |
self._open = True |
170 |
self._open = True |
|
|
171 |
self.exceptions=[] |
172 |
self.call_udm_property_hook('hook_open', self) |
173 |
self.save() |
112 |
|
174 |
|
113 |
def save(self): |
175 |
def save(self): |
114 |
'''saves current state as old state''' |
176 |
'''saves current state as old state''' |
|
137 |
|
199 |
|
138 |
return changes |
200 |
return changes |
139 |
|
201 |
|
|
|
202 |
def _post_unmap( self, info, values ): |
203 |
"""This method can be overwritten to define special un-map |
204 |
methods that can not be done with the default mapping API""" |
205 |
return info |
206 |
|
207 |
def _post_map( self, modlist, diff ): |
208 |
"""This method can be overwritten to define special map methods |
209 |
that can not be done with the default mapping API""" |
210 |
return modlist |
211 |
|
140 |
def hasChanged(self, key): |
212 |
def hasChanged(self, key): |
141 |
'''checks if the given attribute(s) was (were) changed; key can either be a |
213 |
'''checks if the given attribute(s) was (were) changed; key can either be a |
142 |
string (scalar) or a list''' |
214 |
string (scalar) or a list''' |
|
294 |
|
366 |
|
295 |
return self._create() |
367 |
return self._create() |
296 |
|
368 |
|
|
|
369 |
def _create(self): |
370 |
self.exceptions = [] |
371 |
self._ldap_pre_create() |
372 |
self._update_policies() |
373 |
self.call_udm_property_hook('hook_ldap_pre_create', self) |
374 |
|
375 |
# Make sure all default values are set ... |
376 |
for name, p in self.descriptions.items(): |
377 |
# ... if property has no option or any required option is currently enabled |
378 |
if self.has_key(name) and self.descriptions[name].default(self): |
379 |
self[name] # __getitem__ sets default value |
380 |
|
381 |
# iterate over all properties and call checkLdap() of corresponding syntax |
382 |
self._call_checkLdap_on_all_property_syntaxes() |
383 |
|
384 |
al = self._ldap_addlist() |
385 |
al.extend(self._ldap_modlist()) |
386 |
m = univention.admin.modules.get(self.module) |
387 |
|
388 |
# evaluate extended attributes |
389 |
ocs = set() |
390 |
for prop in getattr(m, 'extended_udm_attributes', []): |
391 |
univention.debug.debug(univention.debug.ADMIN, univention.debug.INFO, 'simpleLdap._create: info[%s]:%r = %r'% (prop.name, self.has_key(prop.name), self.info.get(prop.name))) |
392 |
if prop.syntax == 'boolean' and self.info.get(prop.name) == '0': |
393 |
continue |
394 |
if self.has_key(prop.name) and self.info.get(prop.name): |
395 |
ocs.add(prop.objClass) |
396 |
|
397 |
# add object classes of (especially extended) options |
398 |
for option in self.options: |
399 |
try: |
400 |
opt = m.options[option] |
401 |
except KeyError: |
402 |
univention.debug.debug(univention.debug.ADMIN, univention.debug.ERROR, '%r does not specify option %r' % (m.module, option)) |
403 |
continue |
404 |
ocs |= set(opt.objectClasses) |
405 |
|
406 |
# remove duplicated object classes |
407 |
for i in al: |
408 |
key, val = i[0], i[-1] # might be a triple |
409 |
if val and key.lower() == 'objectclass': |
410 |
ocs -= set([val] if isinstance(val, basestring) else val) |
411 |
if ocs: |
412 |
al.append(('objectClass', list(ocs))) |
413 |
|
414 |
al = self.call_udm_property_hook('hook_ldap_addlist', self, al) |
415 |
|
416 |
# ensure univentionObject is set |
417 |
al.append(('objectClass', ['univentionObject',])) |
418 |
al.append(('univentionObjectType', [self.module,])) |
419 |
|
420 |
univention.debug.debug(univention.debug.ADMIN, univention.debug.INFO, "create object with dn: %s" % (self.dn,)) |
421 |
univention.debug.debug(univention.debug.ADMIN, 99, 'Create dn=%r;\naddlist=%r;' % (self.dn, al)) |
422 |
self.lo.add(self.dn, al) |
423 |
self._exists = True |
424 |
|
425 |
# if anything goes wrong we need to remove the already created object, otherwise we run into 'already exists' errors |
426 |
try: |
427 |
self._ldap_post_create() |
428 |
except: |
429 |
# ensure that there is no lock left |
430 |
import traceback, sys |
431 |
exc = sys.exc_info() |
432 |
univention.debug.debug(univention.debug.ADMIN, univention.debug.ERROR, "Post-Create operation failed: %s" % (traceback.format_exc(),)) |
433 |
try: |
434 |
self.cancel() |
435 |
except: |
436 |
univention.debug.debug(univention.debug.ADMIN, univention.debug.ERROR, "Post-create: cancel() failed: %s" % (traceback.format_exc(),)) |
437 |
try: |
438 |
self.remove() |
439 |
except: |
440 |
univention.debug.debug(univention.debug.ADMIN, univention.debug.ERROR, "Post-create: remove() failed: %s" % (traceback.format_exc(),)) |
441 |
raise exc[0], exc[1], exc[2] |
442 |
|
443 |
self.call_udm_property_hook('hook_ldap_post_create', self) |
444 |
|
445 |
self.save() |
446 |
return self.dn |
447 |
|
448 |
def _ldap_pre_ready(self): |
449 |
pass |
450 |
|
451 |
def _ldap_pre_create(self): |
452 |
self.dn = self._ldap_dn() |
453 |
|
454 |
def _ldap_dn(self): |
455 |
identifier = [] |
456 |
for name, prop in self.descriptions.items(): |
457 |
if prop.identifies: |
458 |
identifier.append((self.mapping.mapName(name), self.mapping.mapValue(name, self.info[name]), 2)) |
459 |
return '%s,%s' % (ldap.dn.dn2str([identifier]), self.position.getDn()) |
460 |
|
461 |
def _ldap_addlist(self): |
462 |
return [] |
463 |
|
464 |
def _ldap_post_create(self): |
465 |
pass |
466 |
|
297 |
def modify(self, modify_childs=1,ignore_license=0): |
467 |
def modify(self, modify_childs=1,ignore_license=0): |
298 |
'''modify object''' |
468 |
'''modify object''' |
299 |
|
469 |
|
Lines 306-864
def modify(self, modify_childs=1,ignore_license=0):
|
Link Here
|
---|
|
306 |
|
476 |
|
307 |
return self._modify(modify_childs,ignore_license=ignore_license) |
477 |
return self._modify(modify_childs,ignore_license=ignore_license) |
308 |
|
478 |
|
309 |
def _create_temporary_ou(self): |
479 |
def _modify(self, modify_childs=1, ignore_license=0): |
310 |
name = 'temporary_move_container_%s' % time.time() |
480 |
self.exceptions = [] |
311 |
|
481 |
|
312 |
module = univention.admin.modules.get('container/ou') |
482 |
self.__prevent_ad_property_change() |
313 |
position = univention.admin.uldap.position('%s' % self.lo.base) |
|
|
314 |
|
483 |
|
315 |
temporary_object = module.object(None, self.lo, position) |
484 |
self._ldap_pre_modify() |
316 |
temporary_object.open() |
485 |
self._update_policies() |
317 |
temporary_object['name'] = name |
486 |
self.call_udm_property_hook('hook_ldap_pre_modify', self) |
318 |
temporary_object.create() |
|
|
319 |
|
487 |
|
320 |
return 'ou=%s' % ldap.dn.escape_dn_chars(name) |
488 |
# Make sure all default values are set... |
|
|
489 |
for name, p in self.descriptions.items(): |
490 |
# ... if property has no option or any required option is currently enabled |
491 |
if self.has_key(name) and self.descriptions[name].default(self): |
492 |
self[name] # __getitem__ sets default value |
321 |
|
493 |
|
322 |
def _delete_temporary_ou_if_empty(self, temporary_ou): |
494 |
# iterate over all properties and call checkLdap() of corresponding syntax |
|
|
495 |
self._call_checkLdap_on_all_property_syntaxes() |
323 |
|
496 |
|
324 |
if not temporary_ou: |
497 |
ml = self._ldap_modlist() |
325 |
return |
498 |
ml = self.call_udm_property_hook('hook_ldap_modlist', self, ml) |
|
|
499 |
ml = self._ldap_object_classes(ml) |
326 |
|
500 |
|
327 |
dn = '%s,%s' %(temporary_ou,self.lo.base) |
501 |
#FIXME: timeout without exception if objectClass of Object is not exsistant !! |
|
|
502 |
univention.debug.debug(univention.debug.ADMIN, 99, 'Modify dn=%r;\nmodlist=%r;\noldattr=%r;' % (self.dn, ml, self.oldattr)) |
503 |
self.lo.modify(self.dn, ml, ignore_license=ignore_license) |
328 |
|
504 |
|
329 |
module = univention.admin.modules.get('container/ou') |
505 |
self._ldap_post_modify() |
330 |
temporary_object = univention.admin.modules.lookup(module, None, self.lo, scope='base', base=dn, required=1, unique=1)[0] |
506 |
self.call_udm_property_hook('hook_ldap_post_modify', self) |
331 |
temporary_object.open() |
|
|
332 |
try: |
333 |
temporary_object.remove() |
334 |
except (univention.admin.uexceptions.ldapError, ldap.NOT_ALLOWED_ON_NONLEAF): |
335 |
pass |
336 |
|
507 |
|
337 |
def move(self, newdn, ignore_license=0, temporary_ou=None): |
508 |
self.save() |
338 |
'''move object''' |
509 |
return self.dn |
339 |
univention.debug.debug(univention.debug.ADMIN, univention.debug.INFO, 'move: called for %s to %s'% (self.dn,newdn)) |
|
|
340 |
|
510 |
|
341 |
if not (univention.admin.modules.supports(self.module,'move') |
511 |
def _ldap_pre_modify(self): |
342 |
or univention.admin.modules.supports(self.module,'subtree_move')): # this should have been checked before, but I want to be sure... |
512 |
pass |
343 |
raise univention.admin.uexceptions.invalidOperation |
|
|
344 |
|
513 |
|
345 |
if not self.exists(): |
514 |
def _ldap_post_modify(self): |
346 |
raise univention.admin.uexceptions.noObject |
515 |
pass |
347 |
|
516 |
|
348 |
if _prevent_to_change_ad_properties and self._is_synced_object(): |
517 |
def _ldap_modlist(self): |
349 |
raise univention.admin.uexceptions.invalidOperation(_('Objects from Active Directory can not be moved.')) |
518 |
self.exceptions = [] |
350 |
|
519 |
|
351 |
goaldn = ','.join(ldap.explode_dn(newdn)[1:]) |
520 |
diff_ml = self.diff() |
352 |
goalmodule = univention.admin.modules.identifyOne(goaldn, self.lo.get(goaldn)) |
521 |
ml = univention.admin.mapping.mapDiff(self.mapping, diff_ml) |
353 |
goalmodule = univention.admin.modules.get(goalmodule) |
522 |
ml = self._post_map(ml, diff_ml) |
354 |
if not goalmodule or not hasattr(goalmodule,'childs') or not goalmodule.childs == 1: |
|
|
355 |
raise univention.admin.uexceptions.invalidOperation, _("Destination object can't have sub objects.") |
356 |
|
523 |
|
357 |
if self.dn.lower() == newdn.lower(): |
524 |
# policies |
358 |
if self.dn == newdn: |
525 |
if self.policies != self.oldpolicies: |
359 |
raise univention.admin.uexceptions.ldapError, _('Moving not possible: old and new DN are identical.') |
526 |
if 'univentionPolicyReference' not in self.oldattr.get('objectClass', []): |
360 |
else: |
527 |
ml.append(('objectClass', '', ['univentionPolicyReference'])) |
361 |
# We must use a temporary folder because OpenLDAP does not allow a rename of an container with subobjects |
528 |
ml.append(('univentionPolicyReference', self.oldpolicies, self.policies)) |
362 |
temporary_ou = self._create_temporary_ou() |
|
|
363 |
new_rdn = ldap.explode_rdn(newdn)[0] |
364 |
new_dn = '%s,%s,%s' %(new_rdn,temporary_ou,self.lo.base) |
365 |
self.move(new_dn, ignore_license, temporary_ou) |
366 |
self.dn = new_dn |
367 |
|
529 |
|
368 |
if self.dn.lower() == newdn.lower()[-len(self.dn):]: |
530 |
return ml |
369 |
raise univention.admin.uexceptions.ldapError, _("Moving into one's own sub container not allowed.") |
|
|
370 |
|
371 |
if univention.admin.modules.supports(self.module,'subtree_move'): |
372 |
# check if is subtree: |
373 |
subelements = self.lo.search(base=self.dn, scope='one', attr=[]) |
374 |
if subelements: |
375 |
olddn = self.dn |
376 |
univention.debug.debug(univention.debug.ADMIN, univention.debug.INFO, 'move: found subelements, do subtree move: newdn: %s' % newdn) |
377 |
# create copy of myself |
378 |
module = univention.admin.modules.get(self.module) |
379 |
position = univention.admin.uldap.position(self.lo.base) |
380 |
position.setDn(','.join(ldap.explode_dn(newdn)[1:])) |
381 |
copyobject = module.object(None, self.lo, position) |
382 |
copyobject.open() |
383 |
for key in self.keys(): |
384 |
copyobject[key]=self[key] |
385 |
copyobject.policies=self.policies |
386 |
copyobject.create() |
387 |
moved=[] |
388 |
try: |
389 |
for subolddn, suboldattrs in subelements: |
390 |
univention.debug.debug(univention.debug.ADMIN, univention.debug.INFO, 'move: subelement %s' % subolddn) |
391 |
# Convert the DNs to lowercase before the replacement. The cases might be mixed up if the python lib is |
392 |
# used by the connector, for example: |
393 |
# subolddn: uid=user_test_h80,ou=TEST_H81,LDAP_BASE |
394 |
# self.dn: ou=test_h81,LDAP_BASE |
395 |
# newdn: OU=TEST_H81,ou=test_h82,$LDAP_BASE |
396 |
rdn = ldap.explode_dn(subolddn)[0] |
397 |
subolddn_dn_without_rdn_lower = ','.join(ldap.explode_dn(subolddn)[1:]).lower() |
398 |
subnewdn = '%s,%s' % (rdn, subolddn_dn_without_rdn_lower.replace(self.dn.lower(),newdn)) |
399 |
submodule = univention.admin.modules.identifyOne(subolddn, suboldattrs) |
400 |
submodule = univention.admin.modules.get(submodule) |
401 |
subobject = univention.admin.objects.get(submodule, None, self.lo, position='', dn=subolddn) |
402 |
if not subobject or not (univention.admin.modules.supports(submodule,'move') or |
403 |
univention.admin.modules.supports(submodule,'subtree_move')): |
404 |
raise univention.admin.uexceptions.invalidOperation(_('Unable to move object %(name)s (%(type)s) in subtree, trying to revert changes.' ) % {'name': subolddn[:subolddn.find(',')], 'type': univention.admin.modules.identifyOne(subolddn, suboldattrs)}) |
405 |
subobject.open() |
406 |
subobject.move(subnewdn) |
407 |
moved.append((subolddn,subnewdn)) |
408 |
self.remove() |
409 |
self._delete_temporary_ou_if_empty(temporary_ou) |
410 |
except: |
411 |
univention.debug.debug(univention.debug.ADMIN, univention.debug.ERROR, 'move: subtree move failed, trying to move back.') |
412 |
position=univention.admin.uldap.position(self.lo.base) |
413 |
position.setDn(','.join(ldap.explode_dn(olddn)[1:])) |
414 |
for subolddn, subnewdn in moved: |
415 |
submodule = univention.admin.modules.identifyOne(subnewdn, self.lo.get(subnewdn)) |
416 |
submodule = univention.admin.modules.get(submodule) |
417 |
subobject = univention.admin.objects.get(submodule, None, self.lo, position='', dn=subnewdn) |
418 |
subobject.open() |
419 |
subobject.move(subolddn) |
420 |
copyobject.remove() |
421 |
self._delete_temporary_ou_if_empty(temporary_ou) |
422 |
raise |
423 |
else: |
424 |
# normal move, fails on subtrees |
425 |
res = self._move(newdn, ignore_license=ignore_license) |
426 |
self._delete_temporary_ou_if_empty(temporary_ou) |
427 |
return res |
428 |
|
429 |
else: |
430 |
res = self._move(newdn, ignore_license=ignore_license) |
431 |
self._delete_temporary_ou_if_empty(temporary_ou) |
432 |
return res |
433 |
|
434 |
def move_subelements(self, olddn, newdn, subelements, ignore_license = False): |
435 |
if subelements: |
436 |
univention.debug.debug(univention.debug.ADMIN, univention.debug.INFO, 'move: found subelements, do subtree move') |
437 |
moved = [] |
438 |
try: |
439 |
for subolddn, suboldattrs in subelements: |
440 |
univention.debug.debug(univention.debug.ADMIN, univention.debug.INFO, 'move: subelement %s' % subolddn) |
441 |
subnewdn = subolddn.replace(olddn, newdn) |
442 |
submodule = univention.admin.modules.identifyOne(subolddn, suboldattrs) |
443 |
submodule = univention.admin.modules.get(submodule) |
444 |
subobject = univention.admin.objects.get(submodule, None, self.lo, position='', dn=subolddn) |
445 |
if not subobject or not (univention.admin.modules.supports(submodule, 'move') or |
446 |
univention.admin.modules.supports(submodule, 'subtree_move')): |
447 |
raise univention.admin.uexceptions.invalidOperation(_('Unable to move object %(name)s (%(type)s) in subtree, trying to revert changes.') % {'name': subolddn[:subolddn.find(',')], 'type': univention.admin.modules.identifyOne(subolddn, suboldattrs)}) |
448 |
subobject.open() |
449 |
subobject._move(subnewdn) |
450 |
moved.append((subolddn,subnewdn)) |
451 |
return moved |
452 |
except: |
453 |
univention.debug.debug(univention.debug.ADMIN, univention.debug.ERROR, 'move: subtree move failed, try to move back') |
454 |
for subolddn, subnewdn in moved: |
455 |
submodule = univention.admin.modules.identifyOne(subnewdn, self.lo.get(subnewdn)) |
456 |
submodule = univention.admin.modules.get(submodule) |
457 |
subobject = univention.admin.objects.get(submodule, None, self.lo, position='', dn=subnewdn) |
458 |
subobject.open() |
459 |
subobject.move(subolddn) |
460 |
raise |
461 |
|
462 |
def remove(self, remove_childs=0): |
463 |
'''remove object''' |
464 |
|
465 |
if not self.dn or not self.lo.get(self.dn): |
466 |
raise univention.admin.uexceptions.noObject(self.dn) |
467 |
|
468 |
return self._remove(remove_childs) |
469 |
|
470 |
def get_gid_for_primary_group(self): |
471 |
gidNum = '99999' |
472 |
if self['primaryGroup']: |
473 |
try: |
474 |
gidNum = self.lo.getAttr(self['primaryGroup'], 'gidNumber', required=True)[0] |
475 |
except ldap.NO_SUCH_OBJECT: |
476 |
raise univention.admin.uexceptions.primaryGroup(self['primaryGroup']) |
477 |
return gidNum |
478 |
|
479 |
def get_sid_for_primary_group(self): |
480 |
try: |
481 |
sidNum = self.lo.getAttr(self['primaryGroup'], 'sambaSID', required=True)[0] |
482 |
except ldap.NO_SUCH_OBJECT: |
483 |
raise univention.admin.uexceptions.primaryGroupWithoutSamba(self['primaryGroup']) |
484 |
return sidNum |
485 |
|
486 |
def _update_policies(self): |
487 |
pass |
488 |
|
489 |
def _ldap_pre_ready(self): |
490 |
pass |
491 |
|
492 |
def _ldap_pre_create(self): |
493 |
self.dn = self._ldap_dn() |
494 |
|
495 |
def _ldap_dn(self): |
496 |
identifier = [] |
497 |
for name, prop in self.descriptions.items(): |
498 |
if prop.identifies: |
499 |
identifier.append((self.mapping.mapName(name), self.mapping.mapValue(name, self.info[name]), 2)) |
500 |
return '%s,%s' % (ldap.dn.dn2str([identifier]), self.position.getDn()) |
501 |
|
502 |
def _ldap_post_create(self): |
503 |
pass |
504 |
|
505 |
def _ldap_pre_modify(self): |
506 |
pass |
507 |
|
508 |
def _ldap_post_modify(self): |
509 |
pass |
510 |
|
511 |
def _ldap_pre_move(self, newdn): |
512 |
pass |
513 |
|
514 |
def _ldap_post_move(self, olddn): |
515 |
pass |
516 |
|
517 |
def _ldap_pre_remove(self): |
518 |
pass |
519 |
|
520 |
def _ldap_post_remove(self): |
521 |
pass |
522 |
|
523 |
def _not_implemented_method(attr): |
524 |
def _not_implemented_error(self, *args, **kwargs): |
525 |
raise NotImplementedError('%s() not implemented by %s.%s().' % (attr, self.__module__, self.__class__.__name__)) |
526 |
return _not_implemented_error |
527 |
|
528 |
|
529 |
# add some default abstract methods |
530 |
for _attr in ('_ldap_addlist', '_ldap_modlist', '_ldap_dellist', 'exists', '_move', 'cancel', '_remove', '_create', '_modify'): |
531 |
if not hasattr(base, _attr): |
532 |
setattr(base, _attr, _not_implemented_method(_attr)) |
533 |
|
534 |
|
535 |
class simpleLdap(base): |
536 |
|
537 |
def __init__(self, co, lo, position, dn='', superordinate=None, attributes=None): |
538 |
self._exists = False |
539 |
self.exceptions = [] |
540 |
base.__init__(self, co, lo, position, dn, superordinate) |
541 |
|
542 |
# s4connector_present is a global caching variable than can be |
543 |
# None ==> ldap has not been checked for servers with service "S4 Connector" |
544 |
# True ==> at least one server with IP address (aRecord) is present |
545 |
# False ==> no server is present |
546 |
global s4connector_present |
547 |
if s4connector_present is None: |
548 |
s4connector_present = False |
549 |
searchResult = self.lo.search('(&(|(objectClass=univentionDomainController)(objectClass=univentionMemberServer))(univentionService=S4 Connector))', attr=['aRecord']) |
550 |
s4connector_present = any(ddn for (ddn, attr) in searchResult if 'aRecord' in attr) |
551 |
self.s4connector_present = s4connector_present |
552 |
|
553 |
if not univention.admin.modules.modules: |
554 |
univention.debug.debug(univention.debug.ADMIN, univention.debug.WARN, 'univention.admin.modules.update() was not called') |
555 |
univention.admin.modules.update() |
556 |
|
531 |
|
|
|
532 |
def _ldap_object_classes(self, ml): |
557 |
m = univention.admin.modules.get(self.module) |
533 |
m = univention.admin.modules.get(self.module) |
558 |
if not hasattr(self, 'mapping'): |
534 |
lowerset = lambda vals: set(x.lower() for x in vals) |
559 |
self.mapping = getattr(m, 'mapping', None) |
|
|
560 |
if not hasattr(self, 'descriptions'): |
561 |
self.descriptions = getattr(m, 'property_descriptions', None) |
562 |
|
563 |
self.info = {} |
564 |
self.oldattr = {} |
565 |
if attributes: |
566 |
self.oldattr = attributes |
567 |
elif self.dn: |
568 |
try: |
569 |
self.oldattr = self.lo.get(self.dn, required=True) |
570 |
except ldap.NO_SUCH_OBJECT: |
571 |
pass |
572 |
# raise univention.admin.uexceptions.noObject(self.dn) |
573 |
|
574 |
if self.oldattr: |
575 |
self._exists = True |
576 |
oldinfo = univention.admin.mapping.mapDict(self.mapping, self.oldattr) |
577 |
oldinfo = self._post_unmap(oldinfo, self.oldattr) |
578 |
self.info.update(oldinfo) |
579 |
|
580 |
self.policies = self.oldattr.get('univentionPolicyReference', []) |
581 |
self.__set_options() |
582 |
self.save() |
583 |
|
584 |
def exists( self ): |
585 |
return self._exists |
586 |
|
587 |
def call_udm_property_hook(self, hookname, module, changes = None): |
588 |
m = univention.admin.modules.get( module.module ) |
589 |
if hasattr(m, 'extended_udm_attributes'): |
590 |
for prop in m.extended_udm_attributes: |
591 |
if prop.hook != None: |
592 |
func = getattr(prop.hook, hookname, None) |
593 |
if changes == None: |
594 |
func(module) |
595 |
else: |
596 |
changes = func(module, changes) |
597 |
return changes |
598 |
|
599 |
def open(self): |
600 |
base.open(self) |
601 |
self.exceptions=[] |
602 |
self.call_udm_property_hook('hook_open', self) |
603 |
self.save() |
604 |
|
605 |
def _remove_option( self, name ): |
606 |
if name in self.options: |
607 |
self.options.remove( name ) |
608 |
|
609 |
def __set_options(self): |
610 |
self.options = [] |
611 |
options = univention.admin.modules.options(self.module) |
612 |
if 'objectClass' in self.oldattr: |
613 |
ocs = set(self.oldattr['objectClass']) |
614 |
for opt, option in options.iteritems(): |
615 |
if not option.disabled and option.matches(ocs): |
616 |
self.options.append(opt) |
617 |
else: |
618 |
univention.debug.debug(univention.debug.ADMIN, univention.debug.INFO, 'reset options to default by _define_options') |
619 |
self._define_options(options) |
620 |
|
621 |
def _define_options( self, module_options ): |
622 |
# enable all default options |
623 |
univention.debug.debug(univention.debug.ADMIN, univention.debug.INFO, 'modules/__init__.py _define_options: reset to default options') |
624 |
for name, opt in module_options.items(): |
625 |
if not opt.disabled and opt.default: |
626 |
self.options.append( name ) |
627 |
|
628 |
def option_toggled(self, option): |
629 |
'''Checks if an option was changed. This does not work for not yet existing objects.''' |
630 |
return option in set(self.options) ^ set(self.old_options) |
631 |
|
632 |
def description(self): |
633 |
if self.dn: |
634 |
rdn = self.lo.explodeDn(self.dn)[0] |
635 |
return rdn[rdn.find('=')+1:] |
636 |
else: |
637 |
return 'none' |
638 |
|
639 |
def _post_unmap( self, info, values ): |
640 |
"""This method can be overwritten to define special un-map |
641 |
methods that can not be done with the default mapping API""" |
642 |
return info |
643 |
|
644 |
def _post_map( self, modlist, diff ): |
645 |
"""This method can be overwritten to define special map methods |
646 |
that can not be done with the default mapping API""" |
647 |
return modlist |
648 |
|
649 |
def _ldap_modlist(self): |
650 |
self.exceptions = [] |
651 |
|
652 |
diff_ml = self.diff() |
653 |
ml = univention.admin.mapping.mapDiff(self.mapping, diff_ml) |
654 |
ml = self._post_map(ml, diff_ml) |
655 |
|
656 |
# policies |
657 |
if self.policies != self.oldpolicies: |
658 |
if 'univentionPolicyReference' not in self.oldattr.get('objectClass', []): |
659 |
ml.append(('objectClass', '', ['univentionPolicyReference'])) |
660 |
ml.append(('univentionPolicyReference', self.oldpolicies, self.policies)) |
661 |
|
662 |
return ml |
663 |
|
664 |
def _create(self): |
665 |
self.exceptions = [] |
666 |
self._ldap_pre_create() |
667 |
self._update_policies() |
668 |
self.call_udm_property_hook('hook_ldap_pre_create', self) |
669 |
|
670 |
# Make sure all default values are set ... |
671 |
for name, p in self.descriptions.items(): |
672 |
# ... if property has no option or any required option is currently enabled |
673 |
if self.has_key(name) and self.descriptions[name].default(self): |
674 |
self[name] # __getitem__ sets default value |
675 |
|
535 |
|
676 |
# iterate over all properties and call checkLdap() of corresponding syntax |
536 |
ocs = lowerset(_MergedAttributes(self, ml).get_attribute('objectClass')) |
677 |
self._call_checkLdap_on_all_property_syntaxes() |
537 |
unneeded_ocs = set() |
|
|
538 |
required_ocs = set() |
678 |
|
539 |
|
679 |
al = self._ldap_addlist() |
540 |
# evaluate (extended) options |
680 |
al.extend(self._ldap_modlist()) |
541 |
module_options = univention.admin.modules.options(self.module) |
681 |
m = univention.admin.modules.get(self.module) |
542 |
available_options = set(module_options.keys()) |
|
|
543 |
options = set(self.options) |
544 |
old_options = set(self.old_options) |
545 |
if options != old_options: |
546 |
univention.debug.debug(univention.debug.ADMIN, univention.debug.INFO, 'options=%r; old_options=%r' % (options, old_options)) |
547 |
unavailable_options = (options - available_options) | (old_options - available_options) |
548 |
if unavailable_options: |
549 |
univention.debug.debug(univention.debug.ADMIN, univention.debug.ERROR, '%r does not provide options: %r' % (self.module, unavailable_options)) |
550 |
added_options = options - old_options - unavailable_options |
551 |
removed_options = old_options - options - unavailable_options |
682 |
|
552 |
|
683 |
# evaluate extended attributes |
553 |
# evaluate extended attributes |
684 |
ocs = set() |
|
|
685 |
for prop in getattr(m, 'extended_udm_attributes', []): |
554 |
for prop in getattr(m, 'extended_udm_attributes', []): |
686 |
univention.debug.debug(univention.debug.ADMIN, univention.debug.INFO, 'simpleLdap._create: info[%s]:%r = %r'% (prop.name, self.has_key(prop.name), self.info.get(prop.name))) |
555 |
univention.debug.debug(univention.debug.ADMIN, univention.debug.INFO, 'simpleLdap._modify: extended attribute=%r oc=%r'% (prop.name, prop.objClass)) |
687 |
if prop.syntax == 'boolean' and self.info.get(prop.name) == '0': |
|
|
688 |
continue |
689 |
if self.has_key(prop.name) and self.info.get(prop.name): |
690 |
ocs.add(prop.objClass) |
691 |
|
556 |
|
692 |
# add object classes of (especially extended) options |
557 |
if self.has_key(prop.name) and self.info.get(prop.name) and (True if prop.syntax != 'boolean' else self.info.get(prop.name) != '0'): |
693 |
for option in self.options: |
558 |
required_ocs |= set([prop.objClass]) |
694 |
try: |
|
|
695 |
opt = m.options[option] |
696 |
except KeyError: |
697 |
univention.debug.debug(univention.debug.ADMIN, univention.debug.ERROR, '%r does not specify option %r' % (m.module, option)) |
698 |
continue |
559 |
continue |
699 |
ocs |= set(opt.objectClasses) |
|
|
700 |
|
560 |
|
701 |
# remove duplicated object classes |
561 |
if prop.deleteObjClass: |
702 |
for i in al: |
562 |
unneeded_ocs |= set([prop.objClass]) |
703 |
key, val = i[0], i[-1] # might be a triple |
|
|
704 |
if val and key.lower() == 'objectclass': |
705 |
ocs -= set([val] if isinstance(val, basestring) else val) |
706 |
if ocs: |
707 |
al.append(('objectClass', list(ocs))) |
708 |
|
563 |
|
709 |
al = self.call_udm_property_hook('hook_ldap_addlist', self, al) |
564 |
# if the value is unset we need to remove the attribute completely |
|
|
565 |
if self.oldattr.get(prop.ldapMapping): |
566 |
ml = [x for x in ml if x[0].lower() != prop.ldapMapping.lower()] |
567 |
ml.append((prop.ldapMapping, self.oldattr.get(prop.ldapMapping), '')) |
710 |
|
568 |
|
711 |
# ensure univentionObject is set |
569 |
unneeded_ocs |= reduce(set.union, (set(module_options[option].objectClasses) for option in removed_options), set()) |
712 |
al.append(('objectClass', ['univentionObject',])) |
570 |
required_ocs |= reduce(set.union, (set(module_options[option].objectClasses) for option in added_options), set()) |
713 |
al.append(('univentionObjectType', [self.module,])) |
|
|
714 |
|
571 |
|
715 |
univention.debug.debug(univention.debug.ADMIN, univention.debug.INFO, "create object with dn: %s" % (self.dn,)) |
572 |
ocs -= lowerset(unneeded_ocs) |
716 |
univention.debug.debug(univention.debug.ADMIN, 99, 'Create dn=%r;\naddlist=%r;' % (self.dn, al)) |
573 |
ocs |= lowerset(required_ocs) |
717 |
self.lo.add(self.dn, al) |
574 |
if lowerset(self.oldattr.get('objectClass', [])) == ocs: |
718 |
self._exists = True |
575 |
return ml |
719 |
|
576 |
|
720 |
# if anything goes wrong we need to remove the already created object, otherwise we run into 'already exists' errors |
577 |
univention.debug.debug(univention.debug.ADMIN, univention.debug.INFO, 'OCS=%r; required=%r; removed: %r' % (ocs, required_ocs, unneeded_ocs)) |
721 |
try: |
|
|
722 |
self._ldap_post_create() |
723 |
except: |
724 |
# ensure that there is no lock left |
725 |
import traceback, sys |
726 |
exc = sys.exc_info() |
727 |
univention.debug.debug(univention.debug.ADMIN, univention.debug.ERROR, "Post-Create operation failed: %s" % (traceback.format_exc(),)) |
728 |
try: |
729 |
self.cancel() |
730 |
except: |
731 |
univention.debug.debug(univention.debug.ADMIN, univention.debug.ERROR, "Post-create: cancel() failed: %s" % (traceback.format_exc(),)) |
732 |
try: |
733 |
self.remove() |
734 |
except: |
735 |
univention.debug.debug(univention.debug.ADMIN, univention.debug.ERROR, "Post-create: remove() failed: %s" % (traceback.format_exc(),)) |
736 |
raise exc[0], exc[1], exc[2] |
737 |
|
578 |
|
738 |
self.call_udm_property_hook('hook_ldap_post_create', self) |
579 |
# case normalize object class names |
|
|
580 |
schema = self.lo.get_schema() |
581 |
ocs = set(schema.get_obj(ldap.schema.models.ObjectClass, x).names[0] for x in ocs) |
739 |
|
582 |
|
740 |
self.save() |
583 |
# make sure we still have a structural object class |
741 |
return self.dn |
584 |
if not schema.get_structural_oc(ocs): |
|
|
585 |
structural_ocs = schema.get_structural_oc(unneeded_ocs) |
586 |
if not structural_ocs: |
587 |
univention.debug.debug(univention.debug.ADMIN, univention.debug.ERROR, 'missing structural object class. Modify will fail.') |
588 |
return ml |
589 |
univention.debug.debug(univention.debug.ADMIN, univention.debug.WARN, 'Preventing to remove last structural object class %r' % (structural_ocs,)) |
590 |
ocs -= set(schema.get_obj(ldap.schema.models.ObjectClass, structural_ocs).names) |
742 |
|
591 |
|
743 |
def _modify(self, modify_childs=1, ignore_license=0): |
592 |
# validate removal of object classes |
744 |
self.exceptions = [] |
593 |
must, may = schema.attribute_types(ocs) |
|
|
594 |
allowed = set(name.lower() for attr in may.values() for name in attr.names) | set(name.lower() for attr in must.values() for name in attr.names) |
745 |
|
595 |
|
746 |
self.__prevent_ad_property_change() |
596 |
ml = [x for x in ml if x[0].lower() != 'objectclass'] |
|
|
597 |
ml.append(('objectClass', self.oldattr.get('objectClass', []), list(ocs))) |
598 |
newattr = ldap.cidict.cidict(_MergedAttributes(self, ml).get_attributes()) |
747 |
|
599 |
|
748 |
self._ldap_pre_modify() |
600 |
# make sure only attributes known by the object classes are set |
749 |
self._update_policies() |
601 |
for attr, val in newattr.items(): |
750 |
self.call_udm_property_hook('hook_ldap_pre_modify', self) |
602 |
if not val: |
|
|
603 |
continue |
604 |
if re.sub(';binary$', '', attr.lower()) not in allowed: |
605 |
univention.debug.debug(univention.debug.ADMIN, univention.debug.WARN, 'The attribute %r is not allowed by any object class.' % (attr,)) |
606 |
# ml.append((attr, val, [])) # TODO: Remove the now invalid attribute instead |
607 |
return ml |
751 |
|
608 |
|
752 |
# Make sure all default values are set... |
609 |
# require all MUST attributes to be set |
753 |
for name, p in self.descriptions.items(): |
610 |
for attr in must.values(): |
754 |
# ... if property has no option or any required option is currently enabled |
611 |
if not any(newattr.get(name) or newattr.get('%s;binary' % (name,)) for name in attr.names): |
755 |
if self.has_key(name) and self.descriptions[name].default(self): |
612 |
univention.debug.debug(univention.debug.ADMIN, univention.debug.WARN, 'The attribute %r is required by the current object classes.' % (attr.names,)) |
756 |
self[name] # __getitem__ sets default value |
613 |
return ml |
757 |
|
614 |
|
758 |
# iterate over all properties and call checkLdap() of corresponding syntax |
615 |
ml = [x for x in ml if x[0].lower() != 'objectclass'] |
759 |
self._call_checkLdap_on_all_property_syntaxes() |
616 |
ml.append(('objectClass', self.oldattr.get('objectClass', []), list(ocs))) |
760 |
|
617 |
|
761 |
ml = self._ldap_modlist() |
618 |
return ml |
762 |
ml = self.call_udm_property_hook('hook_ldap_modlist', self, ml) |
|
|
763 |
ml = self._ldap_object_classes(ml) |
764 |
|
619 |
|
765 |
#FIXME: timeout without exception if objectClass of Object is not exsistant !! |
620 |
def move(self, newdn, ignore_license=0, temporary_ou=None): |
766 |
univention.debug.debug(univention.debug.ADMIN, 99, 'Modify dn=%r;\nmodlist=%r;\noldattr=%r;' % (self.dn, ml, self.oldattr)) |
621 |
'''move object''' |
767 |
self.lo.modify(self.dn, ml, ignore_license=ignore_license) |
622 |
univention.debug.debug(univention.debug.ADMIN, univention.debug.INFO, 'move: called for %s to %s'% (self.dn,newdn)) |
768 |
|
623 |
|
769 |
self._ldap_post_modify() |
624 |
if not (univention.admin.modules.supports(self.module,'move') |
770 |
self.call_udm_property_hook('hook_ldap_post_modify', self) |
625 |
or univention.admin.modules.supports(self.module,'subtree_move')): # this should have been checked before, but I want to be sure... |
|
|
626 |
raise univention.admin.uexceptions.invalidOperation |
771 |
|
627 |
|
772 |
self.save() |
628 |
if not self.exists(): |
773 |
return self.dn |
629 |
raise univention.admin.uexceptions.noObject |
774 |
|
630 |
|
775 |
def _ldap_object_classes(self, ml): |
631 |
if _prevent_to_change_ad_properties and self._is_synced_object(): |
776 |
m = univention.admin.modules.get(self.module) |
632 |
raise univention.admin.uexceptions.invalidOperation(_('Objects from Active Directory can not be moved.')) |
777 |
lowerset = lambda vals: set(x.lower() for x in vals) |
|
|
778 |
|
633 |
|
779 |
ocs = lowerset(_MergedAttributes(self, ml).get_attribute('objectClass')) |
634 |
goaldn = ','.join(ldap.explode_dn(newdn)[1:]) |
780 |
unneeded_ocs = set() |
635 |
goalmodule = univention.admin.modules.identifyOne(goaldn, self.lo.get(goaldn)) |
781 |
required_ocs = set() |
636 |
goalmodule = univention.admin.modules.get(goalmodule) |
|
|
637 |
if not goalmodule or not hasattr(goalmodule,'childs') or not goalmodule.childs == 1: |
638 |
raise univention.admin.uexceptions.invalidOperation, _("Destination object can't have sub objects.") |
782 |
|
639 |
|
783 |
# evaluate (extended) options |
640 |
if self.dn.lower() == newdn.lower(): |
784 |
module_options = univention.admin.modules.options(self.module) |
641 |
if self.dn == newdn: |
785 |
available_options = set(module_options.keys()) |
642 |
raise univention.admin.uexceptions.ldapError, _('Moving not possible: old and new DN are identical.') |
786 |
options = set(self.options) |
643 |
else: |
787 |
old_options = set(self.old_options) |
644 |
# We must use a temporary folder because OpenLDAP does not allow a rename of an container with subobjects |
788 |
if options != old_options: |
645 |
temporary_ou = self._create_temporary_ou() |
789 |
univention.debug.debug(univention.debug.ADMIN, univention.debug.INFO, 'options=%r; old_options=%r' % (options, old_options)) |
646 |
new_rdn = ldap.explode_rdn(newdn)[0] |
790 |
unavailable_options = (options - available_options) | (old_options - available_options) |
647 |
new_dn = '%s,%s,%s' %(new_rdn,temporary_ou,self.lo.base) |
791 |
if unavailable_options: |
648 |
self.move(new_dn, ignore_license, temporary_ou) |
792 |
univention.debug.debug(univention.debug.ADMIN, univention.debug.ERROR, '%r does not provide options: %r' % (self.module, unavailable_options)) |
649 |
self.dn = new_dn |
793 |
added_options = options - old_options - unavailable_options |
|
|
794 |
removed_options = old_options - options - unavailable_options |
795 |
|
650 |
|
796 |
# evaluate extended attributes |
651 |
if self.dn.lower() == newdn.lower()[-len(self.dn):]: |
797 |
for prop in getattr(m, 'extended_udm_attributes', []): |
652 |
raise univention.admin.uexceptions.ldapError, _("Moving into one's own sub container not allowed.") |
798 |
univention.debug.debug(univention.debug.ADMIN, univention.debug.INFO, 'simpleLdap._modify: extended attribute=%r oc=%r'% (prop.name, prop.objClass)) |
653 |
|
|
|
654 |
if univention.admin.modules.supports(self.module,'subtree_move'): |
655 |
# check if is subtree: |
656 |
subelements = self.lo.search(base=self.dn, scope='one', attr=[]) |
657 |
if subelements: |
658 |
olddn = self.dn |
659 |
univention.debug.debug(univention.debug.ADMIN, univention.debug.INFO, 'move: found subelements, do subtree move: newdn: %s' % newdn) |
660 |
# create copy of myself |
661 |
module = univention.admin.modules.get(self.module) |
662 |
position = univention.admin.uldap.position(self.lo.base) |
663 |
position.setDn(','.join(ldap.explode_dn(newdn)[1:])) |
664 |
copyobject = module.object(None, self.lo, position) |
665 |
copyobject.open() |
666 |
for key in self.keys(): |
667 |
copyobject[key]=self[key] |
668 |
copyobject.policies=self.policies |
669 |
copyobject.create() |
670 |
moved=[] |
671 |
try: |
672 |
for subolddn, suboldattrs in subelements: |
673 |
univention.debug.debug(univention.debug.ADMIN, univention.debug.INFO, 'move: subelement %s' % subolddn) |
674 |
# Convert the DNs to lowercase before the replacement. The cases might be mixed up if the python lib is |
675 |
# used by the connector, for example: |
676 |
# subolddn: uid=user_test_h80,ou=TEST_H81,LDAP_BASE |
677 |
# self.dn: ou=test_h81,LDAP_BASE |
678 |
# newdn: OU=TEST_H81,ou=test_h82,$LDAP_BASE |
679 |
rdn = ldap.explode_dn(subolddn)[0] |
680 |
subolddn_dn_without_rdn_lower = ','.join(ldap.explode_dn(subolddn)[1:]).lower() |
681 |
subnewdn = '%s,%s' % (rdn, subolddn_dn_without_rdn_lower.replace(self.dn.lower(),newdn)) |
682 |
submodule = univention.admin.modules.identifyOne(subolddn, suboldattrs) |
683 |
submodule = univention.admin.modules.get(submodule) |
684 |
subobject = univention.admin.objects.get(submodule, None, self.lo, position='', dn=subolddn) |
685 |
if not subobject or not (univention.admin.modules.supports(submodule,'move') or |
686 |
univention.admin.modules.supports(submodule,'subtree_move')): |
687 |
raise univention.admin.uexceptions.invalidOperation(_('Unable to move object %(name)s (%(type)s) in subtree, trying to revert changes.' ) % {'name': subolddn[:subolddn.find(',')], 'type': univention.admin.modules.identifyOne(subolddn, suboldattrs)}) |
688 |
subobject.open() |
689 |
subobject.move(subnewdn) |
690 |
moved.append((subolddn,subnewdn)) |
691 |
self.remove() |
692 |
self._delete_temporary_ou_if_empty(temporary_ou) |
693 |
except: |
694 |
univention.debug.debug(univention.debug.ADMIN, univention.debug.ERROR, 'move: subtree move failed, trying to move back.') |
695 |
position=univention.admin.uldap.position(self.lo.base) |
696 |
position.setDn(','.join(ldap.explode_dn(olddn)[1:])) |
697 |
for subolddn, subnewdn in moved: |
698 |
submodule = univention.admin.modules.identifyOne(subnewdn, self.lo.get(subnewdn)) |
699 |
submodule = univention.admin.modules.get(submodule) |
700 |
subobject = univention.admin.objects.get(submodule, None, self.lo, position='', dn=subnewdn) |
701 |
subobject.open() |
702 |
subobject.move(subolddn) |
703 |
copyobject.remove() |
704 |
self._delete_temporary_ou_if_empty(temporary_ou) |
705 |
raise |
706 |
else: |
707 |
# normal move, fails on subtrees |
708 |
res = self._move(newdn, ignore_license=ignore_license) |
709 |
self._delete_temporary_ou_if_empty(temporary_ou) |
710 |
return res |
799 |
|
711 |
|
800 |
if self.has_key(prop.name) and self.info.get(prop.name) and (True if prop.syntax != 'boolean' else self.info.get(prop.name) != '0'): |
712 |
else: |
801 |
required_ocs |= set([prop.objClass]) |
713 |
res = self._move(newdn, ignore_license=ignore_license) |
802 |
continue |
714 |
self._delete_temporary_ou_if_empty(temporary_ou) |
|
|
715 |
return res |
803 |
|
716 |
|
804 |
if prop.deleteObjClass: |
717 |
def _ldap_pre_move(self, newdn): |
805 |
unneeded_ocs |= set([prop.objClass]) |
718 |
pass |
806 |
|
719 |
|
807 |
# if the value is unset we need to remove the attribute completely |
720 |
def _create_temporary_ou(self): |
808 |
if self.oldattr.get(prop.ldapMapping): |
721 |
name = 'temporary_move_container_%s' % time.time() |
809 |
ml = [x for x in ml if x[0].lower() != prop.ldapMapping.lower()] |
|
|
810 |
ml.append((prop.ldapMapping, self.oldattr.get(prop.ldapMapping), '')) |
811 |
|
722 |
|
812 |
unneeded_ocs |= reduce(set.union, (set(module_options[option].objectClasses) for option in removed_options), set()) |
723 |
module = univention.admin.modules.get('container/ou') |
813 |
required_ocs |= reduce(set.union, (set(module_options[option].objectClasses) for option in added_options), set()) |
724 |
position = univention.admin.uldap.position('%s' % self.lo.base) |
814 |
|
725 |
|
815 |
ocs -= lowerset(unneeded_ocs) |
726 |
temporary_object = module.object(None, self.lo, position) |
816 |
ocs |= lowerset(required_ocs) |
727 |
temporary_object.open() |
817 |
if lowerset(self.oldattr.get('objectClass', [])) == ocs: |
728 |
temporary_object['name'] = name |
818 |
return ml |
729 |
temporary_object.create() |
819 |
|
730 |
|
820 |
univention.debug.debug(univention.debug.ADMIN, univention.debug.INFO, 'OCS=%r; required=%r; removed: %r' % (ocs, required_ocs, unneeded_ocs)) |
731 |
return 'ou=%s' % ldap.dn.escape_dn_chars(name) |
821 |
|
732 |
|
822 |
# case normalize object class names |
733 |
def _delete_temporary_ou_if_empty(self, temporary_ou): |
823 |
schema = self.lo.get_schema() |
|
|
824 |
ocs = set(schema.get_obj(ldap.schema.models.ObjectClass, x).names[0] for x in ocs) |
825 |
|
734 |
|
826 |
# make sure we still have a structural object class |
735 |
if not temporary_ou: |
827 |
if not schema.get_structural_oc(ocs): |
736 |
return |
828 |
structural_ocs = schema.get_structural_oc(unneeded_ocs) |
|
|
829 |
if not structural_ocs: |
830 |
univention.debug.debug(univention.debug.ADMIN, univention.debug.ERROR, 'missing structural object class. Modify will fail.') |
831 |
return ml |
832 |
univention.debug.debug(univention.debug.ADMIN, univention.debug.WARN, 'Preventing to remove last structural object class %r' % (structural_ocs,)) |
833 |
ocs -= set(schema.get_obj(ldap.schema.models.ObjectClass, structural_ocs).names) |
834 |
|
737 |
|
835 |
# validate removal of object classes |
738 |
dn = '%s,%s' %(temporary_ou,self.lo.base) |
836 |
must, may = schema.attribute_types(ocs) |
|
|
837 |
allowed = set(name.lower() for attr in may.values() for name in attr.names) | set(name.lower() for attr in must.values() for name in attr.names) |
838 |
|
739 |
|
839 |
ml = [x for x in ml if x[0].lower() != 'objectclass'] |
740 |
module = univention.admin.modules.get('container/ou') |
840 |
ml.append(('objectClass', self.oldattr.get('objectClass', []), list(ocs))) |
741 |
temporary_object = univention.admin.modules.lookup(module, None, self.lo, scope='base', base=dn, required=1, unique=1)[0] |
841 |
newattr = ldap.cidict.cidict(_MergedAttributes(self, ml).get_attributes()) |
742 |
temporary_object.open() |
|
|
743 |
try: |
744 |
temporary_object.remove() |
745 |
except (univention.admin.uexceptions.ldapError, ldap.NOT_ALLOWED_ON_NONLEAF): |
746 |
pass |
842 |
|
747 |
|
843 |
# make sure only attributes known by the object classes are set |
748 |
def move_subelements(self, olddn, newdn, subelements, ignore_license = False): |
844 |
for attr, val in newattr.items(): |
749 |
if subelements: |
845 |
if not val: |
750 |
univention.debug.debug(univention.debug.ADMIN, univention.debug.INFO, 'move: found subelements, do subtree move') |
846 |
continue |
751 |
moved = [] |
847 |
if re.sub(';binary$', '', attr.lower()) not in allowed: |
752 |
try: |
848 |
univention.debug.debug(univention.debug.ADMIN, univention.debug.WARN, 'The attribute %r is not allowed by any object class.' % (attr,)) |
753 |
for subolddn, suboldattrs in subelements: |
849 |
# ml.append((attr, val, [])) # TODO: Remove the now invalid attribute instead |
754 |
univention.debug.debug(univention.debug.ADMIN, univention.debug.INFO, 'move: subelement %s' % subolddn) |
850 |
return ml |
755 |
subnewdn = subolddn.replace(olddn, newdn) |
|
|
756 |
submodule = univention.admin.modules.identifyOne(subolddn, suboldattrs) |
757 |
submodule = univention.admin.modules.get(submodule) |
758 |
subobject = univention.admin.objects.get(submodule, None, self.lo, position='', dn=subolddn) |
759 |
if not subobject or not (univention.admin.modules.supports(submodule, 'move') or |
760 |
univention.admin.modules.supports(submodule, 'subtree_move')): |
761 |
raise univention.admin.uexceptions.invalidOperation(_('Unable to move object %(name)s (%(type)s) in subtree, trying to revert changes.') % {'name': subolddn[:subolddn.find(',')], 'type': univention.admin.modules.identifyOne(subolddn, suboldattrs)}) |
762 |
subobject.open() |
763 |
subobject._move(subnewdn) |
764 |
moved.append((subolddn,subnewdn)) |
765 |
return moved |
766 |
except: |
767 |
univention.debug.debug(univention.debug.ADMIN, univention.debug.ERROR, 'move: subtree move failed, try to move back') |
768 |
for subolddn, subnewdn in moved: |
769 |
submodule = univention.admin.modules.identifyOne(subnewdn, self.lo.get(subnewdn)) |
770 |
submodule = univention.admin.modules.get(submodule) |
771 |
subobject = univention.admin.objects.get(submodule, None, self.lo, position='', dn=subnewdn) |
772 |
subobject.open() |
773 |
subobject.move(subolddn) |
774 |
raise |
851 |
|
775 |
|
852 |
# require all MUST attributes to be set |
776 |
def _move(self, newdn, modify_childs=1, ignore_license=0): |
853 |
for attr in must.values(): |
777 |
self._ldap_pre_move(newdn) |
854 |
if not any(newattr.get(name) or newattr.get('%s;binary' % (name,)) for name in attr.names): |
|
|
855 |
univention.debug.debug(univention.debug.ADMIN, univention.debug.WARN, 'The attribute %r is required by the current object classes.' % (attr.names,)) |
856 |
return ml |
857 |
|
778 |
|
858 |
ml = [x for x in ml if x[0].lower() != 'objectclass'] |
779 |
olddn = self.dn |
859 |
ml.append(('objectClass', self.oldattr.get('objectClass', []), list(ocs))) |
780 |
self.lo.rename(self.dn, newdn) |
|
|
781 |
self.dn = newdn |
860 |
|
782 |
|
861 |
return ml |
783 |
try: |
|
|
784 |
self._move_in_groups(olddn) # can be done always, will do nothing if oldinfo has no attribute 'groups' |
785 |
self._move_in_subordinates(olddn) |
786 |
self._ldap_post_move(olddn) |
787 |
except: |
788 |
# move back |
789 |
univention.debug.debug(univention.debug.ADMIN, univention.debug.WARN, |
790 |
'simpleLdap._move: self._ldap_post_move failed, move object back to %s'%olddn) |
791 |
self.lo.rename(self.dn, olddn) |
792 |
self.dn = olddn |
793 |
raise |
862 |
|
794 |
|
863 |
def _move_in_subordinates(self, olddn): |
795 |
def _move_in_subordinates(self, olddn): |
864 |
result = self.lo.search(base=self.lo.base, filter=filter_format('(&(objectclass=person)(secretary=%s))', [olddn]), attr=['dn']) |
796 |
result = self.lo.search(base=self.lo.base, filter=filter_format('(&(objectclass=person)(secretary=%s))', [olddn]), attr=['dn']) |
Lines 876-899
def _move_in_groups(self, olddn):
|
Link Here
|
---|
|
876 |
newmembers.append(self.dn) |
808 |
newmembers.append(self.dn) |
877 |
self.lo.modify(group, [('uniqueMember', members, newmembers)]) |
809 |
self.lo.modify(group, [('uniqueMember', members, newmembers)]) |
878 |
|
810 |
|
879 |
def _move(self, newdn, modify_childs=1, ignore_license=0): |
|
|
880 |
self._ldap_pre_move(newdn) |
881 |
|
811 |
|
882 |
olddn = self.dn |
812 |
def _ldap_post_move(self, olddn): |
883 |
self.lo.rename(self.dn, newdn) |
813 |
pass |
884 |
self.dn = newdn |
|
|
885 |
|
814 |
|
886 |
try: |
815 |
def remove(self, remove_childs=0): |
887 |
self._move_in_groups(olddn) # can be done always, will do nothing if oldinfo has no attribute 'groups' |
816 |
'''remove object''' |
888 |
self._move_in_subordinates(olddn) |
817 |
|
889 |
self._ldap_post_move(olddn) |
818 |
if not self.dn or not self.lo.get(self.dn): |
890 |
except: |
819 |
raise univention.admin.uexceptions.noObject(self.dn) |
891 |
# move back |
820 |
|
892 |
univention.debug.debug(univention.debug.ADMIN, univention.debug.WARN, |
821 |
return self._remove(remove_childs) |
893 |
'simpleLdap._move: self._ldap_post_move failed, move object back to %s'%olddn) |
|
|
894 |
self.lo.rename(self.dn, olddn) |
895 |
self.dn = olddn |
896 |
raise |
897 |
|
822 |
|
898 |
def _remove(self, remove_childs=0): |
823 |
def _remove(self, remove_childs=0): |
899 |
univention.debug.debug(univention.debug.ADMIN, univention.debug.INFO,'handlers/__init__._remove() called for %s' % self.dn) |
824 |
univention.debug.debug(univention.debug.ADMIN, univention.debug.INFO,'handlers/__init__._remove() called for %s' % self.dn) |
Lines 927-932
def _remove(self, remove_childs=0):
|
Link Here
|
---|
|
927 |
self.call_udm_property_hook('hook_ldap_post_remove', self) |
852 |
self.call_udm_property_hook('hook_ldap_post_remove', self) |
928 |
self.save() |
853 |
self.save() |
929 |
|
854 |
|
|
|
855 |
def _ldap_pre_remove(self): |
856 |
pass |
857 |
|
858 |
def _ldap_post_remove(self): |
859 |
pass |
860 |
|
861 |
def get_gid_for_primary_group(self): |
862 |
gidNum = '99999' |
863 |
if self['primaryGroup']: |
864 |
try: |
865 |
gidNum = self.lo.getAttr(self['primaryGroup'], 'gidNumber', required=True)[0] |
866 |
except ldap.NO_SUCH_OBJECT: |
867 |
raise univention.admin.uexceptions.primaryGroup(self['primaryGroup']) |
868 |
return gidNum |
869 |
|
870 |
def get_sid_for_primary_group(self): |
871 |
try: |
872 |
sidNum = self.lo.getAttr(self['primaryGroup'], 'sambaSID', required=True)[0] |
873 |
except ldap.NO_SUCH_OBJECT: |
874 |
raise univention.admin.uexceptions.primaryGroupWithoutSamba(self['primaryGroup']) |
875 |
return sidNum |
876 |
|
877 |
def _remove_option( self, name ): |
878 |
if name in self.options: |
879 |
self.options.remove( name ) |
880 |
|
881 |
def __set_options(self): |
882 |
self.options = [] |
883 |
options = univention.admin.modules.options(self.module) |
884 |
if 'objectClass' in self.oldattr: |
885 |
ocs = set(self.oldattr['objectClass']) |
886 |
for opt, option in options.iteritems(): |
887 |
if not option.disabled and option.matches(ocs): |
888 |
self.options.append(opt) |
889 |
else: |
890 |
univention.debug.debug(univention.debug.ADMIN, univention.debug.INFO, 'reset options to default by _define_options') |
891 |
self._define_options(options) |
892 |
|
893 |
def _define_options( self, module_options ): |
894 |
# enable all default options |
895 |
univention.debug.debug(univention.debug.ADMIN, univention.debug.INFO, 'modules/__init__.py _define_options: reset to default options') |
896 |
for name, opt in module_options.items(): |
897 |
if not opt.disabled and opt.default: |
898 |
self.options.append( name ) |
899 |
|
900 |
def option_toggled(self, option): |
901 |
'''Checks if an option was changed. This does not work for not yet existing objects.''' |
902 |
return option in set(self.options) ^ set(self.old_options) |
903 |
|
904 |
def description(self): |
905 |
if self.dn: |
906 |
rdn = self.lo.explodeDn(self.dn)[0] |
907 |
return rdn[rdn.find('=')+1:] |
908 |
else: |
909 |
return 'none' |
910 |
|
930 |
def loadPolicyObject(self, policy_type, reset=0): |
911 |
def loadPolicyObject(self, policy_type, reset=0): |
931 |
pathlist=[] |
912 |
pathlist=[] |
932 |
errors=0 |
913 |
errors=0 |
Lines 2583-2604
def __setitem__(self, key, value):
|
Link Here
|
---|
|
2583 |
if raise_after: |
2564 |
if raise_after: |
2584 |
raise raise_after |
2565 |
raise raise_after |
2585 |
|
2566 |
|
2586 |
class simpleLdapSub(simpleLdap): |
|
|
2587 |
|
2588 |
def __init__(self, co, lo, position, dn='', superordinate=None, attributes = [] ): |
2589 |
base.__init__(self, co, lo, position, dn, superordinate ) |
2590 |
|
2591 |
def _create(self): |
2592 |
self._modify() |
2593 |
|
2594 |
def _remove(self, remove_childs=0): |
2595 |
univention.debug.debug(univention.debug.ADMIN, univention.debug.INFO,'_remove() called') |
2596 |
self._ldap_pre_remove() |
2597 |
|
2598 |
ml=self._ldap_dellist() |
2599 |
self.lo.modify(self.dn, ml) |
2600 |
|
2601 |
self._ldap_post_remove() |
2602 |
|
2567 |
|
2603 |
class simplePolicy(simpleLdap): |
2568 |
class simplePolicy(simpleLdap): |
2604 |
def __init__(self, co, lo, position, dn='', superordinate=None, attributes = [] ): |
2569 |
def __init__(self, co, lo, position, dn='', superordinate=None, attributes = [] ): |