Changes between Version 6 and Version 7 of MultipleAuthBackends


Ignore:
Timestamp:
02/27/06 16:05:24 (9 years ago)
Author:
jkocherhans
Comment:

added something closer to concrete code

Legend:

Unmodified
Added
Removed
Modified
  • MultipleAuthBackends

    v6 v7  
    1111Instead of hard-coding checks for a cookie, or a username/password etc. etc. let's let different callables just grab the credentials from the request. In fact, let's put a bunch of these plugins in a list, so if the first one fails, we can try another.
    1212
     13
    1314{{{
    1415#!python
    1516
    16 class UsernamePasswordPlugin:
    17     def extract_credentials(self, request):
    18         return {'username': request.POST['username'], 'password': request.POST['password']}
     17# in settings.py
     18CREDENTIALS_EXTRACTORS = (
     19    'django.contrib.auth.SessionCredentials',
     20    # etc, etc.
     21)
     22}}}
     23
     24{{{
     25#!python
     26
     27class SessionCredentials:
     28        def __init__(self):
     29                self.cred_key = '_credentials'
     30       
     31        def extract_credentials(self, request):
     32                # get the credentials from the request and save them to the session
     33                # if they exist
     34        login = request.POST.get('login', None)
     35                # we probably want to go ahead and enrypt the password here since
     36                # we're going to store it in the session.
     37                password = request.POST.get('password', None)
     38                if login and password:
     39                        credentials = {'login': login, 'password': password}
     40                        request.session[self.cred_key] = credentials
     41                else:
     42                        credentials = request.session.get(self.cred_key, None)
     43                return credentials
     44       
     45        def logout(self, request):
     46                del self.session[self.credentials_key]
    1947
    2048}}}
    2149
    22 We can add plugins for extracting credentials from a sessions, getting an api key, whatever, and configure them like so:
    2350
    24 Add this to settings.py:
    25 {{{
    26 #!python
    27 
    28 CREDENTIALS_PLUGINS = (
    29     'django.contrib.auth.SessionCredentials',
    30     'django.contrib.auth.UsernamePasswordCredentials',
    31 )
    32 }}}
    3351
    3452== Authentication ==
     
    4664}}}
    4765
    48 And now for the plugin:
    4966{{{
    5067#!python
     
    5269from django.contrib.auth.models import User
    5370
    54 class ModelAuthenticator
    55     def authenticate_credentials(self, credentials):
    56         #we need a username and password in the credentials, if they're not there, return None
    57         #keep in mind that other authenticators can look for different types of credentials
    58         #if the username or password or bad, return None
    59         #if everything is kosher, return the USER
     71class ModelAuthenticator:
     72        def authenticate(self, credentials):
     73                from django.contrib.auth.models import User
     74
     75                user = User.objects.get(username=credentials['login'])
     76                if user.password == credentials['password']:
     77                        return user
     78                return None
    6079}}}
    6180
     
    6584== Multiple Backends ==
    6685
    67 Um, so what if I want to use LDAP '''and''' Django models? Funny you should ask.
     86Um, so what if I want to use LDAP '''and''' Django models? Funny you should ask. By the way, this is main interface to the authentication system. You won't use credentials and authenticators directly.
    6887
    6988{{{
    7089#!python
    7190
    72 class AuthenticationUtility:
    73     def authenticate(self, request):
    74         #psuedocode ahead!
    75         for cred_plugin in credentials_plugins:
    76              credentials = cred_plugin.extract_credentials(request)
    77              if credentials is not None:
    78                  for auth_plugin in auth plugins:
    79                      user = auth_plugin.authenticate_credentials(credentials)
    80                      if user is not None:
    81                          return user
     91class AuthUtil:
     92        def __init__(self):
     93                # XXX: these will be configurable in settings.py
     94                self.cred_extractors = (SessionCredentials(),)
     95                self.authenticators = (ModelAuthenticator(),)
     96
     97        def authenticate(self, request):
     98                for cred_extractor in self.cred_extractors:
     99                        credentials = cred_extractor.extract_credentials(request)
     100                        for authenticator in self.authenticators:
     101                                user = authenticator.authenticate(credentials)
     102                                user.cred_extractor = cred_extractor
     103                                user.authenticator = authenticator
     104                                return user
     105                return None
     106
     107        def logout(self, request):
     108                for cred_extractor in self.cred_extractors:
     109                        if hasattr(cred_extractor, 'logout'):
     110                                cred_extractor.logout(request)
    82111}}}
    83112
    84 This is the main interface to the auth system. In english, check for credentials, if we find them, try to authenticate them against every auth backend in settings.py, if none of them authenticate, do it all again with the next type of credentials.
    85113
    86 Credentials plugins should probably have a logout method. I didn't give any examples, but it shouldn't be hard.
     114== Integration ==
    87115
    88 That's most of it. Think you have a use case this won't work for? Please tell django-developers.
     116How will this integrate into Django? I think either the handlers (mod_python, etc.) should start using AuthUtil, or this can be done in decorators. My vote is certainly for the first option.
     117
    89118
    90119== Suggestions ==
     
    97126To me it doesn't make sense for credentials to be stored as a model... they are just a dict or string or object or whatever that get extracted/assembled from the request. You just pass to the auth backends for checking. The auth backend already persists them. The username/password combo isn't blessed. Credentials could just as well be an api key similar to backpack, or whatever.
    98127
    99 I think these abstractions allow for the use cases you mention, but the details certainly need to be worked out better. I explicitly want to ''avoid'' coupling this to django models, but it should ''easy'' to do so since it's most likely the default case. -- Joseph Kocherhans
     128I think these abstractions allow for the use cases you mention, but the details certainly need to be worked out better. I explicitly want to ''avoid'' coupling this to django models, but it should ''easy'' to do so since it's most likely the default case. --Joseph Kocherhans
    100129
    101130
    102131Here is how they approached this problem in Zope3: [http://www.zope.org/Wikis/DevSite/Projects/ComponentArchitecture/Zope3Book/principalplugin.html].  You can find additional resources here: [http://www.zope.org/Wikis/DevSite/Projects/ComponentArchitecture/Zope3Book] -- Linicks
     132
     133I don't believe zope does things that way anymore. That first link looks to be more a step along the way than a final implementation. --Joseph Kocherhans
Back to Top