Building Blocks as simple as possible, but no simpler

21Sep/088

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.

12Sep/080

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.

Filed under: django, python No Comments
5Jan/080

Django Authentication from Flex

This method isn't very good, and I would recommend using this instead. This will still work, but I believe it is better to maintain a proper browser session and not do all this monkey work for authentication.

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" )
	}
}