--- a/pvl/login/server.py Tue Jan 14 21:02:54 2014 +0200
+++ b/pvl/login/server.py Tue Jan 14 21:03:30 2014 +0200
@@ -44,6 +44,8 @@
return pvl.web.response.redirect(self.url(*url, **params))
pubtkt = None
+ invalid_pubtkt = None
+ valid_pubtkt = None
def init (self) :
self.alerts = []
@@ -80,13 +82,22 @@
except pubtkt.ParseError as ex :
self.alert('danger', ex, icon='compare')
+ except pubtkt.VerifyError as ex :
+ self.alert('danger', ex, icon='warning-sign')
+
+ self.invalid_pubtkt = ex.pubtkt
+
except pubtkt.ExpiredError as ex :
- self.pubtkt = ex.pubtkt
self.alert('warning', ex, icon='clock')
+
+ # store it anyways, but not as valid
+ self.pubtkt = ex.pubtkt
- except pubtkt.VerifyError as ex :
- self.pubtkt = ex.pubtkt
- self.alert('danger', ex, icon='warning-sign')
+ else :
+ # it's a parsed, verified and valid pubtkt
+ self.valid_pubtkt = self.pubtkt
+
+ return self.pubtkt
def process_back (self) :
self.server = None
@@ -106,8 +117,12 @@
else :
self.alert('info', "Using SSL for application URL", icon='lock')
scheme = self.app.login_scheme
-
- self.server = self.app.check_server(url.hostname)
+
+ if url.hostname :
+ self.server = self.app.check_server(url.hostname)
+ else :
+ self.server = self.app.login_server
+
self.back = urlparse.urlunparse((scheme, self.server, url.path, url.params, url.query, url.fragment))
def render_alerts (self) :
@@ -454,6 +469,8 @@
def render (self) :
return html.div(class_='container')(
html.form(action=self.url(), method='post')(
+ self.render_alerts(),
+
html.fieldset(
html.legend("Logout {pubtkt.uid}".format(pubtkt=self.pubtkt)),
@@ -464,11 +481,61 @@
)
)
+class SSL (Handler) :
+ TITLE = "SSL"
+
+ OUT = 'tmp/spkac'
+
+ def process_spkac (self, spkac) :
+ log.info("SPKAC: %s", spkac)
+
+ cert = self.app.sign_ssl(self.pubtkt, spkac)
+
+ file = self.response_file(open(cert))
+
+ log.info("Returning client cert: %s", cert)
+
+ return pvl.web.Response(file, mimetype='application/x-x509-user-cert')
+
+ def process (self) :
+ if not self.process_cookie() :
+ return self.redirect(Login, back=self.url())
+
+ self.sslcert_dn = self.request.headers.get('X-Forwarded-SSL-DN')
+
+ if self.request.method == 'POST' :
+ spkac = self.request.form.get('spkac')
+
+ if spkac:
+ return self.process_spkac(spkac)
+
+ def render (self) :
+ if self.sslcert_dn :
+ self.alert('info', "You are currently using a client SSL cert: {self.sslcert_dn}".format(self=self))
+
+ return html.div(class_='container')(
+ html.form(action=self.url(), method='post')(
+ self.render_alerts(),
+ html.fieldset(
+ html.legend("SSL Login"),
+
+ html.keygen(name='spkac', challenge='foo', keytype='RSA'),
+
+ html.button(type='submit', class_='btn')(
+ "Generate Certificate"
+ ),
+ )
+ )
+ )
+
class LoginApplication (pvl.web.Application) :
URLS = urls.Map((
urls.rule('/', Index),
urls.rule('/login', Login),
urls.rule('/logout', Logout),
+
+ # proto
+ urls.rule('/ssl', SSL),
))
PUBLIC_KEY = 'etc/login/public.pem'
@@ -485,10 +552,11 @@
cookie_secure = True
cookie_httponly = True
- def __init__ (self, auth, public_key=PUBLIC_KEY, private_key=PRIVATE_KEY, **opts) :
+ def __init__ (self, auth, ssl=None, public_key=PUBLIC_KEY, private_key=PRIVATE_KEY, **opts) :
super(LoginApplication, self).__init__(**opts)
self._auth = auth
+ self._ssl = ssl
self.server_keys = pubtkt.ServerKeys.config(
public_key = public_key,
private_key = private_key,
@@ -555,3 +623,15 @@
tokens = list(self._auth.access(auth)),
udata = self._auth.userdata(auth),
)
+
+ def sign_ssl (self, pubtkt, spkac) :
+ """
+ Generate a SSL client cert for the given user.
+ """
+
+ if not self._ssl :
+ raise pvl.login.ssl.Error("No ssl CA available for signing")
+
+ return self._ssl.sign_user(pubtkt.uid, spkac,
+ userinfo = pubtkt.udata,
+ )