Django Authorization from Flex/AIR via PyAMF

Django views serve nicely as service end-points for Flex applications. Here are some notes on maintaining authenticated sessions between a Flex/Air/Flash application and your Django backend.
gateway.py
1 2 3 4 5 6 7 8 | from pyamf.remoting.gateway.django import DjangoGateway import myproject.myapp.views as views gw = DjangoGateway({ 'login' : views.login_user, 'logout' : views.logout_user, }) |
views.py
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 | import pyamf from django.contrib import auth from django.contrib.auth import authenticate, login, logout from django.contrib.auth.decorators import login_required from django.contrib.auth.models import User try: pyamf.register_class( User, 'django.contrib.auth.models.User') except ValueError: print "Classes already registered" def logout_user(http_request): logout(http_request) def login_user(http_request, username, password): user = authenticate(username=username, password=password) if user is not None: login(http_request, user) return user return None @login_required def registered_user_protected_function(http_request): return "You are a registered user." @login_required def staff_protected_function(http_request): if http_request.user.is_staff != True: return None return "You are staff." |
from flex
1 2 3 4 | var netConnection:NetConnection = new NetConnection(); netConnection.connect("http://mysite.com/gateway"); var responder:Responder = new Responder(loginResult, handleFault); netConnection.call("login", responder, "username", "password") |
The http_request carries a reference to the currently authenticated user throughout the session. This works for web based Flex application as well as AIR applications on the desktop. Note that I am using a try/except on the pyamf class registration calls. Because this is session based, the classes only need to be registered once. Without the trap, it throws a TypeError letting you know the registration has already taken place.
Django User Authentication Documentation
All of the various things you can do with authentication in Django. It is, of course, based mostly on the use of the very nice Django HTML template system. While those bits aren't handy to the likes of us, it is a good read either way.
pyAMF ByteArray example
This example shows the basic structure for setting up Django/Flex communication. It doesn't cover authentication, but covers a good bit of territory with examples in Flash and Flex.
Dynamic upload paths in Django
I'm refactoring my VE:Session application a bit to the recently released Django 1.0. They have made some outstanding progress with the project overall, but there are enough things that are radically different to cause me to lose sleep searching for answers to small problems. I'd simply overlooked their NewForms branch when I was building the application, and now I am paying for it by standing well behind the curve. It is fun to learn new things though, so that is the upside I suppose.
Anyway, back to the refactoring. In addition to updating the project to the current Django trunk, I want to unify the application under Django. I am currently using SlideshowPro for my image CMS functionality. It is a fine product, but it obsfusicates the application and certainly makes it harder to deploy. This is compounded by the fact that I am using an older version of SSP. Django has some out of the box options for filing your uploads in a data structure. I really need something more customizable for the application though, and I was feverishly searching for a solution. The previous examples were complex hacks to get the job done. Which is fine, but they are now broken with the update to 1.0. D'oh. As luck would have it they are not neccesary any longer, and the same functionality can be added with a few simple lines that Scott Barnham was kind enough to demonstrate:
1 2 3 4 5 6 7 | from django.db import models def get_image_path(instance, filename): return 'photos/%s/%s' % (instance.id, filename) class Photo(models.Model): image = models.ImageField(upload_to=get_image_path) |
Awesome.
I've been knee deep in Java for the past few months, so it is really nice to get back into Python for a bit. With Django reaching a comfortable level of maturity, I see many more sleepless nights in my future.
Django Authentication from Flex
Django 'salts' passwords for added protection. They use SHA1 with a random key applied to further obfuscate the resulting hash. This is a good thing, but it threw me off for a bit. I don't want to send the unencrypted passwords over the wire, so I needed to grab the hash string from Django, break it apart, and reassemble it inside of flex. This did the trick:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 | private function onGetUsersComplete( re:ResultEvent ):void { var users:Array = re.result as Array; var userArray:Array = []; for ( var ii:int = 0; ii < users.length; ii++ ) { var user:UserVO = users[ii] as UserVO; userArray.push( user ); trace( users[ii].first_name ); } var isValidUser:Boolean = false; loginService.removeEventListener(ResultEvent.RESULT, onGetUsersComplete); for ( var i:int = 0; i < users.length; i++ ) { if ( users[i].username == this.username ) { currentUser = users[i] as UserVO; var salt:String = users[i].password.split('$')[1]; //django specific password scheme password = 'sha1$' + salt + '$' + SHA1.hash( salt + password ); loginService.verify_credentials( username, password ); loginService.addEventListener(ResultEvent.RESULT, onVerifyCredentialsResult, false, 0, true ); isValidUser = true; } } if ( !isValidUser ) { sendNotification( ApplicationFacade.LOGIN_FAILED, "Invalid Username" ) } } |















