--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/pvl/login/server.py Mon Jan 13 01:49:34 2014 +0200
@@ -0,0 +1,193 @@
+# encoding: utf-8
+
+import datetime
+import werkzeug
+import werkzeug.urls
+
+import pvl.web
+import pvl.web.response
+
+from pvl.login import pubtkt
+from pvl.web import urls, html
+
+import logging; log = logging.getLogger('pvl.login.server')
+
+class Handler (pvl.web.Handler) :
+ # Bootstrap
+ DOCTYPE = 'html'
+ HTML_XMLNS = None
+ HTML_LANG = 'en'
+ CSS = (
+ '//netdna.bootstrapcdn.com/bootstrap/3.0.3/css/bootstrap.min.css',
+ )
+ JS = (
+ '//code.jquery.com/jquery.js',
+ '//netdna.bootstrapcdn.com/bootstrap/3.0.3/js/bootstrap.min.js',
+ )
+
+ def process_cookie (self) :
+ """
+ Reverse the urlencoding used for the cookie...
+ """
+
+ log.debug("cookies: %s", self.request.cookies)
+
+ cookie = self.request.cookies.get(self.app.cookie_name)
+
+ log.debug("cookie %s=%s", self.app.cookie_name, cookie)
+
+ if cookie :
+ cookie = werkzeug.urls.url_unquote(cookie)
+
+ log.debug("cookie decoded: %s", cookie)
+
+ if cookie :
+ return self.app.load(cookie)
+
+class Index (Handler) :
+ TITLE = u"Päivölä Network Login"
+
+ pubtkt = None
+ cookie_error = None
+
+ def process (self) :
+ try :
+ self.pubtkt = self.process_cookie()
+ except pubtkt.Error as ex :
+ self.cookie_error = ex
+
+ def render_info (self) :
+ if self.cookie_error :
+ return (
+ html.h2("Invalid cookie"),
+ html.p(self.cookie_error),
+ )
+ elif self.pubtkt :
+ return (
+ html.h2("Login: {pubtkt.uid}".format(pubtkt=self.pubtkt)),
+ )
+ else :
+ return (
+ html.a(href=self.url(Login), title="Login")(html.h2("No login")),
+ )
+
+ def render (self) :
+
+ return html.div(class_='container')(
+ self.render_info(),
+ )
+
+class Login (Handler) :
+ TITLE = "Login"
+
+ STYLE = """
+form#login {
+ max-width: 50%;
+ padding: 1em;
+ margin: 0 auto;
+}
+
+ """
+
+
+ auth_error = None
+
+ def process (self) :
+ if self.request.method == 'POST' :
+ back = self.app.login_server
+ username = self.request.form.get('username')
+ password = self.request.form.get('username')
+
+ if username and password :
+ # preprocess
+ username = username.strip().lower()
+
+ try :
+ pt = self.app.auth(username, password)
+
+ except pubtkt.Error as ex :
+ self.auth_errors = ex
+
+ else :
+ # browsers seem to be very particular about quoting ;'s in cookie values...
+ # this follows PHP's setcookie() encoding...
+ cookie = werkzeug.urls.url_quote(self.app.sign(pt))
+
+ # redirect with cookie
+ response = pvl.web.response.redirect(back)
+
+ response.set_cookie(self.app.cookie_name, cookie,
+ domain = self.app.cookie_domain,
+ secure = self.app.cookie_secure,
+ httponly = self.app.cookie_httponly,
+ )
+
+ return response
+
+ def render (self) :
+ return html.div(class_='container')(
+ html.form(action=self.request.path, method='POST', id='login')(
+ html.fieldset(
+ html.legend("Log in"),
+
+ html.div(class_='form-group')(
+ html.label(for_='username', class_='sr-only')("Username"),
+ html.input(name='username', type='text', class_='form-control', placeholder="username", required=True, autofocus=True),
+
+ html.label(for_='password', class_='sr-only')("Password"),
+ html.input(name='password', type='password', class_='form-control', placeholder="Password", required=True),
+ ),
+
+ html.button(type='submit', class_='btn btn-primary')("Login"),
+ )
+ )
+ )
+
+class LoginApplication (pvl.web.Application) :
+ URLS = urls.Map((
+ urls.rule('/', Index),
+ urls.rule('/login', Login),
+ ))
+
+ PUBLIC_KEY = 'etc/login/public.pem'
+ PRIVATE_KEY = 'etc/login/private.pem'
+
+ login_server = 'https://login.test.paivola.fi/'
+ login_expire = datetime.timedelta(hours=1)
+
+ cookie_name = 'auth_pubtkt'
+ cookie_domain = 'test.paivola.fi'
+ cookie_secure = True
+ cookie_httponly = True
+
+ def __init__ (self, public_key=PUBLIC_KEY, private_key=PRIVATE_KEY, **opts) :
+ super(LoginApplication, self).__init__(**opts)
+
+ self.server_keys = pubtkt.ServerKeys.config(
+ public_key = public_key,
+ private_key = private_key,
+ )
+
+ def load (self, cookie) :
+ """
+ Load a pubtkt from a cookie, and verify it.
+ """
+
+ return pubtkt.PubTkt.load(cookie, self.server_keys.public)
+
+ def auth (self, username, password) :
+ """
+ Perform authentication, returning a PubTkt, signed
+ """
+
+ return pubtkt.PubTkt.new(username,
+ expiry = self.login_expire,
+ )
+
+ def sign (self, pubtkt) :
+ """
+ Create a cookie by signing the given pubtkt.
+ """
+
+ return pubtkt.sign(self.server_keys.private)
+