#!/usr/bin/env python from optparse import OptionParser from univention.config_registry import ConfigRegistry from univention.config_registry import handler_unset from univention.uldap import getMachineConnection import sys import getpass import os import psutil import re import subprocess import time import univention.admin.config import univention.admin.modules import univention.admin.uldap import univention.uldap ACCEPTED_ROLES = [ # List of tuples (server/role, ucsschool/singlemaster) ('domaincontroller_master', True), ('domaincontroller_slave', False), ] ucr = ConfigRegistry() ucr.load() class SimpleSamba(object): def __init__(self,): pass def stop(self,): for script in ('/etc/init.d/samba', '/etc/init.d/samba4', 'etc/init.d/samba-ad-dc'): if os.path.isfile(script): pop = subprocess.Popen([script, 'stop']) pop.communicate() if not pop.returncode: self.wait_to_stop() def start(self,): for script in ('/etc/init.d/samba', '/etc/init.d/samba4', 'etc/init.d/samba-ad-dc'): if os.path.isfile(script): pop = subprocess.Popen([script, 'start']) pop.communicate() if not pop.returncode: self.wait_to_start() def restart(self): if self.is_running(): self.stop() self.start() def is_running(self,): try: samba = [(x.pid, x.name) for x in psutil.process_iter() if x.name=='samba'] smbd = [(x.pid, x.name) for x in psutil.process_iter() if x.name=='smbd'] except psutil.NoSuchProcess: pass else: return smbd or samba def wait_to_stop(self,duration=60): print '** Waiting for samba to STOP ... (max %dsec)' % duration for i in xrange(duration): if not self.is_running(): break else: time.sleep(1) def wait_to_start(self,duration=60): print '** Waiting for samba to START ... (max %dsec)' % duration for i in xrange(duration): if self.is_running(): break else: time.sleep(1) class Member(object): def __init__(self, dn, access, usersmod, force=False): self.dn = dn self.access = access self.force = force self.user_obj = usersmod.object(None, access, None, dn) self.user_obj.open() self.old_home = '/home/%s' % (self.name(),) self.new_home = '/home/%s/%s/%s' % (self.school(), self.role(), self.name()) def name(self): return self.user_obj['username'] def role(self): found = re.search(r'cn=(schueler|lehrer|mitarbeiter|lehrer und mitarbeiter),', self.dn) if found: if found.groups()[0] == 'lehrer und mitarbeiter': return 'lehrer' return found.groups()[0] def school(self): found = re.search(r'ou=([^,]+),', self.dn) if found: return found.groups()[0] def home_directory(self,): return self.user_obj['unixhome'] def alter_home_directory(self,): if not self.role(): # skipping e.g. exam users return if self.user_obj['unixhome'] == self.new_home: if self.old_home_directory_exists() and not self.new_home_directory_exists(): self._move_home_directory() else: print 'User %s has already been converted.' % (self.user_obj['username'], ) elif self.user_obj['unixhome'] == self.old_home or self.force: self.user_obj['unixhome'] = self.new_home self.user_obj.modify() self._move_home_directory() else: print 'WARNING: old home directory of user %s is not set to the expected value' % (self.user_obj['username'], ) print ' current value: %r' % (self.user_obj['unixhome'], ) print ' expected value: %r' % (self.old_home, ) print ' Skipping user with DN %r' % (self.user_obj.dn,) def old_home_directory_exists(self): return os.path.isdir(self.old_home) def new_home_directory_exists(self): return os.path.isdir(self.new_home) def _move_home_directory(self): if self.old_home_directory_exists(): print 'Moving home directory of user %s' % self.name() # use system's mv command to keep permissions AND owners (shutil.move might loose them over partition boundaries) subprocess.call(['mv', '--', self.old_home, self.new_home]) else: if self.new_home_directory_exists(): print 'Home directory of user %s has already been updated to the new directory structure' % (self.name(),) else: print 'Home directory of user %s does not exist' % (self.name(),) def server_role(): return ( ucr.get('server/role'), ucr.is_true('ucsschool/singlemaster', False) ) def update_roleshare_ucr_variable(): if 'ucsschool/import/roleshare' in ucr: print 'Unsetting UCR variable ucsschool/import/roleshare...' handler_unset(['ucsschool/import/roleshare']) print 'done' def main(): usage = "%prog " parser = OptionParser(usage=usage) parser.add_option('--username', action='store', dest='username', help = 'Username for LDAP connection' ) parser.add_option('--password', action='store', dest='password', help = 'Password for LDAP connection' ) parser.add_option('--force', action='store_true', dest='force', default=False, help = 'always convert home directory, even if the old value does not match to the expected value' ) (options, args) = parser.parse_args() if len(args) < 1: parser.error('A group name has to be specified!') sys.exit(2) role = server_role() if role not in ACCEPTED_ROLES: print 'This script can be executed in the following environments only:' print '- domaincontroller master in UCS@school single server environment' print '- domaincontroller slave in UCS@school multi server environment' print 'Stopping here.' sys.exit(3) groupname = args[0] # Get username and password for ldap authentication if options.username and options.password: try: lo = getMachineConnection() binddn = lo.search(filter="(uid=%s)" % (options.username,))[0][0] print 'Connecting as %s to LDAP...' % (binddn, ) access = univention.admin.uldap.access( host=ucr.get('ldap/master'), port=int(ucr.get('ldap/master/port', '7389')), base=ucr.get('ldap/base'), binddn=binddn, bindpw=options.password) except IndexError: print "ERROR: user %s does not exist" % (options.username, ) sys.exit(4) except univention.admin.uexceptions.authFail: print 'ERROR: username or password is incorrect' sys.exit(5) position = univention.admin.uldap.position(ucr['ldap/base']) else: if role[0] == 'domaincontroller_slave': user = raw_input('User: ') bindpwd = getpass.getpass('Password: ') try: lo = getMachineConnection() binddn = lo.search(filter="(uid=%s)" % (user,))[0][0] print 'Connecting as %s to LDAP...' % (binddn, ) access = univention.admin.uldap.access( host=ucr.get('ldap/master'), port=int(ucr.get('ldap/master/port', '7389')), base=ucr.get('ldap/base'), binddn=binddn, bindpw=bindpwd) except IndexError: print "ERROR: user %s does not exist" % (user,) sys.exit(4) except univention.admin.uexceptions.authFail: print 'ERROR: username or password is incorrect' sys.exit(5) position = univention.admin.uldap.position(ucr['ldap/base']) else: access, position = univention.admin.uldap.getAdminConnection() update_roleshare_ucr_variable() config = univention.admin.config.config() univention.admin.modules.update() usersmod = univention.admin.modules.get("users/user") univention.admin.modules.init(access, position, usersmod) groupmod = univention.admin.modules.get("groups/group") univention.admin.modules.init(access, position, groupmod) groupobjlist = groupmod.lookup(config, access, 'name=%s' % (groupname,)) try: group = groupobjlist[0] except IndexError: print "ERROR: group %s does not exist!" % (groupname,) sys.exit(6) if not group.open(): members = [Member(x, access, usersmod) for x in group['users'] if re.match( r'^uid=.+,ou=([^,]+),%s$' % ucr.get('ldap/base'), x)] if members: samba = SimpleSamba() samba.stop() try: for member in members: member.alter_home_directory() except univention.admin.uexceptions.permissionDenied: print 'ERROR: user with LDAP binddn %s has insufficient access privileges' % (binddn,) samba.restart() else: print 'The specified group %r contains no members.' % (groupname,) sys.exit(0) if __name__ == '__main__': main()