Index: django/conf/global_settings.py
===================================================================
--- django/conf/global_settings.py	(revision 7832)
+++ django/conf/global_settings.py	(working copy)
@@ -363,6 +363,9 @@
 
 LOGIN_REDIRECT_URL = '/accounts/profile/'
 
+# The class to use as the default AUTH_USER for the authentication system.
+AUTH_USER_MODULE = 'django.contrib.auth.default_user.DefaultUser'
+
 ###########
 # TESTING #
 ###########
Index: django/contrib/auth/default_user.py
===================================================================
--- django/contrib/auth/default_user.py	(revision 0)
+++ django/contrib/auth/default_user.py	(revision 0)
@@ -0,0 +1,114 @@
+from django.db import models
+from django.core import validators
+from django.utils.translation import gettext_lazy as _
+
+from django.contrib.auth.models import Group, Permission, UserTemplate
+
+import datetime
+
+
+class DefaultUserManager(models.Manager):
+    def create_user(self, username, email, password=None):
+        "Creates and saves a User with the given username, e-mail and password."
+        now = datetime.datetime.now()
+        user = self.model(None, username, '', '', email.strip().lower(), 'placeholder', False, True, False, now, now)
+        if password:
+            user.set_password(password)
+        else:
+            user.set_unusable_password()
+        user.save()
+        return user
+
+    def create_superuser(self, username, email, password):
+        u = self.create_user(username, email, password)
+        u.is_staff = True
+        u.is_active = True
+        u.is_superuser = True
+        u.save()
+
+    def make_random_password(self, length=10, allowed_chars='abcdefghjkmnpqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ23456789'):
+        "Generates a random password with the given length and given allowed_chars"
+        # Note that default value of allowed_chars does not have "I" or letters
+        # that look like it -- just to avoid confusion.
+        from random import choice
+        return ''.join([choice(allowed_chars) for i in range(length)])
+
+
+class DefaultUser(UserTemplate):
+    """Users within the Django authentication system are represented by this model.
+
+    Username and password are required. Other fields are optional.
+    """
+    username = models.CharField(_('username'), max_length=30, unique=True, validator_list=[validators.isAlphaNumeric], help_text=_("Required. 30 characters or fewer. Alphanumeric characters only (letters, digits and underscores)."))
+    first_name = models.CharField(_('first name'), max_length=30, blank=True)
+    last_name = models.CharField(_('last name'), max_length=30, blank=True)
+    email = models.EmailField(_('e-mail address'), blank=True)
+    password = models.CharField(_('password'), max_length=128, help_text=_("Use '[algo]$[salt]$[hexdigest]' or use the <a href=\"password/\">change password form</a>."))
+    is_staff = models.BooleanField(_('staff status'), default=False, help_text=_("Designates whether the user can log into this admin site."))
+    is_active = models.BooleanField(_('active'), default=True, help_text=_("Designates whether this user should be treated as active. Unselect this instead of deleting accounts."))
+    is_superuser = models.BooleanField(_('superuser status'), default=False, help_text=_("Designates that this user has all permissions without explicitly assigning them."))
+    last_login = models.DateTimeField(_('last login'), default=datetime.datetime.now)
+    date_joined = models.DateTimeField(_('date joined'), default=datetime.datetime.now)
+    groups = models.ManyToManyField(Group, verbose_name=_('groups'), blank=True,
+        help_text=_("In addition to the permissions manually assigned, this user will also get all permissions granted to each group he/she is in."))
+    user_permissions = models.ManyToManyField(Permission, verbose_name=_('user permissions'), blank=True, filter_interface=models.HORIZONTAL)
+    objects = DefaultUserManager()
+
+    class Admin:
+        fields = (
+            (None, {'fields': ('username', 'password')}),
+            (_('Personal info'), {'fields': ('first_name', 'last_name', 'email')}),
+            (_('Permissions'), {'fields': ('is_staff', 'is_active', 'is_superuser', 'user_permissions')}),
+            (_('Important dates'), {'fields': ('last_login', 'date_joined')}),
+            (_('Groups'), {'fields': ('groups',)}),
+        )
+        list_display = ('username', 'email', 'first_name', 'last_name', 'is_staff')
+        list_filter = ('is_staff', 'is_superuser')
+        search_fields = ('username', 'first_name', 'last_name', 'email')
+        ordering = ('username',)
+
+    def __unicode__(self):
+        return self.username
+
+    def get_absolute_url(self):
+        return "/users/%s/" % urllib.quote(smart_str(self.username))
+
+    def get_full_name(self):
+        "Returns the first_name plus the last_name, with a space in between."
+        full_name = u'%s %s' % (self.first_name, self.last_name)
+        return full_name.strip()
+
+    def set_password(self, raw_password):
+        import random
+        algo = 'sha1'
+        salt = get_hexdigest(algo, str(random.random()), str(random.random()))[:5]
+        hsh = get_hexdigest(algo, salt, raw_password)
+        self.password = '%s$%s$%s' % (algo, salt, hsh)
+
+    def check_password(self, raw_password):
+        """
+        Returns a boolean of whether the raw_password was correct. Handles
+        encryption formats behind the scenes.
+        """
+        # Backwards-compatibility check. Older passwords won't include the
+        # algorithm or salt.
+        if '$' not in self.password:
+            is_correct = (self.password == get_hexdigest('md5', '', raw_password))
+            if is_correct:
+                # Convert the password to the new, more secure format.
+                self.set_password(raw_password)
+                self.save()
+            return is_correct
+        return check_password(raw_password, self.password)
+
+    def set_unusable_password(self):
+        # Sets a value that will never be a valid hash
+        self.password = UNUSABLE_PASSWORD
+
+    def has_usable_password(self):
+        return self.password != UNUSABLE_PASSWORD
+
+    def email_user(self, subject, message, from_email=None):
+        "Sends an e-mail to this User."
+        from django.core.mail import send_mail
+        send_mail(subject, message, from_email, [self.email])
\ No newline at end of file
Index: django/contrib/auth/models.py
===================================================================
--- django/contrib/auth/models.py	(revision 7832)
+++ django/contrib/auth/models.py	(working copy)
@@ -6,6 +6,7 @@
 from django.contrib.contenttypes.models import ContentType
 from django.utils.encoding import smart_str
 from django.utils.translation import ugettext_lazy as _
+from django.conf import settings
 import datetime
 import urllib
 
@@ -104,75 +105,13 @@
     def __unicode__(self):
         return self.name
 
-class UserManager(models.Manager):
-    def create_user(self, username, email, password=None):
-        "Creates and saves a User with the given username, e-mail and password."
-        now = datetime.datetime.now()
-        user = self.model(None, username, '', '', email.strip().lower(), 'placeholder', False, True, False, now, now)
-        if password:
-            user.set_password(password)
-        else:
-            user.set_unusable_password()
-        user.save()
-        return user
+class UserTemplate(models.Model):
+    """ Base class from which all User models inherit.  """
 
-    def create_superuser(self, username, email, password):
-        u = self.create_user(username, email, password)
-        u.is_staff = True
-        u.is_active = True
-        u.is_superuser = True
-        u.save()
-
-    def make_random_password(self, length=10, allowed_chars='abcdefghjkmnpqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ23456789'):
-        "Generates a random password with the given length and given allowed_chars"
-        # Note that default value of allowed_chars does not have "I" or letters
-        # that look like it -- just to avoid confusion.
-        from random import choice
-        return ''.join([choice(allowed_chars) for i in range(length)])
-
-class User(models.Model):
-    """Users within the Django authentication system are represented by this model.
-
-    Username and password are required. Other fields are optional.
-    """
-    username = models.CharField(_('username'), max_length=30, unique=True, validator_list=[validators.isAlphaNumeric], help_text=_("Required. 30 characters or fewer. Alphanumeric characters only (letters, digits and underscores)."))
-    first_name = models.CharField(_('first name'), max_length=30, blank=True)
-    last_name = models.CharField(_('last name'), max_length=30, blank=True)
-    email = models.EmailField(_('e-mail address'), blank=True)
-    password = models.CharField(_('password'), max_length=128, help_text=_("Use '[algo]$[salt]$[hexdigest]' or use the <a href=\"password/\">change password form</a>."))
-    is_staff = models.BooleanField(_('staff status'), default=False, help_text=_("Designates whether the user can log into this admin site."))
-    is_active = models.BooleanField(_('active'), default=True, help_text=_("Designates whether this user should be treated as active. Unselect this instead of deleting accounts."))
-    is_superuser = models.BooleanField(_('superuser status'), default=False, help_text=_("Designates that this user has all permissions without explicitly assigning them."))
-    last_login = models.DateTimeField(_('last login'), default=datetime.datetime.now)
-    date_joined = models.DateTimeField(_('date joined'), default=datetime.datetime.now)
-    groups = models.ManyToManyField(Group, verbose_name=_('groups'), blank=True,
-        help_text=_("In addition to the permissions manually assigned, this user will also get all permissions granted to each group he/she is in."))
-    user_permissions = models.ManyToManyField(Permission, verbose_name=_('user permissions'), blank=True, filter_interface=models.HORIZONTAL)
-    objects = UserManager()
-
     class Meta:
         verbose_name = _('user')
         verbose_name_plural = _('users')
 
-    class Admin:
-        fields = (
-            (None, {'fields': ('username', 'password')}),
-            (_('Personal info'), {'fields': ('first_name', 'last_name', 'email')}),
-            (_('Permissions'), {'fields': ('is_staff', 'is_active', 'is_superuser', 'user_permissions')}),
-            (_('Important dates'), {'fields': ('last_login', 'date_joined')}),
-            (_('Groups'), {'fields': ('groups',)}),
-        )
-        list_display = ('username', 'email', 'first_name', 'last_name', 'is_staff')
-        list_filter = ('is_staff', 'is_superuser')
-        search_fields = ('username', 'first_name', 'last_name', 'email')
-        ordering = ('username',)
-
-    def __unicode__(self):
-        return self.username
-
-    def get_absolute_url(self):
-        return "/users/%s/" % urllib.quote(smart_str(self.username))
-
     def is_anonymous(self):
         "Always returns False. This is a way of comparing User objects to anonymous users."
         return False
@@ -181,42 +120,7 @@
         """Always return True. This is a way to tell if the user has been authenticated in templates.
         """
         return True
-
-    def get_full_name(self):
-        "Returns the first_name plus the last_name, with a space in between."
-        full_name = u'%s %s' % (self.first_name, self.last_name)
-        return full_name.strip()
-
-    def set_password(self, raw_password):
-        import random
-        algo = 'sha1'
-        salt = get_hexdigest(algo, str(random.random()), str(random.random()))[:5]
-        hsh = get_hexdigest(algo, salt, raw_password)
-        self.password = '%s$%s$%s' % (algo, salt, hsh)
-
-    def check_password(self, raw_password):
-        """
-        Returns a boolean of whether the raw_password was correct. Handles
-        encryption formats behind the scenes.
-        """
-        # Backwards-compatibility check. Older passwords won't include the
-        # algorithm or salt.
-        if '$' not in self.password:
-            is_correct = (self.password == get_hexdigest('md5', '', raw_password))
-            if is_correct:
-                # Convert the password to the new, more secure format.
-                self.set_password(raw_password)
-                self.save()
-            return is_correct
-        return check_password(raw_password, self.password)
-
-    def set_unusable_password(self):
-        # Sets a value that will never be a valid hash
-        self.password = UNUSABLE_PASSWORD
-
-    def has_usable_password(self):
-        return self.password != UNUSABLE_PASSWORD
-
+    
     def get_group_permissions(self):
         """
         Returns a list of permission strings that this user has through
@@ -288,11 +192,6 @@
             m.delete()
         return messages
 
-    def email_user(self, subject, message, from_email=None):
-        "Sends an e-mail to this User."
-        from django.core.mail import send_mail
-        send_mail(subject, message, from_email, [self.email])
-
     def get_profile(self):
         """
         Returns site-specific profile for this user. Raises
@@ -310,6 +209,28 @@
                 raise SiteProfileNotAvailable
         return self._profile_cache
 
+
+# Grab the AUTH_USER_MODULE path and classname from the settings. 
+# auth_user_module_parts[0] = module path 
+# auth_user_module_parts[1] = class name 
+auth_user_module_parts = settings.AUTH_USER_MODULE.rsplit('.', 1) 
+auth_user_module = __import__(auth_user_module_parts[0], {}, {}, [auth_user_module_parts[0]]) 
+# Store the auth_user model so it is accessible with the standard 
+# 'from django.contrib.auth.models import User' 
+User = getattr(auth_user_module, auth_user_module_parts[1]) 
+
+# Add the User model to the django models cache 
+# These two lines allow the custom auth_user model to play nicely with syncdb 
+# and other systems that rely on functions like 
+# django.db.models.loading.get_model(...) 
+from django.db.models.loading import cache 
+cache.register_models('auth', User)
+
+# We need to remove whatever we used as the AUTH_USER_MODUlE from the
+# db cache so apps like contenttypes don't think that it's part
+# of contrib.auth
+del cache.app_models['auth'][auth_user_module_parts[1].lower()]
+
 class Message(models.Model):
     """
     The message system is a lightweight way to queue messages for given
Index: docs/authentication.txt
===================================================================
--- docs/authentication.txt	(revision 7832)
+++ docs/authentication.txt	(working copy)
@@ -1164,3 +1164,13 @@
 the ``auth_permission`` table most of the time.
 
 .. _django/contrib/auth/backends.py: http://code.djangoproject.com/browser/django/trunk/django/contrib/auth/backends.py
+
+Overriding the Default Auth User Module
+=======================================
+
+If the default User model doesn't work for you, you can specify your own through
+the AUTH_USER_MODULE variable in settings.py.
+
+Simply import ``django.contrib.auth.UserTemplate`` and have your user model inherit
+from that.  You'll also have to specify your own authentication backend and any
+specific manager methods you'll need, such as create_user.
Index: docs/settings.txt
===================================================================
--- docs/settings.txt	(revision 7832)
+++ docs/settings.txt	(working copy)
@@ -249,6 +249,15 @@
 
 .. _documentation on user profile models: ../authentication/#storing-additional-information-about-users
 
+AUTH_USER_MODULE
+----------------
+
+Default: ``'django.contrib.auth.default_user.User'``
+
+The user model used by this site.  See the `documentation on user models`_ for details.
+
+.. _documentation on user models: ../authentication/#overriding-default-user-model
+
 CACHE_BACKEND
 -------------
 
