Example #1
0
class DHCPServer(UCSSchoolHelperAbstractClass):
    name = DHCPServerName(_('Server name'))
    dhcp_service = DHCPServiceAttribute(_('DHCP service'), required=True)

    def get_own_container(self):
        if self.dhcp_service:
            return self.dhcp_service.dn

    @classmethod
    def get_container(cls, school):
        return cls.get_search_base(school).dhcp

    def get_superordinate(self, lo):
        if self.dhcp_service:
            return self.dhcp_service.get_udm_object(lo)

    @classmethod
    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

    class Meta:
        udm_module = 'dhcp/server'
        name_is_unique = True
Example #2
0
class DHCPSubnet(UCSSchoolHelperAbstractClass):
    name = DHCPSubnetName(_('Subnet address'))
    subnet_mask = DHCPSubnetMask(_('Netmask'))
    broadcast = BroadcastAddress(_('Broadcast'))
    dhcp_service = DHCPServiceAttribute(_('DHCP service'), required=True)

    def get_own_container(self):
        if self.dhcp_service:
            return self.dhcp_service.dn

    @classmethod
    def get_container(cls, school):
        return cls.get_search_base(school).dhcp

    def get_superordinate(self, lo):
        if self.dhcp_service:
            return self.dhcp_service.get_udm_object(lo)

    def get_ipv4_subnet(self):
        network_str = '%s/%s' % (self.name, self.subnet_mask)
        try:
            return ipaddr.IPv4Network(network_str)
        except ValueError as exc:
            logger.info('%r is no valid IPv4Network:\n%s', network_str, exc)

    @classmethod
    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)

    class Meta:
        udm_module = 'dhcp/subnet'
Example #3
0
 def validate(self, value):
     super(DCName, self).validate(value)
     if value:
         regex = re.compile('^[a-zA-Z0-9](([a-zA-Z0-9-]*)([a-zA-Z0-9]$))?$')
         if not regex.match(value):
             raise ValueError(_('Invalid Domain Controller name'))
         if ucr.is_true('ucsschool/singlemaster', False):
             if len(value) > 12:
                 raise ValueError(
                     _('A valid NetBIOS hostname can not be longer than 12 characters.'
                       ))
             if sum([len(value), 1, len(ucr.get('domainname', ''))]) > 63:
                 raise ValueError(
                     _('The length of fully qualified domain name is greater than 63 characters.'
                       ))
Example #4
0
 def validate(self, value):
     if value is not None:
         if self.value_type and not isinstance(value, self.value_type):
             raise ValueError(
                 _('"%(label)s" needs to be a %(type)s') % {
                     'type': self.value_type.__name__,
                     'label': self.label
                 })
         values = value if self.value_type else [value]
         self._validate_syntax(values)
     else:
         if self.required:
             raise ValueError(
                 _('"%s" is required. Please provide this information.') %
                 self.label)
Example #5
0
class Student(User):
    type_name = _('Student')
    type_filter = '(&(objectClass=ucsschoolStudent)(!(objectClass=ucsschoolExam)))'
    roles = [role_pupil]
    default_options = ('ucsschoolStudent', )

    def do_school_change(self, udm_obj, lo, old_school):
        try:
            exam_user = ExamStudent.from_student_dn(lo, old_school,
                                                    self.old_dn)
        except noObject as exc:
            logger.info('No exam user for %r found: %s', self.old_dn, exc)
        else:
            logger.info('Removing exam user %r', exam_user.dn)
            exam_user.remove(lo)

        super(Student, self).do_school_change(udm_obj, lo, old_school)

    @classmethod
    def get_container(cls, school):
        return cls.get_search_base(school).students

    @classmethod
    def get_exam_container(cls, school):
        return cls.get_search_base(school).examUsers

    def get_specific_groups(self, lo):
        groups = super(Student, self).get_specific_groups(lo)
        groups.extend(self.get_students_groups())
        return groups
Example #6
0
class SchoolClass(Group, _MayHaveSchoolPrefix):
    name = SchoolClassName(_('Name'))

    ShareClass = ClassShare

    def create_without_hooks(self, lo, validate):
        success = super(SchoolClass, self).create_without_hooks(lo, validate)
        if self.exists(lo):
            success = success and self.create_share(
                self.get_machine_connection())
        return success

    def create_share(self, lo):
        share = self.ShareClass.from_school_group(self)
        return share.exists(lo) or share.create(lo)

    def modify_without_hooks(self, lo, validate=True, move_if_necessary=None):
        share = self.ShareClass.from_school_group(self)
        if self.old_dn:
            old_name = self.get_name_from_dn(self.old_dn)
            if old_name != self.name:
                # recreate the share.
                # if the name changed
                # from_school_group will have initialized
                # share.old_dn incorrectly
                share = self.ShareClass(name=old_name,
                                        school=self.school,
                                        school_group=self)
                share.name = self.name
        success = super(SchoolClass,
                        self).modify_without_hooks(lo, validate,
                                                   move_if_necessary)
        if success:
            lo_machine = self.get_machine_connection()
            if share.exists(lo_machine):
                success = share.modify(lo_machine)
            else:
                success = self.create_share(lo_machine)
        return success

    def remove_without_hooks(self, lo):
        success = super(SchoolClass, self).remove_without_hooks(lo)
        share = self.ShareClass.from_school_group(self)
        success = success and share.remove(self.get_machine_connection())
        return success

    @classmethod
    def get_container(cls, school):
        return cls.get_search_base(school).classes

    def to_dict(self):
        ret = super(SchoolClass, self).to_dict()
        ret['name'] = self.get_relative_name()
        return ret

    @classmethod
    def get_class_for_udm_obj(cls, udm_obj, school):
        if not cls.is_school_class(school, udm_obj.dn):
            return  # is a workgroup
        return cls
Example #7
0
class BasicGroup(Group):
    school = None
    container = Attribute(_('Container'), required=True)

    def __init__(self, name=None, school=None, **kwargs):
        if 'container' not in kwargs:
            kwargs['container'] = 'cn=groups,%s' % ucr.get('ldap/base')
        super(BasicGroup, self).__init__(name=name, school=school, **kwargs)

    def create_without_hooks(self, lo, validate):
        # prepare LDAP: create containers where this basic group lives if necessary
        container_dn = self.get_own_container()[:-len(ucr.get('ldap/base')) -
                                                1]
        containers = str2dn(container_dn)
        super_container_dn = ucr.get('ldap/base')
        for container_info in reversed(containers):
            dn_part, cn = container_info[0][0:2]
            if dn_part.lower() == 'ou':
                container = OU(name=cn)
            else:
                container = Container(name=cn, school='', group_path='1')
            container.position = super_container_dn
            super_container_dn = container.create(lo, False)
        return super(BasicGroup, self).create_without_hooks(lo, validate)

    def get_own_container(self):
        return self.container

    def build_hook_line(self, hook_time, func_name):
        return None

    @classmethod
    def get_container(cls, school=None):
        return ucr.get('ldap/base')
Example #8
0
 def _alter_udm_obj(self, udm_obj):
     super(SchoolComputer, self)._alter_udm_obj(udm_obj)
     inventory_numbers = self.get_inventory_numbers()
     if inventory_numbers:
         udm_obj['inventoryNumber'] = inventory_numbers
     ipv4_network = self.get_ipv4_network()
     if ipv4_network:
         if self._ip_is_set_to_subnet(ipv4_network):
             logger.info(
                 'IP was set to subnet. Unsetting it on the computer so that UDM can do some magic: Assign next free IP!'
             )
             udm_obj['ip'] = ''
         else:
             udm_obj['ip'] = str(ipv4_network.ip)
     # set network after ip. Otherwise UDM does not do any
     #   nextIp magic...
     network = self.get_network()
     if network:
         # reset network, so that next line triggers free ip
         udm_obj.old_network = None
         try:
             udm_obj['network'] = network.dn
         except nextFreeIp:
             logger.error(
                 'Tried to set IP automatically, but failed! %r is full',
                 network)
             raise nextFreeIp(
                 _('There are no free addresses left in the subnet!'))
Example #9
0
class Container(OU):
	user_path = ContainerPath(_('User path'), udm_name='userPath')
	computer_path = ContainerPath(_('Computer path'), udm_name='computerPath')
	network_path = ContainerPath(_('Network path'), udm_name='networkPath')
	group_path = ContainerPath(_('Group path'), udm_name='groupPath')
	dhcp_path = ContainerPath(_('DHCP path'), udm_name='dhcpPath')
	policy_path = ContainerPath(_('Policy path'), udm_name='policyPath')
	share_path = ContainerPath(_('Share path'), udm_name='sharePath')
	printer_path = ContainerPath(_('Printer path'), udm_name='printerPath')

	class Meta:
		udm_module = 'container/cn'
Example #10
0
 def validate(self, lo, validate_unlikely_changes=False):
     super(SchoolComputer, self).validate(lo, validate_unlikely_changes)
     if self.ip_address:
         name, ip_address = escape_filter_chars(
             self.name), escape_filter_chars(self.ip_address)
         if AnyComputer.get_first_udm_obj(
                 lo, '&(!(cn=%s))(ip=%s)' % (name, ip_address)):
             self.add_error(
                 'ip_address',
                 _('The ip address is already taken by another computer. Please change the ip address.'
                   ))
     if self.mac_address:
         name, mac_address = escape_filter_chars(
             self.name), escape_filter_chars(self.mac_address)
         if AnyComputer.get_first_udm_obj(
                 lo, '&(!(cn=%s))(mac=%s)' % (name, mac_address)):
             self.add_error(
                 'mac_address',
                 _('The mac address is already taken by another computer. Please change the mac address.'
                   ))
Example #11
0
 def validate(self, lo, validate_unlikely_changes=False):
     from ucsschool.lib.models.school import School
     self.errors.clear()
     self.warnings.clear()
     for name, attr in self._attributes.iteritems():
         value = getattr(self, name)
         try:
             attr.validate(value)
         except ValueError as e:
             self.add_error(name, str(e))
     if self._meta.name_is_unique and not self._meta.allow_school_change:
         if self.exists_outside_school(lo):
             self.add_error(
                 'name',
                 _('The name is already used somewhere outside the school. It may not be taken twice and has to be changed.'
                   ))
     if self.supports_school() and self.school:
         if not School.cache(self.school).exists(lo):
             self.add_error(
                 'school',
                 _('The school "%s" does not exist. Please choose an existing one or create it.'
                   ) % self.school)
     if validate_unlikely_changes:
         if self.exists(lo):
             udm_obj = self.get_udm_object(lo)
             try:
                 original_self = self.from_udm_obj(udm_obj, self.school, lo)
             except (UnknownModel, WrongModel):
                 pass
             else:
                 for name, attr in self._attributes.iteritems():
                     if attr.unlikely_to_change:
                         new_value = getattr(self, name)
                         old_value = getattr(original_self, name)
                         if new_value and old_value:
                             if new_value != old_value:
                                 self.add_warning(
                                     name,
                                     _('The value changed from %(old)s. This seems unlikely.'
                                       ) % {'old': old_value})
Example #12
0
class TeachersAndStaff(Teacher):
    type_name = _('Teacher and Staff')
    type_filter = '(&(objectClass=ucsschoolStaff)(objectClass=ucsschoolTeacher))'
    roles = [role_teacher, role_staff]
    default_options = ('ucsschoolStaff', )

    @classmethod
    def get_container(cls, school):
        return cls.get_search_base(school).teachersAndStaff

    def get_specific_groups(self, lo):
        groups = super(TeachersAndStaff, self).get_specific_groups(lo)
        groups.extend(self.get_staff_groups())
        return groups
Example #13
0
 def validate(self, lo, validate_unlikely_changes=False):
     super(User, self).validate(lo, validate_unlikely_changes)
     try:
         udm_obj = self.get_udm_object(lo)
     except UnknownModel:
         udm_obj = None
     except WrongModel as exc:
         udm_obj = None
         self.add_error(
             'name',
             _('It is not supported to change the role of a user. %(old_role)s %(name)s cannot become a %(new_role)s.'
               ) % {
                   'old_role': exc.model.type_name,
                   'name': self.name,
                   'new_role': self.type_name
               })
     if udm_obj:
         original_class = self.get_class_for_udm_obj(udm_obj, self.school)
         if original_class is not self.__class__:
             self.add_error(
                 'name',
                 _('It is not supported to change the role of a user. %(old_role)s %(name)s cannot become a %(new_role)s.'
                   ) % {
                       'old_role': original_class.type_name,
                       'name': self.name,
                       'new_role': self.type_name
                   })
     if self.email:
         name, email = escape_filter_chars(self.name), escape_filter_chars(
             self.email)
         if self.get_first_udm_obj(
                 lo, '&(!(uid=%s))(mailPrimaryAddress=%s)' % (name, email)):
             self.add_error(
                 'email',
                 _('The email address is already taken by another user. Please change the email address.'
                   ))
Example #14
0
class ExamStudent(Student):
    type_name = _('Exam student')
    type_filter = '(&(objectClass=ucsschoolStudent)(objectClass=ucsschoolExam))'
    default_options = ('ucsschoolExam', )

    @classmethod
    def get_container(cls, school):
        return cls.get_search_base(school).examUsers

    @classmethod
    def from_student_dn(cls, lo, school, dn):
        examUserPrefix = ucr.get('ucsschool/ldap/default/userprefix/exam',
                                 'exam-')
        dn = 'uid=%s%s,%s' % (escape_dn_chars(examUserPrefix),
                              explode_dn(dn,
                                         True)[0], cls.get_container(school))
        return cls.from_dn(dn, school, lo)
Example #15
0
class ComputerRoom(Group, _MayHaveSchoolPrefix):
    hosts = Hosts(_('Hosts'))
    users = None

    def to_dict(self):
        ret = super(ComputerRoom, self).to_dict()
        ret['name'] = self.get_relative_name()
        return ret

    @classmethod
    def get_container(cls, school):
        return cls.get_search_base(school).rooms

    def get_computers(self, ldap_connection):
        from ucsschool.lib.models.computer import SchoolComputer
        for host in self.hosts:
            try:
                yield SchoolComputer.from_dn(host, self.school,
                                             ldap_connection)
            except noObject:
                continue
Example #16
0
class Staff(User):
    school_classes = None
    type_name = _('Staff')
    roles = [role_staff]
    type_filter = '(&(!(objectClass=ucsschoolTeacher))(objectClass=ucsschoolStaff))'
    default_options = ('ucsschoolStaff', )

    @classmethod
    def get_container(cls, school):
        return cls.get_search_base(school).staff

    def get_samba_home_path(self, lo):
        """	Do not set sambaHomePath for staff users. """
        return None

    def get_samba_home_drive(self):
        """	Do not set sambaHomeDrive for staff users. """
        return None

    def get_samba_netlogon_script_path(self):
        """	Do not set sambaLogonScript for staff users. """
        return None

    def get_profile_path(self, lo):
        """	Do not set sambaProfilePath for staff users. """
        return None

    def get_school_class_objs(self):
        return []

    @classmethod
    def get_school_classes(cls, udm_obj, obj):
        return {}

    def get_specific_groups(self, lo):
        groups = super(Staff, self).get_specific_groups(lo)
        groups.extend(self.get_staff_groups())
        return groups
Example #17
0
class Share(UCSSchoolHelperAbstractClass):
    name = ShareName(_('Name'))
    school_group = SchoolClassAttribute(_('School class'),
                                        required=True,
                                        internal=True)

    @classmethod
    def from_school_group(cls, school_group):
        return cls(name=school_group.name,
                   school=school_group.school,
                   school_group=school_group)

    from_school_class = from_school_group  # legacy

    @classmethod
    def get_container(cls, school):
        return cls.get_search_base(school).shares

    def do_create(self, udm_obj, lo):
        gid = self.school_group.get_udm_object(lo)['gidNumber']
        udm_obj['host'] = self.get_server_fqdn(lo)
        udm_obj['path'] = self.get_share_path()
        udm_obj['writeable'] = '1'
        udm_obj['sambaWriteable'] = '1'
        udm_obj['sambaBrowseable'] = '1'
        udm_obj['sambaForceGroup'] = '+%s' % self.name
        udm_obj['sambaCreateMode'] = '0770'
        udm_obj['sambaDirectoryMode'] = '0770'
        udm_obj['owner'] = '0'
        udm_obj['group'] = gid
        udm_obj['directorymode'] = '0770'
        if ucr.is_false('ucsschool/default/share/nfs', True):
            try:
                udm_obj.options.remove('nfs')  # deactivate NFS
            except ValueError:
                pass
        logger.info('Creating share on "%s"', udm_obj['host'])
        return super(Share, self).do_create(udm_obj, lo)

    def get_share_path(self):
        if ucr.is_true('ucsschool/import/roleshare', True):
            return '/home/%s/groups/%s' % (self.school_group.school, self.name)
        else:
            return '/home/groups/%s' % self.name

    def do_modify(self, udm_obj, lo):
        old_name = self.get_name_from_dn(self.old_dn)
        if old_name != self.name:
            head, tail = os.path.split(udm_obj['path'])
            tail = self.name
            udm_obj['path'] = os.path.join(head, tail)
            if udm_obj['sambaName'] == old_name:
                udm_obj['sambaName'] = self.name
            if udm_obj['sambaForceGroup'] == '+%s' % old_name:
                udm_obj['sambaForceGroup'] = '+%s' % self.name
        return super(Share, self).do_modify(udm_obj, lo)

    def get_server_fqdn(self, lo):
        domainname = ucr.get('domainname')
        school = self.get_school_obj(lo)
        school_dn = school.dn

        # fetch serverfqdn from OU
        result = lo.get(school_dn, ['ucsschoolClassShareFileServer'])
        if result:
            server_domain_name = lo.get(
                result['ucsschoolClassShareFileServer'][0],
                ['associatedDomain'])
            if server_domain_name:
                server_domain_name = server_domain_name['associatedDomain'][0]
            else:
                server_domain_name = domainname
            result = lo.get(result['ucsschoolClassShareFileServer'][0], ['cn'])
            if result:
                return '%s.%s' % (result['cn'][0], server_domain_name)

        # get alternative server (defined at ou object if a dc slave is responsible for more than one ou)
        ou_attr_ldap_access_write = lo.get(school_dn,
                                           ['univentionLDAPAccessWrite'])
        alternative_server_dn = None
        if len(ou_attr_ldap_access_write) > 0:
            alternative_server_dn = ou_attr_ldap_access_write[
                'univentionLDAPAccessWrite'][0]
            if len(ou_attr_ldap_access_write) > 1:
                logger.warning(
                    'more than one corresponding univentionLDAPAccessWrite found at ou=%s',
                    self.school)

        # build fqdn of alternative server and set serverfqdn
        if alternative_server_dn:
            alternative_server_attr = lo.get(alternative_server_dn, ['uid'])
            if len(alternative_server_attr) > 0:
                alternative_server_uid = alternative_server_attr['uid'][0]
                alternative_server_uid = alternative_server_uid.replace(
                    '$', '')
                if len(alternative_server_uid) > 0:
                    return '%s.%s' % (alternative_server_uid, domainname)

        # fallback
        return '%s.%s' % (school.get_dc_name_fallback(), domainname)

    class Meta:
        udm_module = 'shares/share'
Example #18
0
class SchoolComputer(UCSSchoolHelperAbstractClass):
    ip_address = IPAddress(_('IP address'), required=True)
    subnet_mask = SubnetMask(_('Subnet mask'))
    mac_address = MACAddress(_('MAC address'), required=True)
    inventory_number = InventoryNumber(_('Inventory number'))
    zone = Attribute(_('Zone'))

    type_name = _('Computer')

    DEFAULT_PREFIX_LEN = 24  # 255.255.255.0

    def get_inventory_numbers(self):
        if isinstance(self.inventory_number, basestring):
            return [inv.strip() for inv in self.inventory_number.split(',')]
        if isinstance(self.inventory_number, (list, tuple)):
            return list(self.inventory_number)
        return []

    def _alter_udm_obj(self, udm_obj):
        super(SchoolComputer, self)._alter_udm_obj(udm_obj)
        inventory_numbers = self.get_inventory_numbers()
        if inventory_numbers:
            udm_obj['inventoryNumber'] = inventory_numbers
        ipv4_network = self.get_ipv4_network()
        if ipv4_network:
            if self._ip_is_set_to_subnet(ipv4_network):
                logger.info(
                    'IP was set to subnet. Unsetting it on the computer so that UDM can do some magic: Assign next free IP!'
                )
                udm_obj['ip'] = ''
            else:
                udm_obj['ip'] = str(ipv4_network.ip)
        # set network after ip. Otherwise UDM does not do any
        #   nextIp magic...
        network = self.get_network()
        if network:
            # reset network, so that next line triggers free ip
            udm_obj.old_network = None
            try:
                udm_obj['network'] = network.dn
            except nextFreeIp:
                logger.error(
                    'Tried to set IP automatically, but failed! %r is full',
                    network)
                raise nextFreeIp(
                    _('There are no free addresses left in the subnet!'))

    @classmethod
    def get_container(cls, school):
        return cls.get_search_base(school).computers

    def create(self, lo, validate=True):
        if self.subnet_mask is None:
            self.subnet_mask = self.DEFAULT_PREFIX_LEN
        return super(SchoolComputer, self).create(lo, validate)

    def create_without_hooks(self, lo, validate):
        self.create_network(lo)
        return super(SchoolComputer, self).create_without_hooks(lo, validate)

    def modify_without_hooks(self, lo, validate=True, move_if_necessary=None):
        self.create_network(lo)
        return super(SchoolComputer,
                     self).modify_without_hooks(lo, validate,
                                                move_if_necessary)

    def get_ipv4_network(self):
        if self.subnet_mask is not None:
            network_str = '%s/%s' % (self.ip_address, self.subnet_mask)
        else:
            network_str = str(self.ip_address)
        try:
            return IPv4Network(network_str)
        except (AddressValueError, NetmaskValueError, ValueError):
            logger.warning('Unparsable network: %r', network_str)

    def _ip_is_set_to_subnet(self, ipv4_network=None):
        ipv4_network = ipv4_network or self.get_ipv4_network()
        if ipv4_network:
            return ipv4_network.ip == ipv4_network.network

    def get_network(self):
        ipv4_network = self.get_ipv4_network()
        if ipv4_network:
            network_name = '%s-%s' % (self.school.lower(),
                                      ipv4_network.network)
            network = str(ipv4_network.network)
            netmask = str(ipv4_network.netmask)
            broadcast = str(ipv4_network.broadcast)
            return Network.cache(network_name,
                                 self.school,
                                 network=network,
                                 netmask=netmask,
                                 broadcast=broadcast)

    def create_network(self, lo):
        network = self.get_network()
        if network:
            network.create(lo)
        return network

    def validate(self, lo, validate_unlikely_changes=False):
        super(SchoolComputer, self).validate(lo, validate_unlikely_changes)
        if self.ip_address:
            name, ip_address = escape_filter_chars(
                self.name), escape_filter_chars(self.ip_address)
            if AnyComputer.get_first_udm_obj(
                    lo, '&(!(cn=%s))(ip=%s)' % (name, ip_address)):
                self.add_error(
                    'ip_address',
                    _('The ip address is already taken by another computer. Please change the ip address.'
                      ))
        if self.mac_address:
            name, mac_address = escape_filter_chars(
                self.name), escape_filter_chars(self.mac_address)
            if AnyComputer.get_first_udm_obj(
                    lo, '&(!(cn=%s))(mac=%s)' % (name, mac_address)):
                self.add_error(
                    'mac_address',
                    _('The mac address is already taken by another computer. Please change the mac address.'
                      ))

    @classmethod
    def get_class_for_udm_obj(cls, udm_obj, school):
        oc = udm_obj.lo.get(udm_obj.dn, ['objectClass'])
        object_classes = oc.get('objectClass', [])
        if 'univentionWindows' in object_classes:
            return WindowsComputer
        if 'univentionMacOSClient' in object_classes:
            return MacComputer
        if 'univentionCorporateClient' in object_classes:
            return UCCComputer
        if 'univentionClient' in object_classes:
            return IPComputer

    @classmethod
    def from_udm_obj(cls, udm_obj, school, lo):
        from ucsschool.lib.models.school import School
        obj = super(SchoolComputer, cls).from_udm_obj(udm_obj, school, lo)
        obj.ip_address = udm_obj['ip']
        school_obj = School.cache(obj.school)
        edukativnetz_group = school_obj.get_administrative_group_name(
            'educational', domain_controller=False, as_dn=True)
        if edukativnetz_group in udm_obj['groups']:
            obj.zone = 'edukativ'
        verwaltungsnetz_group = school_obj.get_administrative_group_name(
            'administrative', domain_controller=False, as_dn=True)
        if verwaltungsnetz_group in udm_obj['groups']:
            obj.zone = 'verwaltung'
        network_dn = udm_obj['network']
        if network_dn:
            netmask = Network.get_netmask(network_dn, school, lo)
            obj.subnet_mask = netmask
        obj.inventory_number = ', '.join(udm_obj['inventoryNumber'])
        return obj

    def build_hook_line(self, hook_time, func_name):
        module_part = self._meta.udm_module.split('/')[1]
        return self._build_hook_line(module_part, self.name, self.mac_address,
                                     self.school, self.get_ipv4_network(),
                                     ','.join(self.get_inventory_numbers()),
                                     self.zone)

    def to_dict(self):
        ret = super(SchoolComputer, self).to_dict()
        ret['type_name'] = self.type_name
        ret['type'] = self._meta.udm_module_short
        return ret

    class Meta:
        udm_module = 'computers/computer'
        name_is_unique = True
Example #19
0
class UCSSchoolHelperAbstractClass(object):
    '''
	Base class of all UCS@school models.
	Hides UDM.

	Attributes used for a class are defined like this:

	class MyModel(UCSSchoolHelperAbstractClass):
		my_attribute = Attribute('Label', required=True, udm_name='myAttr')

	From there on my_attribute=value may be passed to __init__,
	my_model.my_attribute can be accessed and the value will be saved
	as obj['myAttr'] in UDM when saving this instance.
	If an attribute of a base class is not wanted, it can be overridden:

	class MyModel(UCSSchoolHelperAbstractClass):
		school = None

	Meta information about the class are defined like this:
	class MyModel(UCSSchoolHelperAbstractClass):
		class Meta:
			udm_module = 'my/model'

	The meta information is then accessible in cls._meta

	Important functions:
		__init__(**kwargs):
			kwargs should be the defined attributes
		create(lo)
			lo is an LDAP connection, specifically univention.admin.access.
			creates a new object. Returns False is the object already exists.
			And True after the creation
		modify(lo)
			modifies an existing object. Returns False if the object does not
			exist and True after the modification (regardless whether something
			actually changed or not)
		remove(lo)
			deletes the object. Returns False if the object does not exist and True
			after the deletion.
		get_all(lo, school, filter_str, easy_filter=False)
			classmethod; retrieves all objects found for this school. filter can be a string
			that is used to narrow down a search. Each property of the class' udm_module
			that is include_in_default_search is queried for that string.
			Example:
			User.get_all(lo, 'school', filter_str='name', easy_filter=True)
			will search in cn=users,ou=school,$base
			for users/user UDM objects with |(username=*name*)(firstname=*name*)(...) and return
			User objects (not UDM objects)
			With easy_filter=False (default) it will use this very filter_str
		get_container(school)
			a classmethod that points to the container where new instances are created
			and existing ones are searched.
		dn
			property, current distinguishable name of the instance. Calculated on the fly, it
			changes if instance.name or instance.school changes.
			instance.old_dn will be set to the original dn when the instance was created
		get_udm_object(lo)
			searches UDM for an entry that corresponds to self. Normally uses the old_dn or dn.
			If cls._meta.name_is_unique then any object with self.name will match
		exists(lo)
			whether this object can be found in UDM.
		from_udm_obj(udm_obj, school, lo)
			classmethod; maps the info of udm_obj into a new instance (and sets school)
		from_dn(dn, school, lo)
			finds dn in LDAP and uses from_udm_obj
		get_first_udm_obj(lo, filter_str)
			returns the first found object of type cls._meta.udm_module that matches an
			arbitrary filter_str

	More features:
	* Validation:
		There are some auto checks built in: Attributes of the model that have a
		UDM syntax attached are validated against this syntax. Attributes that are
		required must be present.
		Attributes that are unlikely_to_change give a warning (not error) if the object
		already exists with other values.
		If the Meta information states that name_is_unique, the complete LDAP is searched
		for the instance's name before continuing.
		validate() can be further customized.
	* Hooks:
		Before create, modify, move and remove, hooks are called if build_hook_line()
		returns something. If the operation was successful, another set of hooks
		are called.
		All scripts in
		/usr/share/ucs-school-import/hooks/%(module)s_{create|modify|move|remove}_{pre|post}.d/
		are called with the name of a temporary file containing the hook_line via run-parts.
		%(module)s is 'ucc' for cls._meta.udm_module == 'computers/ucc' by default and
		can be explicitely set with
		class Meta:
			hook_path = 'computer'
	'''
    __metaclass__ = UCSSchoolHelperMetaClass
    _cache = {}

    _search_base_cache = {}
    _initialized_udm_modules = []
    _empty_hook_paths = set()

    hook_sep_char = '\t'
    hook_path = '/usr/share/ucs-school-import/hooks/'

    name = CommonName(_('Name'), aka=['Name'])
    school = SchoolAttribute(_('School'), aka=['School'])

    @classmethod
    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]

    @classmethod
    def invalidate_all_caches(cls):
        from ucsschool.lib.models.user import User
        from ucsschool.lib.models.network import Network
        from ucsschool.lib.models.utils import _pw_length_cache
        cls._cache.clear()
        # cls._search_base_cache.clear() # useless to clear
        _pw_length_cache.clear()
        Network._netmask_cache.clear()
        User._profile_path_cache.clear()
        User._samba_home_path_cache.clear()

    @classmethod
    def invalidate_cache(cls):
        for key in cls._cache.keys():
            if key[0] == cls.__name__:
                logger.debug('Invalidating %r', key)
                cls._cache.pop(key)

    @classmethod
    def supports_school(cls):
        return 'school' in cls._attributes

    def __init__(self, name=None, school=None, **kwargs):
        '''Initializes a new instance with kwargs.
		Not every kwarg is accepted, though: The name
		must be defined as a attribute at class level
		(or by a base class). All attributes are
		initialized at least with None
		Sets self.old_dn to self.dn, i.e. the name
		in __init__ will determine the old_dn, changing
		it after __init__ will result in trying to move the
		object!
		'''
        self._udm_obj_searched = False
        self._udm_obj = None
        kwargs['name'] = name
        kwargs['school'] = school
        for key, attr in self._attributes.items():
            default = attr.value_default
            if callable(default):
                default = default()
            setattr(self, key, kwargs.get(key, default))
        self.__position = None
        self.old_dn = None
        self.old_dn = self.dn
        self.errors = {}
        self.warnings = {}

    @classmethod
    @LDAP_Connection()
    def get_machine_connection(cls,
                               ldap_user_read=None,
                               ldap_machine_write=None):
        """Shortcut to get a cached ldap connection to the DC Master using this host's credentials"""
        return ldap_machine_write

    @property
    def position(self):
        if self.__position is None:
            return self.get_own_container()
        return self.__position

    @position.setter
    def position(self, position):
        if self.position != position:  # allow dynamic school changes until creation
            self.__position = position

    @property
    def dn(self):
        '''Generates a DN where the lib would assume this
		instance to be. Changing name or school of self will most
		likely change the outcome of self.dn as well
		'''
        if self.name and self.position:
            name = self._meta.ldap_map_function(self.name)
            return '%s=%s,%s' % (self._meta.ldap_name_part,
                                 escape_dn_chars(name), self.position)
        return self.old_dn

    def set_dn(self, dn):
        '''Does not really set dn, as this is generated
		on-the-fly. Instead, sets old_dn in case it was
		missed in the beginning or after create/modify/remove/move
		Also resets cached udm_obj as it may point to somewhere else
		'''
        self._udm_obj_searched = False
        self.position = ldap.dn.dn2str(ldap.dn.str2dn(dn)[1:])
        self.old_dn = dn

    def validate(self, lo, validate_unlikely_changes=False):
        from ucsschool.lib.models.school import School
        self.errors.clear()
        self.warnings.clear()
        for name, attr in self._attributes.iteritems():
            value = getattr(self, name)
            try:
                attr.validate(value)
            except ValueError as e:
                self.add_error(name, str(e))
        if self._meta.name_is_unique and not self._meta.allow_school_change:
            if self.exists_outside_school(lo):
                self.add_error(
                    'name',
                    _('The name is already used somewhere outside the school. It may not be taken twice and has to be changed.'
                      ))
        if self.supports_school() and self.school:
            if not School.cache(self.school).exists(lo):
                self.add_error(
                    'school',
                    _('The school "%s" does not exist. Please choose an existing one or create it.'
                      ) % self.school)
        if validate_unlikely_changes:
            if self.exists(lo):
                udm_obj = self.get_udm_object(lo)
                try:
                    original_self = self.from_udm_obj(udm_obj, self.school, lo)
                except (UnknownModel, WrongModel):
                    pass
                else:
                    for name, attr in self._attributes.iteritems():
                        if attr.unlikely_to_change:
                            new_value = getattr(self, name)
                            old_value = getattr(original_self, name)
                            if new_value and old_value:
                                if new_value != old_value:
                                    self.add_warning(
                                        name,
                                        _('The value changed from %(old)s. This seems unlikely.'
                                          ) % {'old': old_value})

    def add_warning(self, attribute, warning_message):
        warnings = self.warnings.setdefault(attribute, [])
        if warning_message not in warnings:
            warnings.append(warning_message)

    def add_error(self, attribute, error_message):
        errors = self.errors.setdefault(attribute, [])
        if error_message not in errors:
            errors.append(error_message)

    def exists(self, lo):
        return self.get_udm_object(lo) is not None

    def exists_outside_school(self, lo):
        if not self.supports_school():
            return False
        from ucsschool.lib.models.school import School
        udm_obj = self.get_udm_object(lo)
        if udm_obj is None:
            return False
        return not udm_obj.dn.endswith(School.cache(self.school).dn)

    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 build_hook_line(self, hook_time, func_name):
        '''Must be overridden if the model wants to support hooks.
		Do so by something like:
		return self._build_hook_line(self.attr1, self.attr2, 'constant')
		'''
        return None

    def _alter_udm_obj(self, udm_obj):
        for name, attr in self._attributes.iteritems():
            if attr.udm_name:
                value = getattr(self, name)
                if value is not None:
                    udm_obj[attr.udm_name] = value

    def create(self, lo, validate=True):
        '''
		Creates a new UDM instance.
		Calls pre-hooks.
		If the object already exists, returns False.
		If the object does not yet exist, creates it, returns True and
		calls post-hooks.
		'''
        self.call_hooks('pre', 'create')
        success = self.create_without_hooks(lo, validate)
        if success:
            self.call_hooks('post', 'create')
        return success

    def create_without_hooks(self, lo, validate):
        if self.exists(lo):
            return False
        logger.info('Creating %r', self)

        if validate:
            self.validate(lo)
            if self.errors:
                raise ValidationError(self.errors.copy())

        pos = udm_uldap.position(ucr.get('ldap/base'))
        container = self.position
        if not container:
            logger.error('%r cannot determine a container. Unable to create!',
                         self)
            return False
        try:
            pos.setDn(container)
            udm_obj = udm_modules.get(self._meta.udm_module).object(
                None, lo, pos, superordinate=self.get_superordinate(lo))
            udm_obj.open()

            # here is the real logic
            self.do_create(udm_obj, lo)

            # get it fresh from the database (needed for udm_obj._exists ...)
            self.set_dn(self.dn)
            logger.info('%r successfully created', self)
            return True
        finally:
            self.invalidate_cache()

    def do_create(self, udm_obj, lo):
        '''Actual udm_obj manipulation. Override this if
		you want to further change values of udm_obj, e.g.
		def do_create(self, udm_obj, lo):
			udm_obj['used_in_ucs_school'] = '1'
			super(MyModel, self).do_create(udm_obj, lo)
		'''
        self._alter_udm_obj(udm_obj)
        udm_obj.create()

    def modify(self, lo, validate=True, move_if_necessary=None):
        '''
		Modifies an existing UDM instance.
		Calls pre-hooks.
		If the object does not exist, returns False.
		If the object exists, modifies it, returns True and
		calls post-hooks.
		'''
        self.call_hooks('pre', 'modify')
        success = self.modify_without_hooks(lo, validate, move_if_necessary)
        if success:
            self.call_hooks('post', 'modify')
        return success

    def modify_without_hooks(self, lo, validate=True, move_if_necessary=None):
        logger.info('Modifying %r', self)

        if move_if_necessary is None:
            move_if_necessary = self._meta.allow_school_change

        if validate:
            self.validate(lo, validate_unlikely_changes=True)
            if self.errors:
                raise ValidationError(self.errors.copy())

        udm_obj = self.get_udm_object(lo)
        if not udm_obj:
            logger.info('%s does not exist!', self.old_dn)
            return False

        try:
            old_attrs = deepcopy(udm_obj.info)
            self.do_modify(udm_obj, lo)
            # get it fresh from the database
            self.set_dn(self.dn)
            udm_obj = self.get_udm_object(lo)
            same = old_attrs == udm_obj.info
            if move_if_necessary:
                if udm_obj.dn != self.dn:
                    if self.move_without_hooks(lo, udm_obj, force=True):
                        same = False
            if same:
                logger.info('%r not modified. Nothing changed', self)
            else:
                logger.info('%r successfully modified', self)
            # return not same
            return True
        finally:
            self.invalidate_cache()

    def do_modify(self, udm_obj, lo):
        '''Actual udm_obj manipulation. Override this if
		you want to further change values of udm_obj, e.g.
		def do_modify(self, udm_obj, lo):
			udm_obj['used_in_ucs_school'] = '1'
			super(MyModel, self).do_modify(udm_obj, lo)
		'''
        self._alter_udm_obj(udm_obj)
        udm_obj.modify(ignore_license=1)

    def move(self, lo, udm_obj=None, force=False):
        self.call_hooks('pre', 'move')
        success = self.move_without_hooks(lo, udm_obj, force)
        if success:
            self.call_hooks('post', 'move')
        return success

    def move_without_hooks(self, lo, udm_obj, force=False):
        if udm_obj is None:
            udm_obj = self.get_udm_object(lo)
        if udm_obj is None:
            logger.warning('No UDM object found to move from (%r)', self)
            return False
        if self.supports_school() and self.get_school_obj(lo) is None:
            logger.warn('%r wants to move itself to a not existing school',
                        self)
            return False
        logger.info('Moving %r to %r', udm_obj.dn, self)
        if udm_obj.dn == self.dn:
            logger.warning('%r wants to move to its own DN!', self)
            return False
        if force or self._meta.allow_school_change:
            try:
                self.do_move(udm_obj, lo)
            finally:
                self.invalidate_cache()
            self.set_dn(self.dn)
        else:
            logger.warning(
                'Would like to move %s to %r. But it is not allowed!',
                udm_obj.dn, self)
            return False
        return True

    def do_move(self, udm_obj, lo):
        old_school, new_school = self.get_school_from_dn(
            self.old_dn), self.get_school_from_dn(self.dn)
        udm_obj.move(self.dn, ignore_license=1)
        if self.supports_school() and old_school and old_school != new_school:
            self.do_school_change(udm_obj, lo, old_school)

    def change_school(self, school, lo):
        if self.school in self.schools:
            self.schools.remove(self.school)
        if school not in self.schools:
            self.schools.append(school)
        self.school = school
        self.position = self.get_own_container()
        return self.move(lo, force=True)

    def do_school_change(self, udm_obj, lo, old_school):
        logger.info('Going to move %r from school %r to %r', self.old_dn,
                    old_school, self.school)

    def remove(self, lo):
        '''
		Removes an existing UDM instance.
		Calls pre-hooks.
		If the object does not exist, returns False.
		If the object exists, removes it, returns True and
		calls post-hooks.
		'''
        self.call_hooks('pre', 'remove')
        success = self.remove_without_hooks(lo)
        if success:
            self.call_hooks('post', 'remove')
        return success

    def remove_without_hooks(self, lo):
        logger.info('Deleting %r', self)
        udm_obj = self.get_udm_object(lo)
        if udm_obj:
            try:
                udm_obj.remove(remove_childs=True)
                udm_objects.performCleanup(udm_obj)
                self.set_dn(None)
                logger.info('%r successfully removed', self)
                return True
            finally:
                self.invalidate_cache()
        logger.info('%r does not exist!', self)
        return False

    @classmethod
    def get_name_from_dn(cls, dn):
        if dn:
            try:
                name = explode_dn(dn, 1)[0]
            except ldap.DECODING_ERROR:
                name = ''
            return cls._meta.ldap_unmap_function([name])

    @classmethod
    def get_school_from_dn(cls, dn):
        return SchoolSearchBase.getOU(dn)

    @classmethod
    def find_field_label_from_name(cls, field):
        for name, attr in cls._attributes.items():
            if name == field:
                return attr.label

    def get_error_msg(self):
        error_msg = ''
        for key, errors in self.errors.iteritems():
            label = self.find_field_label_from_name(key)
            error_str = ''
            for error in errors:
                error_str += error
                if not (error.endswith('!') or error.endswith('.')):
                    error_str += '.'
                error_str += ' '
            error_msg += '%s: %s' % (label, error_str)
        return error_msg[:-1]

    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 get_school_obj(self, lo):
        from ucsschool.lib.models.school import School
        if not self.supports_school():
            return None
        school = School.cache(self.school)
        try:
            return School.from_dn(school.dn, None, lo)
        except noObject:
            logger.warning('%r does not exist!', school)
            return None

    def get_superordinate(self, lo):
        return None

    def get_own_container(self):
        if self.supports_school() and not self.school:
            return None
        return self.get_container(self.school)

    @classmethod
    def get_container(cls, school):
        '''raises NotImplementedError by default. Needs to be overridden!
		'''
        raise NotImplementedError('%s.get_container()' % (cls.__name__, ))

    @classmethod
    def get_search_base(cls, school_name):
        from ucsschool.lib.models.school import School
        if school_name not in cls._search_base_cache:
            school = School(name=school_name)
            cls._search_base_cache[school_name] = SchoolSearchBase(
                [school.name], dn=school.dn)
        return cls._search_base_cache[school_name]

    @classmethod
    def init_udm_module(cls, lo):
        if cls._meta.udm_module in cls._initialized_udm_modules:
            return
        pos = udm_uldap.position(lo.base)
        udm_modules.init(lo, pos, udm_modules.get(cls._meta.udm_module))
        cls._initialized_udm_modules.append(cls._meta.udm_module)

    @classmethod
    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

    @classmethod
    def lookup(cls, lo, school, filter_s='', superordinate=None):
        try:
            return udm_modules.lookup(cls._meta.udm_module,
                                      None,
                                      lo,
                                      filter=filter_s,
                                      base=cls.get_container(school),
                                      scope='sub',
                                      superordinate=superordinate)
        except noObject:
            logger.warning(
                'Error while getting all %s of %s: probably %r does not exist!',
                cls.__name__, school, cls.get_container(school))
            return []

    @classmethod
    def _attrs_for_easy_filter(cls):
        ret = []
        module = udm_modules.get(cls._meta.udm_module)
        for key, prop in module.property_descriptions.iteritems():
            if prop.include_in_default_search:
                ret.append(key)
        return ret

    @classmethod
    def build_easy_filter(cls, filter_str):
        if filter_str:
            sanitizer = LDAPSearchSanitizer()
            filter_str = sanitizer.sanitize('filter_str',
                                            {'filter_str': filter_str})
            expressions = []
            for key in cls._attrs_for_easy_filter():
                expressions.append(expression(key, filter_str))
            if expressions:
                return conjunction('|', expressions)

    @classmethod
    def from_udm_obj(
        cls, udm_obj, school, lo
    ):  # Design fault. school is part of the DN or the ucsschoolSchool attribute.
        '''Creates a new instance with attributes of the udm_obj.
		Uses get_class_for_udm_obj()
		'''
        cls.init_udm_module(lo)
        klass = cls.get_class_for_udm_obj(udm_obj, school)
        if klass is None:
            logger.warning(
                'UDM object %s does not correspond to a class in UCS school lib!',
                udm_obj.dn)
            raise UnknownModel(udm_obj.dn, cls)
        if klass is not cls:
            logger.info('UDM object %s is not %s, but actually %s', udm_obj.dn,
                        cls.__name__, klass.__name__)
            if not issubclass(klass, cls):
                # security!
                # ExamStudent must not be converted into Teacher/Student/etc.,
                # SchoolClass must not be converted into ComputerRoom
                # while Group must be converted into ComputerRoom, etc. and User must be converted into Student, etc.
                raise WrongModel(udm_obj.dn, klass, cls)
            return klass.from_udm_obj(udm_obj, school, lo)
        udm_obj.open()
        attrs = {
            'school': cls.get_school_from_dn(udm_obj.dn) or school
        }  # TODO: is this adjustment okay?
        for name, attr in cls._attributes.iteritems():
            if attr.udm_name:
                udm_value = udm_obj[attr.udm_name]
                if udm_value == '':
                    udm_value = None
                attrs[name] = udm_value
        obj = cls(**deepcopy(attrs))
        obj.set_dn(udm_obj.dn)
        obj._udm_obj_searched = True
        obj._udm_obj = udm_obj
        return obj

    @classmethod
    def get_class_for_udm_obj(cls, udm_obj, school):
        '''Returns cls by default.
		Can be overridden for base classes:
		class User(UCSSchoolHelperAbstractClass):
			@classmethod
			def get_class_for_udm_obj(cls, udm_obj, school)
				if something:
					return SpecialUser
				return cls

		class SpecialUser(User):
			pass

		Now, User.get_all() will return a list of User and SpecialUser objects
		If this function returns None for a udm_obj, that obj will not
		yield a new instance in get_all() and from_udm_obj() will return None
		for that udm_obj
		'''
        return cls

    def __repr__(self):
        dn = self.dn
        dn = '%r, old_dn=%r' % (dn,
                                self.old_dn) if dn != self.old_dn else repr(dn)
        if self.supports_school():
            return '%s(name=%r, school=%r, dn=%s)' % (
                self.__class__.__name__, self.name, self.school, dn)
        else:
            return '%s(name=%r, dn=%s)' % (self.__class__.__name__, self.name,
                                           dn)

    def __lt__(self, other):
        return self.name < other.name

    @classmethod
    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)

    @classmethod
    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

    @classmethod
    def get_first_udm_obj(cls, lo, filter_str, superordinate=None):
        '''Returns the first UDM object of class cls._meta.udm_module that
		matches a given filter
		'''
        try:
            return cls.get_only_udm_obj(lo, filter_str, superordinate)
        except MultipleObjectsError as exc:
            obj = exc.objs[0]
            obj.open()
            return obj

    @classmethod
    def find_udm_superordinate(cls, dn, lo):
        module = udm_modules.get(cls._meta.udm_module)
        return udm_objects.get_superordinate(module, None, lo, dn)

    def to_dict(self):
        '''Returns a dictionary somewhat representing this instance.
		This dictionary is usually used when sending the instance to
		a browser as JSON.
		By default the attributes are present as well as the dn and
		the udm_module.'''
        ret = {'$dn$': self.dn, 'objectType': self._meta.udm_module}
        for name, attr in self._attributes.iteritems():
            if not attr.internal:
                ret[name] = getattr(self, name)
        return ret

    def _map_func_name_to_code(self, func_name):
        if func_name == 'create':
            return 'A'
        elif func_name == 'modify':
            return 'M'
        elif func_name == 'remove':
            return 'D'
        elif func_name == 'move':
            return 'MV'

    def _build_hook_line(self, *args):
        attrs = []
        for arg in args:
            val = arg
            if arg is None:
                val = ''
            if arg is False:
                val = 0
            if arg is True:
                val = 1
            attrs.append(str(val))
        return self.hook_sep_char.join(attrs)
Example #20
0
class School(UCSSchoolHelperAbstractClass):
	name = SchoolName(_('School name'))
	dc_name = DCName(_('DC Name'))
	dc_name_administrative = DCName(_('DC Name administrative server'))
	class_share_file_server = ShareFileServer(_('Server for class shares'), udm_name='ucsschoolClassShareFileServer')
	home_share_file_server = ShareFileServer(_('Server for Windows home directories'), udm_name='ucsschoolHomeShareFileServer')
	display_name = DisplayName(_('Display name'))
	school = None
	educational_servers = Attribute(_('Educational servers'), unlikely_to_change=True)
	administrative_servers = Attribute(_('Administrative servers'), unlikely_to_change=True)

	def __init__(self, name=None, school=None, **kwargs):
		super(School, self).__init__(name=name, **kwargs)
		self.display_name = self.display_name or self.name

	def build_hook_line(self, hook_time, func_name):
		if func_name == 'create':
			return self._build_hook_line(self.name, self.get_dc_name(or_fallback=False))

	def get_district(self):
		if ucr.is_true('ucsschool/ldap/district/enable'):
			return self.name[:2]

	def get_own_container(self):
		district = self.get_district()
		if district:
			return 'ou=%s,%s' % (escape_dn_chars(district), self.get_container())
		return self.get_container()

	@classmethod
	def get_container(cls, school=None):
		return ucr.get('ldap/base')

	@classmethod
	def cn_name(cls, name, default):
		ucr_var = 'ucsschool/ldap/default/container/%s' % name
		return ucr.get(ucr_var, default)

	def create_default_containers(self, lo):
		cn_pupils = self.cn_name('pupils', 'schueler')
		cn_teachers = self.cn_name('teachers', 'lehrer')
		cn_admins = self.cn_name('admins', 'admins')
		cn_classes = self.cn_name('class', 'klassen')
		cn_rooms = self.cn_name('rooms', 'raeume')
		user_containers = [cn_pupils, cn_teachers, cn_admins]
		group_containers = [cn_pupils, [cn_classes], cn_teachers, cn_rooms]
		if self.shall_create_administrative_objects():
			cn_staff = self.cn_name('staff', 'mitarbeiter')
			cn_teachers_staff = self.cn_name('teachers-and-staff', 'lehrer und mitarbeiter')
			user_containers.extend([cn_staff, cn_teachers_staff])
			group_containers.append(cn_staff)
		containers_with_path = {
			'printer_path': ['printers'],
			'user_path': ['users', user_containers],
			'computer_path': ['computers', ['server', ['dc']]],
			'network_path': ['networks'],
			'group_path': ['groups', group_containers],
			'dhcp_path': ['dhcp'],
			'policy_path': ['policies'],
			'share_path': ['shares', [cn_classes]],
		}

		def _add_container(name, last_dn, base_dn, path, lo):
			if isinstance(name, (list, tuple)):
				base_dn = last_dn
				for cn in name:
					last_dn = _add_container(cn, last_dn, base_dn, path, lo)
			else:
				container = Container(name=name, school=self.name)
				setattr(container, path, '1')
				container.position = base_dn
				last_dn = container.dn
				if not container.exists(lo):
					last_dn = container.create(lo, False)
			return last_dn

		last_dn = self.dn
		path = None
		for path, containers in containers_with_path.iteritems():
			for cn in containers:
				last_dn = _add_container(cn, last_dn, self.dn, path, lo)

	def group_name(self, prefix_var, default_prefix):
		ucr_var = 'ucsschool/ldap/default/groupprefix/%s' % prefix_var
		name_part = ucr.get(ucr_var, default_prefix)
		school_part = self.name.lower()
		return '%s%s' % (name_part, school_part)

	def get_umc_policy_dn(self, name):
		# at least the default ones should exist due to the join script
		return ucr.get('ucsschool/ldap/default/policy/umc/%s' % name, 'cn=ucsschool-umc-%s-default,cn=UMC,cn=policies,%s' % (name, ucr.get('ldap/base')))

	def create_default_groups(self, lo):
		# DC groups
		administrative_group_container = 'cn=ucsschool,cn=groups,%s' % ucr.get('ldap/base')

		# DC-Edukativnetz
		# OU%s-DC-Edukativnetz
		# Member-Edukativnetz
		# OU%s-Member-Edukativnetz
		administrative_group_names = self.get_administrative_group_name('educational', domain_controller='both', ou_specific='both')
		if self.shall_create_administrative_objects():
			administrative_group_names.extend(self.get_administrative_group_name('administrative', domain_controller='both', ou_specific='both'))  # same with Verwaltungsnetz
		for administrative_group_name in administrative_group_names:
			group = BasicGroup.cache(name=administrative_group_name, container=administrative_group_container)
			group.create(lo)

		# cn=ouadmins
		admin_group_container = 'cn=ouadmins,cn=groups,%s' % ucr.get('ldap/base')
		group = BasicGroup.cache(self.group_name('admins', 'admins-'), container=admin_group_container)
		group.create(lo)
		group.add_umc_policy(self.get_umc_policy_dn('admins'), lo)
		try:
			udm_obj = group.get_udm_object(lo)
		except noObject:
			logger.error('Could not load OU admin group %r for adding "school" value', group.dn)
		else:
			admin_option = 'ucsschoolAdministratorGroup'
			if admin_option not in udm_obj.options:
				udm_obj.options.append(admin_option)
			udm_obj['school'] = [self.name]
			udm_obj.modify()

		# cn=schueler
		group = Group.cache(self.group_name('pupils', 'schueler-'), self.name)
		group.create(lo)
		group.add_umc_policy(self.get_umc_policy_dn('pupils'), lo)

		# cn=lehrer
		group = Group.cache(self.group_name('teachers', 'lehrer-'), self.name)
		group.create(lo)
		group.add_umc_policy(self.get_umc_policy_dn('teachers'), lo)

		# cn=mitarbeiter
		if self.shall_create_administrative_objects():
			group = Group.cache(self.group_name('staff', 'mitarbeiter-'), self.name)
			group.create(lo)
			group.add_umc_policy(self.get_umc_policy_dn('staff'), lo)

		if ucr.is_true('ucsschool/import/attach/policy/default-umc-users', True):
			# cn=Domain Users %s
			group = Group.cache("Domain Users %s" % (self.name,), self.name)
			group.create(lo)
			group.add_umc_policy("cn=default-umc-users,cn=UMC,cn=policies,%s" % (ucr.get('ldap/base'),), lo)

	def get_dc_name_fallback(self, administrative=False):
		if administrative:
			return 'dc%sv-01' % self.name.lower()  # this is the naming convention, a trailing v for Verwaltungsnetz DCs
		else:
			return 'dc%s-01' % self.name.lower()

	def get_dc_name(self, administrative=False, or_fallback=True):
		if ucr.is_true('ucsschool/singlemaster', False):
			return ucr.get('hostname')
		elif self.dc_name:
			if administrative:
				return '%sv' % self.dc_name
			else:
				return self.dc_name
		else:
			if or_fallback:
				return self.get_dc_name_fallback(administrative=administrative)
			else:
				return None

	def get_share_fileserver_dn(self, set_by_self, lo):
		if set_by_self:
			set_by_self = self.get_name_from_dn(set_by_self) or set_by_self
		hostname = set_by_self or self.get_dc_name()
		if hostname == self.get_dc_name_fallback():
			# does not matter if exists or not - dc object will be created later
			host = SchoolDC(name=hostname, school=self.name)
			return host.dn

		host = AnyComputer.get_first_udm_obj(lo, 'cn=%s' % escape_filter_chars(hostname))
		if host:
			return host.dn
		else:
			logger.warning('Could not find %s. Using this host as ShareFileServer ("%s").', hostname, ucr.get('hostname'))
			return ucr.get('ldap/hostdn')

	def get_class_share_file_server(self, lo):
		return self.get_share_fileserver_dn(self.class_share_file_server, lo)

	def get_home_share_file_server(self, lo):
		return self.get_share_fileserver_dn(self.home_share_file_server, lo)

	def get_administrative_group_name(self, group_type, domain_controller=True, ou_specific=False, as_dn=False):
		if domain_controller == 'both':
			return flatten([self.get_administrative_group_name(group_type, True, ou_specific, as_dn), self.get_administrative_group_name(group_type, False, ou_specific, as_dn)])
		if ou_specific == 'both':
			return flatten([self.get_administrative_group_name(group_type, domain_controller, False, as_dn), self.get_administrative_group_name(group_type, domain_controller, True, as_dn)])
		if group_type == 'administrative':
			name = 'Verwaltungsnetz'
		else:
			name = 'Edukativnetz'
		if domain_controller:
			name = 'DC-%s' % name
		else:
			name = 'Member-%s' % name
		if ou_specific:
			name = 'OU%s-%s' % (self.name.lower(), name)
		if as_dn:
			return 'cn=%s,cn=ucsschool,cn=groups,%s' % (name, ucr.get('ldap/base'))
		else:
			return name

	def get_administrative_server_names(self, lo):
		dn = self.get_administrative_group_name('administrative', ou_specific=True, as_dn=True)
		return lo.get(dn, ['uniqueMember']).get('uniqueMember', [])

	def get_educational_server_names(self, lo):
		dn = self.get_administrative_group_name('educational', ou_specific=True, as_dn=True)
		return lo.get(dn, ['uniqueMember']).get('uniqueMember', [])

	def add_host_to_dc_group(self, lo):
		logger.info('School.add_host_to_dc_group(): ou_name=%r  dc_name=%r', self.name, self.dc_name)
		if self.dc_name:
			dc = SchoolDCSlave(name=self.dc_name, school=self.name)
			dc.create(lo)
			dc_udm_obj = dc.get_udm_object(lo)
			groups = self.get_administrative_group_name('educational', ou_specific='both', as_dn=True)
			for grp in groups:
				if grp not in dc_udm_obj['groups']:
					dc_udm_obj['groups'].append(grp)
			dc_udm_obj.modify()

	def shall_create_administrative_objects(self):
		return ucr.is_true('ucsschool/ldap/noneducational/create/objects', True)

	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 add_domain_controllers(self, lo):
		logger.info('School.add_domain_controllers(): ou_name=%r', self.name)
		school_dcs = ucr.get('ucsschool/ldap/default/dcs', 'edukativ').split()
		for dc in school_dcs:
			administrative = dc == 'verwaltung'
			dc_name = self.get_dc_name(administrative=administrative)
			server = AnyComputer.get_first_udm_obj(lo, 'cn=%s' % escape_filter_chars(dc_name))
			logger.info('School.add_domain_controllers(): administrative=%r  dc_name=%s  self.dc_name=%r  server=%r', administrative, dc_name, self.dc_name, server)
			if not server and not self.dc_name:
				if administrative:
					administrative_type = 'administrative'
				else:
					administrative_type = 'educational'
				group_dn = self.get_administrative_group_name(administrative_type, ou_specific=True, as_dn=True)
				try:
					hostlist = lo.get(group_dn, ['uniqueMember']).get('uniqueMember', [])
				except ldap.NO_SUCH_OBJECT:
					hostlist = []
				except Exception, e:
					logger.error('cannot read %s: %s', group_dn, e)
					return

				if hostlist:
					continue  # if at least one DC has control over this OU then jump to next 'school_dcs' item ==> do not create default slave objects

				self.create_dc_slave(lo, dc_name, administrative=administrative)

			dhcp_service = self.get_dhcp_service(dc_name)
			dhcp_service.create(lo)
			dhcp_service.add_server(dc_name, lo)
			return True
Example #21
0
class DHCPService(UCSSchoolHelperAbstractClass):
    name = DHCPServiceName(_('Service'))
    hostname = Attribute(_('Hostname'))
    domainname = Attribute(_('Domain'))

    def do_create(self, udm_obj, lo):
        udm_obj.options.append('options')
        udm_obj['option'] = [
            'wpad "http://%s.%s/proxy.pac"' % (self.hostname, self.domainname)
        ]
        return super(DHCPService, self).do_create(udm_obj, lo)

    @classmethod
    def get_container(cls, school):
        return cls.get_search_base(school).dhcp

    def add_server(self, dc_name, lo, force_dhcp_server_move=False):
        """
		Create the given DHCP server within the DHCP service. If the DHCP server
		object already exists somewhere else within the LDAP tree, it may be moved
		to the DHCP service.

		PLEASE NOTE:
		In multiserver environments an existing DHCP server object is always
		moved to the current DHCP service. In single server environments the
		DHCP server object is *ONLY* moved, if the UCR variable dhcpd/ldap/base
		matches to the current DHCP service.
		"""
        from ucsschool.lib.models.school import School

        # create dhcp-server if not exsistant
        school = School.cache(self.school)
        dhcp_server = DHCPServer(name=dc_name,
                                 school=school.name,
                                 dhcp_service=self)
        existing_dhcp_server_dn = DHCPServer.find_any_dn_with_name(dc_name, lo)
        if existing_dhcp_server_dn:
            logger.info('DHCP server %s exists!', existing_dhcp_server_dn)
            old_dhcp_server_container = lo.parentDn(existing_dhcp_server_dn)
            dhcpd_ldap_base = ucr.get('dhcpd/ldap/base', '')
            # only move if
            # - forced via kwargs OR
            # - in multiserver environments OR
            # - desired dhcp server DN matches with UCR config
            if force_dhcp_server_move or not ucr.is_true(
                    'ucsschool/singlemaster',
                    False) or dhcp_server.dn.endswith(',%s' % dhcpd_ldap_base):
                # move if existing DN does not match with desired DN
                if existing_dhcp_server_dn != dhcp_server.dn:
                    # move existing dhcp server object to OU/DHCP service
                    logger.info(
                        'DHCP server %s not in school %r! Removing and creating new one at %s!',
                        existing_dhcp_server_dn, school, dhcp_server.dn)
                    old_superordinate = DHCPServer.find_udm_superordinate(
                        existing_dhcp_server_dn, lo)
                    old_dhcp_server = DHCPServer.from_dn(
                        existing_dhcp_server_dn,
                        None,
                        lo,
                        superordinate=old_superordinate)
                    old_dhcp_server.remove(lo)
                    dhcp_server.create(lo)

            # copy subnets
            # find local interfaces
            interfaces = []
            for interface_name in set([
                    key.split('/')[1] for key in ucr.keys()
                    if key.startswith('interfaces/eth')
            ]):
                try:
                    address = ipaddr.IPv4Network('%s/%s' % (
                        ucr['interfaces/%s/address' % interface_name],
                        ucr['interfaces/%s/netmask' % interface_name],
                    ))
                    interfaces.append(address)
                except ValueError as exc:
                    logger.info('Skipping invalid interface %s:\n%s',
                                interface_name, exc)
            subnet_dns = DHCPSubnet.find_all_dns_below_base(
                old_dhcp_server_container, lo)
            for subnet_dn in subnet_dns:
                dhcp_service = DHCPSubnet.find_udm_superordinate(subnet_dn, lo)
                dhcp_subnet = DHCPSubnet.from_dn(subnet_dn,
                                                 self.school,
                                                 lo,
                                                 superordinate=dhcp_service)
                subnet = dhcp_subnet.get_ipv4_subnet()
                if subnet in interfaces:  # subnet matches any local subnet
                    logger.info('Creating new DHCPSubnet from %s', subnet_dn)
                    new_dhcp_subnet = DHCPSubnet(**dhcp_subnet.to_dict())
                    new_dhcp_subnet.dhcp_service = self
                    new_dhcp_subnet.position = new_dhcp_subnet.get_own_container(
                    )
                    new_dhcp_subnet.set_dn(new_dhcp_subnet.dn)
                    new_dhcp_subnet.create(lo)
                else:
                    logger.info('Skipping non-local subnet %s', subnet)
        else:
            logger.info('No DHCP server named %s found! Creating new one!',
                        dc_name)
            dhcp_server.create(lo)

    def get_servers(self, lo):
        ret = []
        for dhcp_server in DHCPServer.get_all(lo,
                                              self.school,
                                              superordinate=self):
            dhcp_server.dhcp_service = self
            ret.append(dhcp_server)
        return ret

    class Meta:
        udm_module = 'dhcp/service'
Example #22
0
class Group(UCSSchoolHelperAbstractClass):
    name = GroupName(_('Name'))
    description = Description(_('Description'))
    users = Users(_('Users'))

    @classmethod
    def get_container(cls, school):
        return cls.get_search_base(school).groups

    @classmethod
    def is_school_group(cls, school, group_dn):
        return cls.get_search_base(school).isGroup(group_dn)

    @classmethod
    def is_school_workgroup(cls, school, group_dn):
        return cls.get_search_base(school).isWorkgroup(group_dn)

    @classmethod
    def is_school_class(cls, school, group_dn):
        return cls.get_search_base(school).isClass(group_dn)

    @classmethod
    def is_computer_room(cls, school, group_dn):
        return cls.get_search_base(school).isRoom(group_dn)

    def self_is_workgroup(self):
        return self.is_school_workgroup(self.school, self.dn)

    def self_is_class(self):
        return self.is_school_class(self.school, self.dn)

    def self_is_computerroom(self):
        return self.is_computer_room(self.school, self.dn)

    @classmethod
    def get_class_for_udm_obj(cls, udm_obj, school):
        if cls.is_school_class(school, udm_obj.dn):
            return SchoolClass
        elif cls.is_computer_room(school, udm_obj.dn):
            return ComputerRoom
        elif cls.is_school_workgroup(school, udm_obj.dn):
            return WorkGroup
        elif cls.is_school_group(school, udm_obj.dn):
            return SchoolGroup
        return cls

    def add_umc_policy(self, policy_dn, lo):
        if not policy_dn or policy_dn.lower() == 'none':
            logger.warning('No policy added to %r', self)
            return
        try:
            policy = UMCPolicy.from_dn(policy_dn, self.school, lo)
        except noObject:
            logger.warning(
                'Object to be referenced does not exist (or is no UMC-Policy): %s',
                policy_dn)
        else:
            policy.attach(self, lo)

    def build_hook_line(self, hook_time, func_name):
        code = self._map_func_name_to_code(func_name)
        if code != 'M':
            return self._build_hook_line(
                code,
                self.school,
                self.name,
                self.description,
            )
        else:
            # This is probably a bug. See ucs-school-import and Bug #34736
            old_name = self.get_name_from_dn(self.old_dn)
            new_name = self.name
            if old_name != new_name:
                return self._build_hook_line(
                    code,
                    old_name,
                    new_name,
                )

    class Meta:
        udm_module = 'groups/group'
        name_is_unique = True
Example #23
0
class UCCComputer(SchoolComputer):
    type_name = _('Univention Corporate Client')

    class Meta(SchoolComputer.Meta):
        udm_module = 'computers/ucc'
        hook_path = 'computer'
Example #24
0
class MacComputer(SchoolComputer):
    type_name = _('Mac OS X')

    class Meta(SchoolComputer.Meta):
        udm_module = 'computers/macos'
        hook_path = 'computer'
Example #25
0
class WindowsComputer(SchoolComputer):
    type_name = _('Windows system')

    class Meta(SchoolComputer.Meta):
        udm_module = 'computers/windows'
        hook_path = 'computer'
Example #26
0
class DHCPDNSPolicy(Policy):
    empty_attributes = EmptyAttributes(_('Empty attributes'))

    class Meta:
        udm_module = 'policies/dhcp_dns'
Example #27
0
class IPComputer(SchoolComputer):
    type_name = _('Device with IP address')

    class Meta(SchoolComputer.Meta):
        udm_module = 'computers/ipmanagedclient'
        hook_path = 'computer'
Example #28
0
class User(UCSSchoolHelperAbstractClass):
    name = Username(_('Username'), aka=['Username', 'Benutzername'])
    schools = Schools(_('Schools'))
    firstname = Firstname(_('First name'),
                          aka=['First name', 'Vorname'],
                          required=True,
                          unlikely_to_change=True)
    lastname = Lastname(_('Last name'),
                        aka=['Last name', 'Nachname'],
                        required=True,
                        unlikely_to_change=True)
    birthday = Birthday(_('Birthday'),
                        aka=['Birthday', 'Geburtstag'],
                        unlikely_to_change=True)
    email = Email(_('Email'), aka=['Email', 'E-Mail'], unlikely_to_change=True)
    password = Password(_('Password'), aka=['Password', 'Passwort'])
    disabled = Disabled(_('Disabled'), aka=['Disabled', 'Gesperrt'])
    school_classes = SchoolClassesAttribute(_('Class'),
                                            aka=['Class', 'Klasse'])

    type_name = None
    type_filter = '(|(objectClass=ucsschoolTeacher)(objectClass=ucsschoolStaff)(objectClass=ucsschoolStudent))'

    _profile_path_cache = {}
    _samba_home_path_cache = {}
    # _samba_home_path_cache is invalidated in School.invalidate_cache()

    roles = []
    default_options = ()

    def __init__(self, *args, **kwargs):
        super(User, self).__init__(*args, **kwargs)
        if self.school_classes is None:
            self.school_classes = {}  # set a dict for Staff
        if self.school and not self.schools:
            self.schools.append(self.school)

    @classmethod
    def shall_create_mail_domain(cls):
        return ucr.is_true('ucsschool/import/generate/mail/domain')

    def get_roleshare_home_subdir(self):
        from ucsschool.lib.roleshares import roleshare_home_subdir
        return roleshare_home_subdir(self.school, self.roles, ucr)

    def get_samba_home_drive(self):
        return ucr.get('ucsschool/import/set/homedrive')

    def get_samba_netlogon_script_path(self):
        return ucr.get('ucsschool/import/set/netlogon/script/path')

    def get_samba_home_path(self, lo):
        school = School.cache(self.school)
        # if defined then use UCR value
        ucr_variable = ucr.get('ucsschool/import/set/sambahome')
        if ucr_variable is not None:
            samba_home_path = r'\\%s' % ucr_variable.strip('\\')
        # in single server environments the master is always the fileserver
        elif ucr.is_true('ucsschool/singlemaster', False):
            samba_home_path = r'\\%s' % ucr.get('hostname')
        # if there's a cached result then use it
        elif school.dn not in self._samba_home_path_cache:
            samba_home_path = None
            # get windows home server from OU object
            school = self.get_school_obj(lo)
            home_share_file_server = school.home_share_file_server
            if home_share_file_server:
                samba_home_path = r'\\%s' % self.get_name_from_dn(
                    home_share_file_server)
            self._samba_home_path_cache[school.dn] = samba_home_path
        else:
            samba_home_path = self._samba_home_path_cache[school.dn]
        if samba_home_path is not None:
            return r'%s\%s' % (samba_home_path, self.name)

    def get_profile_path(self, lo):
        ucr_variable = ucr.get('ucsschool/import/set/serverprofile/path')
        if ucr_variable is not None:
            return ucr_variable
        school = School.cache(self.school)
        if school.dn not in self._profile_path_cache:
            profile_path = r'%s\%%USERNAME%%\windows-profiles\default'
            for computer in AnyComputer.get_all(
                    lo, self.school,
                    'univentionService=Windows Profile Server'):
                profile_path = profile_path % (r'\\%s' % computer.name)
                break
            else:
                profile_path = profile_path % '%LOGONSERVER%'
            self._profile_path_cache[school.dn] = profile_path
        return self._profile_path_cache[school.dn]

    def is_student(self, lo):
        return self.__check_object_class(lo, 'ucsschoolStudent',
                                         self._legacy_is_student)

    def is_exam_student(self, lo):
        return self.__check_object_class(lo, 'ucsschoolExam',
                                         self._legacy_is_exam_student)

    def is_teacher(self, lo):
        return self.__check_object_class(lo, 'ucsschoolTeacher',
                                         self._legacy_is_teacher)

    def is_staff(self, lo):
        return self.__check_object_class(lo, 'ucsschoolStaff',
                                         self._legacy_is_staff)

    def is_administrator(self, lo):
        return self.__check_object_class(lo, 'ucsschoolAdministrator',
                                         self._legacy_is_admininstrator)

    @classmethod
    def _legacy_is_student(cls, school, dn):
        logger.warning('Using deprecated method is_student()')
        return dn.endswith(cls.get_search_base(school).students)

    @classmethod
    def _legacy_is_exam_student(cls, school, dn):
        logger.warning('Using deprecated method is_exam_student()')
        return dn.endswith(cls.get_search_base(school).examUsers)

    @classmethod
    def _legacy_is_teacher(cls, school, dn):
        logger.warning('Using deprecated method is_teacher()')
        search_base = cls.get_search_base(school)
        return dn.endswith(search_base.teachers) or dn.endswith(
            search_base.teachersAndStaff) or dn.endswith(search_base.admins)

    @classmethod
    def _legacy_is_staff(cls, school, dn):
        logger.warning('Using deprecated method is_staff()')
        search_base = cls.get_search_base(school)
        return dn.endswith(search_base.staff) or dn.endswith(
            search_base.teachersAndStaff)

    @classmethod
    def _legacy_is_admininstrator(cls, school, dn):
        logger.warning('Using deprecated method is_admininstrator()')
        return dn.endswith(cls.get_search_base(school).admins)

    def __check_object_class(self, lo, object_class, fallback):
        obj = self.get_udm_object(lo)
        if not obj:
            raise noObject('Could not read %r' % (self.dn, ))
        if 'ucsschoolSchool' in obj.oldattr:
            return object_class in obj.oldattr.get('objectClass', [])
        return fallback(self.school, self.dn)

    @classmethod
    def get_class_for_udm_obj(cls, udm_obj, school):
        ocs = set(udm_obj.oldattr.get('objectClass', []))
        if ocs >= set(['ucsschoolTeacher', 'ucsschoolStaff']):
            return TeachersAndStaff
        if ocs >= set(['ucsschoolExam', 'ucsschoolStudent']):
            return ExamStudent
        if 'ucsschoolTeacher' in ocs:
            return Teacher
        if 'ucsschoolStaff' in ocs:
            return Staff
        if 'ucsschoolStudent' in ocs:
            return Student
        if 'ucsschoolAdministrator' in ocs:
            return Teacher  # we have no class for a school administrator

        # legacy DN based checks
        if cls._legacy_is_student(school, udm_obj.dn):
            return Student
        if cls._legacy_is_teacher(school, udm_obj.dn):
            if cls._legacy_is_staff(school, udm_obj.dn):
                return TeachersAndStaff
            return Teacher
        if cls._legacy_is_staff(school, udm_obj.dn):
            return Staff
        if cls._legacy_is_exam_student(school, udm_obj.dn):
            return ExamStudent

        return User

    @classmethod
    def from_udm_obj(cls, udm_obj, school, lo):
        obj = super(User, cls).from_udm_obj(udm_obj, school, lo)
        obj.password = None
        obj.school_classes = cls.get_school_classes(udm_obj, obj)
        return obj

    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 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 do_school_change(self, udm_obj, lo, old_school):
        super(User, self).do_school_change(udm_obj, lo, old_school)
        school = self.school

        logger.info('User is part of the following groups: %r',
                    udm_obj['groups'])
        self.remove_from_groups_of_school(old_school, lo)
        self._udm_obj_searched = False
        self.school_classes.pop(old_school, None)
        udm_obj = self.get_udm_object(lo)
        udm_obj['primaryGroup'] = self.primary_group_dn(lo)
        groups = set(udm_obj['groups'])
        at_least_groups = set(self.groups_used(lo))
        if (groups | at_least_groups) != groups:
            udm_obj['groups'] = list(groups | at_least_groups)
        subdir = self.get_roleshare_home_subdir()
        udm_obj['unixhome'] = '/home/' + os.path.join(subdir, 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
        if udm_obj['departmentNumber'] == old_school:
            udm_obj['departmentNumber'] = school
        if school not in udm_obj['school']:
            udm_obj['school'].append(school)
        if old_school in udm_obj['school']:
            udm_obj['school'].remove(old_school)
        udm_obj.modify(ignore_license=True)

    def _alter_udm_obj(self, udm_obj):
        if self.email is not None:
            udm_obj['e-mail'] = self.email
        udm_obj['departmentNumber'] = self.school
        ret = super(User, self)._alter_udm_obj(udm_obj)
        return ret

    def get_mail_domain(self):
        if self.email:
            domain_name = self.email.split('@')[-1]
            return MailDomain.cache(domain_name)

    def create_mail_domain(self, lo):
        mail_domain = self.get_mail_domain()
        if mail_domain is not None and not mail_domain.exists(lo):
            if self.shall_create_mail_domain():
                mail_domain.create(lo)
            else:
                logger.warning('Not allowed to create %r.', mail_domain)

    def set_default_options(self, udm_obj):
        for option in self.get_default_options():
            if option not in udm_obj.options:
                udm_obj.options.append(option)

    @classmethod
    def get_default_options(cls):
        options = set()
        for kls in cls.__bases__:  # u-s-import uses multiple inheritance, we have to cover all parents
            try:
                options.update(kls.get_default_options())
            except AttributeError:
                pass
        options.update(cls.default_options)
        return options

    def get_specific_groups(self, lo):
        groups = self.get_domain_users_groups()
        for school_class in self.get_school_class_objs():
            groups.append(
                self.get_class_dn(school_class.name, school_class.school, lo))
        return groups

    def validate(self, lo, validate_unlikely_changes=False):
        super(User, self).validate(lo, validate_unlikely_changes)
        try:
            udm_obj = self.get_udm_object(lo)
        except UnknownModel:
            udm_obj = None
        except WrongModel as exc:
            udm_obj = None
            self.add_error(
                'name',
                _('It is not supported to change the role of a user. %(old_role)s %(name)s cannot become a %(new_role)s.'
                  ) % {
                      'old_role': exc.model.type_name,
                      'name': self.name,
                      'new_role': self.type_name
                  })
        if udm_obj:
            original_class = self.get_class_for_udm_obj(udm_obj, self.school)
            if original_class is not self.__class__:
                self.add_error(
                    'name',
                    _('It is not supported to change the role of a user. %(old_role)s %(name)s cannot become a %(new_role)s.'
                      ) % {
                          'old_role': original_class.type_name,
                          'name': self.name,
                          'new_role': self.type_name
                      })
        if self.email:
            name, email = escape_filter_chars(self.name), escape_filter_chars(
                self.email)
            if self.get_first_udm_obj(
                    lo, '&(!(uid=%s))(mailPrimaryAddress=%s)' % (name, email)):
                self.add_error(
                    'email',
                    _('The email address is already taken by another user. Please change the email address.'
                      ))
            # mail_domain = self.get_mail_domain(lo)
            # if not mail_domain.exists(lo) and not self.shall_create_mail_domain():
            # 	self.add_error('email', _('The mail domain is unknown. Please change the email address or create the mail domain "%s" using the Univention Directory Manager.') % mail_domain.name)

    def remove_from_school(self, school, lo):
        if not self.exists(lo):
            logger.warning('User does not exists, not going to remove.')
            return False
        try:
            (self.schools or [school]).remove(school)
        except ValueError:
            logger.warning('User is not part of school %r. Not removing.',
                           school)
            return False
        if not self.schools:
            logger.warning('User %r not part of any school, removing it.',
                           self)
            return self.remove(lo)
        if self.school == school:
            if not self.change_school(self.schools[0], lo):
                return False
        else:
            self.remove_from_groups_of_school(school, lo)
        self.school_classes.pop(school, None)
        return True

    def remove_from_groups_of_school(self, school, lo):
        for cls in (SchoolClass, WorkGroup, SchoolGroup):
            for group in cls.get_all(
                    lo, school, filter_format('uniqueMember=%s', (self.dn, ))):
                try:
                    group.users.remove(self.dn)
                except ValueError:
                    pass
                else:
                    logger.info('Removing %r from group %r of school %r.',
                                self.dn, group.dn, school)
                    group.modify(lo)

    def get_group_dn(self, group_name, school):
        return Group.cache(group_name, school).dn

    def get_class_dn(self, class_name, school, lo):
        # Bug #32337: check if the class exists without OU prefix
        # if it does not exist the class name with OU prefix is used
        school_class = SchoolClass.cache(class_name, school)
        if school_class.get_relative_name() == school_class.name:
            if not school_class.exists(lo):
                class_name = '%s-%s' % (school, class_name)
                school_class = SchoolClass.cache(class_name, school)
        return school_class.dn

    def primary_group_dn(self, lo):
        dn = self.get_group_dn('Domain Users %s' % self.school, self.school)
        return self.get_or_create_group_udm_object(dn, lo).dn

    def get_domain_users_groups(self):
        return [
            self.get_group_dn('Domain Users %s' % school, school)
            for school in self.schools
        ]

    def get_students_groups(self):
        prefix = ucr.get('ucsschool/ldap/default/groupprefix/pupils',
                         'schueler-')
        return [
            self.get_group_dn('%s%s' % (prefix, school), school)
            for school in self.schools
        ]

    def get_teachers_groups(self):
        prefix = ucr.get('ucsschool/ldap/default/groupprefix/teachers',
                         'lehrer-')
        return [
            self.get_group_dn('%s%s' % (prefix, school), school)
            for school in self.schools
        ]

    def get_staff_groups(self):
        prefix = ucr.get('ucsschool/ldap/default/groupprefix/staff',
                         'mitarbeiter-')
        return [
            self.get_group_dn('%s%s' % (prefix, school), school)
            for school in self.schools
        ]

    def groups_used(self, lo):
        group_dns = self.get_specific_groups(lo)

        for group_dn in group_dns:
            self.get_or_create_group_udm_object(group_dn, lo)

        return group_dns

    @classmethod
    def get_or_create_group_udm_object(cls, group_dn, lo, fresh=False):
        name = cls.get_name_from_dn(group_dn)
        school = cls.get_school_from_dn(group_dn)
        if Group.is_school_class(school, group_dn):
            group = SchoolClass.cache(name, school)
        else:
            group = Group.cache(name, school)
        if fresh:
            group._udm_obj_searched = False
        group.create(lo)
        return group

    def is_active(self):
        return self.disabled != 'all'

    def build_hook_line(self, hook_time, func_name):
        code = self._map_func_name_to_code(func_name)
        is_teacher = isinstance(self, Teacher) or isinstance(
            self, TeachersAndStaff)
        is_staff = isinstance(self, Staff) or isinstance(
            self, TeachersAndStaff)
        school_class = ','.join(self.school_classes.get(
            self.school,
            []))  # legacy format: only classes of the primary school
        return self._build_hook_line(
            code,
            self.name,
            self.lastname,
            self.firstname,
            self.school,
            school_class,
            '',  # TODO: rights?
            self.email,
            is_teacher,
            self.is_active(),
            is_staff,
            self.password,
        )

    def to_dict(self):
        ret = super(User, self).to_dict()
        display_name = []
        if self.firstname:
            display_name.append(self.firstname)
        if self.lastname:
            display_name.append(self.lastname)
        ret['display_name'] = ' '.join(display_name)
        school_classes = {}
        for school_class in self.get_school_class_objs():
            school_classes.setdefault(school_class.school,
                                      []).append(school_class.name)
        ret['school_classes'] = school_classes
        ret['type_name'] = self.type_name
        ret['type'] = self.__class__.__name__
        ret['type'] = ret['type'][0].lower() + ret['type'][1:]
        return ret

    def get_school_class_objs(self):
        ret = []
        for school, classes in self.school_classes.iteritems():
            for school_class in classes:
                ret.append(SchoolClass.cache(school_class, school))
        return ret

    @classmethod
    def get_school_classes(cls, udm_obj, obj):
        school_classes = {}
        for group in udm_obj['groups']:
            for school in obj.schools:
                if Group.is_school_class(school, group):
                    school_class_name = cls.get_name_from_dn(group)
                    school_classes.setdefault(school,
                                              []).append(school_class_name)
        return school_classes

    @classmethod
    def get_container(cls, school):
        return cls.get_search_base(school).users

    @classmethod
    def lookup(cls, lo, school, filter_s='', superordinate=None):
        filter_object_type = conjunction('&', [
            parse(cls.type_filter),
            parse(filter_format('ucsschoolSchool=%s', [school]))
        ])
        if filter_s:
            filter_object_type = conjunction(
                '&', [filter_object_type, parse(filter_s)])
        objects = udm_modules.lookup(cls._meta.udm_module,
                                     None,
                                     lo,
                                     filter=unicode(filter_object_type),
                                     scope='sub',
                                     superordinate=superordinate)
        objects.extend(obj for obj in super(User, cls).lookup(
            lo, school, filter_s, superordinate=superordinate)
                       if not any(obj.dn == x.dn for x in objects))
        return objects

    class Meta:
        udm_module = 'users/user'
        name_is_unique = True
        allow_school_change = False
Example #29
0
class SchoolDCSlave(SchoolDC):
    groups = Groups(_('Groups'))

    def do_create(self, udm_obj, lo):
        udm_obj['unixhome'] = '/dev/null'
        udm_obj['shell'] = '/bin/bash'
        udm_obj['primaryGroup'] = BasicGroup.cache('DC Slave Hosts').dn
        return super(SchoolDCSlave, self).do_create(udm_obj, lo)

    def _alter_udm_obj(self, udm_obj):
        if self.groups:
            for group in self.groups:
                if group not in udm_obj['groups']:
                    udm_obj['groups'].append(group)
        return super(SchoolDCSlave, self)._alter_udm_obj(udm_obj)

    def move_without_hooks(self, lo, udm_obj=None, force=False):
        try:
            if udm_obj is None:
                try:
                    udm_obj = self.get_only_udm_obj(
                        lo, 'cn=%s' % escape_filter_chars(self.name))
                except MultipleObjectsError:
                    logger.error(
                        'Found more than one DC Slave with hostname "%s"',
                        self.name)
                    return False
                if udm_obj is None:
                    logger.error('Cannot find DC Slave with hostname "%s"',
                                 self.name)
                    return False
            old_dn = udm_obj.dn
            school = self.get_school_obj(lo)
            group_dn = school.get_administrative_group_name('educational',
                                                            ou_specific=True,
                                                            as_dn=True)
            if group_dn not in udm_obj['groups']:
                logger.error('%r has no LDAP access to %r', self, school)
                return False
            if old_dn == self.dn:
                logger.info(
                    'DC Slave "%s" is already located in "%s" - stopping here',
                    self.name, self.school)
            self.set_dn(old_dn)
            if self.exists_outside_school(lo):
                if not force:
                    logger.error('DC Slave "%s" is located in another OU - %s',
                                 self.name, udm_obj.dn)
                    logger.error('Use force=True to override')
                    return False
            if school is None:
                logger.error(
                    'Cannot move DC Slave object - School does not exist: %r',
                    school)
                return False
            self.modify_without_hooks(lo)
            if school.class_share_file_server == old_dn:
                school.class_share_file_server = self.dn
            if school.home_share_file_server == old_dn:
                school.home_share_file_server = self.dn
            school.modify_without_hooks(lo)

            removed = False
            # find dhcp server object by checking all dhcp service objects
            for dhcp_service in AnyDHCPService.get_all(lo, None):
                for dhcp_server in dhcp_service.get_servers(lo):
                    if dhcp_server.name == self.name and not dhcp_server.dn.endswith(
                            ',%s' % school.dn):
                        dhcp_server.remove(lo)
                        removed = True

            if removed:
                own_dhcp_service = school.get_dhcp_service()

                dhcp_server = DHCPServer(name=self.name,
                                         school=self.school,
                                         dhcp_service=own_dhcp_service)
                dhcp_server.create(lo)

            logger.info('Move complete')
            logger.warning('The DC Slave has to be rejoined into the domain!')
        finally:
            self.invalidate_cache()
        return True

    class Meta:
        udm_module = 'computers/domaincontroller_slave'
        name_is_unique = True
        allow_school_change = True
Example #30
0
 def validate(self, value):
     super(SchoolName, self).validate(value)
     if ucr.is_true('ucsschool/singlemaster', False):
         regex = re.compile('^[a-zA-Z0-9](([a-zA-Z0-9-]*)([a-zA-Z0-9]$))?$')
         if not regex.match(value):
             raise ValueError(_('Invalid school name'))