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,7 +8,6 @@ from struct import pack
8 from collections import deque 8 from collections import deque
9 9
10 from os.path import dirname, realpath 10 from os.path import dirname, realpath
11 -from sys import argv, path  
12 path.append(dirname(realpath(__file__)) + '/lib') 11 path.append(dirname(realpath(__file__)) + '/lib')
13 12
14 from Server import Server 13 from Server import Server
@@ -20,10 +19,12 @@ from Communication.EndPoint import CommunicationEndPoint @@ -20,10 +19,12 @@ from Communication.EndPoint import CommunicationEndPoint
20 from Protocol.Http.Http import Http 19 from Protocol.Http.Http import Http
21 from Protocol.Websocket.Websocket import Websocket 20 from Protocol.Websocket.Websocket import Websocket
22 21
  22 +from jinja2 import Environment, FileSystemLoader
  23 +
23 from LdapTree import LdapTree 24 from LdapTree import LdapTree
24 25
25 class Application(EventHandler): 26 class Application(EventHandler):
26 - def __init__(self, hosturi, binddn, basedn, password): 27 + def __init__(self, ip, port, hosturi, binddn, basedn, password):
27 super(Application, self).__init__() 28 super(Application, self).__init__()
28 29
29 self._event_methods = { 30 self._event_methods = {
@@ -35,17 +36,16 @@ class Application(EventHandler): @@ -35,17 +36,16 @@ class Application(EventHandler):
35 36
36 self._websockets = [] 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 random.seed() 45 random.seed()
42 46
43 self.ldaptree = LdapTree(hosturi, binddn, basedn, password, False) 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 def _upgrade(self, event): 49 def _upgrade(self, event):
50 self._websockets.append(event.subject) 50 self._websockets.append(event.subject)
51 # let other also handle the upgrade .. no return True 51 # let other also handle the upgrade .. no return True
@@ -64,13 +64,14 @@ class Application(EventHandler): @@ -64,13 +64,14 @@ class Application(EventHandler):
64 if event.data.isRequest(): 64 if event.data.isRequest():
65 if event.data.getUri() == '/': 65 if event.data.getUri() == '/':
66 resp = protocol.createResponse(event.data, 200, 'OK') 66 resp = protocol.createResponse(event.data, 200, 'OK')
67 - resp.setBody(self._wstestmm[0:]) 67 + resp.setBody(self._page)
68 elif event.data.getUri() == '/ldap': 68 elif event.data.getUri() == '/ldap':
69 resp = protocol.createResponse(event.data, 200, 'OK') 69 resp = protocol.createResponse(event.data, 200, 'OK')
70 resp.setHeader('Content-Type', 'image/svg+xml') 70 resp.setHeader('Content-Type', 'image/svg+xml')
71 resp.setBody(self.ldaptree.graph()) 71 resp.setBody(self.ldaptree.graph())
72 else: 72 else:
73 - resp = protocol.createResponse(event.data, 404, 'Not Found') 73 + resp = protocol.createResponse(
  74 + event.data, 404, 'Not Found')
74 resp.setBody('<h1>404 - Not Found</h1>') 75 resp.setBody('<h1>404 - Not Found</h1>')
75 76
76 self.issueEvent(event.subject, 'send_msg', resp) 77 self.issueEvent(event.subject, 'send_msg', resp)
@@ -133,7 +134,9 @@ def main(): @@ -133,7 +134,9 @@ def main():
133 usage() 134 usage()
134 sys.exit(2) 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 server.bindTcp(args[0], int(args[1]), Http()) 140 server.bindTcp(args[0], int(args[1]), Http())
138 server.start(1.0) 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 #!/usr/bin/python 1 #!/usr/bin/python
2 from os.path import dirname, realpath 2 from os.path import dirname, realpath
3 import getopt, sys 3 import getopt, sys
  4 +reload(sys)
4 sys.path.append(dirname(realpath(__file__)) + '/lib') 5 sys.path.append(dirname(realpath(__file__)) + '/lib')
  6 +sys.setdefaultencoding('utf-8')
5 7
6 import getpass 8 import getpass
7 from LdapTree import LdapTree 9 from LdapTree import LdapTree
@@ -31,7 +31,7 @@ class Connection(CommunicationEndPoint): @@ -31,7 +31,7 @@ class Connection(CommunicationEndPoint):
31 if not self._current_msg or self._current_msg.ready(): 31 if not self._current_msg or self._current_msg.ready():
32 self._current_msg = self._protocol.createMessage( 32 self._current_msg = self._protocol.createMessage(
33 self.getTransport().remote) 33 self.getTransport().remote)
34 - 34 +
35 end = self._protocol.getParser().parse( 35 end = self._protocol.getParser().parse(
36 self._current_msg, self._read_buffer) 36 self._current_msg, self._read_buffer)
37 37
  1 +from os.path import dirname, realpath
  2 +
1 import ldap 3 import ldap
2 import pygraphviz as pgv 4 import pygraphviz as pgv
3 5
  6 +from jinja2 import Environment, FileSystemLoader
  7 +
4 class LdapTree(object): 8 class LdapTree(object):
5 def __init__(self, hosturi, binddn, basedn, password, use_gssapi): 9 def __init__(self, hosturi, binddn, basedn, password, use_gssapi):
6 #ldap.set_option(ldap.OPT_DEBUG_LEVEL, 1) 10 #ldap.set_option(ldap.OPT_DEBUG_LEVEL, 1)
@@ -19,16 +23,23 @@ class LdapTree(object): @@ -19,16 +23,23 @@ class LdapTree(object):
19 self._basedn = basedn 23 self._basedn = basedn
20 self._ldap_result = [] 24 self._ldap_result = []
21 25
  26 + self._data = None
  27 +
22 def text(self, filename = None): 28 def text(self, filename = None):
23 """ 29 """
24 Returns a text representing the directory. 30 Returns a text representing the directory.
25 If filename is given it will be written in that file. 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 if filename: 38 if filename:
28 with open(filename, "w") as text_file: 39 with open(filename, "w") as text_file:
29 - text_file.write(self._text(self._basedn, 0)) 40 + text_file.write(text)
30 else: 41 else:
31 - return self._text(self._basedn, 0) 42 + return text
32 43
33 def graph(self, filename = None): 44 def graph(self, filename = None):
34 """ 45 """
@@ -54,26 +65,11 @@ class LdapTree(object): @@ -54,26 +65,11 @@ class LdapTree(object):
54 else: 65 else:
55 return graph.draw(format='svg') 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 def _graph(self, graph, dn): 68 def _graph(self, graph, dn):
73 """ 69 """
74 Recursive function creating a graphviz graph from the directory. 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 minlen = thislen = 1 73 minlen = thislen = 1
78 edge_start = dn 74 edge_start = dn
79 75
@@ -85,6 +81,7 @@ class LdapTree(object): @@ -85,6 +81,7 @@ class LdapTree(object):
85 sub.add_node( 81 sub.add_node(
86 point, shape='circle', fixedsize='true', width='0.04', 82 point, shape='circle', fixedsize='true', width='0.04',
87 label='', fillcolor='transparent') 83 label='', fillcolor='transparent')
  84 + #sub.add_node(entry, URL='https://www.google.de/')
88 sub.add_node(entry) 85 sub.add_node(entry)
89 graph.add_edge(edge_start, point, arrowhead='none', 86 graph.add_edge(edge_start, point, arrowhead='none',
90 minlen=str(minlen)) 87 minlen=str(minlen))
@@ -94,3 +91,62 @@ class LdapTree(object): @@ -94,3 +91,62 @@ class LdapTree(object):
94 thislen += minlen 91 thislen += minlen
95 92
96 return thislen 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,13 +62,13 @@ class TcpSocket(Socket):
62 62
63 def recv(self, size): 63 def recv(self, size):
64 data = '' 64 data = ''
65 - try: 65 + try:
66 data = self.socket.recv(size) 66 data = self.socket.recv(size)
67 except socket.error as error: 67 except socket.error as error:
68 if error.errno not in CONTINUE: 68 if error.errno not in CONTINUE:
69 raise Transport.Error(Transport.Error.ERR_FAILED) 69 raise Transport.Error(Transport.Error.ERR_FAILED)
70 return None 70 return None
71 - 71 +
72 if not data: 72 if not data:
73 raise Transport.Error(Transport.Error.ERR_REMOTE_CLOSE) 73 raise Transport.Error(Transport.Error.ERR_REMOTE_CLOSE)
74 74
@@ -76,7 +76,7 @@ class TcpSocket(Socket): @@ -76,7 +76,7 @@ class TcpSocket(Socket):
76 76
77 def send(self, data, remote=None): 77 def send(self, data, remote=None):
78 send = 0 78 send = 0
79 - try: 79 + try:
80 if self.socket: 80 if self.socket:
81 send = self.socket.send(data) 81 send = self.socket.send(data)
82 except socket.error as error: 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,7 +80,7 @@
80 var interval = null 80 var interval = null
81 81
82 wstest.data = 'Websockets are supported'; 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 console.log('Websockets test'); 84 console.log('Websockets test');
85 85
86 connection.onopen = function() { 86 connection.onopen = function() {
@@ -114,7 +114,8 @@ @@ -114,7 +114,8 @@
114 } 114 }
115 }/* ]]> */ 115 }/* ]]> */
116 </script> 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 </body> 119 </body>
119 </html> 120 </html>
120 <!-- vim: set ts=4 sw=4: --> 121 <!-- vim: set ts=4 sw=4: -->
No preview for this file type
Please register or login to post a comment