=== modified file 'django/contrib/auth/backends.py'
--- django/contrib/auth/backends.py	2008-07-02 05:05:50 +0000
+++ django/contrib/auth/backends.py	2009-03-05 04:51:35 +0000
@@ -78,3 +78,70 @@
             return User.objects.get(pk=user_id)
         except User.DoesNotExist:
             return None
+
+
+class RemoteUserBackend(ModelBackend):
+    """
+    This backend is to be used in conjunction with the ``RemoteUserMiddleware``
+    found in the middleware module of this package, and is used when the server
+    is handling authentication outside of Django.
+
+    By default, the ``authenticate`` method creates ``User`` objects for
+    usernames that don't already exist in the database.  Subclasses can disable
+    this behavior by setting the ``create_unknown_user`` attribute to
+    ``False``.
+    """
+
+    # Create a User object if not already in the database?
+    create_unknown_user = True
+
+    def authenticate(self, remote_user):
+        """
+        The username passed as ``remote_user`` is considered trusted.  This
+        method simply returns the ``User`` object with the given username,
+        creating a new ``User`` object if ``create_unknown_user`` is ``True``.
+
+        Returns None if ``create_unknown_user`` is ``False`` and a ``User``
+        object with the given username is not found in the database.
+        """
+        if not remote_user:
+            return
+        user = None
+        username = self.clean_username(remote_user)
+
+        # Note that this could be accomplished in one try-except clause, but
+        # instead we use get_or_create when creating unknown users since it has
+        # built-in safeguards for multiple threads.
+        if self.create_unknown_user:
+            user, created = User.objects.get_or_create(username=username)
+            if created:
+                user = self.configure_user(user)
+        else:
+            try:
+                user = User.objects.get(username=username)
+            except User.DoesNotExist:
+                pass
+        return user
+
+    def clean_username(self, username):
+        """
+        Cleans the passed username to remove any extraneous text, and returns
+        the cleaned username.
+
+        By default, this method returns the passed username unmodified.
+        Override this method if you need to do special things with the
+        username, like stripping @realm or cleaning something like
+        cn=user,dc=domain.
+        """
+        return username
+
+    def configure_user(self, user):
+        """
+        Configures a user after creation and returns the updated user.
+
+        By default, this method returns the passed user unmodified.  Override
+        this method if you would like to do additional setup on the user that
+        was just created, such as set groups based on attributes in an LDAP
+        directory.
+        """
+        return user

=== modified file 'django/contrib/auth/middleware.py'
--- django/contrib/auth/middleware.py	2006-09-12 17:30:47 +0000
+++ django/contrib/auth/middleware.py	2009-03-12 05:26:18 +0000
@@ -1,3 +1,7 @@
+from django.contrib import auth
+from django.core.exceptions import ImproperlyConfigured
+
+
 class LazyUser(object):
     def __get__(self, request, obj_type=None):
         if not hasattr(request, '_cached_user'):
@@ -5,8 +9,73 @@
             request._cached_user = get_user(request)
         return request._cached_user
 
+
 class AuthenticationMiddleware(object):
     def process_request(self, request):
         assert hasattr(request, 'session'), "The Django authentication middleware requires session middleware to be installed. Edit your MIDDLEWARE_CLASSES setting to insert 'django.contrib.sessions.middleware.SessionMiddleware'."
         request.__class__.user = LazyUser()
         return None
+
+
+class RemoteUserMiddleware(object):
+    """
+    Middleware for utilizing web-server-provided authentication.
+
+    If request.user is not authenticated, then this middleware attempts to
+    authenticate the username passed in the ``REMOTE_USER`` request header.
+    If authentication is successful, the user is automatically logged in to
+    persist the user in the session.
+
+    The header used is configurable and defaults to ``REMOTE_USER``.  Subclass
+    this class and change the ``header`` attribute if you need to use a
+    different header.
+    """
+
+    # Name of request header to grab username from.  This will be the key as
+    # used in the request.META dictionary, i.e. the normalization of headers to
+    # all uppercase and the addition of "HTTP_" prefix apply.
+    header = "REMOTE_USER"
+
+    def process_request(self, request):
+        # AuthenticationMiddleware is required so that request.user exists.
+        if not hasattr(request, 'user'):
+            raise ImproperlyConfigured(
+                "The Django remote user auth middleware requires the"
+                " authentication middleware to be installed.  Edit your"
+                " MIDDLEWARE_CLASSES setting to insert"
+                " 'django.contrib.auth.middleware.AuthenticationMiddleware'"
+                " before the RemoteUserMiddleware class.")
+        try:
+            username = request.META[self.header]
+        except KeyError:
+            # If specified header doesn't exist then return (leaving
+            # request.user set to AnonymousUser by the
+            # AuthenticationMiddleware).
+            return
+        # If the user is already authenticated and that user is the user we are
+        # getting passed in the headers, then the correct user is already
+        # persisted in the session and we don't need to continue.
+        if request.user.is_authenticated():
+            if request.user.username == self.clean_username(username, request):
+                return
+        # We are seeing this user for the first time in this session, attempt
+        # to authenticate the user.
+        user = auth.authenticate(remote_user=username)
+        if user:
+            # User is valid.  Set request.user and persist user in the session
+            # by logging the user in.
+            request.user = user
+            auth.login(request, user)
+
+    def clean_username(self, username, request):
+        """
+        Allows the backend to clean the username, if the backend defines a
+        clean_username method.
+        """
+        backend_str = request.session[auth.BACKEND_SESSION_KEY]
+        backend = auth.load_backend(backend_str)
+        try:
+            username = backend.clean_username(username)
+        except AttributeError: # Backend has no clean_username method.
+            pass
+        return username

=== modified file 'django/contrib/auth/tests/__init__.py'
--- django/contrib/auth/tests/__init__.py	2008-08-23 18:20:49 +0000
+++ django/contrib/auth/tests/__init__.py	2009-03-09 04:46:11 +0000
@@ -1,6 +1,9 @@
 from django.contrib.auth.tests.basic import BASIC_TESTS
-from django.contrib.auth.tests.views import PasswordResetTest, ChangePasswordTest
+from django.contrib.auth.tests.views \
+        import PasswordResetTest, ChangePasswordTest
 from django.contrib.auth.tests.forms import FORM_TESTS
+from django.contrib.auth.tests.remote_user \
+        import RemoteUserTest, RemoteUserNoCreateTest, RemoteUserCustomTest
 from django.contrib.auth.tests.tokens import TOKEN_GENERATOR_TESTS
 
 # The password for the fixture data users is 'password'

=== added file 'django/contrib/auth/tests/remote_user.py'
--- django/contrib/auth/tests/remote_user.py	1970-01-01 00:00:00 +0000
+++ django/contrib/auth/tests/remote_user.py	2009-03-12 05:21:37 +0000
@@ -0,0 +1,169 @@
+from datetime import datetime
+
+from django.conf import settings
+from django.contrib.auth.backends import RemoteUserBackend
+from django.contrib.auth.models import AnonymousUser, User
+from django.test import TestCase
+
+
+class RemoteUserTest(TestCase):
+
+    middleware = 'django.contrib.auth.middleware.RemoteUserMiddleware'
+    backend = 'django.contrib.auth.backends.RemoteUserBackend'
+    known_user = 'knownuser'
+
+    def setUp(self):
+        self.curr_middleware = settings.MIDDLEWARE_CLASSES
+        self.curr_auth = settings.AUTHENTICATION_BACKENDS
+        settings.MIDDLEWARE_CLASSES += (self.middleware,)
+        settings.AUTHENTICATION_BACKENDS = (self.backend,)
+
+    def test_no_remote_user(self):
+        """
+        Tests requests where no remote user is specified and insures that no
+        users get created.
+        """
+        num_users = User.objects.count()
+
+        response = self.client.get('/')
+        self.assert_(isinstance(response.context['user'], AnonymousUser))
+        self.assertEqual(User.objects.count(), num_users)
+
+        response = self.client.get('/', REMOTE_USER=None)
+        self.assert_(isinstance(response.context['user'], AnonymousUser))
+        self.assertEqual(User.objects.count(), num_users)
+
+        response = self.client.get('/', REMOTE_USER='')
+        self.assert_(isinstance(response.context['user'], AnonymousUser))
+        self.assertEqual(User.objects.count(), num_users)
+
+    def test_unknown_user(self):
+        """
+        Tests the case where the username passed in the header does not exist
+        as a User.
+        """
+        num_users = User.objects.count()
+        response = self.client.get('/', REMOTE_USER='newuser')
+        self.assertEqual(response.context['user'].username, 'newuser')
+        self.assertEqual(User.objects.count(), num_users + 1)
+        User.objects.get(username='newuser')
+
+        # Another request with same user should not create any new users.
+        response = self.client.get('/', REMOTE_USER='newuser')
+        self.assertEqual(User.objects.count(), num_users + 1)
+
+    def test_known_user(self):
+        """
+        Tests the case where the username passed in the header is a valid User.
+        """
+        User.objects.create(username='knownuser')
+        User.objects.create(username='knownuser2')
+        num_users = User.objects.count()
+        response = self.client.get('/', REMOTE_USER='knownuser')
+        self.assertEqual(response.context['user'].username, 'knownuser')
+        self.assertEqual(User.objects.count(), num_users)
+        # Test that a different user passed in the headers causes the new user
+        # to be logged in.
+        response = self.client.get('/', REMOTE_USER='knownuser2')
+        self.assertEqual(response.context['user'].username, 'knownuser2')
+        self.assertEqual(User.objects.count(), num_users)
+
+    def test_last_login(self):
+        """
+        Tests that a user's last_login is set the first time they make a
+        request but not updated in subsequent requests with the same session.
+        """
+        user = User.objects.create(username='knownuser')
+        # Set last_login to something so we can determine if it changes.
+        default_login = datetime(2000, 1, 1)
+        user.last_login = default_login
+        user.save()
+
+        response = self.client.get('/', REMOTE_USER=self.known_user)
+        self.assertNotEqual(default_login, response.context['user'].last_login)
+
+        user = User.objects.get(username='knownuser')
+        user.last_login = default_login
+        user.save()
+        response = self.client.get('/', REMOTE_USER=self.known_user)
+        self.assertEqual(default_login, response.context['user'].last_login)
+
+    def tearDown(self):
+        """Restores settings to avoid breaking other tests."""
+        settings.MIDDLEWARE_CLASSES = self.curr_middleware
+        settings.AUTHENTICATION_BACKENDS = self.curr_auth
+
+
+class RemoteUserNoCreateBackend(RemoteUserBackend):
+    """Backend that doesn't create unknown users."""
+    create_unknown_user = False
+
+
+class RemoteUserNoCreateTest(RemoteUserTest):
+    """
+    Contains the same tests as RemoteUserTest, but using a custom auth backend
+    class that doesn't create unknown users.
+    """
+
+    backend =\
+        'django.contrib.auth.tests.remote_user.RemoteUserNoCreateBackend'
+
+    def test_unknown_user(self):
+        num_users = User.objects.count()
+        response = self.client.get('/', REMOTE_USER='newuser')
+        self.assert_(isinstance(response.context['user'], AnonymousUser))
+        self.assertEqual(User.objects.count(), num_users)
+
+
+class CustomRemoteUserBackend(RemoteUserBackend):
+    """
+    Backend that overrides RemoteUserBackend methods.
+    """
+
+    def clean_username(self, username):
+        """
+        Grabs username before the @ character.
+        """
+        return username.split('@')[0]
+
+    def configure_user(self, user):
+        """
+        Sets user's email address.
+        """
+        user.email = 'user@example.com'
+        user.save()
+        return user
+
+
+class RemoteUserCustomTest(RemoteUserTest):
+
+    backend =\
+        'django.contrib.auth.tests.remote_user.CustomRemoteUserBackend'
+    known_user = 'knownuser@example.com'
+
+    def test_clean_username(self):
+        """
+        The strings passed in REMOTE_USER should be cleaned and the known users
+        should not have been configured with an email address.
+        """
+        User.objects.create(username='knownuser')
+        User.objects.create(username='knownuser2')
+        num_users = User.objects.count()
+
+        response = self.client.get('/', REMOTE_USER='knownuser@abc.com')
+        self.assertEqual(response.context['user'].username, 'knownuser')
+        self.assertEqual(response.context['user'].email, '')
+        self.assertEqual(User.objects.count(), num_users)
+
+        response = self.client.get('/', REMOTE_USER='knownuser2@abc.com')
+        self.assertEqual(response.context['user'].username, 'knownuser2')
+        self.assertEqual(response.context['user'].email, '')
+        self.assertEqual(User.objects.count(), num_users)
+
+    def test_unknown_user(self):
+        """
+        The unknown user created should be configured with an email address.
+        """
+        super(RemoteUserCustomTest, self).test_unknown_user()
+        newuser = User.objects.get(username='newuser')
+        self.assertEqual(newuser.email, 'user@example.com')

=== added file 'docs/ref/authbackends.txt'
--- docs/ref/authbackends.txt	1970-01-01 00:00:00 +0000
+++ docs/ref/authbackends.txt	2009-02-16 05:48:25 +0000
@@ -0,0 +1,37 @@
+.. _ref-authentication-backends:
+
+==========================================
+Built-in authentication backends reference
+==========================================
+
+.. module:: django.contrib.auth.backends
+   :synopsis: Django's built-in authentication backend classes.
+
+This document details the authentication backends that come with Django. For
+information on how how to use them and how to write your own authentication
+backends, see the :ref:`Other authentication sources section
+<authentication-backends>` of the :ref:`User authentication guide
+<topics-auth>`.
+
+
+Available authentication backends
+=================================
+
+The following backends are available in :mod:`django.contrib.auth.backends`:
+
+.. class:: ModelBackend
+
+    This is the default authentication backend used by Django.  It
+    authenticates using usernames and passwords stored in the the
+    :class:`~django.contrib.auth.models.User` model.
+
+
+.. class:: RemoteUserBackend
+
+    .. versionadded:: 1.1
+
+    Use this backend to take advantage of external-to-Django-handled
+    authentication.  It authenticates using usernames passed in
+    :attr:`request.META['REMOTE_USER'] <django.http.HttpRequest.META>`.  See
+    the :ref:`Authenticating against REMOTE_USER <topics-auth-remote-user>`
+    documentation.

=== modified file 'docs/ref/index.txt'
--- docs/ref/index.txt	2008-08-26 19:04:52 +0000
+++ docs/ref/index.txt	2009-02-15 06:05:02 +0000
@@ -5,7 +5,8 @@
 
 .. toctree::
    :maxdepth: 1
-   
+
+   authbackends
    contrib/index
    databases
    django-admin
@@ -19,4 +20,3 @@
    signals
    templates/index
    unicode
-   
\ No newline at end of file

=== added file 'docs/topics/auth-remote-user.txt'
--- docs/topics/auth-remote-user.txt	1970-01-01 00:00:00 +0000
+++ docs/topics/auth-remote-user.txt	2009-03-05 07:14:19 +0000
@@ -0,0 +1,89 @@
+.. _topics-auth-remote-user:
+
+====================================
+Authentication using ``REMOTE_USER``
+====================================
+
+This document describes how to make use of external authentication sources
+(where the Web server sets the ``REMOTE_USER`` environment variable) in your
+Django applications.  This type of authentication solution is typically seen on
+intranet sites, with single sign-on solutions such as IIS and Integrated
+Windows Authentication or Apache and `mod_authnz_ldap`_, `CAS`_, `Cosign`_,
+`WebAuth`_, `mod_auth_sspi`_, etc.
+
+.. _mod_authnz_ldap: http://httpd.apache.org/docs/2.2/mod/mod_authnz_ldap.html
+.. _CAS: http://www.ja-sig.org/products/cas/
+.. _Cosign: http://weblogin.org
+.. _WebAuth: http://www.stanford.edu/services/webauth/
+.. _mod_auth_sspi: http://sourceforge.net/projects/mod-auth-sspi
+
+When the Web server takes care of authentication it typically sets the
+``REMOTE_USER`` environment variable for use in the underlying application.  In
+Django, ``REMOTE_USER`` is made available in the :attr:`request.META
+<django.http.HttpRequest.META>` attribute.  Django can be configured to make
+use of the ``REMOTE_USER`` value using the ``RemoteUserMiddleware`` and
+``RemoteUserBackend`` classes found in :mod:`django.contirb.auth`.
+
+Configuration
+=============
+
+First, you must add the
+:class:`django.contrib.auth.middleware.RemoteUserMiddleware` to the
+:setting:`MIDDLEWARE_CLASSES` setting **after** the
+:class:`django.contrib.auth.middleware.AuthenticationMiddleware`::
+
+    MIDDLEWARE_CLASSES = (
+        ...
+        'django.contrib.auth.middleware.AuthenticationMiddleware',
+        'django.contrib.auth.middleware.RemoteUserMiddleware',
+        ...
+        )
+
+Next, you must replace the :class:`~django.contrib.auth.backends.ModelBackend`
+with ``RemoteUserBackend`` in the :setting:`AUTHENTICATION_BACKENDS` setting::
+
+    AUTHENTICATION_BACKENDS = (
+        'django.contrib.auth.backends.RemoteUserBackend',
+    )
+
+With this setup, ``RemoteUserMiddleware`` will detect the username in
+``request.META['REMOTE_USER']`` and will authenticate and auto-login that user
+using the ``RemoteUserBackend``.
+
+.. note::
+   Since the ``RemoteUserBackend`` inherits from ``ModelBackend``, you will
+   still have all of the same permissions checking that is implemented in
+   ``ModelBackend``.
+
+
+``RemoteUserBackend``
+=====================
+
+.. class:: django.contrib.backends.RemoteUserBackend
+
+If you need more control, you can create your own authentication backend
+that inherits from ``RemoteUserBackend`` and overrides certain parts:
+
+Attributes
+~~~~~~~~~~
+
+.. attribute:: RemoteUserBackend.create_unknown_user
+
+    ``True`` or ``False``.  Determines whether or not a
+    :class:`~django.contrib.auth.models.User` object is created if not already
+    in the database.  Defaults to ``True``.
+
+Methods
+~~~~~~~
+
+.. method:: RemoteUserBackend.clean_username(username)
+
+   Clean unwanted text from the passed username.  This is useful if the
+   username includes unwanted text, such extra LDAP DN information.  This
+   method needs to return the cleaned username.
+
+.. method:: RemoteUserBackend.configure_user(user)
+
+   Configure a newly created user.  This method is called immediately after a
+   new user is created, and can be used to perform custom setup actions for the
+   user.  This methods needs to return the user object.

=== modified file 'docs/topics/auth.txt'
--- docs/topics/auth.txt	2009-02-16 05:10:31 +0000
+++ docs/topics/auth.txt	2009-03-05 07:15:16 +0000
@@ -1263,10 +1263,13 @@
 and the Django-based applications.
 
 So, to handle situations like this, the Django authentication system lets you
-plug in another authentication sources. You can override Django's default
+plug in other authentication sources. You can override Django's default
 database-based scheme, or you can use the default system in tandem with other
 systems.
 
+See the :ref:`authentication backend reference <ref-authentication-backends>`
+for information on the authentication backends included with Django.
+
 Specifying authentication backends
 ----------------------------------
 

=== modified file 'docs/topics/index.txt'
--- docs/topics/index.txt	2008-08-26 19:04:52 +0000
+++ docs/topics/index.txt	2009-02-14 02:37:19 +0000
@@ -17,6 +17,7 @@
    files
    testing
    auth
+   auth-remote-user
    cache
    email
    i18n

