Esempio n. 1
0
    def __call__(self, form, field):
        username = self.username_value_or_field
        if self.username_value_or_field in form.data:
            username = form.data[self.username_value_or_field]

        if app.config["LDAP_DEBUG_MODE"]:
            if username != app.config["LDAP_DEBUG_DATA"]["uid"] or  app.config["LDAP_DEBUG_PASSWORD"] != field.data:
                raise ValidationError("LDAP_DEBUG_PASSWORD set but incorrect, log in as %s, password %s." %
                        (app.config["LDAP_DEBUG_DATA"]["uid"], app.config["LDAP_DEBUG_PASSWORD"]))
            else:
                update_user_data(username, app.config["LDAP_DEBUG_DATA"])
                return

        try:
            connection = ldap.initialize(app.config["LDAP_SERVER"])

            # At this point, we update the user info (since we already have
            # a connection to LDAP)
            #connection.start_tls_s()
            escaped_username = escape_dn_chars(username)
            connection.simple_bind_s(app.config["LDAP_BIND_DN"].format(uid=escaped_username), field.data)
            filter = app.config["LDAP_FILTER"].format(uid=escaped_username)
            base_dn = app.config["LDAP_BASE_DN"].format(uid=escaped_username)
            results = connection.search_s(base_dn, ldap.SCOPE_SUBTREE, filter)
            results = {k:(v if len(v)>1 else v[0]) for k,v in results[0][1].iteritems()}
            connection.unbind_s()

            update_user_data(username, results)

        except ldap.INVALID_CREDENTIALS:
            raise ValidationError(self.message)
        except ldap.LDAPError, e:
            raise ValidationError("LDAP Error: " + (e.message["desc"] if e.message else "%s (%s)"%(e[1],e[0])))
Esempio n. 2
0
def create_user(connection):
    form = UserCreateForm()
    if form.validate_on_submit():
        uid = form.uid.data.encode('utf8')
        sn = form.sn.data.encode('utf8')
        givenName = form.givenName.data.encode('utf8')
        cn = '{0} {1}'.format(givenName, sn)
        password = encrypt_password(form.userPassword.data.encode('utf8'))
        entry = {
            'objectClass': ['inetOrgPerson'],
            'uid': uid,
            'sn': sn,
            'givenName': givenName,
            'cn': cn,
            'userPassword': password,
            'mail': form.mail.data.encode('utf8'),
            'mobile': form.mobile.data.encode('utf8'),
        }
        dn_template = app.config['USER_DN_TEMPLATE'].encode('utf8')
        dn = dn_template % {'uid': escape_dn_chars(uid)}
        try:
            connection.add_s(dn, ldap.modlist.addModlist(entry))
        except ldap.ALREADY_EXISTS:
            raise LDAPError(u"Benutzer mit Login '{0}' "
                            u"bereits vorhanden.".format(uid))
        return redirect(url_for('.edit_user', uid=uid))
    return render_template('user_create.html', form=form)
Esempio n. 3
0
 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)
Esempio n. 4
0
def auth_user(ldap_config, username=None, credentials=None):
    """
    Authenticate a user.
    """
    if not username or not credentials:
        LOG.warning('No username or credential is provided for'
                    ' authentication.')
        return False

    account_base = ldap_config.get('accountBase')
    if account_base is None:
        LOG.warning('Account base needs to be configured to query users')
        return False

    account_pattern = ldap_config.get('accountPattern')
    if account_pattern is None:
        LOG.warning('No account pattern is defined to search for users.')
        LOG.warning('Please configure one.')
        return False

    username = escape_dn_chars(username)
    account_pattern = account_pattern.replace('$USN$', username)

    account_scope = ldap_config.get('accountScope', '')
    account_scope = get_ldap_query_scope(account_scope)

    service_user = ldap_config.get('username')
    service_cred = ldap_config.get('password')

    # Service user is not configured try to authenticate
    # with the given username and credentials.
    if not service_user:
        service_user = username
        service_cred = credentials

    LOG.debug("Creating SERVICE connection...")
    with LDAPConnection(ldap_config, service_user, service_cred) as connection:
        if connection is None:
            LOG.error('Please check your LDAP server '
                      'authentication credentials.')
            LOG.error('Configured username: %s', service_user)
            return False

        user_dn = get_user_dn(connection, account_base, account_pattern,
                              account_scope)

        if user_dn is None:
            LOG.warning("DN lookup failed for user name: '%s'!", username)
            if service_user is None:
                LOG.warning('Anonymous bind might not be enabled.')
            return False

    # Bind with the user's DN to check the password given by the user.
    # If bind is successful the user has given the right password.
    LOG.debug("Creating USER connection...")
    with LDAPConnection(ldap_config, user_dn, credentials) as connection:
        if not connection:
            LOG.info("User: %s cannot be authenticated.", username)

        return connection is not None
Esempio n. 5
0
def create_user(connection):
    form = UserCreateForm()
    if form.validate_on_submit():
        uid = form.uid.data.encode('utf8')
        sn = form.sn.data.encode('utf8')
        givenName = form.givenName.data.encode('utf8')
        cn = '{0} {1}'.format(givenName, sn)
        password = encrypt_password(form.userPassword.data.encode('utf8'))
        entry = {
            'objectClass': ['inetOrgPerson'],
            'uid': uid,
            'sn': sn,
            'givenName': givenName,
            'cn': cn,
            'userPassword': password,
            'mail': form.mail.data.encode('utf8'),
            'mobile': form.mobile.data.encode('utf8'),
        }
        dn_template = app.config['USER_DN_TEMPLATE'].encode('utf8')
        dn = dn_template % {'uid': escape_dn_chars(uid)}
        try:
            connection.add_s(dn, ldap.modlist.addModlist(entry))
        except ldap.ALREADY_EXISTS:
            raise LDAPError(u"Benutzer mit Login '{0}' "
                            u"bereits vorhanden.".format(uid))
        return redirect(url_for('.edit_user', uid=uid))
    return render_template('user_create.html', form=form)
Esempio n. 6
0
	def modify(self, name, old_value, new_value):
		try:
			self.lo.modify(
				"cn={},{}".format(escape_dn_chars(name), self.ldap_base),
				[("ucsschoolUsernameNextNumber", str(old_value), str(new_value))]
			)
		except noObject:
			raise NoValueStored("Name {!r} not found.".format(name))
 def __init__(self, app, **kwargs):
     kwargs.setdefault(
         'position', 'cn=%s,cn=custom attributes,cn=univention' %
         escape_dn_chars(app.id))
     kwargs.setdefault('description', app.name)
     kwargs['module'] = re.split(r'\s*,\s*',
                                 kwargs.get('module', 'users/user'))
     super(ExtendedOption, self).__init__(app, **kwargs)
def join_sync_mode():
    """ Join in bi-directional sync mode via UMC requests """

    # check domain / get configuration:
    print '=== AD-JOIN STARTED ==='
    request_options = {
        "ad_server_address": options.domain_host,
        "username": options.domain_admin,
        "password": options.domain_password,
        "mode": "adconnector"
    }
    result = client.umc_command("adconnector/check_domain",
                                request_options).result

    # configure / save options:
    print '=== AD-JOIN CONFIGURATION ==='
    try:
        kerberos_domain = result['Domain']
        ad_ldap_base = result['LDAP_Base']
        ad_dc_name = result['DC_DNS_Name']
    except KeyError as exc:
        print "\nAn Error while reading the AD Domain configuration: %r" % exc
        exit(1)

    request_options = {
        'Host_IP':
        options.domain_host,
        'KerberosDomain':
        kerberos_domain,
        'LDAP_Base':
        ad_ldap_base,
        'LDAP_BindDN':
        "cn=" + escape_dn_chars(options.domain_admin) + ",cn=users," +
        ad_ldap_base,
        'LDAP_Host':
        ad_dc_name,
        'LDAP_Password':
        options.domain_password,
        'MappingSyncMode':
        "sync"
    }

    conf_result = client.umc_command("adconnector/adconnector/save",
                                     request_options).result
    if not conf_result['success']:
        print "\nThe AD Connector configuration was not saved successfully: %s" % conf_result
        exit(1)

    # start AD connector:
    print '=== AD-JOIN STARTING CONNECTOR ==='
    start_result = client.umc_command("adconnector/service", {
        'action': "start"
    }).result
    if not start_result['success']:
        print "\nThe AD Connector was not started successfully: %s" % start_result
        exit(1)

    print '=== AD-JOIN FINISHED ==='
Esempio n. 9
0
    def test_replica_install_after_restore(self):
        master = self.master
        replica1 = self.replicas[0]
        replica2 = self.replicas[1]

        tasks.install_master(master)
        tasks.install_replica(master, replica1)
        check_replication(master, replica1, "testuser1")

        # backup master.
        backup_path = backup(master)

        suffix = ipautil.realm_to_suffix(master.domain.realm)
        suffix = escape_dn_chars(str(suffix))
        entry_ldif = (
            "dn: cn=meTo{hostname},cn=replica,"
            "cn={suffix},"
            "cn=mapping tree,cn=config\n"
            "changetype: modify\n"
            "replace: nsds5ReplicaEnabled\n"
            "nsds5ReplicaEnabled: off\n\n"

            "dn: cn=caTo{hostname},cn=replica,"
            "cn=o\\3Dipaca,cn=mapping tree,cn=config\n"
            "changetype: modify\n"
            "replace: nsds5ReplicaEnabled\n"
            "nsds5ReplicaEnabled: off").format(
            hostname=replica1.hostname,
            suffix=suffix)
        # disable replication agreement
        tasks.ldapmodify_dm(master, entry_ldif)

        # uninstall master.
        tasks.uninstall_master(master, clean=False)

        # master restore.
        dirman_password = master.config.dirman_password
        master.run_command(['ipa-restore', backup_path],
                           stdin_text=dirman_password + '\nyes')

        # re-initialize topology after restore.
        topo_name = "{}-to-{}".format(master.hostname, replica1.hostname)
        for topo_suffix in 'domain', 'ca':
            arg = ['ipa',
                   'topologysegment-reinitialize',
                   topo_suffix,
                   topo_name,
                   '--left']
            replica1.run_command(arg)

        # wait sometime for re-initialization
        tasks.wait_for_replication(replica1.ldap_connect())

        # install second replica after restore
        tasks.install_replica(master, replica2)
        check_replication(master, replica2, "testuser2")
Esempio n. 10
0
    def test_replica_install_after_restore(self):
        master = self.master
        replica1 = self.replicas[0]
        replica2 = self.replicas[1]

        tasks.install_master(master)
        tasks.install_replica(master, replica1)
        check_replication(master, replica1, "testuser1")

        # backup master.
        backup_path = tasks.get_backup_dir(self.master)

        suffix = ipautil.realm_to_suffix(master.domain.realm)
        suffix = escape_dn_chars(str(suffix))
        entry_ldif = (
            "dn: cn=meTo{hostname},cn=replica,"
            "cn={suffix},"
            "cn=mapping tree,cn=config\n"
            "changetype: modify\n"
            "replace: nsds5ReplicaEnabled\n"
            "nsds5ReplicaEnabled: off\n\n"

            "dn: cn=caTo{hostname},cn=replica,"
            "cn=o\\3Dipaca,cn=mapping tree,cn=config\n"
            "changetype: modify\n"
            "replace: nsds5ReplicaEnabled\n"
            "nsds5ReplicaEnabled: off").format(
            hostname=replica1.hostname,
            suffix=suffix)
        # disable replication agreement
        tasks.ldapmodify_dm(master, entry_ldif)

        # uninstall master.
        tasks.uninstall_master(master, clean=False)

        # master restore.
        dirman_password = master.config.dirman_password
        master.run_command(['ipa-restore', backup_path],
                           stdin_text=dirman_password + '\nyes')

        # re-initialize topology after restore.
        topo_name = "{}-to-{}".format(master.hostname, replica1.hostname)
        for topo_suffix in 'domain', 'ca':
            arg = ['ipa',
                   'topologysegment-reinitialize',
                   topo_suffix,
                   topo_name,
                   '--left']
            replica1.run_command(arg)

        # wait sometime for re-initialization
        tasks.wait_for_replication(replica1.ldap_connect())

        # install second replica after restore
        tasks.install_replica(master, replica2)
        check_replication(master, replica2, "testuser2")
Esempio n. 11
0
 def lookup(cls, value, connection='default'):
     dn = cls.lookup_dn.format(escape_dn_chars(value))
     conn = connections[connection]
     try:
         result = conn.search_s(dn, ldap.SCOPE_BASE)[0][1]
     except ldap.NO_SUCH_OBJECT:
         raise cls.DoesNotExist(dn)
     instance = cls(result)
     instance._fetched = True
     return instance
Esempio n. 12
0
 def lookup(cls, value, connection='default'):
     dn = cls.lookup_dn.format(escape_dn_chars(value))
     conn = connections[connection]
     try:
         result = conn.search_s(dn, ldap.SCOPE_BASE)[0][1]
     except ldap.NO_SUCH_OBJECT:
         raise cls.DoesNotExist(dn)
     instance = cls(result)
     instance._fetched = True
     return instance
Esempio n. 13
0
 def __init__(self, app, lo, pos, create_if_not_exists=False):
     self._localhost = '%s.%s' % (ucr_get('hostname'),
                                  ucr_get('domainname'))
     self._udm_obj = None
     self._rdn = '%s_%s' % (app.id, app.version)
     self._container = 'cn=%s,cn=apps,cn=univention,%s' % (escape_dn_chars(
         app.id), ucr_get('ldap/base'))
     self._lo = lo
     self._pos = pos
     self._reload(app, create_if_not_exists)
Esempio n. 14
0
    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
Esempio n. 15
0
    def _add_self_to_portal(self, portal_obj):
        if isinstance(portal_obj, basestring):
            portal_mod = univention.admin.modules.get('settings/portal')
            portal_obj = univention.admin.objects.get(portal_mod,
                                                      None,
                                                      self.lo,
                                                      position='',
                                                      dn=portal_obj)

        portal_obj.open()
        old_content = portal_obj.info.get('content', [])
        if self.dn in [
                entry for category, entries in old_content for entry in entries
        ]:
            return
        new_content = None
        portal_category_dn = 'cn=%s,cn=categories,cn=portal,cn=univention,%s' % (
            escape_dn_chars(self['category']),
            self.lo.base,
        )
        category_already_in_old_content = any(
            self.lo.compare_dn(portal_category_dn, category)
            for category, entries in old_content)
        if category_already_in_old_content:
            new_content = [[
                category, entries + ([self.dn] if self.lo.compare_dn(
                    category, portal_category_dn) else [])
            ] for category, entries in old_content]
        else:
            new_content = [[portal_category_dn, [self.dn]]] + old_content
        if new_content != old_content:
            try:
                portal_category_mod = univention.admin.modules.get(
                    'settings/portal_category')
                univention.admin.objects.get(portal_category_mod,
                                             None,
                                             self.lo,
                                             position='',
                                             dn=portal_category_dn)
            except univention.admin.uexceptions.noObject:
                # if the settings/portal_category object for the category string does not exist anymore create it
                portal_category_pos = univention.admin.uldap.position(
                    self.lo.parentDn(portal_category_dn))
                portal_category_obj = portal_category_mod.object(
                    None, self.lo, portal_category_pos)
                portal_category_obj['name'] = self['category']
                portal_category_obj['displayName'] = {
                    'admin': [('en_US', 'Administration'),
                              ('de_DE', 'Verwaltung')],
                    'service': [('en_US', 'Applications'),
                                ('de_DE', 'Applikationen')]
                }.get(self['category'], [])
                portal_category_obj.create()
            portal_obj['content'] = new_content
            portal_obj.modify()
Esempio n. 16
0
	def create(self, name, value):
		try:
			self.lo.add(
				"cn={},{}".format(escape_dn_chars(name), self.ldap_base),
				[
					("objectClass", "ucsschoolUsername"),
					("ucsschoolUsernameNextNumber", str(value))
				]
			)
		except objectExists:
			raise NameKeyExists("Cannot create key {!r} - already exists.".format(name))
Esempio n. 17
0
 def update_details(self):
     self.details_form.process(formdata=request.form, obj=self.group)
     if self.details_form.validate_on_submit():
         mod_list = mod_list_from_form(self.group, self.details_form,
                                       ['description'])
         self.connection.modify_s(self.group.dn.encode('utf8'), mod_list)
         cn = self.details_form.cn.data.encode('utf8')
         if cn != self.group.cn:
             self.connection.rename_s(self.group.dn.encode('utf8'),
                                      "cn=" + escape_dn_chars(cn))
         flash(u"Details erfolgreich geändert.", "success")
Esempio n. 18
0
	def retrieve(self, name):
		try:
			res = self.lo.get(
				"cn={},{}".format(escape_dn_chars(name), self.ldap_base),
				attr=["ucsschoolUsernameNextNumber"])["ucsschoolUsernameNextNumber"][0]
		except (KeyError, noObject):
			raise NoValueStored("Name {!r} not found.".format(name))
		try:
			return int(res)
		except ValueError as exc:
			raise BadValueStored("Value for name {!r} has wrong format: {}".format(name, exc))
Esempio n. 19
0
 def update_details(self):
     self.details_form.process(formdata=request.form, obj=self.group)
     if self.details_form.validate_on_submit():
         mod_list = mod_list_from_form(self.group, self.details_form,
                                       ['description'])
         self.connection.modify_s(self.group.dn.encode('utf8'), mod_list)
         cn = self.details_form.cn.data.encode('utf8')
         if cn != self.group.cn:
             self.connection.rename_s(self.group.dn.encode('utf8'),
                                      "cn=" + escape_dn_chars(cn))
         flash(u"Details erfolgreich geändert.", "success")
Esempio n. 20
0
 def ldap(self, command, attrs):
     self.logger.debug('received command %s %s', command, attrs)
     if command == 'SEARCH':
         out = StringIO()
         ldif_writer = LDIFWriter(out)
         qs = get_user_model().objects.all()
         if attrs['filter'] != '(objectClass=*)':
             m = re.match(r'\((\w*)=(.*)\)', attrs['filter'])
             if not m:
                 print 'RESULT'
                 print 'code: 1'
                 print 'info: invalid filter'
                 print
                 return
             for user_attribute, ldap_attribute in MAPPING.iteritems():
                 if ldap_attribute == m.group(1):
                     break
             else:
                 print 'RESULT'
                 print 'code: 1'
                 print 'info: unknown attribute in filter'
                 print
                 return
             value = m.group(2)
             if value.endswith('*') and value.startswith('*'):
                 user_attribute += '__icontains'
                 value = value[1:-1]
             elif value.endswith('*'):
                 user_attribute += '__istartswith'
                 value = value[:-1]
             elif value.startswith('*'):
                 user_attribute += '__iendswith'
                 value = value[1:]
             else:
                 user_attribute += '__iexact'
             value = unescape_filter_chars(value)
             qs = qs.filter(**{user_attribute: value.decode('utf-8')})
         for user in qs:
             o = {}
             for user_attribute, ldap_attribute in MAPPING.iteritems():
                 o[ldap_attribute] = [
                     unicode(getattr(user, user_attribute)).encode('utf-8')
                 ]
             o['objectClass'] = ['inetOrgPerson']
             dn = 'uid=%s,%s' % (escape_dn_chars(
                 o['uid'][0]), attrs['suffix'])
             self.logger.debug(u'sending entry %s %s', dn, o)
             ldif_writer.unparse(dn, o)
         print out.getvalue(),
         out.close()
     print 'RESULT'
     print 'code: 0'
     print 'info: RockNRoll'
     print
Esempio n. 21
0
    def _clean_rdn(self, rdn):
        """ Escape all characters that need escaping for a DN, see RFC 2253 """
        if rdn.find('\\') != -1:
            # already escaped, disregard
            return rdn

        try:
            key, val = rdn.split('=')
            val = val.lstrip()
            return '%s=%s' % (key, escape_dn_chars(val))
        except ValueError:
            return rdn
Esempio n. 22
0
	def cleanup(self):
		lo, po = getAdminConnection()
		self.log.info("Removing new unique-usernames,cn=ucsschool entries...")
		for username in self.unique_basenames_to_remove:
			dn = "cn={},cn=unique-usernames,cn=ucsschool,cn=univention,{}".format(escape_dn_chars(username), lo.base)
			self.log.debug("Removing %r", dn)
			try:
				lo.delete(dn)
			except noObject:
				pass
			except ldapError as exc:
				self.log.error("DN %r -> %s", dn, exc)
		super(UniqueObjectTester, self).cleanup()
Esempio n. 23
0
 def ldap(self, command, attrs):
     self.logger.debug('received command %s %s', command, attrs)
     if command == 'SEARCH':
         out = StringIO()
         ldif_writer = LDIFWriter(out)
         qs = get_user_model().objects.all()
         if attrs['filter'] != '(objectClass=*)':
             m = re.match(r'\((\w*)=(.*)\)', attrs['filter'])
             if not m:
                 print 'RESULT'
                 print 'code: 1'
                 print 'info: invalid filter'
                 print
                 return
             for user_attribute, ldap_attribute in MAPPING.iteritems():
                 if ldap_attribute == m.group(1):
                     break
             else:
                 print 'RESULT'
                 print 'code: 1'
                 print 'info: unknown attribute in filter'
                 print
                 return
             value = m.group(2)
             if value.endswith('*') and value.startswith('*'):
                 user_attribute += '__icontains'
                 value = value[1:-1]
             elif value.endswith('*'):
                 user_attribute += '__istartswith'
                 value = value[:-1]
             elif value.startswith('*'):
                 user_attribute += '__iendswith'
                 value = value[1:]
             else:
                 user_attribute += '__iexact'
             value = unescape_filter_chars(value)
             qs = qs.filter(**{user_attribute: value.decode('utf-8')})
         for user in qs:
             o = {}
             for user_attribute, ldap_attribute in MAPPING.iteritems():
                 o[ldap_attribute] = [unicode(getattr(user, user_attribute)).encode('utf-8')]
             o['objectClass'] = ['inetOrgPerson']
             dn = 'uid=%s,%s' % (escape_dn_chars(o['uid'][0]), attrs['suffix'])
             self.logger.debug(u'sending entry %s %s', dn, o)
             ldif_writer.unparse(dn, o)
         print out.getvalue(),
         out.close()
     print 'RESULT'
     print 'code: 0'
     print 'info: RockNRoll'
     print
Esempio n. 24
0
def create_group(connection):
    form = GroupForm()
    if form.validate_on_submit():
        cn = form.cn.data.encode('utf8')
        entry = {
            'objectClass': ['groupOfMembers'],
            'cn': cn,
            'description': form.description.data.encode('utf8'),
        }
        dn_template = app.config['GROUP_DN_TEMPLATE'].encode('utf8')
        dn = dn_template % {'cn': escape_dn_chars(cn)}
        try:
            connection.add_s(dn, ldap.modlist.addModlist(entry))
        except ldap.ALREADY_EXISTS:
            raise LDAPError(u"Gruppe mit Name '{0}' "
                            u"bereits vorhanden.".format(cn))
        return redirect(url_for('.edit_group', cn=cn))
    return render_template('group_create.html', form=form)
Esempio n. 25
0
def create_group(connection):
    form = GroupForm()
    if form.validate_on_submit():
        cn = form.cn.data.encode('utf8')
        entry = {
            'objectClass': ['groupOfMembers'],
            'cn': cn,
            'description': form.description.data.encode('utf8'),
        }
        dn_template = app.config['GROUP_DN_TEMPLATE'].encode('utf8')
        dn = dn_template % {'cn': escape_dn_chars(cn)}
        try:
            connection.add_s(dn, ldap.modlist.addModlist(entry))
        except ldap.ALREADY_EXISTS:
            raise LDAPError(u"Gruppe mit Name '{0}' "
                            u"bereits vorhanden.".format(cn))
        return redirect(url_for('.edit_group', cn=cn))
    return render_template('group_create.html', form=form)
Esempio n. 26
0
    def authenticate(self, conf, login, password):
        """
        Set the login credentials before the superclass authenticates.

        We use the login credentials provided by the user to bind to the LDAP
        server.
        """
        # The login name is untrusted and must be escaped to prevent
        # LDAP-injection attacks.
        escaped_login = escape_dn_chars(login)
        # Use %-formatting to be consistent with ldap_filter, as we don't
        # want to change python-ldap.
        binddn = conf["u_ldap_binddn_fmt"] % escaped_login
        conf["ldap_binddn"] = binddn
        conf["ldap_password"] = password
        # We don't pass the escaped login because it gets escaped when the
        # filter is created.
        # TODO Does user creation work if login contains exotic characters?
        return super().authenticate(conf, login, password)
Esempio n. 27
0
    def authenticate_block(self, block, username, password):
        utf8_username = username.encode('utf-8')
        utf8_password = password.encode('utf-8')

        for conn in get_connections(block):
            authz_ids = []
            user_basedn = block.get('user_basedn') or block['basedn']

            try:
                if block['user_dn_template']:
                    template = str(block['user_dn_template'])
                    escaped_username = escape_dn_chars(utf8_username)
                    authz_ids.append(template.format(username=escaped_username))
                else:
                    try:
                        if block.get('bind_with_username'):
                            authz_ids.append(utf8_username)
                        elif block['user_filter']:
                            # allow multiple occurences of the username in the filter
                            user_filter = block['user_filter']
                            n = len(user_filter.split('%s')) - 1
                            try:
                                query = filter_format(user_filter, (utf8_username,) * n)
                            except TypeError, e:
                                log.error('user_filter syntax error %r: %s',
                                        block['user_filter'], e)
                                return
                            log.debug('looking up dn for username %r using '
                                    'query %r', username, query)
                            results = conn.search_s(user_basedn, ldap.SCOPE_SUBTREE, query)
                            # remove search references
                            results = [ result for result in results if result[0] is not None]
                            log.debug('found dns %r', results)
                            if len(results) == 0:
                                log.debug('user lookup failed: no entry found, %s' % query)
                            elif not block['multimatch'] and len(results) > 1:
                                log.error('user lookup failed: too many (%d) '
                                        'entries found: %s', len(results), query)
                            else:
                                authz_ids.extend(result[0] for result in results)
                        else:
                            raise NotImplementedError
def check_correct_domain_admin():
    """Check domain administrator saved for ucs-test and
	correct if needed"""

    print('=== Checking / Correcting ucs-test domain administrator ===')

    ucr.load()
    ucr_domain_admin = ucr['tests/domainadmin/account']
    if ucr_domain_admin:
        ucr_domain_admin_parts = str2dn(ucr_domain_admin)
        if ucr_domain_admin_parts[0][0][1] != options.domain_admin:
            ucr_domain_admin_parts[0][0] = ('uid', options.domain_admin,
                                            ucr_domain_admin_parts[0][0][2])
            ucr['tests/domainadmin/account'] = dn2str(ucr_domain_admin_parts)
    else:
        print(
            "=== tests/domainadmin/account is not set, trying to create it ==="
        )
        ucr['tests/domainadmin/account'] = 'uid=%s,cn=users,%s' % (
            escape_dn_chars(options.domain_admin), ucr['ldap/base'])

    ucr.save()
Esempio n. 29
0
def load_user(req):
    auth = req.authorization
    if not auth or not auth.password:
        return None
    uid = escape_dn_chars(auth.username)
    bind_dn = app.config['BIND_DN']
    bind_pw = app.config['BIND_PW']
    with pool.connection(bind_dn, bind_pw) as conn:
        try:
            dn = get_user_dn(conn, uid)
        except ldap.INVALID_CREDENTIALS:
            return LDAPError(u"Falsche Zugangsdaten für DN-Suche.")
        except NoResults:
            return None

    # Bind as user
    try:
        with pool.connection(dn, auth.password) as conn:
            user = get_user(conn, uid)._asdict()
            user['userPassword'] = auth.password
            return AppUser(**user)
    except ldap.INVALID_CREDENTIALS:
        return None
Esempio n. 30
0
def load_user(req):
    auth = req.authorization
    if not auth or not auth.password:
        return None
    uid = escape_dn_chars(auth.username)
    bind_dn = app.config['BIND_DN']
    bind_pw = app.config['BIND_PW']
    with pool.connection(bind_dn, bind_pw) as conn:
        try:
            dn = get_user_dn(conn, uid)
        except ldap.INVALID_CREDENTIALS:
            return LDAPError(u"Falsche Zugangsdaten für DN-Suche.")
        except NoResults:
            return None

    # Bind as user
    try:
        with pool.connection(dn, auth.password) as conn:
            user = get_user(conn, uid)._asdict()
            user['userPassword'] = auth.password
            return AppUser(**user)
    except ldap.INVALID_CREDENTIALS:
        return None
 def __init__(self, app, **kwargs):
     kwargs.setdefault('belongs_to', '%sUser' % (app.id, ))
     kwargs.setdefault(
         'position', 'cn=%s,cn=custom attributes,cn=univention' %
         escape_dn_chars(app.id))
     kwargs.setdefault('tab_name', app.name)
     kwargs.setdefault('ldap_mapping', kwargs['name'])
     kwargs['module'] = re.split(r'\s*,\s*',
                                 kwargs.get('module', 'users/user'))
     if 'options' in kwargs:
         kwargs['options'] = re.split(r'\s*,\s*', kwargs.get('options', []))
     kwargs.setdefault('options', [])
     super(ExtendedAttribute, self).__init__(app, **kwargs)
     if self.syntax == 'Boolean':
         self._syntax = '1.3.6.1.4.1.1466.115.121.1.7'
         self._equality = 'booleanMatch'
         self._substr = None
         if not self.udm_syntax:
             self.udm_syntax = 'TrueFalseUp'
     elif self.syntax == 'BooleanString':
         self._syntax = '1.3.6.1.4.1.1466.115.121.1.15'
         self._equality = 'caseIgnoreMatch'
         self._substr = 'caseIgnoreSubstringsMatch'
         if not self.udm_syntax:
             self.udm_syntax = 'TrueFalseUp'
     elif self.syntax == 'String':
         self._syntax = '1.3.6.1.4.1.1466.115.121.1.15'
         self._equality = 'caseIgnoreMatch'
         self._substr = 'caseIgnoreSubstringsMatch'
         #self._syntax = '1.3.6.1.4.1.1466.115.121.1.26'
         #self._equality = 'caseIgnoreIA5Match'
         #self._substr = None
         if not self.udm_syntax:
             self.udm_syntax = 'string'
     else:
         attribute_logger.warn('Ignoring unknown syntax %r' %
                               (self.syntax, ))
Esempio n. 32
0
def rdn_value_to_dn(keyword, account_type='user'):
    """Convert keyword and account type to DN."""
    keyword = web.safestr(keyword).strip().replace(' ', '')
    keyword = escape_dn_chars(keyword)
    account_type = web.safestr(account_type)

    # No matter what account type is, try to get a domain name.
    domain = keyword.split('@', 1)[-1]

    # Validate account type.
    if account_type not in attrs.ACCOUNT_TYPES_ALL:
        return 'INVALID_ACCOUNT_TYPE'

    # Validate keyword.
    # Keyword is email address.
    if account_type in attrs.ACCOUNT_TYPES_EMAIL and \
       not iredutils.is_email(keyword):
        return 'INVALID_MAIL'

    # Keyword is domain name.
    if account_type in attrs.ACCOUNT_TYPES_DOMAIN and \
       not iredutils.is_domain(keyword):
        return 'INVALID_DOMAIN_NAME'

    # Convert keyword to DN.
    if account_type == 'user':
        dn = '{}={},{}{}={},{}'.format(attrs.RDN_USER, keyword,
                                       attrs.DN_BETWEEN_USER_AND_DOMAIN,
                                       attrs.RDN_DOMAIN, domain,
                                       settings.ldap_basedn)
    elif account_type == 'admin':
        dn = '{}={},{}'.format(attrs.RDN_ADMIN, keyword,
                               settings.ldap_domainadmin_dn)
    elif account_type == 'domain':
        dn = '{}={},{}'.format(attrs.RDN_DOMAIN, keyword, settings.ldap_basedn)

    return dn
Esempio n. 33
0
 def do_query_ipaddr(name):
     return query_ipaddr(qtype, remote, tags, fmt % escape_dn_chars(name))
Esempio n. 34
0
 def _build_dn_with_config(self, login):
     login_esc = escape_dn_chars(login)
     return '{}={},{}'.format(self.user_login_attribute, login_esc,
                              self.user_base_dn)
Esempio n. 35
0
def convert_keyword_to_dn(keyword, accountType='user'):
    '''Convert keyword and account type to DN.'''
    keyword = web.safestr(keyword).strip().replace(' ', '')
    keyword = escape_dn_chars(keyword)
    accountType == web.safestr(accountType)

    # No matter what account type is, try to get a domain name.
    domain = keyword.split('@', 1)[-1]

    # Validate account type.
    if accountType not in attrs.ACCOUNT_TYPES_ALL:
        return 'INVALID_ACCOUNT_TYPE'

    # Validate keyword.
    # Keyword is email address.
    if accountType in attrs.ACCOUNT_TYPES_EMAIL and \
       not iredutils.is_email(keyword):
        return 'INVALID_MAIL'

    # Keyword is domain name.
    if accountType in attrs.ACCOUNT_TYPES_DOMAIN and \
       not iredutils.is_domain(keyword):
        return 'INVALID_DOMAIN_NAME'

    # Convert keyword to DN.
    if accountType == 'user':
        dn = '%s=%s,%s%s=%s,%s' % (
            attrs.RDN_USER, keyword,
            attrs.DN_BETWEEN_USER_AND_DOMAIN,
            attrs.RDN_DOMAIN, domain,
            settings.ldap_basedn,
        )
    elif accountType == 'maillist':
        dn = '%s=%s,%s%s=%s,%s' % (
            attrs.RDN_MAILLIST, keyword,
            attrs.DN_BETWEEN_MAILLIST_AND_DOMAIN,
            attrs.RDN_DOMAIN, domain,
            settings.ldap_basedn,
        )
    elif accountType == 'maillistExternal':
        dn = '%s=%s,%s%s=%s,%s' % (
            attrs.RDN_MAILLIST_EXTERNAL, keyword,
            attrs.DN_BETWEEN_MAILLIST_EXTERNAL_AND_DOMAIN,
            attrs.RDN_DOMAIN, domain,
            settings.ldap_basedn,
        )
    elif accountType == 'alias':
        dn = '%s=%s,%s%s=%s,%s' % (
            attrs.RDN_ALIAS, keyword,
            attrs.DN_BETWEEN_ALIAS_AND_DOMAIN,
            attrs.RDN_DOMAIN, domain,
            settings.ldap_basedn,
        )
    elif accountType == 'admin':
        dn = '%s=%s,%s' % (
            attrs.RDN_ADMIN, keyword,
            settings.ldap_domainadmin_dn,
        )
    elif accountType == 'catchall':
        dn = '%s=@%s,%s%s=%s,%s' % (
            attrs.RDN_CATCHALL, domain,
            attrs.DN_BETWEEN_CATCHALL_AND_DOMAIN,
            attrs.RDN_DOMAIN, domain,
            settings.ldap_basedn,
        )
    elif accountType == 'domain':
        dn = '%s=%s,%s' % (
            attrs.RDN_DOMAIN, keyword,
            settings.ldap_basedn,
        )

    return dn
Esempio n. 36
0
	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()
Esempio n. 37
0
from django.utils.encoding import force_text

from authentic2.a2_rbac.utils import get_default_ou
from django_rbac.utils import get_ou_model
from authentic2.backends import ldap_backend
from authentic2 import crypto

import utils

pytestmark = pytest.mark.skipunless(has_slapd(),
                                    reason='slapd is not installed')

USERNAME = u'etienne.michu'
UID = 'etienne.michu'
CN = 'Étienne Michu'
DN = 'cn=%s,o=ôrga' % escape_dn_chars(CN)
PASS = '******'
EMAIL = '*****@*****.**'


@pytest.fixture
def slapd(request):
    slapd = Slapd()
    slapd.add_db('o=ôrga')
    slapd.add_ldif('''dn: o=ôrga
objectClass: organization
o: ôrga

dn: {dn}
objectClass: inetOrgPerson
userPassword: {password}
Esempio n. 38
0
 def dn(unique_id):
     params = (escape_dn_chars(unique_id), settings.LDAP_USERS_GROUP)
     return 'uniqueIdentifier=%s,%s' % params
Esempio n. 39
0
 def get_dn(self):
     dn_primary = getattr(self, self.primary_key)
     return self.lookup_dn.format(escape_dn_chars(dn_primary))
Esempio n. 40
0
def _handler(ucr, changes):
    changed_entries = set()
    for key in changes.keys():
        match = re.match('ucs/web/overview/entries/(admin|service)/([^/]+)/.*',
                         key)
        if match:
            changed_entries.add(match.group(2))
    changed_entries -= set(
        ['umc', 'invalid-certificate-list', 'root-certificate', 'ldap-master'])
    portal_logger.debug('Changed: %r' % changed_entries)
    if not changed_entries:
        return
    lo, pos = get_machine_connection()
    pos.setDn('cn=entry,cn=portals,cn=univention,%s' % ucr.get('ldap/base'))
    hostname = '%s.%s' % (ucr.get('hostname'), ucr.get('domainname'))

    # iterate over all ipv4 and ipv6 addresses and append them to the link
    local_hosts = [hostname]
    interfaces = Interfaces(ucr)
    for idev, iconf in interfaces.all_interfaces:
        # get ipv4 address of device
        if iconf.ipv4_address():
            local_hosts.append(str(iconf.ipv4_address().ip))

        # get ipv6 addresses of device
        for iname in iconf.ipv6_names:
            local_hosts.append('[%s]' % (iconf.ipv6_address(iname).ip, ))

    portal_logger.debug('Local hosts are: %r' % local_hosts)
    attr_entries = {}
    for changed_entry in changed_entries:
        attr_entries[changed_entry] = {}
    for ucr_key in ucr.keys():
        match = re.match('ucs/web/overview/entries/([^/]+)/([^/]+)/(.*)',
                         ucr_key)
        if not match:
            continue
        category = match.group(1)
        cn = match.group(2)
        key = match.group(3)
        value = ucr.get(ucr_key)
        if cn in attr_entries:
            portal_logger.debug('Matched %r -> %r' % (ucr_key, value))
            entry = attr_entries[cn]
            entry['name'] = cn
            if '_links' not in entry:
                links = []
                for host in local_hosts:
                    if host:
                        links.append(_Link(host=host))
                entry['_links'] = links
            if key == 'link':
                for link in entry['_links']:
                    if value.startswith('http'):
                        link.full = value
                    else:
                        link.path = value
            elif key == 'port_http':
                if value:
                    for link in entry['_links'][:]:
                        if link.protocol == 'https':
                            link = copy(link)
                            entry['_links'].append(link)
                        link.protocol = 'http'
                        link.port = value
            elif key == 'port_https':
                if value:
                    for link in entry['_links'][:]:
                        if link.protocol == 'http':
                            link = copy(link)
                            entry['_links'].append(link)
                        link.protocol = 'https'
                        link.port = value
            elif key == 'icon':
                try:
                    if value.startswith('/univention-management-console'):
                        value = '/univention%s' % value[30:]
                    with open('/var/www/%s' % value, 'rb') as fd:
                        entry['icon'] = b64encode(fd.read()).decode('ASCII')
                except EnvironmentError:
                    pass
            elif key == 'label':
                entry.setdefault('displayName', [])
                entry['displayName'].append(('en_US', value))
            elif key == 'label/de':
                entry.setdefault('displayName', [])
                entry['displayName'].append(('de_DE', value))
            elif key == 'label/fr':
                entry.setdefault('displayName', [])
                entry['displayName'].append(('fr_FR', value))
            elif key == 'description':
                entry.setdefault('description', [])
                entry['description'].append(('en_US', value))
            elif key == 'description/de':
                entry.setdefault('description', [])
                entry['description'].append(('de_DE', value))
            elif key == 'description/fr':
                entry.setdefault('description', [])
                entry['description'].append(('fr_FR', value))
            elif key == 'link-target':
                entry['linkTarget'] = value
            elif key == 'background-color':
                entry['backgroundColor'] = value
            else:
                portal_logger.info('Don\'t know how to handle UCR key %s' %
                                   ucr_key)
    for cn, attrs in attr_entries.items():
        dn = 'cn=%s,%s' % (escape_dn_chars(cn), pos.getDn())
        unprocessed_links = attrs.pop('_links', [])
        my_links = set()
        no_ports = all(not link.port for link in unprocessed_links)
        for link in unprocessed_links:
            if no_ports:
                if link.protocol == 'http':
                    link.port = '80'
                elif link.protocol == 'https':
                    link.port = '443'
            if link:
                my_links.add(('en_US', str(link)))
            if not link.protocol:
                link.protocol = 'http'
                if link:
                    my_links.add(('en_US', str(link)))
                link.protocol = 'https'
                if link:
                    my_links.add(('en_US', str(link)))
        my_links = list(my_links)
        portal_logger.debug('Processing %s' % dn)
        portal_logger.debug('Attrs: %r' % attrs)
        portal_logger.debug('Links: %r' % my_links)
        try:
            obj = init_object('portals/entry', lo, pos, dn)
        except AttributeError:
            portal_logger.error(
                'The handler is not ready yet. Portal modules are not installed. You may have to set the variables again.'
            )
            return
        except udm_errors.noObject:
            portal_logger.debug('DN not found...')
            if my_links:
                portal_logger.debug('... creating')
                attrs['link'] = my_links
                attrs['activated'] = True
                try:
                    create_object_if_not_exists('portals/entry', lo, pos,
                                                **attrs)
                except udm_errors.insufficientInformation as exc:
                    portal_logger.info('Cannot create: %s' % exc)
                try:
                    category_pos = position(ucr.get('ldap/base'))
                    category_pos.setDn('cn=category,cn=portals,cn=univention')
                    category_dn = 'cn=domain-%s,%s' % (
                        escape_dn_chars(category),
                        category_pos.getDn(),
                    )
                    portal_logger.debug('Adding entry to %s' % (category_dn, ))
                    obj = init_object('portals/category', lo, category_pos,
                                      category_dn)
                    entries = obj['entries']
                    entries.append(dn)
                    modify_object('portals/category',
                                  lo,
                                  category_pos,
                                  category_dn,
                                  entries=entries)
                except udm_errors.noObject:
                    portal_logger.debug('DN not found...')
            continue
        links = obj['link']
        portal_logger.debug('Existing links: %r' % links)
        links = [
            _link for _link in links
            if urlsplit(_link[1]).hostname not in local_hosts
        ]
        links.extend(my_links)
        portal_logger.debug('New links: %r' % links)
        if not links:
            portal_logger.debug('Removing DN')
            remove_object_if_exists('portals/entry', lo, pos, dn)
        else:
            portal_logger.debug('Modifying DN')
            attrs['link'] = links
            modify_object('portals/entry', lo, pos, dn, **attrs)
Esempio n. 41
0
 def dn(unique_id):
     """Formats a unique_id into a DN."""
     params = (escape_dn_chars(unique_id), settings.LDAP_USERS_GROUP)
     return 'uniqueIdentifier=%s,%s' % params
Esempio n. 42
0
 def dn(self):
     return 'univentionAppID=%s,%s' % (escape_dn_chars(
         self._rdn), self._container)
Esempio n. 43
0
 def dn(person_unique_id, unique_id):
     """
     Formats an LDAP distinguished name for a remote system id
     """
     params = (escape_dn_chars(unique_id), Person.dn(person_unique_id))
     return 'uniqueIdentifier=%s,%s' % params
Esempio n. 44
0
def query(qname, qclass, qtype, id_, remote):
    """
    Answers a DNS query.
    """
    if qclass != 'IN':
        raise DNSError('Unsupported qclass: %s (expceted "IN")' % qclass)
    domain = Domain(qname.lower())
    edu_domain = None
    tags = set()
    if not hasattr(config, '_zone_domains'):
        config._zone_domains = [Domain(z) for z in config.zones]

    if not hasattr(config, '_edu_zone_domains'):
        config._edu_zone_domains = [Domain(z) for z in config.edu_zones]

    for zone in config._zone_domains:
        if domain <= zone:
            relative = domain[:-len(zone)]
            while relative and relative[-1] in known_tags:
                tags.add(relative.pop())
            break
    else:
        for zone in config._edu_zone_domains:
            if domain <= zone:
                edu_domain = domain[-3]
                relative = domain[:-len(zone)]
                while relative and relative[-1] in known_tags:
                    tags.add(relative.pop())
                break
        else:
            raise DNSError('Not in my zones: %s' % qname)

    answers = []

    searches = config.searches if edu_domain is None \
        else [config.edu_search(edu_domain)]
    raw_search = config.raw_search if edu_domain is None \
        else config.edu_raw_search(edu_domain)

    if qtype in ('A', 'AAAA', 'ANY'):

        def do_query_ipaddr(name):
            return query_ipaddr(qtype, remote, tags, fmt % escape_dn_chars(name))

        for fmt in searches:
            ips = do_query_ipaddr(str(relative) or '@')
            if not ips and len(relative) > 0:
                # Wildcard support.
                # Currently only cn=**.<dc> elements in the LDAP tree are
                # respected. <dc> is a single domain component, and the **
                # matches an arbitrary number of domain components.
                wild_relative = relative[-1:]
                wild_relative.appendleft('**')
                ips = do_query_ipaddr(str(wild_relative))
            if ips:
                more = [make_answer(qname, qtype_, ip) for ip, qtype_ in ips]
                answers.extend(more)
                break

    base = raw_search % escape_dn_chars(str(relative) or '@')
    more = [make_answer(qname, t, a % dict(zone=zone))
            for a, t in query_raw(qtype, remote, tags, base)]
    answers.extend(more)

    if len(relative) == 0 and qtype in ('SOA', 'ANY'):
        extra = make_answer(qname, 'SOA', compose_soa(str(zone)))
        answers.append(extra)

    return answers
Esempio n. 45
0
 def _build_dn_with_config(self, login, user_login_attribute, user_base_dn):
     login_esc = escape_dn_chars(login)
     return f'{user_login_attribute}={login_esc},{user_base_dn}'
Esempio n. 46
0
def convert_keyword_to_dn(keyword, accountType='user'):
    '''Convert keyword and account type to DN.'''
    keyword = web.safestr(keyword).strip().replace(' ', '')
    keyword = escape_dn_chars(keyword)
    accountType == web.safestr(accountType)

    # No matter what account type is, try to get a domain name.
    domain = keyword.split('@', 1)[-1]

    # Validate account type.
    if accountType not in attrs.ACCOUNT_TYPES_ALL:
        return 'INVALID_ACCOUNT_TYPE'

    # Validate keyword.
    # Keyword is email address.
    if accountType in attrs.ACCOUNT_TYPES_EMAIL and \
       not iredutils.is_email(keyword):
        return 'INVALID_MAIL'

    # Keyword is domain name.
    if accountType in attrs.ACCOUNT_TYPES_DOMAIN and \
       not iredutils.is_domain(keyword):
        return 'INVALID_DOMAIN_NAME'

    # Convert keyword to DN.
    if accountType == 'user':
        dn = '%s=%s,%s%s=%s,%s' % (
            attrs.RDN_USER, keyword,
            attrs.DN_BETWEEN_USER_AND_DOMAIN,
            attrs.RDN_DOMAIN, domain,
            settings.ldap_basedn,
        )
    elif accountType == 'maillist':
        dn = '%s=%s,%s%s=%s,%s' % (
            attrs.RDN_MAILLIST, keyword,
            attrs.DN_BETWEEN_MAILLIST_AND_DOMAIN,
            attrs.RDN_DOMAIN, domain,
            settings.ldap_basedn,
        )
    elif accountType == 'maillistExternal':
        dn = '%s=%s,%s%s=%s,%s' % (
            attrs.RDN_MAILLIST_EXTERNAL, keyword,
            attrs.DN_BETWEEN_MAILLIST_EXTERNAL_AND_DOMAIN,
            attrs.RDN_DOMAIN, domain,
            settings.ldap_basedn,
        )
    elif accountType == 'alias':
        dn = '%s=%s,%s%s=%s,%s' % (
            attrs.RDN_ALIAS, keyword,
            attrs.DN_BETWEEN_ALIAS_AND_DOMAIN,
            attrs.RDN_DOMAIN, domain,
            settings.ldap_basedn,
        )
    elif accountType == 'admin':
        dn = '%s=%s,%s' % (
            attrs.RDN_ADMIN, keyword,
            settings.ldap_domainadmin_dn,
        )
    elif accountType == 'catchall':
        dn = '%s=@%s,%s%s=%s,%s' % (
            attrs.RDN_CATCHALL, domain,
            attrs.DN_BETWEEN_CATCHALL_AND_DOMAIN,
            attrs.RDN_DOMAIN, domain,
            settings.ldap_basedn,
        )
    elif accountType == 'domain':
        dn = '%s=%s,%s' % (
            attrs.RDN_DOMAIN, keyword,
            settings.ldap_basedn,
        )

    return dn