summaryrefslogtreecommitdiff
path: root/index.py
blob: 8aa1c35a90996401564d37058104d8f570aef80d (plain)
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
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
#!/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 += '<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))

	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)
		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.message)}, username = username)

		return s.selectpage(l, dn,
			'''Your password has been changed --
			please note that it may take up to 10 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': '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)