View | Details | Raw Unified | Return to bug 42028
Collapse All | Expand All

(-)a/management/univention-directory-manager-modules/modules/univention/admin/handlers/__init__.py (-530 / +495 lines)
 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'''
 Lines 137-142   def diff(self): Link Here 
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'''
 Lines 294-299   def create(self): Link Here 
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 = [] ):

Return to bug 42028