Showing posts with label wsgi. Show all posts
Showing posts with label wsgi. Show all posts

Friday, May 15, 2009

Rshrtnr: The private URI shortener

Over the past couple of days, I've implemented a private URI shortener service for myself, which I have named "Rshrtnr". The derivation of the name is left as an exercise for the reader.

My main motivation for writing it was a criticism of public URI shortening services that I have been seeing in blogs for a long time: if the service has some downtime or suddenly disappears, all of the links that you have created with it are useless. With my approach, I regain some control of where my shortened links point to, and if the service has downtime and/or disappears, I have more options for restoring it.

The code itself is written in Python. SLOCCount says that the core module runs at around 100 SLOC. Most of my time, however, was taken up by working around problems relating to my webhost's Python installation. The supported Python version is 2.4.x, which is ridiculously old (for reference, Gentoo was the last major Linux distribution to switch from Python 2.4 to 2.5, around July 2008). Additionally, for some reason, if I attempt to change the sys.path variable (i.e., the "include path") to use locally installed modules (I am on a shared host), the entire script breaks with zero logged messages anywhere. It runs fine via the command line, but in FastCGI mode, the strangeness occurs.

The two third-party modules that I used were Paste and mysql-python. I store the URIs and their associated aliases in a simple SQL table, and I use Paste for various WSGI/HTTP-related utilities. I "manually" handle routing via parsing the PATH_INFO environment variable.

There are two ways to specify an alias: either explicitly send a custom one as a query parameter with the URI, or let the app make a random one for you. With the latter behavior, it hashes the URI to generate an eight character "unique" alias. Since there are (in theory) 64^8 possibilities, I don't think I'll run out of aliases any time soon, especially since custom aliases can be anywhere from 1 to 15 characters long.

In my opinion, the most interesting feature is that adding URIs requires one to send an OpenPGP-encoded query string, which needs a public key recognized by the app for the operation to succeed. To write this, I simply parsed the output from sending the OpenPGP message to the gpg binary.

Finally, mod_rewrite magic is used to prettify the shortened URIs. Nothing too exciting about that part.

I had thought about hosting a version of Rshrtnr on Google App Engine, but a key component is missing - OpenPGP support.

If anyone wants me to release it, please comment below. There's currently a bunch of webhost-specific things that I would need to abstract out before I release the code to the general public, and unless someone gives me a very good reason, it will be licensed under the AGPL version 3.

Sunday, November 23, 2008

HOWTO Run an OpenID-authenticated WSGI Application (with AuthKit)

According to Blogger, this is going to be post #100. I have no idea if that counts the various dead drafts in my queue or not.

Anyway, if you've been following my Twitter stream, you'll know that I've been playing with Pylons, and by extension, WSGI. One of the things that I'm interested in is OpenID-only authentication, mostly because I hate having to create new account names/passwords everywhere, and I'm too lazy/paranoid to use one of those password management extensions. After several attempts, here is a short Python script which runs a sample web app that requires OpenID authentication for the /private path (via the AuthKit middleware). The OpenID URL that was used to sign in is stored in the environ['REMOTE_USER'] variable. It was tested with AuthKit 0.4.2, Beaker 1.0.3, and Paste 1.7.2.


#!/usr/bin/env python
#
# Copyright (C) 2008  Mark Lee
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# For a copy of the GNU General Public License, see
# <http://www.gnu.org/licenses/>.

import os
from beaker.middleware import SessionMiddleware
from paste.auth.auth_tkt import AuthTKTMiddleware
from authkit.authenticate import middleware, sample_app
from paste.httpserver import serve

app = middleware(sample_app,
                 enable=True,
                 setup_method='openid',
                 openid_store_type='file',
                 openid_store_config=os.getcwd(),
                 openid_path_signedin='/private')

app = AuthTKTMiddleware(SessionMiddleware(app),
                        'some auth ticket secret');
serve(app) # opens a socket at localhost:8080