views:

797

answers:

2

I have a Django app that gets it's data completely from an external source (queried via HTTP). That is, I don't have the option for a local database. Session data is stored in the cache (on my development server I use a SQLite database, so that is no error source). I'm using bleeding edge Django 1.1svn.

Enter the problem: I want to use Django's own authentication system for the users.

It seems quite simple to write my own Authentication Backend, but always just under the condition that you have a local database where to save the users. Without database my main problem is persistence.

I tried it with the following (assume that datasource.get() is a function that returns some kind of dict):

class ModelBackend (object):
    """Login backend."""

    def authenticate (self, username=None, password=None):
        """Check, if a given user/password combination is valid"""

        data = datasource.get ('login', username, password)
        if data and data['ok']:
            return MyUser (username=username)
        else:
            raise TypeError
            return None

    def get_user (self, username):
        """get data about a specific user"""

        try:
          data = datasource.get ('userdata', username)
          if data and data['ok']:
              return data.user
        except:
            pass
        return None


class MyUser (User):
    """Django user who isn't saved in DB"""

    def save (self):
        return None

But the intentionally missing save() method on MyUser seems to break the session storage of a login.

How should MyUser look like without a local database?

+1  A: 

grepping the source showed, that the only place user.save() is actually called (except for user creation and password management code, which you don't need to use at all) is django.contrib.auth.login(), to update user.last_login value.

# TODO: It would be nice to support different login methods, like signed cookies.
user.last_login = datetime.datetime.now()
user.save()

If you don't want user data to rest in DB, try adding dummy save() method. If I'm right, it should work.

def save(self, *args, **kwargs):
    pass

Of course, because you have no persistence at all, you should consider caching datasource.get results, otherwise in worst case you may end up querying data again and again on every single logged in user's hit.

drdaeman
+3  A: 

OK, it's much more complicated than I thought. First, start with http://docs.djangoproject.com/en/dev/howto/auth-remote-user/, but you'll need to extend it with your own backend and user.

class MyRemoteUserBackend (RemoteUserBackend):
    # Create a User object if not already in the database?
    create_unknown_user = False

    def get_user (self, user_id):
        user = somehow_create_an_instance_of (MyUser, user_id)
        return user

    def authenticate (self, **credentials):
        check_credentials ()
        user = somehow_create_an_instance_of (MyUser, credentials)
        return user

Then the user:

class MyUser (User):

    def save (self):
        """saving to DB disabled"""
        pass

    objects = None # we cannot really use this w/o local DB

    username = ""  # and all the other properties likewise.
                   # They're defined as model.CharField or similar,
                   # and we can't allow that

    def get_group_permissions (self):
        """If you don't make your own permissions module,
           the default also will use the DB. Throw it away"""
        return [] # likewise with the other permission defs

    def get_and_delete_messages (self):
        """Messages are stored in the DB. Darn!"""
        return []

Phew! Django really isn't designed for usage without a database...

Boldewyn