summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorequinox <equinox@diac24.net>2011-04-01 05:42:06 +0200
committerequinox <equinox@taifun.local.sublab.org>2011-04-01 05:42:28 +0200
commit49856f36361a71aba71c43c77cebb22340010181 (patch)
tree9d7d86422eac39c779a3bd01432586a0d01b7693
initial checkin
-rw-r--r--.gitignore5
-rw-r--r--dosign.py19
-rwxr-xr-xindex.py128
-rw-r--r--static/app_subdap.pngbin0 -> 4579 bytes
-rw-r--r--static/app_wiki.pngbin0 -> 3778 bytes
-rw-r--r--static/details.js28
-rw-r--r--static/fire.pngbin0 -> 8663 bytes
-rw-r--r--static/layout.css92
-rw-r--r--static/ldap.pngbin0 -> 10942 bytes
-rw-r--r--static/logo.pngbin0 -> 28959 bytes
-rw-r--r--templates/create.html21
-rw-r--r--templates/error.html19
-rw-r--r--templates/layout.xi24
-rw-r--r--templates/login.html20
-rw-r--r--templates/select.html17
-rw-r--r--tmpl.py50
-rw-r--r--versign.py30
17 files changed, 453 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..45858e9
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,5 @@
+cherrypy.config
+*.pyc
+*.pem
+*.pub
+*.orig
diff --git a/dosign.py b/dosign.py
new file mode 100644
index 0000000..e0d3465
--- /dev/null
+++ b/dosign.py
@@ -0,0 +1,19 @@
+import M2Crypto.RSA
+import M2Crypto.EVP
+import time
+import base64, urllib
+
+ts = int(time.time())
+user = 'equinox'
+data = '%d:%s' % (ts, user)
+
+algo = 'sha256'
+
+digest = M2Crypto.EVP.MessageDigest(algo)
+digest.update(data)
+digval = digest.final()
+
+key = M2Crypto.RSA.load_key('rsa.pem')
+signature = key.sign(digval, algo = algo)
+
+print urllib.urlencode([('user', user), ('ts', ts), ('signature', base64.urlsafe_b64encode(signature))])
diff --git a/index.py b/index.py
new file mode 100755
index 0000000..0fd89af
--- /dev/null
+++ b/index.py
@@ -0,0 +1,128 @@
+#!/usr/bin/python
+# -*- coding: utf8 -*-
+
+import sys, os, cgi
+appbase = os.path.dirname(os.path.abspath(__file__))
+sys.path.append(appbase)
+
+import threading
+import cherrypy
+from tmpl import expose, render
+from lxml import etree
+from lxml.html import formfill
+from StringIO import StringIO
+import ldap
+import mx.DateTime
+from accountservice import accountservice
+
+class ldapobj(object):
+ def __init__(s, data):
+ if len(data) != 1:
+ raise ValueError, "invalid number of results"
+ dn, fields = data[0]
+ s._keys = fields.keys()
+ for k, v in fields.iteritems():
+ s.__dict__[k] = v
+ s.dn = dn
+ def __getitem__(s, key):
+ l = s.__dict__[key]
+ if len(l) != 1:
+ raise IndexError, "invalid number of attributes"
+ return l[0]
+ def keys(s):
+ return s._keys
+
+class SubdapSite(object):
+ def __init__(s):
+ cherrypy.config.update({'error_page.404': s.http_404})
+
+ @expose('error.html')
+ def http_404(s, status, message, **kwargs):
+ return render(details = status + ' - ' + message)
+
+ @expose('login.html')
+ def index(s):
+ return render(errors = {})
+ @expose('login.html')
+ def login(s, username = None, password = None):
+ if username == None or password == None:
+ return render(errors = {'password': 'Login incorrect'})
+ if username == '' or password == '':
+ return render(errors = {'password': 'Login incorrect'})
+
+ dn = "cn=%s,ou=people,dc=sublab,dc=org" % (username)
+ try:
+ l = ldap.initialize('ldaps://taifun.local.sublab.org/')
+ l.simple_bind_s(dn, password)
+ except ldap.INVALID_CREDENTIALS:
+ return render(errors = {'password': 'Login incorrect'})
+ except ldap.LDAPError, e:
+ return render(errors = {'password': 'Login incorrect'})
+ # e.message['info']
+
+ user = ldapobj(l.search_s(dn, ldap.SCOPE_BASE, '(objectclass=*)', []))
+ return render('select.html', user = user)
+
+## x = ''
+# for r in data:
+# if r[0] != dn:
+# continue
+## x += '<tr><th>%s</th></tr>\n' % (r[0])
+# for k, v in r[1].iteritems():
+# for value in v:
+# details.append([k, value])
+## x += '<tr><td>%s</td><td><pre>%s</pre></td></tr>\n' % (k, "<hr>".join(v))
+
+ @expose('create.html')
+ def create(s):
+ return render(errors = {}, username = '')
+ @expose('create.html')
+ def docreate(s, username = None, password = None, password2 = None):
+ errors = {}
+ if username == None or username == '':
+ errors['username'] = 'please specify an user name'
+ elif accountservice.name_valid(username) != 'valid':
+ errors['username'] = 'username invalid or taken'
+ if password == None or len(password) < 6:
+ errors['password'] = 'please specify a password of at least 6 characters'
+ if password2 != password:
+ errors['password2'] = 'passwords did not match'
+ if len(errors) > 0:
+ return render(errors = errors, username = username)
+
+ accountservice.name_create(username, password)
+ return s.login(username, password)
+
+ @cherrypy.expose
+ def kill(s):
+ import sys
+ sys.exit(0)
+
+config = {
+ 'global': {
+ 'server.socket_port': 8080,
+ 'server.socket_host': '127.0.0.1',
+# 'server.socket_host': '::1',
+ 'tools.staticdir.root': appbase,
+ },
+ '/': {
+ 'tools.decode.on': True,
+ 'tools.encode.on': True,
+ 'tools.gzip.on': True,
+ 'tools.proxy.on': True,
+# 'tools.caching.on': False,
+# 'tools.expires.secs': 0,
+# 'tools.expires.force': True,
+ },
+ '/static': {
+ 'tools.staticdir.on': True,
+ 'tools.staticdir.dir': 'static',
+ },
+}
+
+if len(sys.argv) >= 2 and sys.argv[1] == 'standalone':
+ cherrypy.quickstart(SubdapSite(), "/", config)
+else:
+ cherrypy.config.update({'environment': 'embedded'})
+ application = cherrypy.Application(SubdapSite(), script_name = None, config = config)
+
diff --git a/static/app_subdap.png b/static/app_subdap.png
new file mode 100644
index 0000000..6e985bc
--- /dev/null
+++ b/static/app_subdap.png
Binary files differ
diff --git a/static/app_wiki.png b/static/app_wiki.png
new file mode 100644
index 0000000..80c894b
--- /dev/null
+++ b/static/app_wiki.png
Binary files differ
diff --git a/static/details.js b/static/details.js
new file mode 100644
index 0000000..bd42bb4
--- /dev/null
+++ b/static/details.js
@@ -0,0 +1,28 @@
+
+function add(type) {
+ var node = document.getElementById("hperson");
+
+ ntr = document.createElement("tr");
+ ntd = document.createElement("td");
+ ntxt = document.createTextNode("x");
+ ntd.appendChild(ntxt);
+ ntr.appendChild(ntd);
+
+ node.parentNode.insertBefore(ntr, node);
+
+ window.alert('tag' + node.nodeName);
+ for (node = node.nextSibling; node != null; node = node.nextSibling) {
+ window.alert('tag' + node.nodeName);
+ if (node.nodeName != 'TR')
+ continue;
+ var td = node.getElementsByTagName("TD");
+ if (td == null)
+ continue;
+ var input = td[1].getElementsByTagName("INPUT");
+ if (input == null)
+ continue;
+ for (var k = 0; k < input.length; k++)
+ input[k].style.display = 'none';
+ }
+}
+
diff --git a/static/fire.png b/static/fire.png
new file mode 100644
index 0000000..4d9ff87
--- /dev/null
+++ b/static/fire.png
Binary files differ
diff --git a/static/layout.css b/static/layout.css
new file mode 100644
index 0000000..eb670f5
--- /dev/null
+++ b/static/layout.css
@@ -0,0 +1,92 @@
+body, table {
+ font-family:sans;
+ font-size:11pt;
+}
+body#formcont {
+ margin:auto;
+ text-align:center;
+}
+body#formcont < div {
+ display:inline-block;
+}
+img#logo {
+ display:inline-block;
+ margin:3em;
+ margin-right:0.5em;
+}
+body#formcont form,
+body#formcont div.cont {
+ display:inline-block;
+ vertical-align:top;
+ text-align:right;
+ border-left:1px solid #ccc;
+ margin:3em;
+ margin-left:0.5em;
+ padding:2em;
+ min-height:180px;
+}
+
+p.app {
+ border:1px solid #ccc;
+ padding:2pt;
+ padding-right:8pt;
+}
+p.app img {
+ display:inline-block;
+ margin:2px;
+ border:0px;
+ vertical-align:middle;
+}
+p.app a {
+ text-decoration:none;
+}
+
+a#create {
+ display:block;
+ font-size:75%;
+ text-align:right;
+ margin-bottom:1em;
+}
+b.error {
+ display:block;
+ color:red;
+ font-style:italic;
+ font-size:9pt;
+ margin-bottom:3pt;
+ max-width:15em;
+}
+div#creatpw {
+ margin-top:1em;
+}
+input[type=submit] {
+ margin-top:1em;
+}
+body#error img {
+ float:left;
+ margin:2em;
+ margin-right:0.5em;
+}
+body#error div#cont {
+ margin:2em;
+ margin-left:260px;
+ border-left:1px solid #ccc;
+ padding:1em;
+ padding-bottom:10em;
+}
+body#error h1 {
+ margin-top:1em;
+}
+body#error code {
+ font-size:10pt;
+}
+body#error pre {
+ max-width:45em;
+ font-size:8.5pt;
+ border:1px solid #d73f0b;
+ padding:1em;
+ background-color:#210;
+ color:#ffb100;
+}
+
+
+
diff --git a/static/ldap.png b/static/ldap.png
new file mode 100644
index 0000000..cc7559c
--- /dev/null
+++ b/static/ldap.png
Binary files differ
diff --git a/static/logo.png b/static/logo.png
new file mode 100644
index 0000000..e1968a0
--- /dev/null
+++ b/static/logo.png
Binary files differ
diff --git a/templates/create.html b/templates/create.html
new file mode 100644
index 0000000..2ac6563
--- /dev/null
+++ b/templates/create.html
@@ -0,0 +1,21 @@
+<!DOCTYPE html>
+<html xmlns="http://www.w3.org/1999/xhtml"
+ xmlns:py="http://genshi.edgewall.org/"
+ xmlns:sub="http://local.sublab.org/subdap/xmlns-templates"
+ xmlns:xi="http://www.w3.org/2001/XInclude">
+ <xi:include href="layout.xi"/>
+ <head>
+ <title>create</title>
+ </head>
+ <sub:bodyform>
+ <form action='docreate' method='POST'>
+ <div>uid: <input type='text' name='username' size="10" value="${username}"/></div>
+ <b class="error" py:if="'username' in errors">${errors.username}</b>
+ <div id="creatpw">password: <input type='password' name='password' size="10" value=""/></div>
+ <b class="error" py:if="'password' in errors">${errors.password}</b>
+ <div>repeat: <input type='password' name='password2' size="10" value=""/></div>
+ <b class="error" py:if="'password2' in errors">${errors.password2}</b>
+ <input type='submit' value='create'/>
+ </form>
+ </sub:bodyform>
+</html>
diff --git a/templates/error.html b/templates/error.html
new file mode 100644
index 0000000..0399863
--- /dev/null
+++ b/templates/error.html
@@ -0,0 +1,19 @@
+<!DOCTYPE html>
+<html xmlns="http://www.w3.org/1999/xhtml"
+ xmlns:py="http://genshi.edgewall.org/"
+ xmlns:sub="http://local.sublab.org/subdap/xmlns-templates"
+ xmlns:xi="http://www.w3.org/2001/XInclude">
+ <xi:include href="layout.xi"/>
+ <head>
+ <title>login</title>
+ </head>
+ <body id="error">
+ <img src="static/fire.png" alt="subdap"/>
+ <div id="cont">
+ <h1>kaputt!</h1>
+ <p><code>subdap.exe</code> hat ein Problem festgestellt und musste beendet werden.<br/>
+ Bitte schicken sie einen Fehlerbericht an <code>equinox ! diac24 ! net</code>.</p>
+ <pre>${details}</pre>
+ </div>
+ </body>
+</html>
diff --git a/templates/layout.xi b/templates/layout.xi
new file mode 100644
index 0000000..6a5418a
--- /dev/null
+++ b/templates/layout.xi
@@ -0,0 +1,24 @@
+<html xmlns="http://www.w3.org/1999/xhtml"
+ xmlns:py="http://genshi.edgewall.org/"
+ xmlns:sub="http://local.sublab.org/subdap/xmlns-templates"
+ py:strip="">
+<!-- vim:syntax=html
+ -->
+
+ <py:match path="head" once="true">
+ <head py:attrs="select('@*')">
+ <title py:with="title = list(select('title/text()'))">subdap<py:if test="title">: ${title}</py:if></title>
+ <link rel="stylesheet" href="${url('static/layout.css')}" type="text/css"/>
+ ${select('*[local-name()!="title"]')}
+ </head>
+ </py:match>
+
+ <py:match path="sub:bodyform" once="true">
+ <body id="formcont" py:attrs="select('@*')">
+ <div>
+ <img id="logo" src="static/ldap.png" alt="subdap"/>
+ ${select('*')}
+ </div>
+ </body>
+ </py:match>
+</html>
diff --git a/templates/login.html b/templates/login.html
new file mode 100644
index 0000000..41b6c43
--- /dev/null
+++ b/templates/login.html
@@ -0,0 +1,20 @@
+<!DOCTYPE html>
+<html xmlns="http://www.w3.org/1999/xhtml"
+ xmlns:py="http://genshi.edgewall.org/"
+ xmlns:sub="http://local.sublab.org/subdap/xmlns-templates"
+ xmlns:xi="http://www.w3.org/2001/XInclude">
+ <xi:include href="layout.xi"/>
+ <head>
+ <title>login</title>
+ </head>
+ <sub:bodyform>
+ <form action='login' method='POST'>
+ <a href="create" id="create">Konto erstellen</a>
+ <div>uid: <input type='text' name='username' size="10"/></div>
+ <b class="error" py:if="'username' in errors">${errors.username}</b>
+ <div>passwort: <input type='password' name='password' size="10"/></div>
+ <b class="error" py:if="'password' in errors">${errors.password}</b>
+ <input type='submit' value='login'/>
+ </form>
+ </sub:bodyform>
+</html>
diff --git a/templates/select.html b/templates/select.html
new file mode 100644
index 0000000..e7dc1f2
--- /dev/null
+++ b/templates/select.html
@@ -0,0 +1,17 @@
+<!DOCTYPE html>
+<html xmlns="http://www.w3.org/1999/xhtml"
+ xmlns:py="http://genshi.edgewall.org/"
+ xmlns:sub="http://local.sublab.org/subdap/xmlns-templates"
+ xmlns:xi="http://www.w3.org/2001/XInclude">
+ <xi:include href="layout.xi"/>
+ <head>
+ <title>login</title>
+ </head>
+ <sub:bodyform>
+ <div class="cont">
+ Hallo ${user['cn']}!
+ <p class="app"><a href="http://sublab.org"><img src="${url('static/app_wiki.png')}" alt="wiki"/> sublab.org wiki</a></p>
+ <p class="app"><a href=""><img src="${url('static/app_subdap.png')}" alt="wiki"/> Benutzerdaten</a></p>
+ </div>
+ </sub:bodyform>
+</html>
diff --git a/tmpl.py b/tmpl.py
new file mode 100644
index 0000000..6551728
--- /dev/null
+++ b/tmpl.py
@@ -0,0 +1,50 @@
+import os, traceback
+
+import cherrypy
+from genshi.core import Stream
+from genshi.output import encode, get_serializer
+from genshi.template import Context, TemplateLoader
+
+loader = TemplateLoader(
+ os.path.join(os.path.dirname(__file__), 'templates'),
+ auto_reload=True
+)
+
+def render(*args, **kwargs):
+ if args:
+ assert len(args) == 1, 'Expected exactly one argument, but got %r' % (args,)
+ template = loader.load(args[0])
+ else:
+ template = cherrypy.thread_data.template
+ #ctxt = Context(url = cherrypy.url)
+ ctxt = Context(url = myurl)
+ ctxt.push(kwargs)
+ return template.generate(ctxt)
+
+def exc(exc):
+ return render('error.html', details = traceback.format_exc())
+
+def expose(filename, method='html', encoding='utf-8', **options):
+ def decorate(func):
+ @cherrypy.expose
+ def wrapper(*args, **kwargs):
+ cherrypy.thread_data.template = loader.load(filename)
+ cherrypy.thread_data.func = func
+ opt = options.copy()
+ if method == 'html':
+ opt.setdefault('doctype', 'html')
+ serializer = get_serializer(method, **opt)
+ try:
+ stream = func(*args, **kwargs)
+ except Exception, e:
+ stream = exc(e)
+ if not isinstance(stream, Stream):
+ return stream
+ return encode(serializer(stream), method = serializer, encoding = encoding)
+ return wrapper
+ return decorate
+
+def myurl(url):
+ return '/subdap/' + url
+
+
diff --git a/versign.py b/versign.py
new file mode 100644
index 0000000..aa406df
--- /dev/null
+++ b/versign.py
@@ -0,0 +1,30 @@
+import M2Crypto.RSA
+import M2Crypto.EVP
+import time, sys
+import base64, cgi
+
+str = sys.argv[1]
+data = cgi.parse_qs(str)
+
+ts = int(data['ts'][0])
+user = data['user'][0]
+signature = base64.urlsafe_b64decode(data['signature'][0])
+
+data = '%d:%s' % (ts, user)
+
+algo = 'sha256'
+
+digest = M2Crypto.EVP.MessageDigest(algo)
+digest.update(data)
+digval = digest.final()
+
+key = M2Crypto.RSA.load_pub_key('rsa.pub')
+
+try:
+ if key.verify(digval, signature, algo = algo):
+ print 'OKAY'
+ else:
+ print 'FAIL'
+except:
+ print 'FAIL (hard)'
+