Commit 214fd623719fa8934a2a56a4674f9f5e296752b5

Authored by Georg Hopp
1 parent c7f367aa

Add code for a clickable HTML representation.

... ... @@ -8,7 +8,6 @@ from struct import pack
8 8 from collections import deque
9 9
10 10 from os.path import dirname, realpath
11   -from sys import argv, path
12 11 path.append(dirname(realpath(__file__)) + '/lib')
13 12
14 13 from Server import Server
... ... @@ -20,10 +19,12 @@ from Communication.EndPoint import CommunicationEndPoint
20 19 from Protocol.Http.Http import Http
21 20 from Protocol.Websocket.Websocket import Websocket
22 21
  22 +from jinja2 import Environment, FileSystemLoader
  23 +
23 24 from LdapTree import LdapTree
24 25
25 26 class Application(EventHandler):
26   - def __init__(self, hosturi, binddn, basedn, password):
  27 + def __init__(self, ip, port, hosturi, binddn, basedn, password):
27 28 super(Application, self).__init__()
28 29
29 30 self._event_methods = {
... ... @@ -35,17 +36,16 @@ class Application(EventHandler):
35 36
36 37 self._websockets = []
37 38
38   - self._wstest = open('websocket.html', 'r+b')
39   - self._wstestmm = mmap.mmap(self._wstest.fileno(), 0)
  39 + env = Environment(loader=FileSystemLoader(
  40 + dirname(realpath(__file__)) + '/templates'))
  41 + template = env.get_template('websocket.html.j2')
  42 + # TODO get ip and port or better our complete base uri here.
  43 + self._page = template.render(ip=ip, port=port)
40 44
41 45 random.seed()
42 46
43 47 self.ldaptree = LdapTree(hosturi, binddn, basedn, password, False)
44 48
45   - def __del__(self):
46   - self._wstestmm.close()
47   - self._wstest.close()
48   -
49 49 def _upgrade(self, event):
50 50 self._websockets.append(event.subject)
51 51 # let other also handle the upgrade .. no return True
... ... @@ -64,13 +64,14 @@ class Application(EventHandler):
64 64 if event.data.isRequest():
65 65 if event.data.getUri() == '/':
66 66 resp = protocol.createResponse(event.data, 200, 'OK')
67   - resp.setBody(self._wstestmm[0:])
  67 + resp.setBody(self._page)
68 68 elif event.data.getUri() == '/ldap':
69 69 resp = protocol.createResponse(event.data, 200, 'OK')
70 70 resp.setHeader('Content-Type', 'image/svg+xml')
71 71 resp.setBody(self.ldaptree.graph())
72 72 else:
73   - resp = protocol.createResponse(event.data, 404, 'Not Found')
  73 + resp = protocol.createResponse(
  74 + event.data, 404, 'Not Found')
74 75 resp.setBody('<h1>404 - Not Found</h1>')
75 76
76 77 self.issueEvent(event.subject, 'send_msg', resp)
... ... @@ -133,7 +134,9 @@ def main():
133 134 usage()
134 135 sys.exit(2)
135 136
136   - server = Server(Application(hosturi, binddn, basedn, password))
  137 + server = Server(
  138 + Application(
  139 + args[0], int(args[1], hosturi, binddn, basedn, password))
137 140 server.bindTcp(args[0], int(args[1]), Http())
138 141 server.start(1.0)
139 142
... ...
  1 +#!/usr/bin/python
  2 +
  3 +import time
  4 +import random
  5 +import mmap
  6 +from struct import pack
  7 +from collections import deque
  8 +
  9 +from os.path import dirname, realpath
  10 +import sys
  11 +reload(sys)
  12 +from sys import argv, path, setdefaultencoding
  13 +path.append(dirname(realpath(__file__)) + '/lib')
  14 +setdefaultencoding('utf-8')
  15 +import re
  16 +
  17 +from Server import Server
  18 +
  19 +from Event.EventHandler import EventHandler
  20 +from Event.EventDispatcher import EventDispatcher
  21 +from Communication.EndPoint import CommunicationEndPoint
  22 +
  23 +from Protocol.Http.Http import Http
  24 +from Protocol.Websocket.Websocket import Websocket
  25 +from jinja2 import Environment, FileSystemLoader
  26 +
  27 +from LdapTree import LdapTree
  28 +
  29 +class Application(EventHandler):
  30 + def __init__(self, ip, port, hosturi, binddn, basedn, password):
  31 + super(Application, self).__init__()
  32 +
  33 + self._event_methods = {
  34 + CommunicationEndPoint.eventId('new_msg') : self._handle_data,
  35 + }
  36 +
  37 + self._ldaptree = LdapTree(hosturi, binddn, basedn, password, False)
  38 +
  39 + env = Environment(loader=FileSystemLoader(
  40 + dirname(realpath(__file__)) + '/templates'))
  41 + self._template = env.get_template('simple.html.j2')
  42 +
  43 + random.seed()
  44 +
  45 + @property
  46 + def _body(self):
  47 + return self._template.render(ldaptree=self._ldaptree).encode('utf8')
  48 +
  49 + def _handle_data(self, event):
  50 + protocol = event.subject.getProtocol()
  51 +
  52 + if event.subject.hasProtocol(Http):
  53 + if event.data.isRequest():
  54 + if event.data.getUri() == '/':
  55 + resp = protocol.createResponse(event.data, 200, 'OK')
  56 + resp.setBody(self._body)
  57 + else:
  58 + resp = protocol.createResponse(event.data, 404, 'Not Found')
  59 + resp.setBody('<h1>404 - Not Found</h1>')
  60 +
  61 + self.issueEvent(event.subject, 'send_msg', resp)
  62 +
  63 + return True
  64 +
  65 +
  66 +def usage():
  67 + print "Usage: " + sys.argv[0] + " ARGUMENT... [OPTIONS]... bindip bindport\n"
  68 + print "Start a webserver on the given bindip and bindport. On the page a"
  69 + print "tree representation of all DNs starting with a given base DN is"
  70 + print "visualized."
  71 + print "Only simple binds to the directory with DN and password are supported.\n"
  72 + print "ARGUMENTS:\n"
  73 + print " {:30s} : {:s}".format('-H, --hosturi=URI', 'The URI to the ldap server to query in the form:')
  74 + print " {:30s} {:s}".format('', 'ldap[s]://host.uri[:port]')
  75 + print " {:30s} : {:s}".format('-D, --binddn=DN', 'The DN to use for the LDAP bind.')
  76 + print " {:30s} : {:s}".format('-p, --password=PASSWORD', 'The password to use for the LDAP bind.')
  77 + print " {:30s} : {:s}\n".format('-b, --basedn=DN', 'The DN to start the tree with.')
  78 + print "OPTIONS:\n"
  79 + print " {:30s} : {:s}".format('-h, --help', 'Show this help page')
  80 +
  81 +def main():
  82 + try:
  83 + opts, args = getopt.getopt(
  84 + sys.argv[1:],
  85 + 'hH:D:b:p:',
  86 + ['help', 'hosturi=', 'binddn=', 'basedn=', 'password='])
  87 + except getopt.GetoptError as err:
  88 + print str(err)
  89 + usage()
  90 + sys.exit(2)
  91 +
  92 + hosturi = binddn = basedn = password = None
  93 +
  94 + for o, a in opts:
  95 + if o in ["-h", "--help"]:
  96 + usage()
  97 + sys.exit(0)
  98 + elif o in ["-H", "--hosturi"]:
  99 + hosturi = a
  100 + elif o in ["-D", "--binddn"]:
  101 + binddn = a
  102 + elif o in ["-b", "--basedn"]:
  103 + basedn = a
  104 + elif o in ["-p", "--password"]:
  105 + password = a
  106 + else:
  107 + print "unknown parameter: " + a
  108 + usage()
  109 + sys.exit(2)
  110 +
  111 + if not hosturi or not binddn or not basedn or not password:
  112 + usage()
  113 + sys.exit(2)
  114 +
  115 + server = Server(
  116 + Application(
  117 + args[0], int(args[1], hosturi, binddn, basedn, password))
  118 + server.bindTcp(args[0], int(args[1]), Http())
  119 + server.start(1.0)
  120 +
  121 +if __name__ == '__main__':
  122 + main()
  123 +# vim: set ft=python et ts=8 sw=4 sts=4:
... ...
1 1 #!/usr/bin/python
2 2 from os.path import dirname, realpath
3 3 import getopt, sys
  4 +reload(sys)
4 5 sys.path.append(dirname(realpath(__file__)) + '/lib')
  6 +sys.setdefaultencoding('utf-8')
5 7
6 8 import getpass
7 9 from LdapTree import LdapTree
... ...
... ... @@ -31,7 +31,7 @@ class Connection(CommunicationEndPoint):
31 31 if not self._current_msg or self._current_msg.ready():
32 32 self._current_msg = self._protocol.createMessage(
33 33 self.getTransport().remote)
34   -
  34 +
35 35 end = self._protocol.getParser().parse(
36 36 self._current_msg, self._read_buffer)
37 37
... ...
  1 +from os.path import dirname, realpath
  2 +
1 3 import ldap
2 4 import pygraphviz as pgv
3 5
  6 +from jinja2 import Environment, FileSystemLoader
  7 +
4 8 class LdapTree(object):
5 9 def __init__(self, hosturi, binddn, basedn, password, use_gssapi):
6 10 #ldap.set_option(ldap.OPT_DEBUG_LEVEL, 1)
... ... @@ -19,16 +23,23 @@ class LdapTree(object):
19 23 self._basedn = basedn
20 24 self._ldap_result = []
21 25
  26 + self._data = None
  27 +
22 28 def text(self, filename = None):
23 29 """
24 30 Returns a text representing the directory.
25 31 If filename is given it will be written in that file.
26 32 """
  33 + env = Environment(loader=FileSystemLoader(
  34 + dirname(dirname(realpath(__file__))) + '/templates'))
  35 + template = env.get_template('simple.txt.j2')
  36 + text = template.render(ldaptree=self).encode('utf8')
  37 +
27 38 if filename:
28 39 with open(filename, "w") as text_file:
29   - text_file.write(self._text(self._basedn, 0))
  40 + text_file.write(text)
30 41 else:
31   - return self._text(self._basedn, 0)
  42 + return text
32 43
33 44 def graph(self, filename = None):
34 45 """
... ... @@ -54,26 +65,11 @@ class LdapTree(object):
54 65 else:
55 66 return graph.draw(format='svg')
56 67
57   - def _text(self, dn, level):
58   - """
59   - Recursive function that returns a string representation of the
60   - directory where each depth is indicated by a dash.
61   - """
62   - result = self._ldap.search_s(dn, ldap.SCOPE_ONELEVEL)
63   - indent = '-' * level
64   - text = indent + dn + "\n"
65   -
66   - for entry in (entry[0] for entry in result):
67   - if entry:
68   - text += self._text(entry, level + 1)
69   -
70   - return text
71   -
72 68 def _graph(self, graph, dn):
73 69 """
74 70 Recursive function creating a graphviz graph from the directory.
75 71 """
76   - result = self._ldap.search_s(dn, ldap.SCOPE_ONELEVEL)
  72 + result = self.node(dn)
77 73 minlen = thislen = 1
78 74 edge_start = dn
79 75
... ... @@ -85,6 +81,7 @@ class LdapTree(object):
85 81 sub.add_node(
86 82 point, shape='circle', fixedsize='true', width='0.04',
87 83 label='', fillcolor='transparent')
  84 + #sub.add_node(entry, URL='https://www.google.de/')
88 85 sub.add_node(entry)
89 86 graph.add_edge(edge_start, point, arrowhead='none',
90 87 minlen=str(minlen))
... ... @@ -94,3 +91,62 @@ class LdapTree(object):
94 91 thislen += minlen
95 92
96 93 return thislen
  94 +
  95 + @property
  96 + def all(self):
  97 + if self._data == None:
  98 + self._data = {}
  99 + result = self._ldap.search_s(self._basedn, ldap.SCOPE_SUBTREE)
  100 + for entry in result:
  101 + self._data[entry[0]] = entry[1:][0]
  102 +
  103 + return self._data
  104 +
  105 + @property
  106 + def dn_tree(self):
  107 + retval = {}
  108 + for d in self.all.keys():
  109 + current = retval
  110 + for k in reversed(d.split(',')):
  111 + try:
  112 + current = current[k]
  113 + except:
  114 + current[k] = {}
  115 + current = current[k]
  116 + return retval
  117 +
  118 + @property
  119 + def hirarchy(self):
  120 + return self._hirarchy(self.dn_tree)
  121 +
  122 + def _hirarchy(self, dn, base=[], depth=0):
  123 + """
  124 + Hirarchie generates a flat list where each parent is
  125 + followed by all its childs, so that in a template one
  126 + can simple iterate over it to display the complete tree.
  127 + Recently I learned that "recursive loops" are possible
  128 + within jinja2. So we can alter the template that it
  129 + ensures child displays correctly
  130 + """
  131 + retval = []
  132 + for d in dn.keys():
  133 + base_name = ','.join(reversed(base))
  134 + name = ','.join(reversed(base + [d]))
  135 + retval.append((depth, base_name, name))
  136 + retval += self._hirarchy(dn[d], base + [d], depth+1)
  137 + return retval
  138 +
  139 + def childs(self, dn):
  140 + """
  141 + Recently I learned that "recursive loops" are possible
  142 + within jinja2. So we can alter the template that it
  143 + ensures child displays correctly. So this function should
  144 + return all child dn's to a given dn.
  145 + """
  146 + return [d for d in self.hirarchy if d[1] == dn]
  147 +
  148 + def node(self, dn):
  149 + if dn in self.all:
  150 + return self.all[dn]
  151 + else:
  152 + return {}
... ...
... ... @@ -62,13 +62,13 @@ class TcpSocket(Socket):
62 62
63 63 def recv(self, size):
64 64 data = ''
65   - try:
  65 + try:
66 66 data = self.socket.recv(size)
67 67 except socket.error as error:
68 68 if error.errno not in CONTINUE:
69 69 raise Transport.Error(Transport.Error.ERR_FAILED)
70 70 return None
71   -
  71 +
72 72 if not data:
73 73 raise Transport.Error(Transport.Error.ERR_REMOTE_CLOSE)
74 74
... ... @@ -76,7 +76,7 @@ class TcpSocket(Socket):
76 76
77 77 def send(self, data, remote=None):
78 78 send = 0
79   - try:
  79 + try:
80 80 if self.socket:
81 81 send = self.socket.send(data)
82 82 except socket.error as error:
... ...
  1 +<!DOCTYPE html>
  2 +<html>
  3 + <head>
  4 + <meta charset="utf-8"/>
  5 + <title>Ldap</title>
  6 + <style type="text/css">
  7 + ul {
  8 + list-style-type: none;
  9 + }
  10 + ul.attributes {
  11 + border: 1px solid black;
  12 + font-weight: normal;
  13 + display: none;
  14 + padding: 5px;
  15 + margin: 0;
  16 + }
  17 + ul.childs {
  18 + display: none;
  19 + }
  20 + button {
  21 + cursor: pointer;
  22 + background-color: rgba(0,0,0,0);
  23 + border: none;
  24 + color: black;
  25 + padding: 2px;
  26 + text-align: center;
  27 + text-decoration: none;
  28 + display: inline-block;
  29 + font-size: 16px;
  30 + }
  31 + span.linked {
  32 + font-weight: bold;
  33 + cursor: pointer;
  34 + }
  35 + </style>
  36 + </head>
  37 +
  38 + <body>
  39 + <h1>Ldap</h1>
  40 +
  41 + <script language="Javascript">
  42 + function toggle(e, cls) {
  43 + attr = e.parentElement.getElementsByClassName(cls)[0];
  44 + if(attr.style.display == 'block')
  45 + attr.style.display = 'none';
  46 + else
  47 + attr.style.display = 'block';
  48 + }
  49 + </script>
  50 +
  51 + <ul class="tree">
  52 + {% set depth=0 -%}
  53 + {% for d in ldaptree.hirarchy -%}
  54 + {% if depth>=d[0] and depth!=0 -%}
  55 + <ul class="childs" {% if depth < 3 -%}style="display: block;"{% endif -%}>
  56 + </ul>
  57 + </li>
  58 + {% endif -%}
  59 + {% if depth < d[0] -%}
  60 + {% set depth=d[0] -%}
  61 + <ul class="childs" {% if depth < 3 -%}style="display: block;"{% endif -%}>
  62 + {% endif -%}
  63 + {% if depth > d[0] -%}
  64 + {% for i in range(depth-d[0]) -%}
  65 + </ul>
  66 + </li>
  67 + {% endfor -%}
  68 + {% set depth=d[0] -%}
  69 + {% endif -%}
  70 + <li>
  71 + <span {% if ldaptree.childs(d[2]) -%}class="linked"{% endif -%}
  72 + onclick="toggle(this, 'childs')">dn: {{ d[2]|e }}</span>
  73 + <button onclick="toggle(this, 'attributes')">[Attributes]</button>
  74 + <ul class="attributes">
  75 + {% for k in ldaptree.node(d[2]).keys() -%}
  76 + {% if ldaptree.node(d[2]) is string -%}
  77 + <li>{{ k }}: {{ ldaptree.node(d[2])[k]|e }}</li>
  78 + {% else -%}
  79 + {% for v in ldaptree.node(d[2])[k] -%}
  80 + <li>{{ k }}: {{ v|e }}</li>
  81 + {% endfor -%}
  82 + {% endif -%}
  83 + {% endfor -%}
  84 + </ul>
  85 + {% endfor -%}
  86 + </ul>
  87 + </body>
  88 +</html>
  89 +<!-- vim: set et ts=2 sw=2: -->
... ...
  1 +{% for d in ldaptree.hirarchy -%}
  2 +{{ '--'*d[0] }} dn: {{ d[2] }}
  3 +{% for k in ldaptree.node(d[2]).keys() -%}
  4 +{% if ldaptree.node(d[2]) is string -%}
  5 +{{ ' '*d[0] }} {{ k }}: {{ ldaptree.node(d[2])[k] }}
  6 +{% else -%}
  7 +{% for v in ldaptree.node(d[2])[k] -%}
  8 +{{ ' '*d[0] }} {{ k }}: {{ v }}
  9 +{% endfor -%}
  10 +{% endif -%}
  11 +{% endfor -%}
  12 +{% endfor -%}
... ...
... ... @@ -80,7 +80,7 @@
80 80 var interval = null
81 81
82 82 wstest.data = 'Websockets are supported';
83   - connection = new WebSocket('ws://127.0.0.1:8080/');
  83 + connection = new WebSocket('ws://172.27.1.139:8080/');
84 84 console.log('Websockets test');
85 85
86 86 connection.onopen = function() {
... ... @@ -114,7 +114,8 @@
114 114 }
115 115 }/* ]]> */
116 116 </script>
117   - <img src="http://127.0.0.1:8080/ldap" />
  117 + <object data="http://{{ ip }}:{{ port }}/ldap" type="image/svg+xml" class="mailicon">
  118 + </object>
118 119 </body>
119 120 </html>
120 121 <!-- vim: set ts=4 sw=4: -->
... ...
No preview for this file type
Please register or login to post a comment