#!/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, urllib
from accountservice import accountservice
import ticket
tgt_sites = ['wiki']
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 LoginError(Exception):
pass
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 = {})
def login_perform(s, username, password):
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:
raise LoginError('Login incorrect')
except ldap.LDAPError, e:
raise LoginError('Login incorrect')
# e.message['info']
return (l, dn)
@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'})
try: l, dn = s.login_perform(username, password)
except LoginError, e:
return render(errors = {'password': str(e)})
return s.selectpage(l, dn)
def selectpage(s, l, dn, message = None):
user = ldapobj(l.search_s(dn, ldap.SCOPE_BASE, '(objectclass=*)', []))
tgts = {}
for site in tgt_sites:
tgts[site] = urllib.urlencode(ticket.tgt_create(site, user['cn']))
return render('select.html', user = user, tgts = tgts, message = message)
## x = ''
# for r in data:
# if r[0] != dn:
# continue
## x += '
%s |
\n' % (r[0])
# for k, v in r[1].iteritems():
# for value in v:
# details.append([k, value])
## x += '%s | %s |
\n' % (k, "
".join(v))
def newpass_validate(s, username, password, password2, expectstate):
errors = {}
if username == None or username == '':
errors['username'] = 'please specify an user name'
elif accountservice.name_valid(username) != expectstate:
errors['username'] = 'username invalid'
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 errors
return None
@expose('create.html')
def create(s, username = None, password = None, password2 = None):
if cherrypy.request.method.upper() == 'GET':
return render(errors = {}, username = '')
errors = s.newpass_validate(username, password, password2, 'valid')
if errors is not None:
return render(errors = errors, username = username)
accountservice.name_create(username, password)
if os.fork() == 0:
accountservice.kprop()
os._exit(0)
return s.login(username, password)
@expose('pwchange.html')
def pwchange(s, username = None, oldpassword = None, password = None, password2 = None):
if cherrypy.request.method.upper() == 'GET':
return render(errors = {}, username = username)
errors = s.newpass_validate(username, password, password2, 'exists')
if errors is not None:
return render(errors = errors, username = username)
try: l, dn = s.login_perform(username, oldpassword)
except LoginError, e:
return render(errors = {'oldpassword': str(e)}, username = username)
import kerberos
try:
assert kerberos.changePassword(username + '@SUBLAB.ORG', oldpassword, password) == True
except kerberos.PwdChangeError, e:
return render(errors = {'password2': str(e[0])}, username = username)
if os.fork() == 0:
accountservice.kprop()
os._exit(0)
return s.selectpage(l, dn,
'''Your password has been changed --
please note that it may take a few minutes for Kerberos to update
its three heads. At any moment, either your old password or your new
password will work.''')
@cherrypy.expose
def kill(s):
import sys
sys.exit(0)
config = {
'global': {
'server.socket_port': 8080,
'server.socket_host': '0.0.0.0',
# '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':
import tmpl
tmpl.myurl = lambda x: x
cherrypy.quickstart(SubdapSite(), "/", config)
else:
cherrypy.config.update({'environment': 'embedded'})
application = cherrypy.Application(SubdapSite(), script_name = None, config = config)