def get_only_udm_obj(cls, lo, filter_str, superordinate=None, base=None): '''Returns the one UDM object of class cls._meta.udm_module that matches a given filter. If more than one is found, a MultipleObjectsError is raised If none is found, None is returned ''' cls.init_udm_module(lo) if cls._meta.udm_filter: filter_str = '(&(%s)(%s))' % (cls._meta.udm_filter, filter_str) logger.debug('Getting %s UDM object by filter: %s', cls.__name__, filter_str) objs = udm_modules.lookup(cls._meta.udm_module, None, lo, scope='sub', base=base or ucr.get('ldap/base'), filter=str(filter_str), superordinate=superordinate) if len(objs) == 0: return None if len(objs) > 1: raise MultipleObjectsError(objs) obj = objs[0] obj.open() return obj
def create_without_hooks(self, lo, validate): district = self.get_district() if district: ou = OU(name=district) ou.position = ucr.get('ldap/base') ou.create(lo, False) # setting class_share_file_server and home_share_file_server: # 1. set to None # 2. create school # 3. (maybe) create file_servers <- that is why this is necessary # 4. set file_servers # 5. modify school saved_class_share_file_server = self.class_share_file_server saved_home_share_file_server = self.home_share_file_server self.class_share_file_server = None self.home_share_file_server = None try: success = super(School, self).create_without_hooks(lo, validate) if not success: logger.warning('Creating %r failed (maybe it already exists?)! Trying to set it up nonetheless', self) self.modify_without_hooks(lo) # In a single server environment the default DHCP container must # be set to the DHCP container in the school ou. Otherwise newly # imported computers have the DHCP objects in the wrong DHCP container if ucr.is_true('ucsschool/singlemaster', False): if not ucr.get('dhcpd/ldap/base'): handler_set(['dhcpd/ldap/base=cn=dhcp,%s' % (self.dn)]) ucr.load() self.create_default_containers(lo) self.create_default_groups(lo) self.add_host_to_dc_group(lo) if not self.add_domain_controllers(lo): return False if self.dc_name_administrative: self.create_dc_slave(lo, self.dc_name_administrative, administrative=True) dhcp_service = self.get_dhcp_service(self.dc_name_administrative) dhcp_service.create(lo) dhcp_service.add_server(self.dc_name_administrative, lo) finally: logger.debug('Resetting share file servers from None to %r and %r', saved_home_share_file_server, saved_class_share_file_server) self.class_share_file_server = saved_class_share_file_server self.home_share_file_server = saved_home_share_file_server self.class_share_file_server = self.get_class_share_file_server(lo) self.home_share_file_server = self.get_home_share_file_server(lo) logger.debug('Now it is %r and %r - %r should be modified accordingly', self.home_share_file_server, self.class_share_file_server, self) self.modify_without_hooks(lo) # if requested, then create dhcp_dns policy that clears univentionDhcpDomainNameServers at OU level # to prevent problems with "wrong" DHCP DNS policy connected to ldap base if ucr.is_true('ucsschool/import/generate/policy/dhcp/dns/clearou', False): policy = DHCPDNSPolicy(name='dhcp-dns-clear', school=self.name, empty_attributes=['univentionDhcpDomainNameServers']) policy.create(lo) policy.attach(self, lo) return success
def get_all(cls, lo, filter_str=None, easy_filter=False, respect_local_oulist=True): schools = super(School, cls).get_all(lo, school=None, filter_str=filter_str, easy_filter=easy_filter) oulist = ucr.get('ucsschool/local/oulist') if oulist and respect_local_oulist: logger.debug('All Schools: Schools overridden by UCR variable ucsschool/local/oulist') ous = [x.strip() for x in oulist.split(',')] schools = [school for school in schools if school.name in ous] return cls._filter_local_schools(schools, lo)
def find_any_dn_with_name(cls, name, lo): logger.debug('Searching first dhcpServer with cn=%s', name) try: dn = lo.searchDn(filter=filter_format( '(&(objectClass=dhcpServer)(cn=%s))', [name]), base=ucr.get('ldap/base'))[0] except IndexError: dn = None logger.debug('... %r found', dn) return dn
def cache(cls, *args, **kwargs): '''Initializes a new instance and caches it for subsequent calls. Useful when using School.cache(school_name) a lot in different functions, in loops, etc. ''' args = list(args) if args: kwargs['name'] = args.pop(0) if args: kwargs['school'] = args.pop(0) key = [cls.__name__] + [(k, kwargs[k]) for k in sorted(kwargs)] key = tuple(key) if key not in cls._cache: logger.debug('Initializing %r', key) obj = cls(**kwargs) cls._cache[key] = obj return cls._cache[key]
def get_udm_object(self, lo): '''Returns the UDM object that corresponds to self. If self._meta.name_is_unique it searches for any UDM object with self.name. If not (which is the default) it searches for self.old_dn or self.dn Returns None if no object was found. Caches the result, even None If you want to re-search, you need to explicitely set self._udm_obj_searched = False ''' self.init_udm_module(lo) if self._udm_obj_searched is False or ( self._udm_obj and self._udm_obj.lo.binddn != lo.binddn): dn = self.old_dn or self.dn superordinate = self.get_superordinate(lo) if dn is None: logger.debug('Getting %s UDM object: No DN!', self.__class__.__name__) return if self._meta.name_is_unique: if self.name is None: return None udm_name = self._attributes['name'].udm_name name = self.get_name_from_dn(dn) filter_str = '%s=%s' % (udm_name, escape_filter_chars(name)) self._udm_obj = self.get_first_udm_obj(lo, filter_str, superordinate) else: logger.debug('Getting %s UDM object by dn: %s', self.__class__.__name__, dn) try: self._udm_obj = udm_modules.lookup( self._meta.udm_module, None, lo, scope='base', base=dn, superordinate=superordinate)[0] except (noObject, IndexError): self._udm_obj = None else: self._udm_obj.open() self._udm_obj_searched = True return self._udm_obj
def from_binddn(cls, lo): logger.debug('All local schools: Showing all OUs which DN %s can read.', lo.binddn) # get all schools of the user which are present on this server user_schools = lo.search(base=lo.binddn, scope='base', attr=['ucsschoolSchool'])[0][1].get('ucsschoolSchool', []) if user_schools: schools = [] for ou in user_schools: try: schools.append(cls.from_dn(cls(name=ou).dn, None, lo)) except noObject: pass return cls._filter_local_schools(schools, lo) if 'ou=' in lo.binddn: # user has no ucsschoolSchool attribute (not migrated yet) # we got an OU in the user DN -> school teacher or assistent # restrict the visibility to current school # (note that there can be schools with a DN such as ou=25g18,ou=25,dc=...) school_dn = lo.binddn[lo.binddn.find('ou='):] logger.debug('Schools from binddn: Found an OU in the LDAP binddn. Restricting schools to only show %s', school_dn) school = cls.from_dn(school_dn, None, lo) logger.debug('Schools from binddn: Found school: %r', school) return cls._filter_local_schools([school], lo) logger.warning('Schools from binddn: Unable to identify OU of this account - showing all local OUs!') return School.get_all(lo)
def call_hooks(self, hook_time, func_name): '''Calls run-parts in os.path.join(self.hook_path, '%s_%s_%s.d' % (self._meta.hook_path, func_name, hook_time)) if self.build_hook_line(hook_time, func_name) returns a non-empty string Usage in lib itself: hook_time in ['pre', 'post'] func_name in ['create', 'modify', 'remove'] In the lib, post-hooks are only called if the corresponding function returns True ''' # verify path hook_path = self._meta.hook_path path = os.path.join(self.hook_path, '%s_%s_%s.d' % (hook_path, func_name, hook_time)) if path in self._empty_hook_paths: return None if not os.path.isdir(path) or not os.listdir(path): logger.debug('%s not found or empty.', path) self._empty_hook_paths.add(path) return None logger.debug('%s shall be executed', path) dn = None if hook_time == 'post': dn = self.old_dn logger.debug('Building hook line: %r.build_hook_line(%r, %r)', self, hook_time, func_name) line = self.build_hook_line(hook_time, func_name) if not line: logger.debug('No line. Skipping!') return None line = line.strip() + '\n' # create temporary file with data with tempfile.NamedTemporaryFile() as tmpfile: tmpfile.write(line) tmpfile.flush() # invoke hook scripts # <script> <temporary file> [<ldap dn>] command = ['run-parts', path, '--arg', tmpfile.name] if dn: command.extend(('--arg', dn)) ret_code = subprocess.call(command) return ret_code == 0
def do_create(self, udm_obj, lo): if not self.schools: self.schools = [self.school] self.set_default_options(udm_obj) self.create_mail_domain(lo) password_created = False if not self.password: logger.debug('No password given. Generating random one') old_password = self.password # None or '' self.password = create_passwd(dn=self.dn) password_created = True udm_obj['primaryGroup'] = self.primary_group_dn(lo) udm_obj['groups'] = self.groups_used(lo) subdir = self.get_roleshare_home_subdir() udm_obj['unixhome'] = '/home/' + os.path.join(subdir, self.name) udm_obj['overridePWHistory'] = '1' udm_obj['overridePWLength'] = '1' if self.disabled is None: udm_obj['disabled'] = 'none' if 'mailbox' in udm_obj: udm_obj['mailbox'] = '/var/spool/%s/' % self.name samba_home = self.get_samba_home_path(lo) if samba_home: udm_obj['sambahome'] = samba_home profile_path = self.get_profile_path(lo) if profile_path: udm_obj['profilepath'] = profile_path home_drive = self.get_samba_home_drive() if home_drive is not None: udm_obj['homedrive'] = home_drive script_path = self.get_samba_netlogon_script_path() if script_path is not None: udm_obj['scriptpath'] = script_path success = super(User, self).do_create(udm_obj, lo) if password_created: # to not show up in host_hooks self.password = old_password return success
def get_all(cls, lo, school, filter_str=None, easy_filter=False, superordinate=None): ''' Returns a list of all objects that can be found in cls.get_container() with the correct udm_module If filter_str is given, all udm properties with include_in_default_search are queried for that string (so that it should be the value) ''' cls.init_udm_module(lo) complete_filter = cls._meta.udm_filter if easy_filter: filter_from_filter_str = cls.build_easy_filter(filter_str) else: filter_from_filter_str = filter_str if filter_from_filter_str: if complete_filter: complete_filter = conjunction( '&', [complete_filter, filter_from_filter_str]) else: complete_filter = filter_from_filter_str complete_filter = str(complete_filter) logger.debug('Getting all %s of %s with filter %r', cls.__name__, school, complete_filter) ret = [] for udm_obj in cls.lookup(lo, school, complete_filter, superordinate=superordinate): udm_obj.open() try: ret.append(cls.from_udm_obj(udm_obj, school, lo)) except UnknownModel: continue return ret
def from_dn(cls, dn, school, lo, superordinate=None): '''Returns a new instance based on the UDM object found at dn raises noObject if the udm_module does not match the dn or dn is not found ''' cls.init_udm_module(lo) if school is None and cls.supports_school(): school = cls.get_school_from_dn(dn) if school is None: logger.warn('Unable to guess school from %r', dn) try: logger.debug('Looking up %s with dn %r', cls.__name__, dn) udm_obj = udm_modules.lookup(cls._meta.udm_module, None, lo, filter=cls._meta.udm_filter, base=dn, scope='base', superordinate=superordinate)[0] except IndexError: # happens when cls._meta.udm_module does not "match" the dn raise WrongObjectType(dn, cls) return cls.from_udm_obj(udm_obj, school, lo)
def create_dc_slave(self, lo, name, administrative=False): if administrative and not self.shall_create_administrative_objects(): logger.warning('Not creating %s: An administrative DC shall not be created as by UCR variable %r', name, 'ucsschool/ldap/noneducational/create/objects') return False if not self.exists(lo): logger.error('%r does not exist. Cannot create %s', self, name) return False if administrative: groups = self.get_administrative_group_name('administrative', ou_specific='both', as_dn=True) else: groups = self.get_administrative_group_name('educational', ou_specific='both', as_dn=True) logger.debug('DC shall become member of %r', groups) dc = SchoolDCSlave(name=name, school=self.name, groups=groups) if dc.exists(lo): logger.info('%r exists. Setting groups, do not move to %r!', dc, self) # call dc.move() if really necessary to move return dc.modify(lo, move_if_necessary=False) else: existing_host = AnyComputer.get_first_udm_obj(lo, 'cn=%s' % escape_filter_chars(name)) if existing_host: logger.error('Given host name "%s" is already in use and no domaincontroller slave system. Please choose another name.', name) return False return dc.create(lo)
def do_modify(self, udm_obj, lo): self.create_mail_domain(lo) self.password = self.password or None removed_schools = set(udm_obj['school']) - set(self.schools) if removed_schools: # change self.schools back, so schools can be removed by remove_from_school() self.schools = udm_obj['school'] for removed_school in removed_schools: logger.info('Removing %r from school %r...', self, removed_school) if not self.remove_from_school(removed_school, lo): logger.error('Error removing %r from school %r.', self, removed_school) return False mandatory_groups = self.groups_used(lo) for group_dn in udm_obj['groups'][:]: logger.debug('Checking group %s for removal', group_dn) if group_dn not in mandatory_groups: logger.debug('Group not mandatory! Part of a school?') try: school_class = SchoolClass.from_dn(group_dn, None, lo) except noObject: logger.debug('No. Leaving it alone...') continue logger.debug('Yes, part of %s!', school_class.school) if school_class.school not in self.school_classes: continue # if the key isn't set we don't change anything to the groups. to remove the groups it has to be an empty list classes = self.school_classes[school_class.school] remove = school_class.name not in classes and school_class.get_relative_name( ) not in classes if remove: logger.debug('Removing it!') udm_obj['groups'].remove(group_dn) else: logger.debug( 'Leaving it alone: Part of own school and either non-school class or new school classes were not defined at all' ) for group_dn in mandatory_groups: logger.debug('Checking group %s for adding', group_dn) if group_dn not in udm_obj['groups']: logger.debug('Group is not yet part of the user. Adding...') udm_obj['groups'].append(group_dn) return super(User, self).do_modify(udm_obj, lo)
def find_all_dns_below_base(cls, dn, lo): logger.debug('Searching all univentionDhcpSubnet in %r', dn) return lo.searchDn(filter='(objectClass=univentionDhcpSubnet)', base=dn)
def invalidate_cache(cls): for key in cls._cache.keys(): if key[0] == cls.__name__: logger.debug('Invalidating %r', key) cls._cache.pop(key)