Some little python code to visualize an LDAP structure.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 

152 lines
4.9 KiB

from os.path import dirname, realpath
import ldap
import pygraphviz as pgv
from jinja2 import Environment, FileSystemLoader
class LdapTree(object):
def __init__(self, hosturi, binddn, basedn, password, use_gssapi):
#ldap.set_option(ldap.OPT_DEBUG_LEVEL, 1)
self._ldap = ldap.initialize(hosturi)
"""
Setting ldap.OPT_REFERRALS to 0 was neccessary to query a samba4
active directory... Currently I don't know if it is a good idea
to keep it generally here.
"""
self._ldap.set_option(ldap.OPT_REFERRALS, 0)
if use_gssapi:
sasl_auth = ldap.sasl.sasl({},'GSSAPI')
self._ldap.sasl_interactive_bind_s("", sasl_auth)
else:
self._ldap.bind(binddn, password, ldap.AUTH_SIMPLE)
self._basedn = basedn
self._ldap_result = []
self._data = None
def text(self, filename = None):
"""
Returns a text representing the directory.
If filename is given it will be written in that file.
"""
env = Environment(loader=FileSystemLoader(
dirname(dirname(realpath(__file__))) + '/templates'))
template = env.get_template('simple.txt.j2')
text = template.render(ldaptree=self).encode('utf8')
if filename:
with open(filename, "w") as text_file:
text_file.write(text)
else:
return text
def graph(self, filename = None):
"""
Returns an svg representing the directory.
If filename is given it will be written in that file.
"""
graph = pgv.AGraph(
directed=True, charset='utf-8', fixedsize='true', ranksep=0.1)
graph.node_attr.update(
style='rounded,filled', width='0', height='0', shape='box',
fillcolor='#E5E5E5', concentrate='true', fontsize='8.0',
fontname='Arial', margin='0.03')
graph.edge_attr.update(arrowsize='0.55')
self._graph(graph, self._basedn)
graph.layout(prog='dot')
if filename:
graph.draw(path=filename, format='svg')
return None
else:
return graph.draw(format='svg')
def _graph(self, graph, dn):
"""
Recursive function creating a graphviz graph from the directory.
"""
result = self.node(dn)
minlen = thislen = 1
edge_start = dn
for entry in (entry[0] for entry in result):
if entry:
point = entry + '_p'
sub = graph.add_subgraph()
sub.graph_attr['rank'] = 'same'
sub.add_node(
point, shape='circle', fixedsize='true', width='0.04',
label='', fillcolor='transparent')
#sub.add_node(entry, URL='https://www.google.de/')
sub.add_node(entry)
graph.add_edge(edge_start, point, arrowhead='none',
minlen=str(minlen))
graph.add_edge(point, entry)
edge_start = point
minlen = self._graph(graph, entry)
thislen += minlen
return thislen
@property
def all(self):
if self._data == None:
self._data = {}
result = self._ldap.search_s(self._basedn, ldap.SCOPE_SUBTREE)
for entry in result:
self._data[entry[0]] = entry[1:][0]
return self._data
@property
def dn_tree(self):
retval = {}
for d in self.all.keys():
current = retval
for k in reversed(d.split(',')):
try:
current = current[k]
except:
current[k] = {}
current = current[k]
return retval
@property
def hirarchy(self):
return self._hirarchy(self.dn_tree)
def _hirarchy(self, dn, base=[], depth=0):
"""
Hirarchie generates a flat list where each parent is
followed by all its childs, so that in a template one
can simple iterate over it to display the complete tree.
Recently I learned that "recursive loops" are possible
within jinja2. So we can alter the template that it
ensures child displays correctly
"""
retval = []
for d in dn.keys():
base_name = ','.join(reversed(base))
name = ','.join(reversed(base + [d]))
retval.append((depth, base_name, name))
retval += self._hirarchy(dn[d], base + [d], depth+1)
return retval
def childs(self, dn):
"""
Recently I learned that "recursive loops" are possible
within jinja2. So we can alter the template that it
ensures child displays correctly. So this function should
return all child dn's to a given dn.
"""
return [d for d in self.hirarchy if d[1] == dn]
def node(self, dn):
if dn in self.all:
return self.all[dn]
else:
return {}