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
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
|
#!/usr/bin/env python
# -*- coding: utf-8
#
# mumble-auth.py - Sample script to show the basics of using server controlled
# context menu entries for in-client registration tickets.
#
# Requirements:
# * python 2.5 or higher
# * cherrypy 3 or higher
# * python ice bindings
# * murmur + mumble rev 1530 or higher
import cherrypy
import Ice
from threading import Semaphore
import random, time
#
#--- Config stuff
#
baseurl = "http://localhost:8080/register?id=" # Baseurl for registrations
storage_time = 10*24*60*60 # Time in seconds a reg entry is guaranteed to be valid
proxy = "Meta:tcp -h 127.0.0.1 -p 6502"
group = "admin" # ACL group which has the right to send regurls
host = "0.0.0.0" # Interface to listen on
port = 8080 # Cherrypy port
slice = "Murmur.ice"
production = False # Set this to true to surpress tracebacks
# Runtime generate bindings
Ice.loadSlice(slice)
import Murmur
# Global vars
sema_ids = Semaphore()
ids = {} #Contains list(sid, user, time)
class MetaCallbackI(Murmur.MetaCallback):
def started(self, s, current=None):
serverR = Murmur.ServerCallbackPrx.uncheckedCast(adapter.addWithUUID(ServerCallbackI(server, current.adapter)))
s.addCallback(serverR)
def stopped(self, s, current=None): pass # Unused callback
class ServerCallbackI(Murmur.ServerCallback):
def __init__(self, server, adapter):
self.server = server
self.contextR=Murmur.ServerContextCallbackPrx.uncheckedCast(adapter.addWithUUID(ServerContextCallbackI(server)))
def userConnected(self, p, current=None):
if p.userid != 0: # SuperUser is always allowed
# Check if the user is in the right acl class and add the context menu
allowed = False
for acl in self.server.getACL(0)[1]:
if acl.name == group and p.userid in acl.members:
allowed = True
break
if not allowed:
return
self.server.addContextCallback(p.session,
"sendregurl",
"Send registration URL",
self.contextR, Murmur.ContextUser)
def userDisconnected(self, p, current=None): pass # Unused callbacks
def userStateChanged(self, p, current=None): pass
def channelCreated(self, c, current=None): pass
def channelRemoved(self, c, current=None): pass
def channelStateChanged(self, c, current=None): pass
class ServerContextCallbackI(Murmur.ServerContextCallback):
#--- Server message template strings
err_notallowed = "You are not allowed to send registration urls"
msg_regurl = "You've been allowed to register with this mumble server, " \
"please <a href='%(url)s'>click here to register</a>."
def __init__(self, server):
self.server = server
def contextAction(self, action, p, session, chanid, current=None):
if action == "sendregurl" and p:
if p.userid != 0:
# If it isn't SuperUser check if he is in the right acl group
allowed = False
for acl in self.server.getACL(0)[1]:
if acl.name == group and p.userid in acl.members:
allowed = True
break
if not allowed:
self.server.sendMessage(p, self.err_notallowed)
return
sema_ids.acquire()
try:
# Take the chance to cleanup old entries
todel = []
for key, reg in ids.iteritems():
if int(time.time()) - reg[2] > storage_time:
todel.append(key)
for key in todel:
del ids[key]
# Ok, now generate his registration id (64bit)
rid = None
while not rid or rid in ids:
rid = random.randrange(0, pow(2,64))
sid = self.server.id()
# Remember his username
try:
username = self.server.getState(session).name
except Murmur.InvalidSessionException:
username = ""
# Save everything
ids[str(rid)] = (sid, username, int(time.time()))
# Send reg url to the user
url = baseurl + str(rid)
self.server.sendMessage(session,
self.msg_regurl % {'url':url})
finally:
sema_ids.release()
class mumble_auth(object):
#--- Web template strings
template = """<?xml version="1.0" encoding="utf-8" ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
<head>
<title>%(title)s</title>
<style type="text/css">
label { display:block; font-size:smaller; }
fieldset { border:1px solid; width:250px; }
h1 { border-bottom:2px solid; }
h2 { border-bottom:2px solid red; color:red; }
</style>
</head>
<body>
<h1>%(title)s</h1>
%(body)s
</body>
</html>"""
err_id = """<h2>Invalid ID</h2><p>
The ID for your registration has already been used or is invalid.<br />
Please request a new registration link from a Mumble administrator.<br /></p>"""
err_username = """<h2>Invalid username</h2><p>
The username you chose contains characters which are not allowed.<br/>
Please only use normal characters without whitespaces.<br /></p>"""
err_username_existing = """<h2>Username already registered</h2><p>
The username you chose is already registered on this server.<br />
Please choose another one.<br /></p>"""
err_password = """<h2>Invalid password</h2><p>
The password you chose didn't match the criteria enforced by this server.<br/>
Your password should be at least 6 characters long.<br /></p>"""
err_password_mismatch = """<h2>Passwords do not match</h2><p>
The password you entered did not match the one in the confirmation box.<br/>
Make sure both boxes contain the same password.<br /></p>"""
snippet_retry = '<br/><a href="%(url)s">Click here to try again</a>'
body_index = """<p>This is the mumble-auth script, to be able to register yourself
an account please ask a admin on the mumble server.</p>"""
body_complete = """<h2>Success</h2><p>
You have been registered with the server successfully,<br />
please try to login to the server with your new login credentials!</p>"""
body_regform = """
<form action="doregister" method="post">
<fieldset>
<legend>Enter user information</legend>
<label for="username">Username</label>
<input id="username" type="text" name="username" value="%(username)s"
size="20" maxlength="40" /><br />
<label for="password">Password</label>
<input id="password" type="password" name="password" value=""
size="20" maxlength="40" /><br />
<label for="cpassword">Confirm password</label>
<input id="cpassword" type="password" name="cpassword" value=""
size="20" maxlength="40" /><br />
<input type="hidden" name="id" value="%(id)s" />
<input id="register" type="submit" value="Register" />
</fieldset>
</form>"""
def __init__(self, murmur):
self.murmur = murmur
def index(self):
title = "Mumble Auth - Index"
return self.template % {'title':title,
'body':self.body_index}
index.exposed = True
def register(self, id = None):
title = "Mumble Auth - Register";
if not id in ids:
body = self.err_id # Invalid ID
else:
# In case of a valid ID
body = self.body_regform % {'username':ids[id][1],
'id':id}
return self.template % {'title':title,
'body':body}
register.exposed = True
def doregister(self, id = None, username = None, password = None, cpassword = None):
title = "Mumble Auth - Register";
sema_ids.acquire()
try:
# Check if all parameters are ok
if not id in ids:
body = self.err_id # Invalid id
elif not username or " " in username: # Invalid username
body = self.err_username + \
self.snippet_retry % {'url':baseurl+id}
elif len(password) < 6: # Password criteria didn't match
body = self.err_password + \
self.snippet_retry % {'url':baseurl+id}
elif password != cpassword: # Password mismatch
body = self.err_password_mismatch + \
self.snippet_retry % {'url':baseurl+id}
else:
# Ok, try to register him
server = self.murmur.getServer(ids[id][0])
if (len(server.getRegisteredUsers(username))!=0):
body = self.err_username_existing + \
self.snippet_retry % {'url':baseurl+id}
else:
# Register user
info = {Murmur.UserInfo.UserName:username,
Murmur.UserInfo.UserPassword:password}
server.registerUser(info)
# Void registration id
del ids[id]
# Success
body = self.body_complete
finally:
sema_ids.release()
return self.template % {'title':title,
'body':body}
doregister.exposed = True
if __name__ == "__main__":
random.seed()
ice = Ice.initialize()
try:
meta = Murmur.MetaPrx.checkedCast(ice.stringToProxy(proxy))
adapter = ice.createObjectAdapterWithEndpoints("Callback.Client", "tcp -h 127.0.0.1")
metaR=Murmur.MetaCallbackPrx.uncheckedCast(adapter.addWithUUID(MetaCallbackI()))
adapter.activate()
meta.addCallback(metaR)
for server in meta.getBootedServers():
serverR=Murmur.ServerCallbackPrx.uncheckedCast(adapter.addWithUUID(ServerCallbackI(server, adapter)))
server.addCallback(serverR)
if production: cherrypy.config.update({'environment': 'production'})
cherrypy.server.socket_host = host
cherrypy.server.socket_port = port
cherrypy.quickstart(mumble_auth(meta))
finally:
ice.shutdown()
ice.waitForShutdown()
|