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
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 {}
|