Example #1
0
    def normalize_dn(dn):
        # Capitalize the attribute names as an LDAP server might.

        # NOTE(blk-u): Special case for this tested value, used with
        # test_user_id_comma. The call to str2dn here isn't always correct
        # here, because `dn` is escaped for an LDAP filter. str2dn() normally
        # works only because there's no special characters in `dn`.
        if dn == 'cn=Doe\\5c, John,ou=Users,cn=example,cn=com':
            return 'CN=Doe\\, John,OU=Users,CN=example,CN=com'

        # NOTE(blk-u): Another special case for this tested value. When a
        # roleOccupant has an escaped comma, it gets converted to \2C.
        if dn == 'cn=Doe\\, John,ou=Users,cn=example,cn=com':
            return 'CN=Doe\\2C John,OU=Users,CN=example,CN=com'

        try:
            dn = ldap.dn.str2dn(common.utf8_encode(dn))
        except ldap.DECODING_ERROR:
            # NOTE(amakarov): In case of IDs instead of DNs in group members
            # they must be handled as regular values.
            return normalize_value(dn)

        norm = []
        for part in dn:
            name, val, i = part[0]
            name = common.utf8_decode(name)
            name = name.upper()
            norm.append([(name, val, i)])
        return common.utf8_decode(ldap.dn.dn2str(norm))
Example #2
0
    def add_s(self, dn, modlist):
        """Add an object with the specified attributes at dn."""
        if server_fail:
            raise ldap.SERVER_DOWN

        id_attr_in_modlist = False
        id_attr = self._dn_to_id_attr(dn)
        id_value = self._dn_to_id_value(dn)

        # The LDAP API raises a TypeError if attr name is None.
        for k, dummy_v in modlist:
            if k is None:
                raise TypeError('must be string, not None. modlist=%s' %
                                modlist)

            if k == id_attr:
                for val in dummy_v:
                    if common.utf8_decode(val) == id_value:
                        id_attr_in_modlist = True

        if not id_attr_in_modlist:
            LOG.debug('id_attribute=%(attr)s missing, attributes=%(attrs)s' %
                      {'attr': id_attr, 'attrs': modlist})
            raise ldap.NAMING_VIOLATION
        key = self.key(dn)
        LOG.debug('add item: dn=%(dn)s, attrs=%(attrs)s', {
            'dn': common.utf8_decode(dn), 'attrs': modlist})
        if key in self.db:
            LOG.debug('add item failed: dn=%s is already in store.',
                      common.utf8_decode(dn))
            raise ldap.ALREADY_EXISTS(dn)

        self.db[key] = {k: _internal_attr(k, v) for k, v in modlist}
        self.db.sync()
Example #3
0
    def simple_bind_s(self, who='', cred='',
                      serverctrls=None, clientctrls=None):
        """Provide for compatibility but this method is ignored."""
        if server_fail:
            raise ldap.SERVER_DOWN
        whos = ['cn=Admin', CONF.ldap.user]
        if (common.utf8_decode(who) in whos and
                common.utf8_decode(cred) in ['password', CONF.ldap.password]):
            return

        attrs = self.db.get(self.key(who))
        if not attrs:
            LOG.debug('who=%s not found, binding anonymously',
                      common.utf8_decode(who))

        db_password = ''
        if attrs:
            try:
                db_password = attrs['userPassword'][0]
            except (KeyError, IndexError):
                LOG.debug('bind fail: password for who=%s not found',
                          common.utf8_decode(who))
                raise ldap.INAPPROPRIATE_AUTH

        if cred != common.utf8_encode(db_password):
            LOG.debug('bind fail: password for who=%s does not match',
                      common.utf8_decode(who))
            raise ldap.INVALID_CREDENTIALS
Example #4
0
    def modify_s(self, dn, modlist):
        """Modify the object at dn using the attribute list.

        :param dn: an LDAP DN
        :param modlist: a list of tuples in the following form:
                      ([MOD_ADD | MOD_DELETE | MOD_REPACE], attribute, value)
        """
        if server_fail:
            raise ldap.SERVER_DOWN

        key = self.key(dn)
        LOG.debug('modify item: dn=%(dn)s attrs=%(attrs)s', {
            'dn': common.utf8_decode(dn), 'attrs': modlist})
        try:
            entry = self.db[key]
        except KeyError:
            LOG.debug('modify item failed: dn=%s not found.',
                      common.utf8_decode(dn))
            raise ldap.NO_SUCH_OBJECT

        for cmd, k, v in modlist:
            values = entry.setdefault(k, [])
            if cmd == ldap.MOD_ADD:
                v = _internal_attr(k, v)
                for x in v:
                    if x in values:
                        raise ldap.TYPE_OR_VALUE_EXISTS
                values += v
            elif cmd == ldap.MOD_REPLACE:
                values[:] = _internal_attr(k, v)
            elif cmd == ldap.MOD_DELETE:
                if v is None:
                    if not values:
                        LOG.debug('modify item failed: '
                                  'item has no attribute "%s" to delete', k)
                        raise ldap.NO_SUCH_ATTRIBUTE
                    values[:] = []
                else:
                    for val in _internal_attr(k, v):
                        try:
                            values.remove(val)
                        except ValueError:
                            LOG.debug('modify item failed: '
                                      'item has no attribute "%(k)s" with '
                                      'value "%(v)s" to delete', {
                                          'k': k, 'v': val})
                            raise ldap.NO_SUCH_ATTRIBUTE
            else:
                LOG.debug('modify item failed: unknown command %s', cmd)
                raise NotImplementedError('modify_s action %s not'
                                          ' implemented' % cmd)
        self.db[key] = entry
        self.db.sync()
Example #5
0
    def delete_ext_s(self, dn, serverctrls, clientctrls=None):
        """Remove the ldap object at specified dn."""
        if server_fail:
            raise ldap.SERVER_DOWN

        try:
            key = self.key(dn)
            LOG.debug('FakeLdap delete item: dn=%s', common.utf8_decode(dn))
            del self.db[key]
        except KeyError:
            LOG.debug('delete item failed: dn=%s not found.',
                      common.utf8_decode(dn))
            raise ldap.NO_SUCH_OBJECT
        self.db.sync()
Example #6
0
    def delete_ext_s(self, dn, serverctrls, clientctrls=None):
        """Remove the ldap object at specified dn."""
        if server_fail:
            raise ldap.SERVER_DOWN

        try:
            if CONTROL_TREEDELETE in [c.controlType for c in serverctrls]:
                LOG.debug('FakeLdap subtree_delete item: dn=%s',
                          common.utf8_decode(dn))
                children = self._getChildren(dn)
                for c in children:
                    del self.db[c]

            key = self.key(dn)
            LOG.debug('FakeLdap delete item: dn=%s', common.utf8_decode(dn))
            del self.db[key]
        except KeyError:
            LOG.debug('delete item failed: dn=%s not found.',
                      common.utf8_decode(dn))
            raise ldap.NO_SUCH_OBJECT
        self.db.sync()
Example #7
0
    def test_utf8_conversion(self):
        value_unicode = u'fäké1'
        value_utf8 = value_unicode.encode('utf-8')

        result_utf8 = common_ldap.utf8_encode(value_unicode)
        self.assertEqual(value_utf8, result_utf8)

        result_utf8 = common_ldap.utf8_encode(value_utf8)
        self.assertEqual(value_utf8, result_utf8)

        result_unicode = common_ldap.utf8_decode(value_utf8)
        self.assertEqual(value_unicode, result_unicode)

        result_unicode = common_ldap.utf8_decode(value_unicode)
        self.assertEqual(value_unicode, result_unicode)

        self.assertRaises(TypeError,
                          common_ldap.utf8_encode,
                          100)

        result_unicode = common_ldap.utf8_decode(100)
        self.assertEqual(u'100', result_unicode)
Example #8
0
    def delete_ext_s(self, dn, serverctrls, clientctrls=None):
        """Remove the ldap object at specified dn."""
        if server_fail:
            raise ldap.SERVER_DOWN

        try:
            children = self._getChildren(dn)
            if children:
                raise ldap.NOT_ALLOWED_ON_NONLEAF

        except KeyError:
            LOG.debug('delete item failed: dn=%s not found.',
                      common.utf8_decode(dn))
            raise ldap.NO_SUCH_OBJECT
        super(FakeLdapNoSubtreeDelete, self).delete_ext_s(dn,
                                                          serverctrls,
                                                          clientctrls)
Example #9
0
 def normalize_value(value):
     return common.utf8_decode(value)
Example #10
0
 def dn(self, dn):
     return common.utf8_decode(dn)
Example #11
0
 def normalize_value(value):
     return common.utf8_decode(value)
Example #12
0
    def search_s(self, base, scope,
                 filterstr='(objectClass=*)', attrlist=None, attrsonly=0):
        """Search for all matching objects under base using the query.

        Args:
        base -- dn to search under
        scope -- search scope (base, subtree, onelevel)
        filterstr -- filter objects by
        attrlist -- attrs to return. Returns all attrs if not specified

        """
        if server_fail:
            raise ldap.SERVER_DOWN

        if (not filterstr) and (scope != ldap.SCOPE_BASE):
            raise AssertionError('Search without filter on onelevel or '
                                 'subtree scope')

        if scope == ldap.SCOPE_BASE:
            try:
                item_dict = self.db[self.key(base)]
            except KeyError:
                LOG.debug('search fail: dn not found for SCOPE_BASE')
                raise ldap.NO_SUCH_OBJECT
            results = [(base, item_dict)]
        elif scope == ldap.SCOPE_SUBTREE:
            # FIXME - LDAP search with SUBTREE scope must return the base
            # entry, but the code below does _not_. Unfortunately, there are
            # several tests that depend on this broken behavior, and fail
            # when the base entry is returned in the search results. The
            # fix is easy here, just initialize results as above for
            # the SCOPE_BASE case.
            # https://bugs.launchpad.net/keystone/+bug/1368772
            try:
                item_dict = self.db[self.key(base)]
            except KeyError:
                LOG.debug('search fail: dn not found for SCOPE_SUBTREE')
                raise ldap.NO_SUCH_OBJECT
            results = [(base, item_dict)]
            extraresults = [(k[len(self.__prefix):], v)
                            for k, v in self.db.items()
                            if re.match('%s.*,%s' %
                                        (re.escape(self.__prefix),
                                         re.escape(self.dn(base))), k)]
            results.extend(extraresults)
        elif scope == ldap.SCOPE_ONELEVEL:

            def get_entries():
                base_dn = ldap.dn.str2dn(common.utf8_encode(base))
                base_len = len(base_dn)

                for k, v in self.db.items():
                    if not k.startswith(self.__prefix):
                        continue
                    k_dn_str = k[len(self.__prefix):]
                    k_dn = ldap.dn.str2dn(common.utf8_encode(k_dn_str))
                    if len(k_dn) != base_len + 1:
                        continue
                    if k_dn[-base_len:] != base_dn:
                        continue
                    yield (k_dn_str, v)

            results = list(get_entries())

        else:
            # openldap client/server raises PROTOCOL_ERROR for unexpected scope
            raise ldap.PROTOCOL_ERROR

        objects = []
        for dn, attrs in results:
            # filter the objects by filterstr
            id_attr, id_val, _ = ldap.dn.str2dn(common.utf8_encode(dn))[0][0]
            id_attr = common.utf8_decode(id_attr)
            id_val = common.utf8_decode(id_val)
            match_attrs = attrs.copy()
            match_attrs[id_attr] = [id_val]
            attrs_checked = set()
            if not filterstr or _match_query(common.utf8_decode(filterstr),
                                             match_attrs,
                                             attrs_checked):
                if (filterstr and
                        (scope != ldap.SCOPE_BASE) and
                        ('objectclass' not in attrs_checked)):
                    raise AssertionError('No objectClass in search filter')
                # filter the attributes by attrlist
                attrs = {k: v for k, v in attrs.items()
                         if not attrlist or k in common.utf8_decode(attrlist)}
                objects.append((dn, attrs))

        return objects
Example #13
0
 def _dn_to_id_value(self, dn):
     return common.utf8_decode(
         ldap.dn.str2dn(common.utf8_encode(dn))[0][0][1])
Example #14
0
 def dn(self, dn):
     return common.utf8_decode(dn)