def test_all_roles_modification_set_network(ip_subnet, ip_network, ip_netmask, ip_range):
	ucr = configRegistry.ConfigRegistry()
	ucr.load()

	for role in udm_test.UCSTestUDM.COMPUTER_MODULES:
		computerProperties = {
			'mac': '01:23:45:67:89:ab',
			'name': uts.random_name()
		}

		with udm_test.UCSTestUDM() as udm:
			dNSCn = 'cn=dns,%s' % (ucr.get('ldap/base'),)

			forwardZoneName = '%s.%s' % (uts.random_name(), uts.random_name())
			forwardZone = udm.create_object('dns/forward_zone', zone=forwardZoneName, position=dNSCn, nameserver=uts.random_string(numeric=False))
			reverseZone = udm.create_object('dns/reverse_zone', subnet=ip_subnet, position=dNSCn, nameserver=uts.random_string(numeric=False))
			dhcpService = udm.create_object('dhcp/service', service=uts.random_name())

			networkProperties = {
				'name': uts.random_name(),
				'network': ip_network,
				'netmask': ip_netmask,
				'dnsEntryZoneForward': forwardZone,
				'dnsEntryZoneReverse': reverseZone,
				'dhcpEntryZone': dhcpService,
				'ipRange': ip_range,
			}
			network = udm.create_object('networks/network', **networkProperties)

			computer = udm.create_object(role, **computerProperties)
			udm.modify_object(role, dn=computer, network=network)
			aRecord = (utils.get_ldap_connection().getAttr(computer, 'aRecord') or [b''])[0].decode('ASCII')
			aaaRecord = (utils.get_ldap_connection().getAttr(computer, 'aAAARecord') or [b''])[0].decode('ASCII')

			# FIXME: workaround for possibly remaining locks
			if aaaRecord:
				udm.addCleanupLock('aAAARecord', aaaRecord)
			if aRecord:
				udm.addCleanupLock('aRecord', aRecord)
			udm.addCleanupLock('mac', '01:23:45:67:89:ab')

			utils.verify_ldap_object(computer, {'univentionNetworkLink': [network]})
			assert aRecord or aaaRecord
			if aRecord:
				assert aRecord in ['%s.%s' % (ip_subnet, oktett) for oktett in range(2, 255)], 'IP address of computer not in range of its network'
			if aaaRecord:
				assert aaaRecord in ['%s:0000:0000:0000:%0.4x' % (ip_subnet, oktett) for oktett in range(2, 255)], 'IP address of computer not in range of its network'
			if aRecord:
				utils.verify_ldap_object('relativeDomainName=%s,%s' % (computerProperties['name'], forwardZone), {'aRecord': [aRecord]})
				utils.verify_ldap_object('relativeDomainName=%s,%s' % (aRecord.split(".")[-1], reverseZone), {'pTRRecord': ['%s.%s.' % (computerProperties['name'], forwardZoneName)]})
				utils.verify_ldap_object('cn=%s,%s' % (computerProperties['name'], dhcpService), {'univentionDhcpFixedAddress': [aRecord]})
			if aaaRecord:
				utils.verify_ldap_object('relativeDomainName=%s,%s' % (computerProperties['name'], forwardZone), {'aAAARecord': [aaaRecord]})
				utils.verify_ldap_object('relativeDomainName=%s,%s' % ('.'.join(reversed(''.join(aaaRecord.split(':')[4:]))), reverseZone), {'pTRRecord': ['%s.%s.' % (computerProperties['name'], forwardZoneName)]})
				utils.verify_ldap_object('cn=%s,%s' % (computerProperties['name'], dhcpService), {'univentionDhcpFixedAddress': [str(ipaddress.IPv6Address(aaaRecord))]})
    def test_container_cn_rename_uppercase_rollback(self, udm, ucr):
        """Rename a container/cn with un-moveable subobjects from lower to upper case"""
        user_name = uts.random_string()
        network_name = uts.random_string()

        cn_name = uts.random_string()
        cn_name_new = cn_name.upper()

        cn = udm.create_object('container/cn', name=cn_name)
        wait_for_drs_replication('cn=%s' % cn_name)
        udm.create_user(position=cn, username=user_name)
        udm.create_object('networks/network',
                          position=cn,
                          name=network_name,
                          network='1.1.1.1',
                          netmask='24')

        with pytest.raises(udm_test.UCSTestUDM_ModifyUDMObjectFailed):
            udm.modify_object('container/cn', dn=cn, name=cn_name_new)

        new_cn = 'cn=%s,%s' % (ldap.dn.escape_dn_chars(cn_name_new),
                               ucr.get('ldap/base'))
        new_user = '******' % (ldap.dn.escape_dn_chars(user_name),
                                        ldap.dn.escape_dn_chars(cn_name_new),
                                        ucr.get('ldap/base'))
        utils.verify_ldap_object(new_cn, should_exist=True)
        utils.verify_ldap_object(new_user, should_exist=True)

        lo = utils.get_ldap_connection()
        for dn, entry in lo.search(
                filter=ldap.filter.filter_format('cn=%s', [cn_name])):
            assert entry.get('cn')[0] == cn_name.encode(
                'UTF-8'), 'cn = %s; expected: %s' % (entry.get('cn')[0],
                                                     cn_name)
예제 #3
0
def test_check_univentionDefaultGroup_membership_after_create(udm):
    """Check default primary group membership after users/user create"""
    # from users/user: lookup univentionDefaultGroup
    lo = utils.get_ldap_connection()
    pos = position(lo.base)
    searchResult = lo.search(filter='(objectClass=univentionDefault)',
                             base='cn=univention,' + pos.getDomain(),
                             attr=['univentionDefaultGroup'])
    if not searchResult or not searchResult[0][1]:
        utils.fail(
            'Test system is broken: univentionDefaultGroup value not found')
    groupdn = searchResult[0][1]['univentionDefaultGroup'][0].decode('utf-8')

    # lookup previous members for comparison
    searchResult = lo.search(base=groupdn,
                             scope='base',
                             attr=['uniqueMember', 'memberUid'])
    if not searchResult or not searchResult[0][1]:
        utils.fail(
            'Test system is broken: univentionDefaultGroup object missing: %s'
            % groupdn)
    uniqueMember = searchResult[0][1]['uniqueMember']
    memberUid = searchResult[0][1]['memberUid']

    # now create users/user object
    userdn, uid = udm.create_user(primaryGroup=groupdn)

    # and check if the object has been added to univentionDefaultGroup
    uniqueMember.append(userdn.encode('utf-8'))
    memberUid.append(uid.encode('utf-8'))
    utils.verify_ldap_object(groupdn, {'uniqueMember': uniqueMember})
    utils.verify_ldap_object(groupdn, {'memberUid': memberUid})
def test_invalid_zone_names(zone):
	lo = utils.get_ldap_connection()
	with udm_test.UCSTestUDM() as udm, _ucr.UCSTestConfigRegistry() as ucr:
		pos = 'cn=dns,%s' % (udm.LDAP_BASE,)
		dn = 'zoneName=%s,%s' % (ldap.dn.escape_dn_chars(zone), pos)
		attrs = {
			'nSRecord': ['%(hostname)s.%(domainname)s.'],
			'objectClass': ['dNSZone', 'top'],
			'dNSTTL': ['10800'],
			'relativeDomainName': ['@'],
			'zoneName': [zone],
			'sOARecord': ['%(hostname)s.%(domainname)s. root.%(domainname)s. 9 28800 7200 604800 10800'],
		}
		al = [(key, [v % dict(ucr) for v in val]) for key, val in attrs.items()]
		print(('Creating', dn))
		lo.add(dn, al)
		try:
			utils.wait_for_replication_and_postrun()
			check(zone)

			lo.modify(dn, [('dNSTTL', '10800', '10900')])
			utils.wait_for_replication_and_postrun()
			check(zone)
		finally:
			lo.delete(dn)

		utils.wait_for_replication_and_postrun()
		check(zone, True)
예제 #5
0
def test_ignore_user_with_functional_flag(stopped_s4_connector, udm):
    """Create users/user and test "functional" object flag"""
    # bugs: [34395]
    license_before = subprocess.Popen(['univention-license-check'],
                                      stdout=subprocess.PIPE).communicate()[0]

    # create user and check its existence
    user_dn = udm.create_user(check_for_drs_replication=False,
                              wait_for=False)[0]
    utils.verify_ldap_object(user_dn)
    stdout = subprocess.Popen(
        [udm_test.UCSTestUDM.PATH_UDM_CLI_CLIENT, 'users/user', 'list'],
        stdout=subprocess.PIPE).communicate()[0]
    if not user_dn.lower().encode('UTF-8') in stdout.lower():
        utils.fail(
            'Cannot find user DN %s in output of "udm users/user list":\n%s' %
            (user_dn, stdout))

    # perform a license check
    license_after = subprocess.Popen(['univention-license-check'],
                                     stdout=subprocess.PIPE).communicate()[0]
    if license_before == license_after:
        utils.fail('License check failed to detect normal user')

    # add 'functional' flag to user
    lo = utils.get_ldap_connection()
    lo.modify(user_dn, (('univentionObjectFlag', b'', b'functional'), ))
    utils.wait_for_replication()
    stdout = subprocess.Popen(
        [udm_test.UCSTestUDM.PATH_UDM_CLI_CLIENT, 'users/user', 'list'],
        stdout=subprocess.PIPE).communicate()[0]
    if user_dn.lower().encode('UTF-8') in stdout.lower():
        utils.fail(
            '"udm users/user list" still finds user object with functional flag'
        )

    # perform a license check
    license_after = subprocess.Popen(['univention-license-check'],
                                     stdout=subprocess.PIPE).communicate()[0]
    if license_before != license_after:
        utils.fail('License check detected to "functional" user')

    # remove 'functional' flag to user
    lo.modify(user_dn, (('univentionObjectFlag', b'functional', b''), ))
    utils.wait_for_replication()
    stdout = subprocess.Popen(
        [udm_test.UCSTestUDM.PATH_UDM_CLI_CLIENT, 'users/user', 'list'],
        stdout=subprocess.PIPE).communicate()[0]
    if not user_dn.lower().encode('UTF-8') in stdout.lower():
        utils.fail(
            'Cannot find user DN %s in output of "udm users/user list" after removing flag:\n%s'
            % (user_dn, stdout))

    # perform a license check
    license_after = subprocess.Popen(['univention-license-check'],
                                     stdout=subprocess.PIPE).communicate()[0]
    if license_before == license_after:
        utils.fail('License check failed to detect normal user')
예제 #6
0
def get_dn_of_extension_by_name(extension_type, name):
    """
    Returns a list of DNs of UDM extension objects with given type an name, or [] if no object has been found.
    """
    assert (extension_type in VALID_EXTENSION_TYPES)
    if extension_type == 'module':
        assert ('/' in name)
    searchfilter = {
        'hook': '(&(objectClass=univentionUDMHook)(cn=%s))' % name,
        'syntax': '(&(objectClass=univentionUDMSyntax)(cn=%s))' % name,
        'module': '(&(objectClass=univentionUDMModule)(cn=%s))' % name,
    }[extension_type]
    return get_ldap_connection().searchDn(filter=searchfilter)
예제 #7
0
def test_check_ppd():
    """Check PPD files"""
    # bugs: [43417]
    ldap_printer = []
    printer_files = []
    print('searching for printer models')
    for dn, attr in utils.get_ldap_connection().search(
            filter='(objectClass=univentionPrinterModels)',
            attr=['printerModel']):
        for printerModel in attr.get('printerModel', ()):
            printerModel = printerModel.decode('UTF-8')
            model, desc = shlex.split(printerModel)
            desc = printerModel.split('"')[3]
            if desc.startswith('deprecated (only available'):
                continue
            if model.endswith('.ppd') or model.endswith('.ppd.gz'):
                model = model.split('/')[-1]
                ldap_printer.append(model)

    for root, dirs, files in os.walk('/usr/share/ppd/'):
        for file_ in files:
            if file_.endswith('.ppd') or file_.endswith('ppd.gz'):
                printer_files.append(file_)

    for line in subprocess.check_output(
        ['/usr/lib/cups/driver/foomatic-db-compressed-ppds',
         'list']).decode('UTF-8', 'replace').splitlines():
        file_ = shlex.split(line)[0]
        printer_files.append(file_.split('/')[-1])

    # check if we found something
    assert ldap_printer
    assert printer_files

    # check diff
    missing_files = set(ldap_printer) - set(printer_files)
    missing_printers = set(printer_files) - set(ldap_printer)
    message = ''
    if missing_files:
        # ignore missing cups-pdf ppd (univention-cups-pdf is not installed)
        if missing_files - {
                'CUPS-PDF.ppd', 'CUPS-PDF_opt.ppd', 'CUPS-PDF_noopt.ppd'
        }:
            message += 'No PPD file found for LDAP printers:\n' + '\n\t'.join(
                missing_files)
    if missing_printers:
        message += '\n\n' + 'No LDAP printer found for PPD files:\n' + '\n\t'.join(
            missing_printers)
    if message:
        print(message, file=sys.stderr)
        sys.exit(1)
예제 #8
0
def verify_udm_object(module, dn, expected_properties):
    # type: (Any, str, Optional[Mapping[str, Union[bytes, Text, Tuple[str, ...], List[str]]]]) -> None
    """
	Verify an object exists with the given `dn` in the given UDM `module` with
	some properties. Setting `expected_properties` to `None` requires the
	object to not exist.
	:param dict expected_properties: is a dictionary of (property,value) pairs.

	:raises AssertionError: in case of a mismatch.
	"""
    lo = utils.get_ldap_connection(admin_uldap=True)
    try:
        position = univention.admin.uldap.position(lo.base)
        udm_module = univention.admin.modules.get(module)
        if not udm_module:
            univention.admin.modules.update()
            udm_module = univention.admin.modules.get(module)
        udm_object = univention.admin.objects.get(udm_module, None, lo,
                                                  position, dn)
        udm_object.open()
    except univention.admin.uexceptions.noObject:
        if expected_properties is None:
            return
        raise

    if expected_properties is None:
        raise AssertionError("UDM object {} should not exist".format(dn))

    difference = {}
    for (key, value) in expected_properties.items():
        udm_value = udm_object.info.get(key, [])
        if udm_value is None:
            udm_value = []
        if isinstance(udm_value, (bytes, six.string_types)):
            udm_value = {udm_value}
        if not isinstance(value, (tuple, list)):
            value = {value}
        value = {_to_unicode(v).lower() for v in value}
        udm_value = {_to_unicode(v).lower() for v in udm_value}
        if udm_value != value:
            try:
                value = {_normalize_dn(dn) for dn in value}
                udm_value = {_normalize_dn(dn) for dn in udm_value}
            except ldap.DECODING_ERROR:
                pass
        if udm_value != value:
            difference[key] = (udm_value, value)
    assert not difference, '\n'.join(
        '{}: {} != expected {}'.format(key, udm_value, value)
        for key, (udm_value, value) in difference.items())
예제 #9
0
def test_from_primary_group_removal(udm):
    """Create users/user"""

    lo = utils.get_ldap_connection()
    groupdn = udm.create_object('groups/group', name=uts.random_string())
    groupdn2 = udm.create_object('groups/group', name=uts.random_string())
    sid = lo.getAttr(groupdn, 'sambaSID', required=True)
    user = udm.create_user(primaryGroup=groupdn, groups=[groupdn2])[0]
    utils.verify_ldap_object(user, {'sambaPrimaryGroupSID': sid})

    utils.verify_ldap_object(groupdn, {'uniqueMember': [user]})
    udm.modify_object('groups/group', dn=groupdn, remove={'users': [user]})
    utils.verify_ldap_object(groupdn, {'uniqueMember': []})
    utils.verify_ldap_object(
        user, {'sambaPrimaryGroupSID': []})  # This fails, Bug #27160
        def test_container(parent, add_user):
            if parent is None:
                parent = ucr.get('ldap/base')
            user_name = 'X' + uts.random_string(
            )  # test preserving name (case sensitivity)

            cn_name = uts.random_string()
            cn_name_new = cn_name.upper()

            cn = udm.create_object('container/cn',
                                   position=parent,
                                   name=cn_name)
            if add_user:
                udm.create_user(position=cn, username=user_name)

            try:
                udm.modify_object('container/cn', dn=cn, name=cn_name_new)
            except AssertionError:
                pass
            lo = utils.get_ldap_connection()
            for dn, entry in lo.search(filter='ou=temporary_move_container_*'):
                to_be_removed = udm._cleanup.setdefault('container/ou', [])
                to_be_removed.append(dn)
                assert False, 'ou = %s remained' % dn

            new_cn = 'cn=%s,%s' % (ldap.dn.escape_dn_chars(cn_name_new),
                                   parent)
            new_user = '******' % (ldap.dn.escape_dn_chars(user_name),
                                      new_cn)

            utils.verify_ldap_object(new_cn, should_exist=True)
            if add_user:
                for dn, entry in lo.search(filter=ldap.filter.filter_format(
                        'uid=%s', [
                            user_name,
                        ])):
                    assert entry.get('uid')[0] == user_name.encode(
                        'UTF-8'
                    ), 'CASE SENSITIVITY: uid = %s; expected: %s' % (
                        entry.get('uid')[0], user_name)
                utils.verify_ldap_object(new_user, should_exist=True)

            for dn, entry in lo.search(
                    filter=ldap.filter.filter_format('cn=%s', [cn_name_new])):
                assert entry.get('cn')[0] == cn_name_new.encode(
                    'UTF-8'), 'cn = %s; expected: %s' % (entry.get('cn')[0],
                                                         cn_name_new)
            return new_cn
    def test_object_move_and_standard_container_modify(self, udm):
        """move the object and modify the standard container flag at the same time"""
        # bugs: [41694]
        # exposure: dangerous

        lo = utils.get_ldap_connection()

        for object_type in ('container/cn', 'container/ou'):
            defalt_containers = 'cn=default containers,%s' % (
                udm.UNIVENTION_CONTAINER, )
            print('testing', object_type)
            computerPath = lo.getAttr(defalt_containers,
                                      'univentionComputersObject')
            userPath = lo.getAttr(defalt_containers, 'univentionUsersObject')

            utils.verify_ldap_object(
                defalt_containers, {
                    'univentionUsersObject': userPath,
                    'univentionComputersObject': computerPath
                })
            old_dn = udm.create_object(
                object_type, **{
                    'name': uts.random_string(),
                    'computerPath': '1'
                })
            computerPath.append(old_dn)

            utils.verify_ldap_object(
                defalt_containers, {
                    'univentionUsersObject': userPath,
                    'univentionComputersObject': computerPath
                })

            new_dn = udm.modify_object(
                object_type, **{
                    'name': uts.random_string(),
                    'dn': old_dn,
                    'computerPath': '0',
                    'userPath': '1'
                })
            computerPath.remove(old_dn)
            userPath.append(new_dn)
            utils.verify_ldap_object(
                defalt_containers, {
                    'univentionUsersObject': userPath,
                    'univentionComputersObject': computerPath
                })
    def test_container_ou_rename_uppercase_rollback_with_special_characters(
            self, udm, ucr):
        """Rename a container/ou with un-moveable subobjects from lower to upper case with special characters"""
        user_name = uts.random_string()
        network_name = uts.random_string()

        ou_name = uts.random_name_special_characters()
        ou_name_new = ou_name.upper()

        ou = udm.create_object('container/ou', name=ou_name)
        wait_for_drs_replication(ldap.filter.filter_format('ou=%s', [ou_name]))
        udm.create_user(position=ou, username=user_name)
        udm.create_object('networks/network',
                          position=ou,
                          name=network_name,
                          network='1.1.1.1',
                          netmask='24')

        with pytest.raises(udm_test.UCSTestUDM_ModifyUDMObjectFailed) as exc:
            udm.modify_object('container/ou', dn=ou, name=ou_name_new)
        # This operation is not allowed on this object: Unable to move object ayj9blkm9k (networks/network) in subtree, trying to revert changes.
        assert 'Unable to move object' in str(exc.value)

        new_ou = 'ou=%s,%s' % (ldap.dn.escape_dn_chars(ou_name_new),
                               ucr.get('ldap/base'))
        new_user = '******' % (ldap.dn.escape_dn_chars(user_name),
                                        ldap.dn.escape_dn_chars(ou_name_new),
                                        ucr.get('ldap/base'))
        utils.verify_ldap_object(new_ou, should_exist=True)
        utils.verify_ldap_object(new_user, should_exist=True)

        lo = utils.get_ldap_connection()
        for dn, entry in lo.search(
                filter=ldap.filter.filter_format('ou=%s', (ou_name, ))):
            assert entry.get('ou')[0] == ou_name.encode(
                'UTF-8'), 'ou = %s; expected: %s' % (entry.get('ou')[0],
                                                     ou_name)
예제 #13
0
def test_udm_users_user_bcrypt_password(restart_slapd_after_test, udm, ucr):
    from univention.config_registry import handler_set
    """Test users/user and users/ldap bcrypt password handling"""
    # bugs: [52693]
    handler_set(['ldap/pw-bcrypt=true'])
    handler_set(['password/hashing/bcrypt=true'])
    utils.restart_slapd()

    for module in ['users/user', 'users/ldap']:
        lo = utils.get_ldap_connection()
        name = uts.random_username()
        attr = dict(password='******', username=name, lastname='test')
        dn = udm.create_object(module,
                               wait_for_replication=True,
                               check_for_drs_replication=True,
                               wait_for=True,
                               **attr)

        ldap_o = lo.search('uid={}'.format(name),
                           attr=['userPassword', 'pwhistory'])[0]
        assert ldap_o[1]['userPassword'][0].startswith(b'{BCRYPT}'), ldap_o
        assert ldap_o[1]['pwhistory'][0].split()[0].startswith(
            b'{BCRYPT}'), ldap_o

        # authentication
        univention.admin.uldap.access(binddn=dn, bindpw='univention')
        with pytest.raises(univention.admin.uexceptions.authFail):
            univention.admin.uldap.access(binddn=dn, bindpw='univention1')

        # password change
        udm.modify_object(module, dn=dn, password='******')
        ldap_o = lo.search('uid={}'.format(name),
                           attr=['userPassword', 'pwhistory'])[0]
        assert ldap_o[1]['userPassword'][0].startswith(b'{BCRYPT}'), ldap_o
        assert ldap_o[1]['pwhistory'][0].split()[0].startswith(
            b'{BCRYPT}'), ldap_o
        assert ldap_o[1]['pwhistory'][0].split()[1].startswith(
            b'{BCRYPT}'), ldap_o
        univention.admin.uldap.access(binddn=dn, bindpw='univention1')

        # password history
        # TODO how can we check univention.admin.uexceptions.pwalreadyused?
        with pytest.raises(udm_test.UCSTestUDM_ModifyUDMObjectFailed):
            udm.modify_object(module, dn=dn, password='******')

        # mixed password history
        handler_set(['password/hashing/bcrypt=false'])
        udm.stop_cli_server()
        udm.modify_object(module, dn=dn, password='******')
        ldap_o = lo.search('uid={}'.format(name),
                           attr=['userPassword', 'pwhistory'])[0]
        assert not ldap_o[1]['userPassword'][0].startswith(b'{BCRYPT}'), ldap_o
        assert ldap_o[1]['pwhistory'][0].split()[0].startswith(
            b'{BCRYPT}'), ldap_o
        assert ldap_o[1]['pwhistory'][0].split()[1].startswith(
            b'{BCRYPT}'), ldap_o
        assert not ldap_o[1]['pwhistory'][0].split()[2].startswith(
            b'{BCRYPT}'), ldap_o
        with pytest.raises(udm_test.UCSTestUDM_ModifyUDMObjectFailed):
            udm.modify_object(module, dn=dn, password='******')
        with pytest.raises(udm_test.UCSTestUDM_ModifyUDMObjectFailed):
            udm.modify_object(module, dn=dn, password='******')
        with pytest.raises(udm_test.UCSTestUDM_ModifyUDMObjectFailed):
            udm.modify_object(module, dn=dn, password='******')

        # and back
        handler_set(['password/hashing/bcrypt=true'])
        udm.stop_cli_server()
        udm.modify_object(module, dn=dn, password='******')
        ldap_o = lo.search('uid={}'.format(name),
                           attr=['userPassword', 'pwhistory'])[0]
        assert ldap_o[1]['userPassword'][0].startswith(b'{BCRYPT}'), ldap_o

        # disable
        univention.admin.uldap.access(binddn=dn, bindpw='univention4')
        udm.modify_object(module, dn=dn, disabled='1')
        with pytest.raises(univention.admin.uexceptions.authFail):
            univention.admin.uldap.access(binddn=dn, bindpw='univention4')
        udm.modify_object(module, dn=dn, disabled='0')
        univention.admin.uldap.access(binddn=dn, bindpw='univention4')

        # 2a variant and cost factor
        handler_set(['password/hashing/bcrypt/prefix=2a'])
        handler_set(['password/hashing/bcrypt/cost_factor=7'])
        udm.stop_cli_server()
        udm.modify_object(module, dn=dn, password='******')
        ldap_o = lo.search('uid={}'.format(name),
                           attr=['userPassword', 'pwhistory'])[0]
        assert ldap_o[1]['userPassword'][0].startswith(
            b'{BCRYPT}$2a$07$'), ldap_o
        univention.admin.uldap.access(binddn=dn, bindpw='univention5')
예제 #14
0
 def _lo(self):
     # type: () -> univention.admin.uldap.access
     if self.__lo is None:
         self.__lo = utils.get_ldap_connection()
     return self.__lo
예제 #15
0
class UCSTestSchool(object):
	_lo = utils.get_ldap_connection()
	_ucr = univention.testing.ucr.UCSTestConfigRegistry()
	_ucr.load()

	LDAP_BASE = _ucr['ldap/base']

	PATH_CMD_BASE = '/usr/share/ucs-school-import/scripts'
	PATH_CMD_CREATE_OU = PATH_CMD_BASE + '/create_ou'

	PATH_CMD_IMPORT_USER = PATH_CMD_BASE + '/import_user'
	CN_STUDENT = _ucr.get('ucsschool/ldap/default/container/pupils', 'schueler')
	CN_TEACHERS = _ucr.get('ucsschool/ldap/default/container/teachers', 'lehrer')
	CN_TEACHERS_STAFF = _ucr.get('ucsschool/ldap/default/container/teachers-and-staff', 'lehrer und mitarbeiter')
	CN_ADMINS = _ucr.get('ucsschool/ldap/default/container/admins', 'admins')
	CN_STAFF = _ucr.get('ucsschool/ldap/default/container/staff', 'mitarbeiter')

	def __init__(self):
		self._cleanup_ou_names = set()

	def __enter__(self):
		return self

	def __exit__(self, exc_type, exc_value, etraceback):
		if exc_type:
			print '*** Cleanup after exception: %s %s' % (exc_type, exc_value)
		try:
			self.cleanup()
		except:
			print ''.join(traceback.format_exception(exc_type, exc_value, etraceback))
			raise

	def open_ldap_connection(self, binddn=None, bindpw=None, ldap_server=None, admin=False, machine=False):
		'''Opens a new LDAP connection using the given user LDAP DN and
		password. The connection is established to the given server or
		(if None is given) to the server defined by the UCR variable
		ldap/server/name is used.
		If admin is set to True, a connection is setup by getAdminConnection().
		If machine is set to True, a connection to the master is setup by getMachoneConnection().
		'''

		assert not (admin and machine)

		account = utils.UCSTestDomainAdminCredentials()
		if not ldap_server:
			ldap_server = self._ucr.get('ldap/master')
		port = int(self._ucr.get('ldap/server/port', 7389))

		try:
			if admin:
				lo = udm_uldap.getAdminConnection()[0]
			elif machine:
				lo = udm_uldap.getMachineConnection(ldap_master=True)[0]
			else:
				lo = udm_uldap.access(host=ldap_server, port=port, base=self._ucr.get('ldap/base'), binddn=account.binddn, bindpw=account.bindpw, start_tls=2)
		except udm_errors.noObject:
			raise
		except LDAPError as exc:
			raise SchoolLDAPError('Opening LDAP connection failed: %s' % (exc,))

		return lo

	def _remove_udm_object(self, module, dn, raise_exceptions=False):
		"""
			Tries to remove UDM object specified by given dn.
			Return None on success or error message.
		"""
		try:
			dn = self._lo.searchDn(base=dn)[0]
		except (ldap.NO_SUCH_OBJECT, IndexError):
			if raise_exceptions:
				raise
			return 'missing object'

		msg = None
		cmd = [utu.UCSTestUDM.PATH_UDM_CLI_CLIENT_WRAPPED, module, 'remove', '--dn', dn]
		print '*** Calling following command: %r' % cmd
		retval = subprocess.call(cmd)
		if retval:
			msg = '*** ERROR: failed to remove UCS@school %s object: %s' % (module, dn)
			print msg
		return msg

	def _set_password(self, userdn, password, raise_exceptions=False):
		"""
			Tries to set a password for the given user.
			Return None on success or error message.
		"""
		try:
			dn = self._lo.searchDn(base=userdn)[0]
		except (ldap.NO_SUCH_OBJECT, IndexError):
			if raise_exceptions:
				raise
			return 'missing object'

		msg = None
		cmd = [utu.UCSTestUDM.PATH_UDM_CLI_CLIENT_WRAPPED, 'users/user', 'modify', '--dn', dn, '--set', 'password=%s' % password]
		print '*** Calling following command: %r' % cmd
		retval = subprocess.call(cmd)
		if retval:
			msg = 'ERROR: failed to set password for UCS@school user %s' % (userdn)
			print msg
		return msg

	def cleanup(self, wait_for_replication=True):
		""" Cleanup all objects created by the UCS@school test environment """
		for ou_name in self._cleanup_ou_names:
			self.cleanup_ou(ou_name, wait_for_replication=False)
		if wait_for_replication:
			utils.wait_for_replication()

	def cleanup_ou(self, ou_name, wait_for_replication=True):
		""" Removes the given school ou and all its corresponding objects like groups """

		print ''
		print '*** Purging OU %s and related objects' % ou_name
		# remove OU specific groups
		for grpdn in (
			'cn=OU%(ou)s-Member-Verwaltungsnetz,cn=ucsschool,cn=groups,%(basedn)s',
			'cn=OU%(ou)s-Member-Edukativnetz,cn=ucsschool,cn=groups,%(basedn)s',
			'cn=OU%(ou)s-Klassenarbeit,cn=ucsschool,cn=groups,%(basedn)s',
			'cn=OU%(ou)s-DC-Verwaltungsnetz,cn=ucsschool,cn=groups,%(basedn)s',
			'cn=OU%(ou)s-DC-Edukativnetz,cn=ucsschool,cn=groups,%(basedn)s',
			'cn=admins-%(ou)s,cn=ouadmins,cn=groups,%(basedn)s',
		):
			grpdn = grpdn % {'ou': ou_name, 'basedn': self._ucr.get('ldap/base')}
			self._remove_udm_object('groups/group', grpdn)

		# remove OU recursively
		if self._ucr.is_true('ucsschool/ldap/district/enable'):
			oudn = 'ou=%(ou)s,ou=%(district)s,%(basedn)s' % {'ou': ou_name, 'district': ou_name[0:2], 'basedn': self._ucr.get('ldap/base')}
		else:
			oudn = 'ou=%(ou)s,%(basedn)s' % {'ou': ou_name, 'basedn': self._ucr.get('ldap/base')}
		self._remove_udm_object('container/ou', oudn)
		print '*** Purging OU %s and related objects (%s): done\n\n' % (ou_name, oudn)
		if wait_for_replication:
			utils.wait_for_replication()

	def create_ou(self, ou_name=None, name_edudc=None, name_admindc=None, displayName='', name_share_file_server=None, use_cli=False, wait_for_replication=True):
		"""
		Creates a new OU with random or specified name. The function may also set a specified
		displayName. If "displayName" is None, a random displayName will be set. If "displayName"
		equals to the empty string (''), the displayName won't be set. "name_edudc" may contain
		the optional name for an educational dc slave. "name_admindc" may contain
		the optional name for an administrative dc slave. If name_share_file_server is set, the
		class share file server and the home share file server will be set.
		If use_cli is set to True, the old CLI interface is used. Otherwise the UCS@school python
		library is used.
		PLEASE NOTE: if name_edudc is set to the hostname of the master or backup, name_edudc will be unset automatically,
			because it's not allowed to specify the hostname of the master or any backup in any situation!

		Return value: (ou_name, ou_dn)
			ou_name: name of the created OU
			ou_dn:   DN of the created OU object
		"""
		# create random display name for OU
		charset = uts.STR_ALPHANUMDOTDASH + uts.STR_ALPHA.upper() + '()[]/,;:_#"+*@<>~ßöäüÖÄÜ$%&!     '
		if displayName is None:
			displayName = uts.random_string(length=random.randint(5, 50), charset=charset)

		# it is not allowed to set the master as name_edudc ==> resetting name_edudc
		if isinstance(name_edudc, str):
			if name_edudc.lower() == self._ucr.get('ldap/master', '').split('.', 1)[0].lower():
				print '*** It is not allowed to set the master as name_edudc ==> resetting name_edudc'
				name_edudc = None
			elif any([name_edudc.lower() == backup.split('.', 1)[0].lower() for backup in self._ucr.get('ldap/backup', '').split(' ')]):
				print '*** It is not allowed to set any backup as name_edudc ==> resetting name_edudc'
				name_edudc = None

		# create random OU name
		if not ou_name:
			ou_name = uts.random_string(length=random.randint(3, 12))

		# remember OU name for cleanup
		self._cleanup_ou_names.add(ou_name)

		if not use_cli:
			kwargs = {
				'name': ou_name,
				'dc_name': name_edudc
			}
			if name_admindc:
				kwargs['dc_name_administrative'] = name_admindc
			if name_share_file_server:
				kwargs['class_share_file_server'] = name_share_file_server
				kwargs['home_share_file_server'] = name_share_file_server
			if displayName:
				kwargs['display_name'] = displayName

			print ''
			print '*** Creating new OU %r' % (ou_name,)
			lo = self.open_ldap_connection()
			School.invalidate_all_caches()
			School.init_udm_module(lo)  # TODO FIXME has to be fixed in ucs-school-lib - should be done automatically
			result = School(**kwargs).create(lo)
			print '*** Result of School(...).create(): %r' % (result,)
			print '\n\n'
		else:
			# build command line
			cmd = [self.PATH_CMD_CREATE_OU]
			if displayName:
				cmd += ['--displayName', displayName]
			cmd += [ou_name]
			if name_edudc:
				cmd += [name_edudc]

			print '*** Calling following command: %r' % cmd
			retval = subprocess.call(cmd)
			if retval:
				utils.fail('create_ou failed with exitcode %s' % retval)

		if wait_for_replication:
			utils.wait_for_replication()

		ou_dn = 'ou=%s,%s' % (ou_name, self.LDAP_BASE)
		return ou_name, ou_dn

	def get_district(self, ou_name):
		try:
			return ou_name[:2]
		except IndexError:
			raise SchoolError('The OU name "%s" is too short for district mode' % ou_name)

	def get_ou_base_dn(self, ou_name):
		"""
		Returns the LDAP DN for the given school OU name (the district mode will be considered).
		"""
		return '%(school)s,%(district)s%(basedn)s' % {
			'school': 'ou=%s' % ou_name,
			'basedn': self.LDAP_BASE,
			'district': 'ou=%s,' % self.get_district(ou_name) if self._ucr.is_true('ucsschool/ldap/district/enable') else ''
		}

	def get_user_container(self, ou_name, is_teacher=False, is_staff=False):
		"""
		Returns user container for specified user role and ou_name.
		"""
		if is_teacher and is_staff:
			return 'cn=%s,cn=users,%s' % (self.CN_TEACHERS_STAFF, self.get_ou_base_dn(ou_name))
		if is_teacher:
			return 'cn=%s,cn=users,%s' % (self.CN_TEACHERS, self.get_ou_base_dn(ou_name))
		if is_staff:
			return 'cn=%s,cn=users,%s' % (self.CN_STAFF, self.get_ou_base_dn(ou_name))
		return 'cn=%s,cn=users,%s' % (self.CN_STUDENT, self.get_ou_base_dn(ou_name))

	def get_workinggroup_dn(self, ou_name, group_name):
		"""
		Return the DN of the specified working group.
		"""
		return 'cn=%s-%s,cn=schueler,cn=groups,%s' % (ou_name, group_name, self.get_ou_base_dn(ou_name))

	def get_workinggroup_share_dn(self, ou_name, group_name):
		"""
		Return the DN of the share object for the specified working group.
		"""
		return 'cn=%s-%s,cn=shares,%s' % (ou_name, group_name, self.get_ou_base_dn(ou_name))

	def create_teacher(self, *args, **kwargs):
		return self.create_user(*args, is_teacher=True, is_staff=False, **kwargs)

	def create_student(self, *args, **kwargs):
		return self.create_user(*args, is_teacher=False, is_staff=False, **kwargs)

	def create_exam_student(self, *args, **kwargs):
		pass

	def create_staff(self, *args, **kwargs):
		return self.create_user(*args, is_staff=True, is_teacher=False, **kwargs)

	def create_teacher_and_staff(self, *args, **kwargs):
		return self.create_user(*args, is_staff=True, is_teacher=True, **kwargs)

	def create_user(
		self, ou_name, schools=None, username=None, firstname=None, lastname=None, classes=None,
		mailaddress=None, is_teacher=False, is_staff=False, is_active=True, password='******',
		use_cli=False, wait_for_replication=True
	):
		"""
		Create a user in specified OU with given attributes. If attributes are not specified, random
		values will be used for username, firstname and lastname. If password is not None, the given
		password will be set for this user.

		Return value: (user_name, user_dn)
			user_name: name of the created user
			user_dn:   DN of the created user object
		"""
		if not ou_name:
			raise SchoolMissingOU('No OU name specified')

		# set default values
		if username is None:
			username = uts.random_username()
		if firstname is None:
			firstname = uts.random_string(length=10, numeric=False)
		if lastname is None:
			lastname = uts.random_string(length=10, numeric=False)
		if mailaddress is None:
			mailaddress = ''
		if schools is None:
			schools = [ou_name]

		user_dn = 'uid=%s,%s' % (username, self.get_user_container(ou_name, is_teacher, is_staff))
		if use_cli:
			if classes is None:
				classes = ''
			if classes:
				if not all(["-" in c for c in classes.split(',')]):
					utils.fail('*** Class names must be <school-ou>-<class-name>.')
			# create import file
			line = 'A\t%s\t%s\t%s\t%s\t%s\t\t%s\t%d\t%d\t%d\n' % (username, lastname, firstname, ou_name, classes, mailaddress, int(is_teacher), int(is_active), int(is_staff))
			with tempfile.NamedTemporaryFile() as tmp_file:
				tmp_file.write(line)
				tmp_file.flush()

				cmd = [self.PATH_CMD_IMPORT_USER, tmp_file.name]
				print '*** Calling following command: %r' % cmd
				retval = subprocess.call(cmd)
				if retval:
					utils.fail('create_ou failed with exitcode %s' % retval)

			if password is not None:
				self._set_password(user_dn, password)
		else:
			school_classes = defaultdict(list)
			if classes:
				for kls in classes.split(','):
					school_classes[kls.partition('-')[0]].append(kls)
			kwargs = {
				'school': ou_name,
				'schools': schools,
				'name': username,
				'firstname': firstname,
				'lastname': lastname,
				'email': mailaddress,
				'password': password,
				'disabled': not is_active,
				"school_classes": dict(school_classes)
			}
			print '*** Creating new user %r with %r.' % (username, kwargs)
			lo = self.open_ldap_connection()
			User.invalidate_all_caches()
			User.init_udm_module(lo)  # TODO FIXME has to be fixed in ucs-school-lib - should be done automatically
			cls = Student
			if is_teacher and is_staff:
				cls = TeachersAndStaff
			elif is_teacher and not is_staff:
				cls = Teacher
			elif not is_teacher and is_staff:
				cls = Staff
			result = cls(**kwargs).create(lo)
			print '*** Result of %s(...).create(): %r' % (cls.__name__, result,)

		if wait_for_replication:
			utils.wait_for_replication()

		return username, user_dn

	def create_school_admin(self, ou_name, username=None, schools=None, firstname=None, lastname=None, mailaddress=None, is_active=True, password='******', wait_for_replication=True):
		position = 'cn=admins,cn=users,%s' % (self.get_ou_base_dn(ou_name))
		groups = ["cn=admins-%s,cn=ouadmins,cn=groups,%s" % (ou_name, self.LDAP_BASE)]
		if username is None:
			username = uts.random_username()
		if firstname is None:
			firstname = uts.random_string(length=10, numeric=False)
		if lastname is None:
			lastname = uts.random_string(length=10, numeric=False)
		if mailaddress is None:
			mailaddress = ''
		kwargs = {
			'school': ou_name,
			'schools': schools,
			'username': username,
			'firstname': firstname,
			'lastname': lastname,
			'email': mailaddress,
			'password': password,
			'disabled': not(is_active),
			'options': ['samba', 'ucsschoolAdministrator', 'kerberos', 'posix', 'mail'],
		}
		udm = udm_test.UCSTestUDM()
		dn, school_admin = udm.create_user(position=position, groups=groups, **kwargs)
		if wait_for_replication:
			utils.wait_for_replication()
		return school_admin, dn

	def create_domain_admin(self, ou_name, username=None, password='******'):
		position = 'cn=admins,cn=users,%s' % (self.get_ou_base_dn(ou_name))
		groups = ["cn=Domain Admins,cn=groups,%s" % (self.LDAP_BASE,)]
		udm = udm_test.UCSTestUDM()
		if username is None:
			username = uts.random_username()
		kwargs = {
			'school': ou_name,
			'username': username,
			'password': password,
		}
		dn, domain_admin = udm.create_user(position=position, groups=groups, **kwargs)
		return domain_admin, dn

	def create_global_user(self, username=None, password='******'):
		position = 'cn=users,%s' % (self.LDAP_BASE,)
		udm = udm_test.UCSTestUDM()
		if username is None:
			username = uts.random_username()
		kwargs = {
			'username': username,
			'password': password,
		}
		dn, global_user = udm.create_user(position=position, **kwargs)
		return global_user, dn

	def create_school_class(self, ou_name, class_name=None, description=None, users=None, wait_for_replication=True):
		if class_name is None:
			class_name = uts.random_username()
		if not class_name.startswith('{}-'.format(ou_name)):
			class_name = '{}-{}'.format(ou_name, class_name)
		grp_dn = 'cn={},cn=klassen,cn=schueler,cn=groups,ou={},{}'.format(class_name, ou_name, self.LDAP_BASE)
		kwargs = {
			'school': ou_name,
			'name': class_name,
			'description': description,
			'users': users or [],
		}
		print('*** Creating new school class "{}" with {}...'.format(class_name, kwargs))
		lo = self.open_ldap_connection()
		SchoolClass.invalidate_all_caches()
		SchoolClass.init_udm_module(lo)
		result = SchoolClass(**kwargs).create(lo)
		print('*** Result of SchoolClass(...).create(): {}'.format(result))

		if wait_for_replication:
			utils.wait_for_replication()

		return class_name, grp_dn

	def create_workgroup(self, ou_name, workgroup_name=None, description=None, users=None, wait_for_replication=True):
		"""
		Creates a new workgroup in specified ou <ou_name>. If no name for the workgroup is specified,
		a random name is used. <name> has to be of format "<OU>-<WGNAME>" or "<WGNAME>".
		Group members may also be specified a list of user DNs in <users>.
		"""
		if workgroup_name is None:
			workgroup_name = uts.random_username()
		if not workgroup_name.startswith('{}-'.format(ou_name)):
			workgroup_name = '{}-{}'.format(ou_name, workgroup_name)
		grp_dn = 'cn={},cn=schueler,cn=groups,ou={},{}'.format(workgroup_name, ou_name, self.LDAP_BASE)
		kwargs = {
			'school': ou_name,
			'name': workgroup_name,
			'description': description,
			'users': users or [],
		}
		print('*** Creating new WorkGroup "{}" with {}...'.format(workgroup_name, kwargs))
		lo = self.open_ldap_connection()
		WorkGroup.invalidate_all_caches()
		WorkGroup.init_udm_module(lo)
		result = WorkGroup(**kwargs).create(lo)
		print('*** Result of WorkGroup(...).create(): {}'.format(result))

		if wait_for_replication:
			utils.wait_for_replication()

		return workgroup_name, grp_dn

	def create_computerroom(self, ou_name, name=None, description=None, host_members=None, wait_for_replication=True):
		"""
		Create a room in specified OU with given attributes. If attributes are not specified, random
		values will be used for roomname and description.

		Return value: (room_name, room_dn)
			room_name: name of the created room
			room_dn:   DN of the created room object
		"""
		if not ou_name:
			raise SchoolMissingOU('No OU name specified')

		# set default values
		if name is None:
			name = uts.random_name()
		if description is None:
			description = uts.random_string(length=10, numeric=False)

		host_members = host_members or []
		if not isinstance(host_members, (list, tuple)):
			host_members = [host_members]
		kwargs = {
			'school': ou_name,
			'name': '%s-%s' % (ou_name, name),
			'description': description,
			'hosts': host_members,
		}
		print '*** Creating new room %r' % (name,)
		lo = self.open_ldap_connection()
		obj = ComputerRoom(**kwargs)
		result = obj.create(lo)
		print '*** Result of ComputerRoom(...).create(): %r' % (result,)
		if wait_for_replication:
			utils.wait_for_replication()
		return name, result

	def create_windows(self):
		pass

	def create_mac(self):
		pass

	def create_ucc(self):
		pass

	def create_ip_managed_client(self):
		pass

	def create_school_dc_slave(self):
		pass
def _get_samba_sid(dn):
    lo = utils.get_ldap_connection()
    res = lo.search(base=dn, filter='objectClass=*', attr=['sambaSID'])
    return res[0][1].get('sambaSID')[0].decode('ASCII')
def lo():
    return utils.get_ldap_connection()
예제 #18
0
def test_filename_validation(modify, prefix, path, position, attr, ocs, name):
    lo = utils.get_ldap_connection()
    with udm_test.UCSTestUDM() as udm:
        pos = '%s,%s' % (
            position,
            udm.LDAP_BASE,
        )
        filename = filename_modify = '%s%s%s' % (prefix, name,
                                                 strings.random_string())
        if modify:
            dn_modify = '%s=%s,%s' % (attr, ldap.dn.escape_dn_chars(filename),
                                      pos)
            filename = filename.replace('/', '').replace('.', '')
        dn = '%s=%s,%s' % (attr, ldap.dn.escape_dn_chars(filename), pos)
        fullpath = os.path.join(path, filename)
        fullpath_modify = os.path.join(path, filename_modify)
        attrs = {
            attr: [filename],
            'cn': [filename],
            'objectClass': ['top', 'univentionObjectMetadata', ocs],
            'univentionOwnedByPackage': ['foo'],
            'univentionOwnedByPackageVersion': ['1'],
            attr.replace('Filename', 'Data'): [
                bz2.compress(
                    '\n' if modify else
                    'root:$6$5cAInBgG$7rdZuEujGK1QFoprcNspXsXHsymW3Txp0kDyHFsE.omI.3T0xek3KIneFPZ99Z8dwZnZ2I2O/Tk8x4mNNGSE4.:16965:0:99999:7:::'
                )
            ],
            attr.replace('Filename', 'Active'): ['TRUE'],
        }
        al = [(key, [v for v in val]) for key, val in attrs.items()]
        print(('Creating', dn))
        dn = lo.add(dn, al) or dn
        try:
            utils.wait_for_replication_and_postrun()
            if modify:
                assert os.path.exists(fullpath)
                if ocs == 'univentionLDAPExtensionACL':
                    assert os.path.exists(fullpath + '.info')

                print(('Modifying into', dn_modify))
                dn = lo.modify(dn, [
                    (attr, filename, filename_modify),
                    ('cn', filename, filename_modify),
                ]) or dn
                print(('Modified', dn))
                assert dn == dn_modify
                utils.wait_for_replication_and_postrun()

            # object was renamed (if modify). make sure the old files do not exists anymore.
            assert not os.path.exists(fullpath_modify), err(fullpath_modify)
            assert not os.path.exists(fullpath), err(fullpath)
            if ocs == 'univentionLDAPExtensionACL':
                assert not os.path.exists(fullpath + '.info'), err(fullpath +
                                                                   '.info')
                assert not os.path.exists(fullpath_modify +
                                          '.info'), err(fullpath_modify +
                                                        '.info')

            # create fake files and see if the listener would remove them.
            with open(fullpath_modify, 'w') as fd:
                fd.write('TEMP')
            if ocs == 'univentionLDAPExtensionACL':
                with open(fullpath_modify + '.info', 'w') as fd:
                    fd.write('TEMP')
        finally:
            lo.delete(dn)

        utils.wait_for_replication_and_postrun()
        assert os.path.exists(fullpath_modify), err(fullpath_modify)
        assert 'TEMP' in err(fullpath_modify)
        os.unlink(fullpath_modify)
        if ocs == 'univentionLDAPExtensionACL':
            assert os.path.exists(fullpath_modify +
                                  '.info'), err(fullpath_modify)
            assert 'TEMP' in err(fullpath_modify + '.info')
            os.unlink(fullpath_modify + '.info')
예제 #19
0
 def _lo(self):
     if self.__lo is None:
         self.__lo = utils.get_ldap_connection()
     return self.__lo
    def test_container_ou_rename_uppercase_with_special_characters(
            self, udm, ucr):
        """Rename a container/ou with subobjects from lower to upper case with special characters"""
        # bugs: [35959]
        lo = utils.get_ldap_connection()
        existing_temporary_ous = lo.searchDn(
            filter='ou=temporary_move_container_*')

        def test_organizational_unit(parent, add_user):
            if parent is None:
                parent = ucr.get('ldap/base')
            user_name = 'X' + uts.random_string(
            )  # test preserving name (case sensitivity)

            ou_name = uts.random_name_special_characters()
            ou_name_new = ou_name.upper()

            ou = udm.create_object('container/ou',
                                   position=parent,
                                   name=ou_name,
                                   wait_for=True)
            if add_user:
                udm.create_user(position=ou, username=user_name)

            try:
                udm.modify_object('container/ou',
                                  dn=ou,
                                  name=ou_name_new,
                                  wait_for=True)
            except AssertionError:
                pass
            for dn, entry in lo.search(filter='ou=temporary_move_container_*'):
                if dn not in existing_temporary_ous:
                    to_be_removed = udm._cleanup.setdefault('container/ou', [])
                    to_be_removed.append(dn)
                assert dn in existing_temporary_ous, 'ou = %s remained' % dn

            new_ou = 'ou=%s,%s' % (ldap.dn.escape_dn_chars(ou_name_new),
                                   parent)
            new_user = '******' % (ldap.dn.escape_dn_chars(user_name),
                                      new_ou)

            utils.verify_ldap_object(new_ou, {'ou': [ou_name_new]},
                                     should_exist=True)
            if add_user:
                for dn, entry in lo.search(filter=ldap.filter.filter_format(
                        'uid=%s', [user_name])):
                    assert entry.get('uid')[0] == user_name.encode(
                        'UTF-8'
                    ), 'CASE SENSITIVITY: uid = %s; expected: %s' % (
                        entry.get('uid')[0], user_name)
                utils.verify_ldap_object(new_user, should_exist=True)

            return new_ou

        # EMPTY
        # FIRST LEVEL
        first_level_unit = test_organizational_unit(parent=None,
                                                    add_user=False)

        # SECOND LEVEL
        test_organizational_unit(parent=first_level_unit, add_user=False)

        # WITH USER
        # FIRST LEVEL
        first_level_unit = test_organizational_unit(parent=None, add_user=True)

        # SECOND LEVEL
        test_organizational_unit(parent=first_level_unit, add_user=True)