Example #1
0
    def _ldap_modify(self):
        """modifies attributs of self on the ldap directory.
        """
        modlist = list()
        orgin = self.attributes_factory(name='__attrs__', parent=self)

        for key in orgin:
            # MOD_DELETE
            if not key in self.attrs:
                moddef = (MOD_DELETE, encode(key), None)
                modlist.append(moddef)
        for key in self.attrs:
            # MOD_ADD
            value = self.attrs[key]
            if not self.attrs.is_binary(key):
                value = encode(value)
            if key not in orgin:
                moddef = (MOD_ADD, encode(key), value)
                modlist.append(moddef)
            # MOD_REPLACE
            elif self.attrs[key] != orgin[key]:
                moddef = (MOD_REPLACE, encode(key), value)
                modlist.append(moddef)
        if modlist:
            self.ldap_session.modify(encode(self.DN), modlist)
Example #2
0
 def _ldap_add(self):
     # adds self to the ldap directory.
     attrs = {}
     for key, value in self.attrs.items():
         if not self.attrs.is_binary(key):
             value = encode(value)
         attrs[encode(key)] = value
     self.ldap_session.add(encode(self.DN), attrs)
Example #3
0
 def _ldap_add(self):
     # adds self to the ldap directory.
     attrs = {}
     for key, value in self.attrs.items():
         if not self.attrs.is_binary(key):
             value = encode(value)
         attrs[encode(key)] = value
     self.ldap_session.add(encode(self.DN), attrs)
Example #4
0
    def __iter__(self):
        if self.name is None:
            return
        cookie = ''
        while True:
            try:
                res = self.ldap_session.search(
                    scope=ONELEVEL,
                    baseDN=encode(self.DN),
                    attrlist=[''],
                    page_size=self._page_size,
                    cookie=cookie,
                )
            except NO_SUCH_OBJECT:
                # happens if not persisted yet
                res = list()
            if isinstance(res, tuple):
                res, cookie = res
            for dn, _ in res:
                key = decode(explode_dn(dn)[0])
                # do not yield if node is supposed to be deleted
                if key not in self._deleted_children:
                    yield key
            if not cookie:
                break

        # also yield keys of children not persisted yet.
        for key in self._added_children:
            yield key
Example #5
0
 def _ldap_delete(self):
     """delete self from the ldap-directory.
     """
     self.parent._keys[self.name] = False
     del self.parent.storage[self.name]
     del self.parent._keys[self.name]
     self.ldap_session.delete(encode(self.DN))
Example #6
0
    def __iter__(self):
        if self.name is None:
            return
        cookie = ''
        while True:
            try:
                res = self.ldap_session.search(
                    scope=ONELEVEL,
                    baseDN=encode(self.DN),
                    attrlist=[''],
                    page_size=self._page_size,
                    cookie=cookie,
                )
            except NO_SUCH_OBJECT:
                # happens if not persisted yet
                res = list()
            if isinstance(res, tuple):
                res, cookie = res
            for dn, _ in res:
                key = decode(explode_dn(dn)[0])
                # do not yield if node is supposed to be deleted
                if key not in self._deleted_children:
                    yield key
            if not cookie:
                break

        # also yield keys of children not persisted yet.
        for key in self._added_children:
            yield key
Example #7
0
 def _enumerateUsers(self, exact_match=False, **kw):
     matches = []
     cookie = None
     while True:
         res = self.users.search(
             criteria=encode(kw),
             attrlist=exact_match and self.users.context.search_attrlist
             or ['login'],
             exact_match=exact_match,
             or_search=not exact_match,
             page_size=not exact_match and self._ldap_props.page_size
             or None,
             cookie=cookie,
         )
         if isinstance(res, tuple):
             batch_matches, cookie = res
         else:
             batch_matches, cookie = res, ''
         matches += batch_matches
         if not cookie:
             break
     if not matches:
         raise ValueError('No matches')
     else:
         return matches
Example #8
0
    def _perform(self, function, *args, **kwargs):
        """Try to perform the given function with the given argument.

        If LDAP directory is down, bind again and retry given function.

        XXX: * Improve retry logic in LDAPSession
             * Extend LDAPSession object to handle Fallback server(s)
        """

        # XXX BUG:
        # It is complete wrong to encode/ decode in here every string
        # LDAP handles binary data too and so fetching or setting binary
        # data fails. We need to refactor this part.

        args = encode(args)
        kwargs = encode(kwargs)
        if self._communicator._con is None:
            self._communicator.bind()
        return decode(function(*args, **kwargs))
Example #9
0
 def setProperties(self, obj, mapping):
     for id in mapping:
         assert (id in self._properties)
     ldapprincipal = self._get_ldap_principal()
     for id in mapping:
         self._properties[id] = ldapprincipal.attrs[id] = encode(
             mapping[id])
     try:
         ldapprincipal.context()
     except Exception, e:
         # XXX: specific exception(s)
         logger.error('LDAPUserPropertySheet.setProperties: %s' % str(e))
Example #10
0
 def node_attributes(self):
     rdn = self.request['rdn']
     base = self.request['base']
     if base == 'users':
         users = ILDAPUsersConfig(self.plugin)
         baseDN = users.baseDN
     else:
         groups = ILDAPGroupsConfig(self.plugin)
         baseDN = groups.baseDN
     root = LDAPNode(baseDN, self.props)
     node = root[rdn]
     ret = dict()
     for key, val in node.attrs.items():
         try:
             if not node.attrs.is_binary(key):
                 ret[encode(key)] = encode(val)
             else:
                 ret[encode(key)] = "(Binary Data with %d Bytes)" % len(val)
         except UnicodeDecodeError, e:
             ret[key.encode('utf-8')] = '! (UnicodeDecodeError)'
         except Exception, e:
             ret[key.encode('utf-8')] = '! (Unknown Exception)'
Example #11
0
 def _calculate_key(self, dn, attrs):
     if self._key_attr == 'rdn':
         # explode_dn is ldap world
         key = explode_dn(encode(dn))[0]
     else:
         key = attrs[self._key_attr]
         if isinstance(key, list):
             if len(key) != 1:
                 msg = u"Expected one value for '%s' " % (self._key_attr,)
                 msg += u"not %s: '%s'." % (len(key), key)
                 raise KeyError(msg)
             key = key[0]
     return decode(key)
Example #12
0
 def _calculate_key(self, dn, attrs):
     if self._key_attr == 'rdn':
         # explode_dn is ldap world
         key = decode(explode_dn(encode(dn))[0])
     else:
         key = attrs[self._key_attr]
         if isinstance(key, list):
             if len(key) != 1:
                 msg = u"Expected one value for '%s' " % (self._key_attr,)
                 msg += u"not %s: '%s'." % (len(key), key)
                 raise KeyError(msg)
             key = key[0]
     return key
Example #13
0
    def _perform(self, function, *args, **kwargs):
        """Try to perform the given function with the given argument.

        If LDAP directory is down, bind again and retry given function.

        XXX: * Improve retry logic in LDAPSession
             * Extend LDAPSession object to handle Fallback server(s)
        """
        args = encode(args)
        kwargs = encode(kwargs)
        if self._communicator._con is None:
            self._communicator.bind()
        
        # XXX: it seems except block is never reached, call of
        #      self._communicator.bind() above already raises error if
        #      communication fails. And why server down?
        #try:
        #    return decode(function(*args, **kwargs))
        #except ldap.SERVER_DOWN:
        #    self._communicator.bind()
        #    return decode(function(*args, **kwargs))
        
        return decode(function(*args, **kwargs))
Example #14
0
 def node_by_dn(self, dn, strict=False):
     """Return node from tree by DN.
     """
     root = node = self.root
     base_dn = root.name
     if not dn.endswith(base_dn):
         raise ValueError(u'Invalid base DN')
     dn = dn[:len(dn) - len(base_dn)].strip(',')
     for rdn in reversed(explode_dn(encode(dn))):
         try:
             node = node[rdn]
         except KeyError:
             if strict:
                 raise ValueError(u'Tree contains no node by given DN. '
                                  u'Failed at RDN {}'.format(rdn))
             return None
     return node
Example #15
0
 def node_by_dn(self, dn, strict=False):
     """Return node from tree by DN.
     """
     root = node = self.root
     base_dn = root.name
     if not dn.endswith(base_dn):
         raise ValueError(u'Invalid base DN')
     dn = dn[:len(dn) - len(base_dn)].strip(',')
     for rdn in reversed(explode_dn(encode(dn))):
         try:
             node = node[rdn]
         except KeyError:
             if strict:
                 raise ValueError(u'Tree contains no node by given DN. '
                                  u'Failed at RDN {}'.format(rdn))
             return None
     return node
Example #16
0
 def _enumerateGroups(self, exact_match=False, **kw):
     matches = []
     cookie = None
     while True:
         res = self.groups.search(
             criteria=encode(kw),
             attrlist=None,
             exact_match=exact_match,
             page_size=self._ldap_props.page_size,
             cookie=cookie,
         )
         if isinstance(res, tuple):
             batch_matches, cookie = res
         else:
             batch_matches, cookie = res, ''
         matches += batch_matches
         if not cookie:
             break
     if not matches:
         raise ValueError('No matches')
     else:
         return matches
Example #17
0
    def test_encode(self):
        self.assertEqual(
            encode(
                b'\x01\x05\x00\x00\x00\x00\x00\x05\x15\x00\x00\x00\xd4'
                b'\xa0\xff\xff\xaeW\x82\xa9P\xcf8\xaf&\x0e\x00\x00'
            ), (
                b'\x01\x05\x00\x00\x00\x00\x00\x05\x15\x00\x00\x00\xd4'
                b'\xa0\xff\xff\xaeW\x82\xa9P\xcf8\xaf&\x0e\x00\x00'
            )
        )
        self.assertEqual(encode(u'\xe4'), b'\xc3\xa4')
        self.assertEqual(encode([u'\xe4']), [b'\xc3\xa4'])
        self.assertEqual(
            encode({u'\xe4': u'\xe4'}),
            {b'\xc3\xa4': b'\xc3\xa4'}
        )
        self.assertEqual(encode(b'\xc3\xa4'), b'\xc3\xa4')

        node = BaseNode()
        node.allow_non_node_childs = True
        node['foo'] = u'\xe4'
        self.assertEqual(encode(node), {b'foo': b'\xc3\xa4'})
Example #18
0
 def search(self, queryFilter=None, criteria=None, attrlist=None,
            relation=None, relation_node=None, exact_match=False,
            or_search=False, or_keys=None, or_values=None,
            page_size=None, cookie=None, get_nodes=False):
     attrset = set(attrlist or [])
     attrset.discard('dn')
     attrset.discard('rdn')
     # Create queryFilter from all filter definitions
     # filter for this search ANDed with the default filters defined on self
     search_filter = LDAPFilter(queryFilter)
     search_filter &= LDAPDictFilter(criteria,
                                     or_search=or_search,
                                     or_keys=or_keys,
                                     or_values=or_values,
                                     )
     _filter = LDAPFilter(self.search_filter)
     _filter &= LDAPDictFilter(self.search_criteria)
     _filter &= search_filter
     # relation filters
     if relation_node is None:
         relation_node = self
     relations = [relation, self.search_relation]
     for relation in relations:
         if not relation:
             continue
         if isinstance(relation, LDAPRelationFilter):
             _filter &= relation
         else:
             _filter &= LDAPRelationFilter(relation_node, relation)
     # perform the backend search
     matches = self.ldap_session.search(
         str(_filter),
         self.search_scope,
         baseDN=encode(self.DN),
         force_reload=self._reload,
         attrlist=list(attrset),
         page_size=page_size,
         cookie=cookie,
     )
     if type(matches) is tuple:
         matches, cookie = matches
     # check exact match
     if exact_match and len(matches) > 1:
         raise ValueError(u"Exact match asked but result not unique")
     if exact_match and len(matches) == 0:
         raise ValueError(u"Exact match asked but result length is zero")
     # extract key and desired attributes
     res = []
     for dn, attrs in matches:
         dn = decode(dn)
         if attrlist is not None:
             resattr = dict()
             for k, v in attrs.iteritems():
                 if k in attrlist:
                     # Check binary binary attribute directly from root
                     # data to avoid initing attrs for a simple search.
                     if k in self.root._binary_attributes:
                         resattr[decode(k)] = v
                     else:
                         resattr[decode(k)] = decode(v)
             if 'dn' in attrlist:
                 resattr[u'dn'] = dn
             if 'rdn' in attrlist:
                 rdn = explode_dn(encode(dn))[0]
                 resattr[u'rdn'] = decode(rdn)
             if get_nodes:
                 res.append((self.node_by_dn(dn, strict=True), resattr))
             else:
                 res.append((dn, resattr))
         else:
             if get_nodes:
                 res.append(self.node_by_dn(dn, strict=True))
             else:
                 res.append(dn)
     if cookie is not None:
         return (res, cookie)
     return res
Example #19
0
 def _set_baseDN(self, baseDN):
     if isinstance(baseDN, str):
         # make sure its utf8
         baseDN = decode(baseDN)
     baseDN = encode(baseDN)
     self._communicator.baseDN = baseDN
Example #20
0
    def search(self, queryFilter=None, criteria=None, attrlist=None,
               relation=None, relation_node=None, exact_match=False,
               or_search=False, or_keys=None, or_values=None,
               page_size=None, cookie=None):
        attrset = set(attrlist or [])
        attrset.discard('dn')

        # fetch also the key attribute
        if not self._key_attr == 'rdn':
            attrset.add(self._key_attr)

        # Create queryFilter from all filter definitions
        # filter for this search ANDed with the default filters defined on self
        search_filter = LDAPFilter(queryFilter)
        search_filter &= LDAPDictFilter(criteria,
                                        or_search=or_search,
                                        or_keys=or_keys,
                                        or_values=or_values,
                                        )
        _filter = LDAPFilter(self.search_filter)
        _filter &= LDAPDictFilter(self.search_criteria)
        _filter &= search_filter

        # relation filters
        if relation_node is None:
            relation_node = self
        relations = [relation, self.search_relation]
        for relation in relations:
            if not relation:
                continue
            if isinstance(relation, LDAPRelationFilter):
                _filter &= relation
            else:
                _filter &= LDAPRelationFilter(relation_node, relation)

        # XXX: Is it really good to filter out entries without the key attr or
        # would it be better to fail? (see also __iter__ secondary key)
        if self._key_attr != 'rdn' and self._key_attr not in _filter:
            _filter &= '(%s=*)' % (self._key_attr,)

        # perform the backend search
        matches = self.ldap_session.search(
            str(_filter),
            self.search_scope,
            baseDN=encode(self.DN),
            force_reload=self._reload,
            attrlist=list(attrset),
            page_size=page_size,
            cookie=cookie,
            )
        if type(matches) is tuple:
            matches, cookie = matches

        # XXX: Is ValueError appropriate?
        # XXX: why do we need to fail at all? shouldn't this be about
        # substring vs equality match?
        if exact_match and len(matches) > 1:
            raise ValueError(u"Exact match asked but result not unique")
        if exact_match and len(matches) == 0:
            raise ValueError(u"Exact match asked but result length is zero")

        # extract key and desired attributes
        res = []
        for dn, attrs in matches:
            key = self._calculate_key(dn, attrs)
            if attrlist is not None:
                resattr = dict()
                for k, v in attrs.iteritems():
                    if k in attrlist:
                        if self.attrs.is_binary(k):
                            resattr[decode(k)] = v
                        else:
                            resattr[decode(k)] = decode(v)
                if 'dn' in attrlist:
                    resattr[u'dn'] = decode(dn)
                res.append((key, resattr))
            else:
                res.append(key)
        if cookie is not None:
            return (res, cookie)
        return res
Example #21
0
    def enumerateUsers(self,
                       id=None,
                       login=None,
                       exact_match=False,
                       sort_by=None,
                       max_results=None,
                       **kw):
        """-> ( user_info_1, ... user_info_N )

        o Return mappings for users matching the given criteria.

        o 'id' or 'login', in combination with 'exact_match' true, will
          return at most one mapping per supplied ID ('id' and 'login'
          may be sequences).

        o If 'exact_match' is False, then 'id' and / or login may be
          treated by the plugin as "contains" searches (more complicated
          searches may be supported by some plugins using other keyword
          arguments).

        o If 'sort_by' is passed, the results will be sorted accordingly.
          known valid values are 'id' and 'login' (some plugins may support
          others).

        o If 'max_results' is specified, it must be a positive integer,
          limiting the number of returned mappings.  If unspecified, the
          plugin should return mappings for all users satisfying the criteria.

        o Minimal keys in the returned mappings:

          'id' -- (required) the user ID, which may be different than
                  the login name

          'login' -- (required) the login name

          'pluginid' -- (required) the plugin ID (as returned by getId())

          'editurl' -- (optional) the URL to a page for updating the
                       mapping's user

        o Plugin *must* ignore unknown criteria.

        o Plugin may raise ValueError for invalid criteria.

        o Insufficiently-specified criteria may have catastrophic
          scaling issues for some implementations.
        """
        default = tuple()
        if not self.is_plugin_active(pas_interfaces.IUserEnumerationPlugin):
            return default
        # TODO: sort_by in node.ext.ldap
        if login:
            if not isinstance(login, basestring):
                # XXX TODO
                raise NotImplementedError('sequence is not supported yet.')
            kw['login'] = login

        # pas search users gives both login and name if login is meant
        if "login" in kw and "name" in kw:
            del kw["name"]

        if id:
            if not isinstance(id, basestring):
                # XXX TODO
                raise NotImplementedError('sequence is not supported yet.')
            kw['id'] = id
        users = self.users
        if not users:
            return default
        if not exact_match:
            for value in users.principal_attrmap.values():
                kw[value] = kw.values()[0]
        matches = []
        cookie = None
        while True:
            try:
                res = users.search(
                    criteria=encode(kw),
                    attrlist=exact_match and users.context.search_attrlist
                    or ['login'],
                    exact_match=exact_match,
                    or_search=not exact_match,
                    page_size=not exact_match and self._ldap_props.page_size
                    or None,
                    cookie=cookie,
                )
                if isinstance(res, tuple):
                    batch_matches, cookie = res
                else:
                    batch_matches, cookie = res, ''
            except ValueError:
                return default
            matches += batch_matches
            if not cookie:
                break
        if len(matches) >= 100:
            msg = 'Too many search results. Please, narrow your search.'
            IStatusMessage(self.REQUEST).add(msg, type='warning')
            return default
        pluginid = self.getId()
        ret = list()
        for id, attrs in matches:
            if 'login' not in attrs:
                continue
            ret.append({
                'id': id,
                'login': attrs['login'][0],
                'pluginid': pluginid
            })
        if max_results and len(ret) > max_results:
            ret = ret[:max_results]
        return ret
Example #22
0
 def _ldap_delete(self):
     # delete self from the ldap-directory.
     del self.parent.storage[self.name]
     self.ldap_session.delete(encode(self.DN))
Example #23
0
 def search(self, queryFilter=None, criteria=None, attrlist=None,
            relation=None, relation_node=None, exact_match=False,
            or_search=False, or_keys=None, or_values=None,
            page_size=None, cookie=None, get_nodes=False):
     attrset = set(attrlist or [])
     attrset.discard('dn')
     attrset.discard('rdn')
     # Create queryFilter from all filter definitions
     # filter for this search ANDed with the default filters defined on self
     search_filter = LDAPFilter(queryFilter)
     search_filter &= LDAPDictFilter(criteria,
                                     or_search=or_search,
                                     or_keys=or_keys,
                                     or_values=or_values,
                                     )
     _filter = LDAPFilter(self.search_filter)
     _filter &= LDAPDictFilter(self.search_criteria)
     _filter &= search_filter
     # relation filters
     if relation_node is None:
         relation_node = self
     relations = [relation, self.search_relation]
     for relation in relations:
         if not relation:
             continue
         if isinstance(relation, LDAPRelationFilter):
             _filter &= relation
         else:
             _filter &= LDAPRelationFilter(relation_node, relation)
     # perform the backend search
     matches = self.ldap_session.search(
         str(_filter),
         self.search_scope,
         baseDN=encode(self.DN),
         force_reload=self._reload,
         attrlist=list(attrset),
         page_size=page_size,
         cookie=cookie,
     )
     if type(matches) is tuple:
         matches, cookie = matches
     # check exact match
     if exact_match and len(matches) > 1:
         raise ValueError(u"Exact match asked but result not unique")
     if exact_match and len(matches) == 0:
         raise ValueError(u"Exact match asked but result length is zero")
     # extract key and desired attributes
     res = []
     for dn, attrs in matches:
         dn = decode(dn)
         if attrlist is not None:
             resattr = dict()
             for k, v in attrs.iteritems():
                 if k in attrlist:
                     # Check binary binary attribute directly from root
                     # data to avoid initing attrs for a simple search.
                     if k in self.root._binary_attributes:
                         resattr[decode(k)] = v
                     else:
                         resattr[decode(k)] = decode(v)
             if 'dn' in attrlist:
                 resattr[u'dn'] = dn
             if 'rdn' in attrlist:
                 rdn = explode_dn(encode(dn))[0]
                 resattr[u'rdn'] = decode(rdn)
             if get_nodes:
                 res.append((self.node_by_dn(dn, strict=True), resattr))
             else:
                 res.append((dn, resattr))
         else:
             if get_nodes:
                 res.append(self.node_by_dn(dn, strict=True))
             else:
                 res.append(dn)
     if cookie is not None:
         return (res, cookie)
     return res
Example #24
0
 def _ldap_delete(self):
     # delete self from the ldap-directory.
     del self.parent.storage[self.name]
     self.ldap_session.delete(encode(self.DN))
Example #25
0
    def enumerateGroups(self,
                        id=None,
                        exact_match=False,
                        sort_by=None,
                        max_results=None,
                        **kw):
        """ -> ( group_info_1, ... group_info_N )

        o Return mappings for groups matching the given criteria.

        o 'id' in combination with 'exact_match' true, will
          return at most one mapping per supplied ID ('id' and 'login'
          may be sequences).

        o If 'exact_match' is False, then 'id' may be treated by
          the plugin as "contains" searches (more complicated searches
          may be supported by some plugins using other keyword arguments).

        o If 'sort_by' is passed, the results will be sorted accordingly.
          known valid values are 'id' (some plugins may support others).

        o If 'max_results' is specified, it must be a positive integer,
          limiting the number of returned mappings.  If unspecified, the
          plugin should return mappings for all groups satisfying the
          criteria.

        o Minimal keys in the returned mappings:

          'id' -- (required) the group ID

          'pluginid' -- (required) the plugin ID (as returned by getId())

          'properties_url' -- (optional) the URL to a page for updating the
                              group's properties.

          'members_url' -- (optional) the URL to a page for updating the
                           principals who belong to the group.

        o Plugin *must* ignore unknown criteria.

        o Plugin may raise ValueError for invalid critera.

        o Insufficiently-specified criteria may have catastrophic
          scaling issues for some implementations.
        """
        default = ()
        if not self.is_plugin_active(pas_interfaces.IGroupEnumerationPlugin):
            return default
        groups = self.groups
        if not groups:
            return default
        if id and exact_match:
            if id in self.getGroupIds():
                return ({'id': id, 'pluginid': self.getId()})
            else:
                return default
        elif id:
            kw['id'] = id
        if not kw:  # show all
            matches = groups.ids
        else:
            matches = []
            cookie = None
            while True:
                try:
                    res = groups.search(
                        criteria=encode(kw),
                        attrlist=None,
                        exact_match=exact_match,
                        page_size=self._ldap_props.page_size,
                        cookie=cookie,
                    )
                    if isinstance(res, tuple):
                        batch_matches, cookie = res
                    else:
                        batch_matches, cookie = res, ''
                except ValueError:
                    return default
                matches += batch_matches
                if not cookie:
                    break
        if len(matches) >= 100:
            msg = 'Too many search results. Please, narrow your search.'
            IStatusMessage(self.REQUEST).add(msg, type='warning')
            return default
        if sort_by == 'id':
            matches = sorted(matches)
        pluginid = self.getId()
        ret = list()
        if exact_match:
            for id, attrs in matches:
                ret.append({'id': id, 'pluginid': pluginid})
        else:
            for id in matches:
                ret.append({'id': id, 'pluginid': pluginid})
        if max_results and len(ret) > max_results:
            ret = ret[:max_results]
        return ret