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

(-)ucs-school-umc-distribution/umc/python/distribution/util.py (-1 / +1 lines)
 Lines 227-233    Link Here 
227
			# initiate a new search base using the ou in the group
227
			# initiate a new search base using the ou in the group
228
			schoolDN = entryObj.dn[entryObj.dn.find('ou='):]
228
			schoolDN = entryObj.dn[entryObj.dn.find('ou='):]
229
			school = ldap_connection.explodeDn(schoolDN, 1)[0]
229
			school = ldap_connection.explodeDn(schoolDN, 1)[0]
230
			_search_base = SchoolSearchBase(school, school, schoolDN)
230
			_search_base = SchoolSearchBase({school: schoolDN}, school, schoolDN)
231
231
232
			# open group object
232
			# open group object
233
			name_pattern = re.compile('^%s-' % (re.escape(_search_base.school)), flags=re.I)
233
			name_pattern = re.compile('^%s-' % (re.escape(_search_base.school)), flags=re.I)
(-)ucs-school-lib/python/schoolldap.py (-47 / +96 lines)
 Lines 102-110    Link Here 
102
MACHINE_WRITE = 'ldap_machine_write'
102
MACHINE_WRITE = 'ldap_machine_write'
103
ADMIN_WRITE = 'ldap_admin_write'
103
ADMIN_WRITE = 'ldap_admin_write'
104
104
105
105
class LDAP_ConnectionError( Exception ):
106
class LDAP_ConnectionError( Exception ):
106
	pass
107
	pass
107
108
109
108
def open_ldap_connection( binddn, bindpw, ldap_server ):
110
def open_ldap_connection( binddn, bindpw, ldap_server ):
109
	'''Opens a new LDAP connection using the given user LDAP DN and
111
	'''Opens a new LDAP connection using the given user LDAP DN and
110
	password. The connection is open to the given server or if None the
112
	password. The connection is open to the given server or if None the
 Lines 119-124    Link Here 
119
121
120
	return lo
122
	return lo
121
123
124
122
def get_ldap_connections( connection_types, force = False ):
125
def get_ldap_connections( connection_types, force = False ):
123
	global _ldap_connections, _user_dn, _password
126
	global _ldap_connections, _user_dn, _password
124
127
 Lines 146-151    Link Here 
146
149
147
	return connections
150
	return connections
148
151
152
149
def LDAP_Connection( *connection_types ):
153
def LDAP_Connection( *connection_types ):
150
	"""This decorator function provides an open LDAP connection that can
154
	"""This decorator function provides an open LDAP connection that can
151
	be accessed via the variable ldap_connection and a valid position
155
	be accessed via the variable ldap_connection and a valid position
 Lines 222-230    Link Here 
222
		return wraps( func )( wrapper_func )
226
		return wraps( func )( wrapper_func )
223
	return inner_wrapper
227
	return inner_wrapper
224
228
229
225
class LicenseError( Exception ):
230
class LicenseError( Exception ):
226
	pass
231
	pass
227
232
233
228
@LDAP_Connection(MACHINE_READ, MACHINE_WRITE)
234
@LDAP_Connection(MACHINE_READ, MACHINE_WRITE)
229
def check_license(ldap_machine_read = None, ldap_machine_write = None, ldap_position = None, search_base = None ):
235
def check_license(ldap_machine_read = None, ldap_machine_write = None, ldap_position = None, search_base = None ):
230
	"""Checks the license cases and throws exceptions accordingly. The logic is copied from the UDM UMC module."""
236
	"""Checks the license cases and throws exceptions accordingly. The logic is copied from the UDM UMC module."""
 Lines 304-369    Link Here 
304
	except udm_errors.licenseGPLversion:
310
	except udm_errors.licenseGPLversion:
305
		raise LicenseError(_('Your license status could not be validated. Thus, you are not eligible to support and maintenance. If you have bought a license, please contact Univention or your Univention partner.'))
311
		raise LicenseError(_('Your license status could not be validated. Thus, you are not eligible to support and maintenance. If you have bought a license, please contact Univention or your Univention partner.'))
306
312
313
307
def _init_search_base(ldap_connection, force=False):
314
def _init_search_base(ldap_connection, force=False):
315
	# initiate the list of available schools and set the default search base
308
	global _search_base
316
	global _search_base
309
317
310
	if _search_base and not force:
318
	if _search_base and not force:
311
		# search base has already been initiated... we are done
319
		# search base has already been initiated... we are done
312
		return
320
		return
313
321
314
	# initiate the list of available schools and set the default search base
322
	availableSchools = {}
315
	if ldap_connection.binddn.find('ou=') > 0:
323
	schoolname = None
324
	school_dn = None
325
326
	school_dn = getOUDN(ldap_connection.binddn)
327
	if school_dn:
328
		MODULE.info('LDAP_Connection: user account is bound to school.')
316
		# we got an OU in the user DN -> school teacher or assistent
329
		# we got an OU in the user DN -> school teacher or assistent
317
		# restrict the visibility to current school
330
		# restrict the visibility to current school
318
		# (note that there can be schools with a DN such as ou=25g18,ou=25,dc=...)
331
		# (note that there can be schools with a DN such as ou=25g18,ou=25,dc=...)
319
		schoolDN = ldap_connection.binddn[ldap_connection.binddn.find('ou='):] 
332
		schoolname = SchoolSearchBase.getOU(school_dn)
320
		school = (ldap_connection.explodeDn( schoolDN, 1 )[0], )  # note: school is a tuple with one element
333
		availableSchools = {schoolname: school_dn}
321
		_search_base = SchoolSearchBase(school, school[0], schoolDN)
322
		MODULE.info('LDAP_Connection: setting schoolDN: %s' % _search_base.schoolDN)
323
	else:
334
	else:
324
		MODULE.warn( 'LDAP_Connection: unable to identify ou of this account - showing all OUs!' )
335
		MODULE.warn('LDAP_Connection: unable to identify ou of this account - showing all OUs!')
325
		#_ouswitchenabled = True
336
326
		oulist = ucr.get('ucsschool/local/oulist')
337
		oulist = ucr.get('ucsschool/local/oulist')
327
		availableSchools = []
338
		district_mode = ucs.is_true('ucsschool/ldap/district/enable')
339
		searchscope = 'sub' if district_mode else 'one'
340
341
		# get a list of available OUs via UDM module container/ou
342
		availableSchools = udm_modules.lookup(
343
			'container/ou', None, ldap_connection,
344
			filter='objectClass=ucsschoolOrganizationalUnit',
345
			scope=searchscope, superordinate=None,
346
			base=ucr.get('ldap/base')
347
		)
348
		availableSchools = ((ou['name'], ou.dn) for ou in availableSchools)
349
328
		if oulist:
350
		if oulist:
329
			# OU list override via UCR variable (it can be necessary to adjust the list of
351
			# OU list override via UCR variable (it can be necessary to adjust the list of
330
			# visible schools on specific systems manually)
352
			# visible schools on specific systems manually)
331
			availableSchools = [ x.strip() for x in oulist.split(',') ]
353
			MODULE.info('LDAP_Connection: availableSchools overridden by UCR variable ucsschool/local/oulist')
332
			MODULE.info( 'LDAP_Connection: availableSchools overridden by UCR variable ucsschool/local/oulist')
354
			oulist = oulist.split(',')
333
		else:
355
			availableSchools = ((name, dn) for name, dn in availableSchools if name in oulist)
334
			# get a list of available OUs via UDM module container/ou
335
			availableSchools = udm_modules.lookup(
336
				'container/ou', None, ldap_connection, 'objectClass=ucsschoolOrganizationalUnit',
337
				scope='one', superordinate=None,
338
				base=ucr.get('ldap/base')
339
			)
340
			availableSchools = [ou['name'] for ou in availableSchools]
341
356
342
		# use the first available OU as default search base
357
		availableSchools = dict(availableSchools)
358
343
		if not availableSchools:
359
		if not availableSchools:
344
			MODULE.warn('LDAP_Connection: ERROR, COULD NOT FIND ANY OU!!!')
360
			MODULE.warn('LDAP_Connection: ERROR, COULD NOT FIND ANY OU!!!')
345
			_search_base = SchoolSearchBase([''])
346
		else:
347
			MODULE.info( 'LDAP_Connection: availableSchools=%s' % availableSchools )
348
			_search_base = SchoolSearchBase(availableSchools)
349
361
362
	MODULE.info('LDAP_Connection: availableSchools=%s; school=%s; school_dn=%s' % (availableSchools, schoolname, school_dn))
363
	_search_base = SchoolSearchBase(availableSchools, schoolname, school_dn)
364
365
350
class SchoolSearchBase(object):
366
class SchoolSearchBase(object):
351
	"""This class serves a wrapper for all the different search bases (users,
367
	"""This class serves a wrapper for all the different search bases (users,
352
	groups, students, teachers etc.). It is initiated with a particular ou.
368
	groups, students, teachers etc.). It is initiated with a particular ou.
353
	The class is inteded for read access only, instead of switching the
369
	The class is inteded for read access only, instead of switching the
354
	search base, a new instance can simply be created.
370
	search base, a new instance can simply be created.
355
	"""
371
	"""
356
	def __init__( self, availableSchools, school = None, dn = None, ldapBase = None ):
372
	def __init__(self, availableSchools, school=None, dn=None, ldapBase=None):
357
		if ldapBase:
373
		if ldapBase:
358
			self._ldapBase = ldapBase
374
			self._ldapBase = ldapBase
359
		else:
375
		else:
360
			self._ldapBase = ucr.get('ldap/base')
376
			self._ldapBase = ucr.get('ldap/base')
361
377
362
		self._availableSchools = availableSchools
378
		self._availableSchools = availableSchools
363
		self._school = school or availableSchools[0]
379
#		self._school = school or availableSchools[0]
364
		# FIXME: search for OU to get correct dn
380
#		# FIXME: search for OU to get correct dn
365
		self._schoolDN = dn or 'ou=%s,%s' % (self.school, self._ldapBase )
381
#		self._schoolDN = dn or 'ou=%s,%s' % (self.school, self._ldapBase )
366
382
383
		self._school = school
384
		if not self._school and availableSchools:
385
			self._school = availableSchools.keys()[0]
386
387
		if dn:  # school DN is given
388
			self._schoolDN = dn
389
		else:
390
			# school DN is not given, try to guess it from the dict of all schools
391
			if self.school in availableSchools:
392
				self._schoolDN = availableSchools[self.school]
393
			else:
394
				# should not happen... use a poor man's fallback
395
				MODULE.error('Could not find corresponding school DN for schoolOU "%s"!' % self.school)
396
				self._schoolDN = 'ou=%s,%s' % (self.school, self._ldapBase )
397
367
		# prefixes
398
		# prefixes
368
		self._containerAdmins = ucr.get('ucsschool/ldap/default/container/admins', 'admins')
399
		self._containerAdmins = ucr.get('ucsschool/ldap/default/container/admins', 'admins')
369
		self._containerStudents = ucr.get('ucsschool/ldap/default/container/pupils', 'schueler')
400
		self._containerStudents = ucr.get('ucsschool/ldap/default/container/pupils', 'schueler')
 Lines 375-399    Link Here 
375
		self._examUserContainerName = ucr.get('ucsschool/ldap/default/container/exam', 'examusers')
406
		self._examUserContainerName = ucr.get('ucsschool/ldap/default/container/exam', 'examusers')
376
		self._examGroupNameTemplate = ucr.get('ucsschool/ldap/default/groupname/exam', 'OU%(ou)s-Klassenarbeit')
407
		self._examGroupNameTemplate = ucr.get('ucsschool/ldap/default/groupname/exam', 'OU%(ou)s-Klassenarbeit')
377
408
378
	@staticmethod
409
	@classmethod
379
	def getOU(dn):
410
	def getOU(cls, dn):
380
		'''Return the school OU for a given DN.'''
411
		"""Return the school OU for a given DN.
381
		if dn.find('ou=') < 0:
382
			# no 'ou=' in the string
383
			return None
384
		schoolDN = dn[dn.find('ou='):]
385
		school = univention.uldap.explodeDn( schoolDN, 1 )[0]
386
		return school
387
412
413
			>>> getOU('uid=a,fou=bar,Ou=dc1,oU=dc,dc=foo,dc=bar')
414
			'dc1'
415
		"""
416
		school_dn = cls.getOUDN(dn)
417
		if school_dn:
418
			return univention.uldap.explodeDn(school_dn, True)[0]
419
420
	@classmethod
421
	def getOUDN(cls, dn):
422
		"""Return the School OU-DN part for a given DN.
423
424
			>>> getOUDN('uid=a,fou=bar,Ou=dc1,oU=dc,dc=foo,dc=bar')
425
			'Ou=dc1,oU=dc,dc=foo,dc=bar'
426
			>>> getOUDN('ou=dc1,ou=dc,dc=foo,dc=bar')
427
			'ou=dc1,ou=dc,dc=foo,dc=bar'
428
		"""
429
		match = cls.getOUDN.RE_OU.search(dn)
430
		if match:
431
			return match.group(1)
432
	getOUDN.RE_OU = re.compile('(?:^|,)(ou=.*)$', re.I)
433
388
	@property
434
	@property
389
	def availableSchools(self):
435
	def availableSchools(self):
390
		return self._availableSchools
436
		return self._availableSchools
391
437
392
	@property
438
	@property
393
	def allSchoolBases(self):
439
	def allSchoolBases(self):
440
		"""returns a generator containing a SchoolSearchBase for every available school"""
394
		availableSchools = self.availableSchools
441
		availableSchools = self.availableSchools
395
		for school in availableSchools:
442
		for school, school_dn in availableSchools.iteritems():
396
			yield self.__class__(availableSchools, school, None, self._ldapBase)
443
			yield self.__class__(availableSchools, school, school_dn, self._ldapBase)
397
444
398
	@property
445
	@property
399
	def school(self):
446
	def school(self):
 Lines 405-415    Link Here 
405
452
406
	@property
453
	@property
407
	def users(self):
454
	def users(self):
408
		return "cn=users,%s" % self.schoolDN
455
		return "cn=users,%s" % (self.schoolDN,)
409
456
410
	@property
457
	@property
411
	def groups(self):
458
	def groups(self):
412
		return "cn=groups,%s" % self.schoolDN
459
		return "cn=groups,%s" % (self.schoolDN,)
413
460
414
	@property
461
	@property
415
	def workgroups(self):
462
	def workgroups(self):
 Lines 449-463    Link Here 
449
496
450
	@property
497
	@property
451
	def shares(self):
498
	def shares(self):
452
		return "cn=shares,%s" % self.schoolDN
499
		return "cn=shares,%s" % (self.schoolDN,)
453
500
454
	@property
501
	@property
455
	def printers(self):
502
	def printers(self):
456
		return "cn=printers,%s" % self.schoolDN
503
		return "cn=printers,%s" % (self.schoolDN,)
457
504
458
	@property
505
	@property
459
	def computers(self):
506
	def computers(self):
460
		return "cn=computers,%s" % self.schoolDN
507
		return "cn=computers,%s" % (self.schoolDN,)
461
508
462
	@property
509
	@property
463
	def examUsers(self):
510
	def examUsers(self):
 Lines 548-559    Link Here 
548
595
549
		# make sure that at least one school OU
596
		# make sure that at least one school OU
550
		msg = ''
597
		msg = ''
551
		if not search_base.availableSchools[0]:
598
		if not search_base.availableSchools:
552
			request.status = MODULE_ERR
599
			request.status = MODULE_ERR
553
			msg = _('Could not find any school. You have to create a school before continuing. Use the \'Add school\' UMC module to create one.')
600
			msg = _("Could not find any school. You have to create a school before continuing. Use the 'Add school' UMC module to create one.")
554
601
555
		# return list of school OUs
602
		# return list of school OUs
556
		self.finished(request.id, search_base.availableSchools, msg)
603
		self.finished(request.id, search_base.availableSchools.keys(), msg)
557
604
558
	def _groups( self, ldap_connection, school, ldap_base, pattern = None, scope = 'sub' ):
605
	def _groups( self, ldap_connection, school, ldap_base, pattern = None, scope = 'sub' ):
559
		"""Returns a list of all groups of the given school"""
606
		"""Returns a list of all groups of the given school"""
 Lines 640-645    Link Here 
640
687
641
		return userresult
688
		return userresult
642
689
690
643
class LDAP_Filter:
691
class LDAP_Filter:
644
692
645
	@staticmethod
693
	@staticmethod
 Lines 678-683    Link Here 
678
726
679
		return '(&%s)' % ''.join( expressions )
727
		return '(&%s)' % ''.join( expressions )
680
728
729
681
class Display:
730
class Display:
682
	@staticmethod
731
	@staticmethod
683
	def user( udm_object ):
732
	def user( udm_object ):
(-)ucs-school-umc-installer/umc/python/schoolinstaller/__init__.py (-1 / +1 lines)
 Lines 568-574    Link Here 
568
				result = udm_modules.lookup('container/ou', None, lo, base=ucrMaster.get('ldap/base'), scope='sub', filter='name=%s' % schoolOU)
568
				result = udm_modules.lookup('container/ou', None, lo, base=ucrMaster.get('ldap/base'), scope='sub', filter='name=%s' % schoolOU)
569
				if result:
569
				if result:
570
					# OU already exists... find all joined slave systems in the ou
570
					# OU already exists... find all joined slave systems in the ou
571
					searchBase = SchoolSearchBase([schoolOU], ldapBase=ucrMaster.get('ldap/base'))
571
					searchBase = SchoolSearchBase({schoolOU: result[0].dn}, ldapBase=ucrMaster.get('ldap/base'))
572
					slaves = udm_modules.lookup('computers/domaincontroller_slave', None, lo, base=searchBase.computers, scope='sub', filter='service=LDAP')
572
					slaves = udm_modules.lookup('computers/domaincontroller_slave', None, lo, base=searchBase.computers, scope='sub', filter='service=LDAP')
573
573
574
					# make sure that no joined DC slave is the main DC for this school
574
					# make sure that no joined DC slave is the main DC for this school
(-)ucs-school-umc-computerroom/umc/python/computerroom/__init__.py (-4 / +3 lines)
 Lines 289-298    Link Here 
289
289
290
		# match the corresponding school OU
290
		# match the corresponding school OU
291
		school = None
291
		school = None
292
		roomParts = explodeDn(roomDN)
292
		for ischool, ischool_dn in search_base.availableSchools.iteritems():
293
		for ischool in search_base.availableSchools:
293
			pattern = re.compile('.*%s$' % (re.escape(ischool_dn)), re.I)
294
			if ('ou=%s' % ischool) in roomParts:
294
			if pattern.match(roomDN):
295
				# match
296
				school = ischool
295
				school = ischool
297
				break
296
				break
298
		else:
297
		else:
(-)ucs-school-umc-csv-import/umc/python/schoolcsvimport/util.py (-2 / +2 lines)
 Lines 227-233    Link Here 
227
	@classmethod
227
	@classmethod
228
	def get_search_base(cls, school):
228
	def get_search_base(cls, school):
229
		if school not in cls._search_base_cache:
229
		if school not in cls._search_base_cache:
230
			cls._search_base_cache[school] = SchoolSearchBase([], school)
230
			cls._search_base_cache[school] = SchoolSearchBase({}, school)
231
		return cls._search_base_cache[school]
231
		return cls._search_base_cache[school]
232
232
233
	@classmethod
233
	@classmethod
 Lines 657-663    Link Here 
657
	@classmethod
657
	@classmethod
658
	def get_search_base(cls, school):
658
	def get_search_base(cls, school):
659
		if school not in cls._search_base_cache:
659
		if school not in cls._search_base_cache:
660
			cls._search_base_cache[school] = SchoolSearchBase([], school)
660
			cls._search_base_cache[school] = SchoolSearchBase({}, school)
661
		return cls._search_base_cache[school]
661
		return cls._search_base_cache[school]
662
662
663
class Group(UCSSchoolHelperAbstractClass):
663
class Group(UCSSchoolHelperAbstractClass):

Return to bug 31407