LinuxTAG was interesting.. I got a better grasp of the Linux and Open Source crowd in Berlin (and in Germany in general). I think my geek-side will be satisfied there. I've decided to check out Sugar-on-a-stick after chatting to them there (and pointed one of the developers in the direction of Numptyphysics, which just may appear as a Sugar package at some point). I'm looking forward to settling down in a proper apartment again, and setting up a media center for myself - A nice ATOM dual core + NVidia (ION) server combination (TEO-X had one there) should provide all the power that I need (with lower power consumption) for XBMC & a retro (& not so retro) gaming setup. :-)
Also interesting was Büro 2.0, which involves a shared workspace and services for Open Source companies and freelancers in Berlin. If I decide to be a freelancer there, this might be an option.
— by Robert Thomson, created 30th Jun, 2009, last modified 7th Jul, 2009 | Tags: Tech, World
This Italian weather is killing us. We're about to step out the door and head to the mild temperature in Berlin for a week. We're taking our big suitcases, and we'll leave them there, because we will be travelling around Italy in August and we can't take so much with us. And I'm taking my laptop, since I want to work from Berlin. I plan to visit LinuxTAG during the week too.
— by Robert Thomson, created 20th Jun, 2009, last modified 20th Jun, 2009 | Tags: World
Announcing Seantis Questionnaire, a Django Questionnaire/Survey application that I developed for Seantis GmbH in Switzerland.
Features
- Multiple Questionnaires, multiple pages (QuestionSets) per questionnaire, multiple runs of same questionnaire per subject (eg. for annual surveys)
- Multilingual using the django transmeta application.
- Questions (ie. 6) & SubQuestions (ie. 6a), with appropriate layout tweaks.
- Email Invitations (direct URL to Questionnaire)
- Does _not_ require Django’s authentication system to answer questionnaires
- CSV export of answers.
- Complex dependencies utilising a full Boolean Expression Parser, and Javascript support. eg. (6a=yes and 6b=yes) OR 5=no
- Built-in Question types including:
- Open (single line), Open (text area)
- Yes/No, Yes/No/Dont Know, Yes/No/Comment
- Single Choice, Single Choice + Freeform Option
- Multiple Choice, Multiple Choice + Freeform Option(s)
- Number Range
- Time Period
- Easily extensible question types (Question Processor + Template, and Answer Processor)
- Django Signals on completion of QuestionSets and Questionnaires
- Javascript to hide irrelevant questions (works fine without Javascript too) and disable irrelevant form fields.
Talk to Seantis GmbH about custom development.
Download Seantis Questionnaire from GitHub. It is released under the same license as Django itself.
— by Robert Thomson, created 15th Jun, 2009, last modified 18th Jun, 2009 | Tags: Tech
Zope 3's Pluggable Authentication Utility doesn't automatically store a cookie saying that you're logged in if you've been authenticated once. Instead, it uses credentials and authentication plugins. For each credentials plugin, it passes the credential's output to each of the authentication plugins, and if any succeed, then it returns a Principal. It sounds logical enough, except that it does that every time, instead of storing a variable in my session stating that I'm already authenticated. Since I'll be authenticating against an external system, I didn't like the idea of checking the password on every request.. It wasn't immediately obvious to me how I could simply achieve this within the Pluggable Authentication framework, and I wasn't sure I needed the power of it anyway, so I decided to create my own IAuthentication implementation, and forget about the PAU altogether.
What it does
This implementation stores all the IPrincipal information (id, name, and description) as a signed cookie in the user's browser. By default, the session will expire after 6 hours of non-use, and the cookie's timestamp will be updated every 5 minutes with a new timestamp.
The Code
The signed string code is located in securestring.py, and the authentication code in auth.py. You also need to implement the authentication in your login form, and add it as a local utility to your application/site.
securestring.py
# coding: utf-8
import md5, random
SECRET='SomeRandomStringThatYouShouldNotShare' # CHANGE THIS
def make_sstring(question, string, r = None):
if r is None:
r = ''.join([random.choice('1234567890abcdef') for x in '12345678'])
m = md5.md5(SECRET + question + ":" + string + r)
return "%s|%s|%s" % (string, r, m.hexdigest())
def get_sstring(question, securestring):
string, r, md5 = securestring.split('|',2)
if make_sstring(question, string, r) == securestring:
return string
return False
auth.py
# coding: utf-8
from zope.app.security.interfaces import IAuthentication, IUnauthenticatedPrincipal, PrincipalLookupError, IPrincipal, ILogout
from zope import interface, schema, security
from securestring import make_sstring, get_sstring
from zope.app.component import hooks
from zope.traversing.browser.absoluteurl import absoluteURL
import time
from urllib import urlencode
class Principal(object):
interface.implements(IPrincipal)
def __init__(self, id, title, description):
self.id = id
self.title = title
self.description = description
def __str__(self):
return "<Principal: %s>" % self.title
def make_authenticated(request, principal):
id = principal.id
title = (principal.title or '').replace("::","..")
description = (principal.description or '').replace("::","..")
tm = int(time.time())
sstring = "%d::%s::%s::%s" % (tm, id, title, description)
sstring = make_sstring('z3c_sstring_login', sstring)
request.response.setCookie('z3c_sstring_login', sstring, path="/")
return principal
class SStringAuthenticator(object):
interface.implements(IAuthentication, ILogout)
loginpagename = 'login'
timeout_in_seconds = 60*60*6 # 6 hours
update_timeout = 60*5 # how often to update the cookie
def logout(self, request):
request.response.expireCookie('z3c_sstring_login', path="/")
def authenticate(self, request):
sstring = request.cookies.get('z3c_sstring_login', None)
if sstring is None:
return None
sstring = get_sstring('z3c_sstring_login', sstring)
if not sstring:
return None
try:
tm, id, title, description = sstring.split('::',3)
tm = int(tm)
now = int(time.time())
if (now - tm) < self.timeout_in_seconds:
principal = Principal(id, title, description)
if (now-tm) > self.update_timeout:
make_authenticated(request, principal)
return principal
except:
pass
def unauthenticatedPrincipal(self):
# not really sure what to do here, but it doesnt seem to hurt
return None
def unauthorized(self, id, request):
site = hooks.getSite()
stack = request.getTraversalStack()
stack.reverse()
query = request.get('QUERY_STRING')
camefrom = '/'.join([request.getURL(path_only=True)] + stack)
if query:
camefrom = camefrom + '?' + query
url = '%s/@@%s?%s' % (absoluteURL(site, request),
self.loginpagename,
urlencode({'camefrom': camefrom}))
request.response.redirect(url)
def getPrincipal(self, id):
principal = Principal(id, id, id)
interface.directlyProvides(principal, IUnauthenticatedPrincipal)
return principal
Install it as a local utility
In your application, you can add it as a local utility. Since I'm using Grok, I'll give a Grok example:
class MyApp(grok.Application)
grok.local_utility(SStringAuthenticator, provides=IAuthentication)
You could of course also provide a setup function, to modify the string.
Local Utilities will only appear on NEW objects, so your existing applications/sites won't make use of it.
Authenticate from your login form
I wasn't sure the best way to do the actual authentication here, so I thought I'd just leave it up to the login form. If successful, the login form should just call auth.make_authenticated(request, principal) where Principal is an instance of an IPrincipal (you can use auth.Principal if you like, but there's no doubt a better way).
The Alternative using Pluggable Auth
I realised afterwards that it would be possible to do the same thing within the pluggable auth framework. You could do it almost exactly the same way, except that the credentials plugin should just return the cookie if it's set (credentials just returns a dict, so you're not limited to just a login/password), and the authentication plugin can just check if it's valid. The credentials plugin can still redirect unauthorized users to a login page. http://grok.zope.org/documentation/how-to/authentication-with-grok should be able to give you an idea about how to implement Pluggable Authentication.
— by Robert Thomson, created 9th Jun, 2009, last modified 18th Jun, 2009 | Tags: Tech