Everything Tastes Better With Chilli

Well, it does. 

Per-User Database Authentication in Django

A colleague was recently talking about the need for a data entry interface to a database, and I glibly said "let me see how easy it is to put a Django-admin front-end on it!".  It turned out to be some-what less easy than expected; this post documents my solution to one of the less-easy aspects of it (there were others, but those were mostly to do with concerns of scale and better-documented issues surrounding legacy databases).  It is still quite an ugly solution, and please see the end for some fairly important caveats.  I'm documenting it here though because I did find other people with the same problem, but have yet to come across a solution.

The problem is this: Django assumes that in general it will control the database, including creating the schema and implementing any logic.  This particular database made the opposite assumption; front-ends were merely conduits, and any verification etc would be handled internally.  In this case that wasn't much more complicated than updating fields such as "added by" via triggers, but the principles apply across a range of scenarios.  Here's a random example of the Django way.

So, to play nicely with the database we'd ideally like each connection to be made using the credentials of the user using the application, and Django really isn't built for this.  One reason is that django.db.connection is a module-constant initialised with the database settings from settings.py, but if you are willing to swallow your pride we can also exploit this to our advantage.

The first step is write an authentication back-end so the user will be authenticated using their database credentials:

(I also changed the base admin template so it doesn't offer the "change password" option).  This is fairly standard; I'm using Oracle, but obviously any database can be made to work similarly.  Note that we still need the default database account in settings.py, and this will be used to create the new user in the django.contrib.auth tables for example.

Step two is, err, to save the password in the session (I told you I felt dirty), so we can create a database connection using it each request.  This needs to happen in the login method, where you have access to both the session and the username/password.

Step three is to convert django.db.connection to use these credentials instead of the defaults from settings.py; I did this using a custom middleware:

At this point we're almost done, but a problem still remains: I'm using Oracle where each user has their own private schema, and the application is expecting to find a bunch of tables which don't exist in the user's schema.  We could get around this using synonyms for example, but instead I used a signal that is fired each time a connection is created (there seems to be precious little information or examples about this signal either) in order to change the schema the connection is using:

This needs to be executed each time, so I put it in models.py.

So, all in all, a fairly minimal solution, but...

Here be Dragons!  As I mentioned, there are some pretty important caveats to be aware of if you ever use this (I accept no responsibility for any breakage, break-ins, yadda yadda yadda).  Most importantly, you are storing the user's password in plain-text for the duration of the session.  For our purposes this is fine; it's an internal application where security isn't exactly critical.  Your mileage may vary, significantly.

Secondly and perhaps more importantly, the various other Django applications still need access to their tables, but now you are connecting as a different user, so each user using the application will also need permission on these tables.  This is the bit that turns my stomach the most!

Lastly, there may well be consequences, race conditions, etc in manipulating django.db.connection that I have not come across yet.

Please let me know if there is an easier/cleaner way!  (I still feel dirty)

Loading mentions Retweet
Filed under  //   hacking  

Comments [0]

Token Sydney Harbour panorama

Had to be done, cliched though it is. This was taken from Ball's Head point at dusk. That fucking monstrosity in front of the bridge is the Blues Point tower, by Harry Seidler.

Loading mentions Retweet

Comments [0]

Spit to Manly Walk, Sydney

A much easier walk around the headlands, winding up in Manly. With panoramas, this time.

         
Click here to download:
Spit_to_Manly_Walk_Sydney.zip (1575 KB)

Loading mentions Retweet

Comments [0]

Garie Beach, Royal National Park, NSW

I spent a week in Sydney over the new year. There was walking. This particular lot is from around the coastal track around Garie Beach, in the Royal National Park. Fantastic weather (rain had been predicted!) as you can see, and for those of you expecting it -- yes, I attempted a panorama, but it was so a-kilter that I haven't even bothered stitching it, sorry.

                   
Click here to download:
Garie_Beach_Royal_National_Par.zip (3425 KB)

Loading mentions Retweet

Comments [0]

Organ Pipes Track

Nothing special; I went for a quick walk to break in some new shoes. These are the "Organ Pipes", the cliff at the top of Mount Wellington you can see from Hobart.

       
Click here to download:
Organ_Pipes_Track.zip (1664 KB)

Loading mentions Retweet

Comments [1]

Tarn Shelf panoramas

A sample of the views available less than an hour into the walk!

   
Click here to download:
Tarn_Shelf_panoramas.zip (442 KB)

Loading mentions Retweet

Comments [0]

Tarn Shelf, Mt Field National Park

A shorter-than-intended (left it a bit late leaving, and it's a long way from home!) walk at Mt Field. The first shot is a token one from Russell Falls, which is a paved 10 minute walk from the car park.

The main walk is a loop starting from Lake Dobson, which is a 20 minute drive above Russell Falls. It climbs up briefly through a pandani grove before hitting alpine territory, with some spectacular vistas (wait for the panoramas to be posted separately). Didn't get too far into it unfortunately; next time when I'm more organised perhaps. Just (it was scurrying under the board-walk) caught an echnidna on the return trip.

                 
Click here to download:
Tarn_Shelf_Mt_Field_National_P.zip (4250 KB)

Loading mentions Retweet

Comments [0]

Echo Point, Blue Mountains

   
Click here to download:
Echo_Point_Blue_Mountains.zip (513 KB)

Loading mentions Retweet

Comments [0]

Collinsvale Panoramas

Sent separately to be resized to a larger width.

       
Click here to download:
Collinsvale_Panoramas.zip (1331 KB)

Loading mentions Retweet

Comments [0]

Photos, take 2, part 1

Apologies; there was a hiccup in posterous last it seems: I sent one email, with attachments, and got two posts, identical apart from one lacking any image gallery. So I deleted the gallery-less one, and... wound up with one post, with no gallery. (and no undo). The original email in gmail doesn't have the attachments either, perhaps because of the volume. so, anyway, here's the first lot of photos, again. Fingers crossed.

                 
Click here to download:
Photos_take_2_part_1.zip (4010 KB)

Loading mentions Retweet

Comments [0]