LdapTree.py
4.92 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
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 {}