]> jfr.im git - irc/quakenet/qwebirc.git/blob - qwebirc/engines/authgateengine.py
Add a much better QuakeNet login system.
[irc/quakenet/qwebirc.git] / qwebirc / engines / authgateengine.py
1 from twisted.web import resource, server, static
2 import config, urlparse, urllib, hashlib, re
3 import qwebirc.util.rijndael, qwebirc.util.ciphers
4 import qwebirc.util
5 import qwebirc.util.qjson as json
6
7 authgate = config.AUTHGATEPROVIDER.twisted
8 BLOCK_SIZE = 128/8
9
10 class AuthgateEngine(resource.Resource):
11 isLeaf = True
12
13 def __init__(self, prefix):
14 self.__prefix = prefix
15 self.__hit = qwebirc.util.HitCounter()
16
17 def deleteCookie(self, request, key):
18 request.addCookie(key, "", path="/", expires="Sat, 29 Jun 1996 01:44:48 GMT")
19
20 def render_GET(self, request):
21 if request.args.get("logout"):
22 self.deleteCookie(request, "user")
23
24 a = authgate(request, config.AUTHGATEDOMAIN)
25 try:
26 ticket = a.login_required(accepting=lambda x: True)
27 except a.redirect_exception, e:
28 pass
29 else:
30 # only used for informational purposes, the backend stores this seperately
31 # so if the user changes it just their front end will be messed up!
32 request.addCookie("user", ticket.username, path="/")
33
34 qt = ticket.get("qticket")
35 if not qt is None:
36 getSessionData(request)["qticket"] = decodeQTicket(qt)
37
38 self.__hit()
39 if request.getCookie("jslogin"):
40 self.deleteCookie(request, "jslogin")
41 return """<html><head><script>window.opener.__qwebircAuthCallback(%s);</script></head></html>""" % json.dumps(ticket.username)
42
43 location = request.getCookie("redirect")
44 if location is None:
45 location = "/"
46 else:
47 self.deleteCookie(request, "redirect")
48 _, _, path, params, query, _ = urlparse.urlparse(urllib.unquote(location))
49 location = urlparse.urlunparse(("", "", path, params, query, ""))
50
51 request.redirect(location)
52 request.finish()
53
54 return server.NOT_DONE_YET
55
56 @property
57 def adminEngine(self):
58 return dict(Logins=((self.__hit,),))
59
60 def decodeQTicket(qticket, p=re.compile("\x00*$"), cipher=qwebirc.util.rijndael.rijndael(hashlib.sha256(config.QTICKETKEY).digest()[:16])):
61 def decrypt(data):
62 l = len(data)
63 if l < BLOCK_SIZE * 2 or l % BLOCK_SIZE != 0:
64 raise Exception("Bad qticket.")
65
66 iv, data = data[:16], data[16:]
67 cbc = qwebirc.util.ciphers.CBC(cipher, iv)
68
69 # technically this is a flawed padding algorithm as it allows chopping at BLOCK_SIZE, we don't
70 # care about that though!
71 b = range(0, l-BLOCK_SIZE, BLOCK_SIZE)
72 for i, v in enumerate(b):
73 q = cbc.decrypt(data[v:v+BLOCK_SIZE])
74 if i == len(b) - 1:
75 yield re.sub(p, "", q)
76 else:
77 yield q
78 return "".join(decrypt(qticket))
79
80 def getSessionData(request):
81 return authgate.get_session_data(request)
82
83 def login_optional(request):
84 return authgate(request, config.AUTHGATEDOMAIN).login_optional()
85