Welcome, guest | Sign In | My Account | Store | Cart
#!/usr/bin/env python
# coding: utf-8
#
# Copyright (c) 2009 Andrew Grigorev <andrew@ei-grad.ru>
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
#

class DnTree:
    """
    Represent a set of distinguished names as tree.

    Build a hierarchy of Internet Domain names or LDAP DN.
    """

    def __init__(self, all_dn=set(), delimeter="."):
        """
        Create new DnTree object.

        all_dn    - set of dn to initially add to a tree
        delimeter - delimter used to split relative name and parent DN.
        """

        self.delimeter = delimeter

        self.childs = {}
        self.parent = {}
        self.fake_parent = {} # distinguished name of a node
                              # that should be a parent
                              # but not exists in a current tree

        self.dn_set = set() # all nodes in a tree

        self.parent[""] = ""
        self.childs[""] = [] # used by get_roots()

        for dn in all_dn:
            self.add(dn)

    def __str__(self):
        "Draw tree in ASCII graphics."

        def print_node(dn, prefix=""):
            if self.parent[dn] == "":
                ret1 = "%s%s\n" % ( prefix, dn)
            else:
                ret1 = "%s%s\n" % ( prefix, self.get_rdn(dn))
            for i in self.get_childs(dn):
                ret1 += print_node(i, prefix+" ")
            return ret1

        ret = ""
        for i in self.get_roots():
            ret += print_node(i)
        return ret

    def __repr__(self):
        return '<DnTree(%s)>' % ", ".join(self.get_roots())

    def get_roots(self):
        "Return list of root nodes without parent."
        try:
            return self.childs[""]
        except KeyError:
            return []

    def get_childs(self, dn):
        "Return list of childs of dn."
        if dn in self.childs.keys():
            return self.childs[dn]
        return []

    def get_parent_dn(self, dn):
        """Return distinguished name of parent node (if it belongs to tree,
        else return empty string).

        dn --- distinguished name of node, which parent to return
        """
        _len = dn.find(self.delimeter)
        if _len == -1:
            return ""
        else:
            if dn[_len+1:] in self.dn_set:
                return dn[_len+1:]
            else:
                return ""

    def get_fake_parent_dn(self, dn):
        """Get distinguished name of node that should be a parent of dn,
        although it does not belongs to tree.

        dn --- distinguished name of node, which fake parent to return
        """
        _len = dn.find(self.delimeter)
        if _len == -1:
            return ""
        else:
            return dn[_len+1:]

    def get_rdn(self, dn):
        """Get relative distinguished name.

        dn --- distinguished name
        """
        _len = dn.find(self.delimeter)
        if _len == -1:
            return dn
        else:
            return dn[:_len]

    def add(self, dn):
        """Add node to tree.

        dn --- distinguished name of node.
        """

        if dn in self.dn_set:
            raise ValueError('A record with dn=%s already exists!' % dn)

        parent_dn = self.get_parent_dn(dn)
        fake_parent = self.get_fake_parent_dn(dn)

        if parent_dn != fake_parent:
            try:
                self.fake_parent[fake_parent].append(dn)
            except KeyError:
                self.fake_parent[fake_parent] = [dn]

        self.parent[dn] = parent_dn
        try:
            self.childs[parent_dn].append(dn)
        except KeyError:
            self.childs[parent_dn] = [dn]

        try:
            for i in self.fake_parent.pop(dn):
                self.childs[""].remove(i)
                self.parent[i] = dn
                try:
                    self.childs[dn].append(i)
                except KeyError:
                    self.childs[dn] = [i]
        except KeyError:
            pass

        self.dn_set.add(dn)

    def rename(self, old_dn, new_dn):
        """Rename node.

        old_dn --- old distinguished name of node.
        new_dn --- new distinguished name of node.
        """
        if old_dn not in self.dn_set:
            raise ValueError("A record with dn=%s don't exists!" % new_dn)
        if new_dn in self.dn_set:
            raise ValueError("A record with dn=%s already exists!" % new_dn)
        self.remove(old_dn)
        self.add(new_dn)

    def remove(self, dn):
        """Remove node and its subtree.

        dn --- distinguished name of node.
        """

        if dn not in self.dn_set:
            raise ValueError('A record with dn=%s doesn\'t exists!' % dn)

        parent = self.get_parent_dn(dn)
        fake_parent = self.get_fake_parent_dn(dn)

        if parent != fake_parent:
            self.fake_parent[fake_parent].remove(dn)

        self.childs[parent].remove(dn)

        if dn in self.childs.keys():
            childs = tuple(self.childs[dn])
            for i in childs:
                self.remove(i)
            self.childs.pop(dn)

        self.parent.pop(dn)
        self.dn_set.remove(dn)


if __name__ == '__main__':

    import sys

    try:
        import ldap
    except ImportError:
        print('You need python-ldap installed to run this.')
        sys.exit(1)

    if len(sys.argv) not in (5, 6):
        print '''
    Using:
        python %s <uri> <bind_dn> <bind_pw> <base_dn> [<filter>]
''' % sys.argv[0]
        sys.exit(1)

    uri = sys.argv[1]
    bind_dn = sys.argv[2]
    bind_pw = sys.argv[3]
    base_dn = sys.argv[4]
    search_filter = '(objectClass=*)'

    if len(sys.argv) == 6:
        search_filter = sys.argv[5]

    conn = ldap.initialize(uri)
    conn.simple_bind_s(bind_dn, bind_pw)

    res = conn.search_s(base_dn, ldap.SCOPE_SUBTREE, search_filter, [])

    print str(DnTree(set([e[0] for e in res]), ','))

#    from itertools import permutations
#
#    s = []
#    for l in range(1, 3):
#        s += [ ".".join(i) for i in permutations('12', l) ]
#
#    print 'Initialize tree with a list:'
#    print s
#
#    t = DnTree(s)
#
#    print 'Our DnTree:'
#    print t
#
#    print 'We can add, remove and rename nodes:'
#    t.add('3.2.1')
#    print t
#
#    print 'Node would be removed with its subtree:'
#    t.remove('2.1')
#    print t # removed both '2.1' and '3.2.1'
#
#    print "And '3.2.1' is not in subtree of '1':"
#    t.add('3.2.1')
#    print t
#
#    print 'Remove 1:'
#    t.remove('1')
#    print t

Diff to Previous Revision

--- revision 4 2010-08-23 19:34:39
+++ revision 5 2011-05-04 19:58:38
@@ -203,12 +203,13 @@
 
 if __name__ == '__main__':
 
+    import sys
+
     try:
         import ldap
     except ImportError:
-        print 'You need python-ldap installed to run this.'
-
-    import sys
+        print('You need python-ldap installed to run this.')
+        sys.exit(1)
 
     if len(sys.argv) not in (5, 6):
         print '''

History