Code

Ticket #3011: #3011-extendable_auth_user-1.3.diff

File #3011-extendable_auth_user-1.3.diff, 41.5 KB (added by Kronuz, 2 years ago)

Updated for django 1.3

Line 
1diff --git a/django/contrib/admin/sites.py b/django/contrib/admin/sites.py
2index b03bce4..be60e96 100644
3--- django/contrib/admin/sites.py
4+++ django/contrib/admin/sites.py
5@@ -1,7 +1,6 @@
6 import re
7 from django import http, template
8 from django.contrib.admin import ModelAdmin, actions
9-from django.contrib.admin.forms import AdminAuthenticationForm
10 from django.contrib.auth import REDIRECT_FIELD_NAME
11 from django.contrib.contenttypes import views as contenttype_views
12 from django.views.decorators.csrf import csrf_protect
13@@ -315,6 +314,7 @@ class AdminSite(object):
14         Displays the login form for the given HttpRequest.
15         """
16         from django.contrib.auth.views import login
17+        from django.contrib.admin.forms import AdminAuthenticationForm
18         context = {
19             'title': _('Log in'),
20             'root_path': self.root_path,
21diff --git a/django/contrib/auth/base.py b/django/contrib/auth/base.py
22new file mode 100644
23index 0000000..3465c64
24--- /dev/null
25+++ django/contrib/auth/base.py
26@@ -0,0 +1,484 @@
27+import datetime
28+import urllib
29+
30+from django.contrib import auth
31+from django.contrib.auth.signals import user_logged_in
32+from django.core.exceptions import ImproperlyConfigured
33+from django.db import models
34+from django.db.models.manager import EmptyManager
35+from django.contrib.contenttypes.models import ContentType
36+from django.utils.encoding import smart_str
37+from django.utils.hashcompat import md5_constructor, sha_constructor
38+from django.utils.translation import ugettext_lazy as _
39+from django.utils.crypto import constant_time_compare
40+from django.conf import settings
41+
42+
43+UNUSABLE_PASSWORD = '!' # This will never be a valid hash
44+
45+def get_hexdigest(algorithm, salt, raw_password):
46+    """
47+    Returns a string of the hexdigest of the given plaintext password and salt
48+    using the given algorithm ('md5', 'sha1' or 'crypt').
49+    """
50+    raw_password, salt = smart_str(raw_password), smart_str(salt)
51+    if algorithm == 'crypt':
52+        try:
53+            import crypt
54+        except ImportError:
55+            raise ValueError('"crypt" password algorithm not supported in this environment')
56+        return crypt.crypt(raw_password, salt)
57+
58+    if algorithm == 'md5':
59+        return md5_constructor(salt + raw_password).hexdigest()
60+    elif algorithm == 'sha1':
61+        return sha_constructor(salt + raw_password).hexdigest()
62+    raise ValueError("Got unknown password algorithm type in password.")
63+
64+def check_password(raw_password, enc_password):
65+    """
66+    Returns a boolean of whether the raw_password was correct. Handles
67+    encryption formats behind the scenes.
68+    """
69+    algo, salt, hsh = enc_password.split('$')
70+    return constant_time_compare(hsh, get_hexdigest(algo, salt, raw_password))
71+
72+def update_last_login(sender, user, **kwargs):
73+    """
74+    A signal receiver which updates the last_login date for
75+    the user logging in.
76+    """
77+    user.last_login = datetime.datetime.now()
78+    user.save()
79+user_logged_in.connect(update_last_login)
80+
81+class SiteProfileNotAvailable(Exception):
82+    pass
83+
84+class PermissionManager(models.Manager):
85+    def get_by_natural_key(self, codename, app_label, model):
86+        return self.get(
87+            codename=codename,
88+            content_type=ContentType.objects.get_by_natural_key(app_label, model)
89+        )
90+
91+class Permission(models.Model):
92+    """The permissions system provides a way to assign permissions to specific users and groups of users.
93+
94+    The permission system is used by the Django admin site, but may also be useful in your own code. The Django admin site uses permissions as follows:
95+
96+        - The "add" permission limits the user's ability to view the "add" form and add an object.
97+        - The "change" permission limits a user's ability to view the change list, view the "change" form and change an object.
98+        - The "delete" permission limits the ability to delete an object.
99+
100+    Permissions are set globally per type of object, not per specific object instance. It is possible to say "Mary may change news stories," but it's not currently possible to say "Mary may change news stories, but only the ones she created herself" or "Mary may only change news stories that have a certain status or publication date."
101+
102+    Three basic permissions -- add, change and delete -- are automatically created for each Django model.
103+    """
104+    name = models.CharField(_('name'), max_length=50)
105+    content_type = models.ForeignKey(ContentType)
106+    codename = models.CharField(_('codename'), max_length=100)
107+    objects = PermissionManager()
108+
109+    class Meta:
110+        verbose_name = _('permission')
111+        verbose_name_plural = _('permissions')
112+        unique_together = (('content_type', 'codename'),)
113+        ordering = ('content_type__app_label', 'content_type__model', 'codename')
114+
115+    def __unicode__(self):
116+        return u"%s | %s | %s" % (
117+            unicode(self.content_type.app_label),
118+            unicode(self.content_type),
119+            unicode(self.name))
120+
121+    def natural_key(self):
122+        return (self.codename,) + self.content_type.natural_key()
123+    natural_key.dependencies = ['contenttypes.contenttype']
124+
125+class Group(models.Model):
126+    """Groups are a generic way of categorizing users to apply permissions, or some other label, to those users. A user can belong to any number of groups.
127+
128+    A user in a group automatically has all the permissions granted to that group. For example, if the group Site editors has the permission can_edit_home_page, any user in that group will have that permission.
129+
130+    Beyond permissions, groups are a convenient way to categorize users to apply some label, or extended functionality, to them. For example, you could create a group 'Special users', and you could write code that would do special things to those users -- such as giving them access to a members-only portion of your site, or sending them members-only e-mail messages.
131+    """
132+    name = models.CharField(_('name'), max_length=80, unique=True)
133+    permissions = models.ManyToManyField(Permission, verbose_name=_('permissions'), blank=True)
134+
135+    class Meta:
136+        verbose_name = _('group')
137+        verbose_name_plural = _('groups')
138+
139+    def __unicode__(self):
140+        return self.name
141+
142+class UserManager(models.Manager):
143+    def create_user(self, username, email, password=None):
144+        """
145+        Creates and saves a User with the given username, e-mail and password.
146+        """
147+        now = datetime.datetime.now()
148+
149+        # Normalize the address by lowercasing the domain part of the email
150+        # address.
151+        try:
152+            email_name, domain_part = email.strip().split('@', 1)
153+        except ValueError:
154+            pass
155+        else:
156+            email = '@'.join([email_name, domain_part.lower()])
157+
158+        user = self.model(username=username, email=email, is_staff=False,
159+                         is_active=True, is_superuser=False, last_login=now,
160+                         date_joined=now)
161+
162+        user.set_password(password)
163+        user.save(using=self._db)
164+        return user
165+
166+    def create_superuser(self, username, email, password):
167+        u = self.create_user(username, email, password)
168+        u.is_staff = True
169+        u.is_active = True
170+        u.is_superuser = True
171+        u.save(using=self._db)
172+        return u
173+
174+    def make_random_password(self, length=10, allowed_chars='abcdefghjkmnpqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ23456789'):
175+        "Generates a random password with the given length and given allowed_chars"
176+        # Note that default value of allowed_chars does not have "I" or letters
177+        # that look like it -- just to avoid confusion.
178+        from random import choice
179+        return ''.join([choice(allowed_chars) for i in range(length)])
180+
181+
182+# A few helper functions for common logic between User and AnonymousUser.
183+def _user_get_all_permissions(user, obj):
184+    permissions = set()
185+    anon = user.is_anonymous()
186+    for backend in auth.get_backends():
187+        if not anon or backend.supports_anonymous_user:
188+            if hasattr(backend, "get_all_permissions"):
189+                if obj is not None:
190+                    if backend.supports_object_permissions:
191+                        permissions.update(
192+                            backend.get_all_permissions(user, obj)
193+                        )
194+                else:
195+                    permissions.update(backend.get_all_permissions(user))
196+    return permissions
197+
198+
199+def _user_has_perm(user, perm, obj):
200+    anon = user.is_anonymous()
201+    active = user.is_active
202+    for backend in auth.get_backends():
203+        if (not active and not anon and backend.supports_inactive_user) or \
204+                    (not anon or backend.supports_anonymous_user):
205+            if hasattr(backend, "has_perm"):
206+                if obj is not None:
207+                    if (backend.supports_object_permissions and
208+                        backend.has_perm(user, perm, obj)):
209+                            return True
210+                else:
211+                    if backend.has_perm(user, perm):
212+                        return True
213+    return False
214+
215+
216+def _user_has_module_perms(user, app_label):
217+    anon = user.is_anonymous()
218+    active = user.is_active
219+    for backend in auth.get_backends():
220+        if (not active and not anon and backend.supports_inactive_user) or \
221+                    (not anon or backend.supports_anonymous_user):
222+            if hasattr(backend, "has_module_perms"):
223+                if backend.has_module_perms(user, app_label):
224+                    return True
225+    return False
226+
227+
228+class UserTemplate(models.Model):
229+    """
230+    Users within the Django authentication system are represented by this model.
231+
232+    Username and password are required. Other fields are optional.
233+    """
234+    username = models.CharField(_('username'), max_length=30, unique=True, help_text=_("Required. 30 characters or fewer. Letters, numbers and @/./+/-/_ characters"))
235+    first_name = models.CharField(_('first name'), max_length=30, blank=True)
236+    last_name = models.CharField(_('last name'), max_length=30, blank=True)
237+    email = models.EmailField(_('e-mail address'), blank=True)
238+    password = models.CharField(_('password'), max_length=128, help_text=_("Use '[algo]$[salt]$[hexdigest]' or use the <a href=\"password/\">change password form</a>."))
239+    is_staff = models.BooleanField(_('staff status'), default=False, help_text=_("Designates whether the user can log into this admin site."))
240+    is_active = models.BooleanField(_('active'), default=True, help_text=_("Designates whether this user should be treated as active. Unselect this instead of deleting accounts."))
241+    is_superuser = models.BooleanField(_('superuser status'), default=False, help_text=_("Designates that this user has all permissions without explicitly assigning them."))
242+    last_login = models.DateTimeField(_('last login'), default=datetime.datetime.now)
243+    date_joined = models.DateTimeField(_('date joined'), default=datetime.datetime.now)
244+    groups = models.ManyToManyField(Group, verbose_name=_('groups'), blank=True,
245+        help_text=_("In addition to the permissions manually assigned, this user will also get all permissions granted to each group he/she is in."))
246+    user_permissions = models.ManyToManyField(Permission, verbose_name=_('user permissions'), blank=True)
247+    objects = UserManager()
248+
249+    class Meta:
250+        abstract = True
251+
252+    def __unicode__(self):
253+        return self.username
254+
255+    def get_absolute_url(self):
256+        return "/users/%s/" % urllib.quote(smart_str(self.username))
257+
258+    def is_anonymous(self):
259+        """
260+        Always returns False. This is a way of comparing User objects to
261+        anonymous users.
262+        """
263+        return False
264+
265+    def is_authenticated(self):
266+        """
267+        Always return True. This is a way to tell if the user has been
268+        authenticated in templates.
269+        """
270+        return True
271+
272+    def get_full_name(self):
273+        "Returns the first_name plus the last_name, with a space in between."
274+        full_name = u'%s %s' % (self.first_name, self.last_name)
275+        return full_name.strip()
276+
277+    def set_password(self, raw_password):
278+        if raw_password is None:
279+            self.set_unusable_password()
280+        else:
281+            import random
282+            algo = 'sha1'
283+            salt = get_hexdigest(algo, str(random.random()), str(random.random()))[:5]
284+            hsh = get_hexdigest(algo, salt, raw_password)
285+            self.password = '%s$%s$%s' % (algo, salt, hsh)
286+
287+    def check_password(self, raw_password):
288+        """
289+        Returns a boolean of whether the raw_password was correct. Handles
290+        encryption formats behind the scenes.
291+        """
292+        # Backwards-compatibility check. Older passwords won't include the
293+        # algorithm or salt.
294+        if '$' not in self.password:
295+            is_correct = (self.password == get_hexdigest('md5', '', raw_password))
296+            if is_correct:
297+                # Convert the password to the new, more secure format.
298+                self.set_password(raw_password)
299+                self.save()
300+            return is_correct
301+        return check_password(raw_password, self.password)
302+
303+    def set_unusable_password(self):
304+        # Sets a value that will never be a valid hash
305+        self.password = UNUSABLE_PASSWORD
306+
307+    def has_usable_password(self):
308+        if self.password is None \
309+            or self.password == UNUSABLE_PASSWORD:
310+            return False
311+        else:
312+            return True
313+
314+    def get_group_permissions(self, obj=None):
315+        """
316+        Returns a list of permission strings that this user has through
317+        his/her groups. This method queries all available auth backends.
318+        If an object is passed in, only permissions matching this object
319+        are returned.
320+        """
321+        permissions = set()
322+        for backend in auth.get_backends():
323+            if hasattr(backend, "get_group_permissions"):
324+                if obj is not None:
325+                    if backend.supports_object_permissions:
326+                        permissions.update(
327+                            backend.get_group_permissions(self, obj)
328+                        )
329+                else:
330+                    permissions.update(backend.get_group_permissions(self))
331+        return permissions
332+
333+    def get_all_permissions(self, obj=None):
334+        return _user_get_all_permissions(self, obj)
335+
336+    def has_perm(self, perm, obj=None):
337+        """
338+        Returns True if the user has the specified permission. This method
339+        queries all available auth backends, but returns immediately if any
340+        backend returns True. Thus, a user who has permission from a single
341+        auth backend is assumed to have permission in general. If an object
342+        is provided, permissions for this specific object are checked.
343+        """
344+
345+        # Active superusers have all permissions.
346+        if self.is_active and self.is_superuser:
347+            return True
348+
349+        # Otherwise we need to check the backends.
350+        return _user_has_perm(self, perm, obj)
351+
352+    def has_perms(self, perm_list, obj=None):
353+        """
354+        Returns True if the user has each of the specified permissions.
355+        If object is passed, it checks if the user has all required perms
356+        for this object.
357+        """
358+        for perm in perm_list:
359+            if not self.has_perm(perm, obj):
360+                return False
361+        return True
362+
363+    def has_module_perms(self, app_label):
364+        """
365+        Returns True if the user has any permissions in the given app
366+        label. Uses pretty much the same logic as has_perm, above.
367+        """
368+        # Active superusers have all permissions.
369+        if self.is_active and self.is_superuser:
370+            return True
371+
372+        return _user_has_module_perms(self, app_label)
373+
374+    def get_and_delete_messages(self):
375+        messages = []
376+        for m in self.message_set.all():
377+            messages.append(m.message)
378+            m.delete()
379+        return messages
380+
381+    def email_user(self, subject, message, from_email=None):
382+        "Sends an e-mail to this User."
383+        from django.core.mail import send_mail
384+        send_mail(subject, message, from_email, [self.email])
385+
386+    def get_profile(self):
387+        """
388+        Returns site-specific profile for this user. Raises
389+        SiteProfileNotAvailable if this site does not allow profiles.
390+        """
391+        if not hasattr(self, '_profile_cache'):
392+            from django.conf import settings
393+            if not getattr(settings, 'AUTH_PROFILE_MODULE', False):
394+                raise SiteProfileNotAvailable('You need to set AUTH_PROFILE_MO'
395+                                              'DULE in your project settings')
396+            try:
397+                app_label, model_name = settings.AUTH_PROFILE_MODULE.split('.')
398+            except ValueError:
399+                raise SiteProfileNotAvailable('app_label and model_name should'
400+                        ' be separated by a dot in the AUTH_PROFILE_MODULE set'
401+                        'ting')
402+
403+            try:
404+                model = models.get_model(app_label, model_name)
405+                if model is None:
406+                    raise SiteProfileNotAvailable('Unable to load the profile '
407+                        'model, check AUTH_PROFILE_MODULE in your project sett'
408+                        'ings')
409+                self._profile_cache = model._default_manager.using(self._state.db).get(user__id__exact=self.id)
410+                self._profile_cache.user = self
411+            except (ImportError, ImproperlyConfigured):
412+                raise SiteProfileNotAvailable
413+        return self._profile_cache
414+
415+    def _get_message_set(self):
416+        import warnings
417+        warnings.warn('The user messaging API is deprecated. Please update'
418+                      ' your code to use the new messages framework.',
419+                      category=DeprecationWarning)
420+        return self._message_set
421+    message_set = property(_get_message_set)
422+
423+class Message(models.Model):
424+    """
425+    The message system is a lightweight way to queue messages for given
426+    users. A message is associated with a User instance (so it is only
427+    applicable for registered users). There's no concept of expiration or
428+    timestamps. Messages are created by the Django admin after successful
429+    actions. For example, "The poll Foo was created successfully." is a
430+    message.
431+    """
432+    user = models.ForeignKey(getattr(settings, 'AUTH_USER_MODULE', 'auth.User'), related_name='_message_set')
433+    message = models.TextField(_('message'))
434+
435+    def __unicode__(self):
436+        return self.message
437+
438+class AnonymousUser(object):
439+    id = None
440+    username = ''
441+    is_staff = False
442+    is_active = False
443+    is_superuser = False
444+    _groups = EmptyManager()
445+    _user_permissions = EmptyManager()
446+
447+    def __init__(self):
448+        pass
449+
450+    def __unicode__(self):
451+        return 'AnonymousUser'
452+
453+    def __str__(self):
454+        return unicode(self).encode('utf-8')
455+
456+    def __eq__(self, other):
457+        return isinstance(other, self.__class__)
458+
459+    def __ne__(self, other):
460+        return not self.__eq__(other)
461+
462+    def __hash__(self):
463+        return 1 # instances always return the same hash value
464+
465+    def save(self):
466+        raise NotImplementedError
467+
468+    def delete(self):
469+        raise NotImplementedError
470+
471+    def set_password(self, raw_password):
472+        raise NotImplementedError
473+
474+    def check_password(self, raw_password):
475+        raise NotImplementedError
476+
477+    def _get_groups(self):
478+        return self._groups
479+    groups = property(_get_groups)
480+
481+    def _get_user_permissions(self):
482+        return self._user_permissions
483+    user_permissions = property(_get_user_permissions)
484+
485+    def get_group_permissions(self, obj=None):
486+        return set()
487+
488+    def get_all_permissions(self, obj=None):
489+        return _user_get_all_permissions(self, obj=obj)
490+
491+    def has_perm(self, perm, obj=None):
492+        return _user_has_perm(self, perm, obj=obj)
493+
494+    def has_perms(self, perm_list, obj=None):
495+        for perm in perm_list:
496+            if not self.has_perm(perm, obj):
497+                return False
498+        return True
499+
500+    def has_module_perms(self, module):
501+        return _user_has_module_perms(self, module)
502+
503+    def get_and_delete_messages(self):
504+        return []
505+
506+    def is_anonymous(self):
507+        return True
508+
509+    def is_authenticated(self):
510+        return False
511diff --git a/django/contrib/auth/models.py b/django/contrib/auth/models.py
512index 24195c8..6291350 100644
513--- django/contrib/auth/models.py
514+++ django/contrib/auth/models.py
515@@ -1,484 +1,49 @@
516-import datetime
517-import urllib
518-
519-from django.contrib import auth
520-from django.contrib.auth.signals import user_logged_in
521-from django.core.exceptions import ImproperlyConfigured
522-from django.db import models
523-from django.db.models.manager import EmptyManager
524-from django.contrib.contenttypes.models import ContentType
525-from django.utils.encoding import smart_str
526-from django.utils.hashcompat import md5_constructor, sha_constructor
527+from django.conf import settings
528 from django.utils.translation import ugettext_lazy as _
529-from django.utils.crypto import constant_time_compare
530-
531-
532-UNUSABLE_PASSWORD = '!' # This will never be a valid hash
533-
534-def get_hexdigest(algorithm, salt, raw_password):
535-    """
536-    Returns a string of the hexdigest of the given plaintext password and salt
537-    using the given algorithm ('md5', 'sha1' or 'crypt').
538-    """
539-    raw_password, salt = smart_str(raw_password), smart_str(salt)
540-    if algorithm == 'crypt':
541-        try:
542-            import crypt
543-        except ImportError:
544-            raise ValueError('"crypt" password algorithm not supported in this environment')
545-        return crypt.crypt(raw_password, salt)
546-
547-    if algorithm == 'md5':
548-        return md5_constructor(salt + raw_password).hexdigest()
549-    elif algorithm == 'sha1':
550-        return sha_constructor(salt + raw_password).hexdigest()
551-    raise ValueError("Got unknown password algorithm type in password.")
552+from django.core.exceptions import ImproperlyConfigured
553 
554-def check_password(raw_password, enc_password):
555-    """
556-    Returns a boolean of whether the raw_password was correct. Handles
557-    encryption formats behind the scenes.
558-    """
559-    algo, salt, hsh = enc_password.split('$')
560-    return constant_time_compare(hsh, get_hexdigest(algo, salt, raw_password))
561+from django.contrib.auth.base import *
562 
563-def update_last_login(sender, user, **kwargs):
564-    """
565-    A signal receiver which updates the last_login date for
566-    the user logging in.
567-    """
568-    user.last_login = datetime.datetime.now()
569-    user.save()
570-user_logged_in.connect(update_last_login)
571 
572-class SiteProfileNotAvailable(Exception):
573+class AuthNotAvailable(Exception):
574     pass
575 
576-class PermissionManager(models.Manager):
577-    def get_by_natural_key(self, codename, app_label, model):
578-        return self.get(
579-            codename=codename,
580-            content_type=ContentType.objects.get_by_natural_key(app_label, model)
581-        )
582-
583-class Permission(models.Model):
584-    """The permissions system provides a way to assign permissions to specific users and groups of users.
585-
586-    The permission system is used by the Django admin site, but may also be useful in your own code. The Django admin site uses permissions as follows:
587-
588-        - The "add" permission limits the user's ability to view the "add" form and add an object.
589-        - The "change" permission limits a user's ability to view the change list, view the "change" form and change an object.
590-        - The "delete" permission limits the ability to delete an object.
591-
592-    Permissions are set globally per type of object, not per specific object instance. It is possible to say "Mary may change news stories," but it's not currently possible to say "Mary may change news stories, but only the ones she created herself" or "Mary may only change news stories that have a certain status or publication date."
593-
594-    Three basic permissions -- add, change and delete -- are automatically created for each Django model.
595-    """
596-    name = models.CharField(_('name'), max_length=50)
597-    content_type = models.ForeignKey(ContentType)
598-    codename = models.CharField(_('codename'), max_length=100)
599-    objects = PermissionManager()
600-
601-    class Meta:
602-        verbose_name = _('permission')
603-        verbose_name_plural = _('permissions')
604-        unique_together = (('content_type', 'codename'),)
605-        ordering = ('content_type__app_label', 'content_type__model', 'codename')
606-
607-    def __unicode__(self):
608-        return u"%s | %s | %s" % (
609-            unicode(self.content_type.app_label),
610-            unicode(self.content_type),
611-            unicode(self.name))
612-
613-    def natural_key(self):
614-        return (self.codename,) + self.content_type.natural_key()
615-    natural_key.dependencies = ['contenttypes.contenttype']
616-
617-class Group(models.Model):
618-    """Groups are a generic way of categorizing users to apply permissions, or some other label, to those users. A user can belong to any number of groups.
619-
620-    A user in a group automatically has all the permissions granted to that group. For example, if the group Site editors has the permission can_edit_home_page, any user in that group will have that permission.
621-
622-    Beyond permissions, groups are a convenient way to categorize users to apply some label, or extended functionality, to them. For example, you could create a group 'Special users', and you could write code that would do special things to those users -- such as giving them access to a members-only portion of your site, or sending them members-only e-mail messages.
623-    """
624-    name = models.CharField(_('name'), max_length=80, unique=True)
625-    permissions = models.ManyToManyField(Permission, verbose_name=_('permissions'), blank=True)
626-
627-    class Meta:
628-        verbose_name = _('group')
629-        verbose_name_plural = _('groups')
630-
631-    def __unicode__(self):
632-        return self.name
633-
634-class UserManager(models.Manager):
635-    def create_user(self, username, email, password=None):
636-        """
637-        Creates and saves a User with the given username, e-mail and password.
638-        """
639-        now = datetime.datetime.now()
640-
641-        # Normalize the address by lowercasing the domain part of the email
642-        # address.
643-        try:
644-            email_name, domain_part = email.strip().split('@', 1)
645-        except ValueError:
646-            pass
647-        else:
648-            email = '@'.join([email_name, domain_part.lower()])
649-
650-        user = self.model(username=username, email=email, is_staff=False,
651-                         is_active=True, is_superuser=False, last_login=now,
652-                         date_joined=now)
653-
654-        user.set_password(password)
655-        user.save(using=self._db)
656-        return user
657-
658-    def create_superuser(self, username, email, password):
659-        u = self.create_user(username, email, password)
660-        u.is_staff = True
661-        u.is_active = True
662-        u.is_superuser = True
663-        u.save(using=self._db)
664-        return u
665-
666-    def make_random_password(self, length=10, allowed_chars='abcdefghjkmnpqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ23456789'):
667-        "Generates a random password with the given length and given allowed_chars"
668-        # Note that default value of allowed_chars does not have "I" or letters
669-        # that look like it -- just to avoid confusion.
670-        from random import choice
671-        return ''.join([choice(allowed_chars) for i in range(length)])
672-
673-
674-# A few helper functions for common logic between User and AnonymousUser.
675-def _user_get_all_permissions(user, obj):
676-    permissions = set()
677-    anon = user.is_anonymous()
678-    for backend in auth.get_backends():
679-        if not anon or backend.supports_anonymous_user:
680-            if hasattr(backend, "get_all_permissions"):
681-                if obj is not None:
682-                    if backend.supports_object_permissions:
683-                        permissions.update(
684-                            backend.get_all_permissions(user, obj)
685-                        )
686-                else:
687-                    permissions.update(backend.get_all_permissions(user))
688-    return permissions
689-
690-
691-def _user_has_perm(user, perm, obj):
692-    anon = user.is_anonymous()
693-    active = user.is_active
694-    for backend in auth.get_backends():
695-        if (not active and not anon and backend.supports_inactive_user) or \
696-                    (not anon or backend.supports_anonymous_user):
697-            if hasattr(backend, "has_perm"):
698-                if obj is not None:
699-                    if (backend.supports_object_permissions and
700-                        backend.has_perm(user, perm, obj)):
701-                            return True
702-                else:
703-                    if backend.has_perm(user, perm):
704-                        return True
705-    return False
706-
707-
708-def _user_has_module_perms(user, app_label):
709-    anon = user.is_anonymous()
710-    active = user.is_active
711-    for backend in auth.get_backends():
712-        if (not active and not anon and backend.supports_inactive_user) or \
713-                    (not anon or backend.supports_anonymous_user):
714-            if hasattr(backend, "has_module_perms"):
715-                if backend.has_module_perms(user, app_label):
716-                    return True
717-    return False
718-
719-
720-class User(models.Model):
721-    """
722-    Users within the Django authentication system are represented by this model.
723-
724-    Username and password are required. Other fields are optional.
725-    """
726-    username = models.CharField(_('username'), max_length=30, unique=True, help_text=_("Required. 30 characters or fewer. Letters, numbers and @/./+/-/_ characters"))
727-    first_name = models.CharField(_('first name'), max_length=60, blank=True)
728-    last_name = models.CharField(_('last name'), max_length=60, blank=True)
729-    email = models.EmailField(_('e-mail address'), max_length=255, blank=True)
730-    password = models.CharField(_('password'), max_length=128, help_text=_("Use '[algo]$[salt]$[hexdigest]' or use the <a href=\"password/\">change password form</a>."))
731-    is_staff = models.BooleanField(_('staff status'), default=False, help_text=_("Designates whether the user can log into this admin site."))
732-    is_active = models.BooleanField(_('active'), default=True, help_text=_("Designates whether this user should be treated as active. Unselect this instead of deleting accounts."))
733-    is_superuser = models.BooleanField(_('superuser status'), default=False, help_text=_("Designates that this user has all permissions without explicitly assigning them."))
734-    last_login = models.DateTimeField(_('last login'), default=datetime.datetime.now)
735-    date_joined = models.DateTimeField(_('date joined'), default=datetime.datetime.now)
736-    groups = models.ManyToManyField(Group, verbose_name=_('groups'), blank=True,
737-        help_text=_("In addition to the permissions manually assigned, this user will also get all permissions granted to each group he/she is in."))
738-    user_permissions = models.ManyToManyField(Permission, verbose_name=_('user permissions'), blank=True)
739-    objects = UserManager()
740-
741-    class Meta:
742-        verbose_name = _('user')
743-        verbose_name_plural = _('users')
744-
745-    def __unicode__(self):
746-        return self.username
747-
748-    def get_absolute_url(self):
749-        return "/users/%s/" % urllib.quote(smart_str(self.username))
750-
751-    def is_anonymous(self):
752-        """
753-        Always returns False. This is a way of comparing User objects to
754-        anonymous users.
755-        """
756-        return False
757-
758-    def is_authenticated(self):
759-        """
760-        Always return True. This is a way to tell if the user has been
761-        authenticated in templates.
762-        """
763-        return True
764-
765-    def get_full_name(self):
766-        "Returns the first_name plus the last_name, with a space in between."
767-        full_name = u'%s %s' % (self.first_name, self.last_name)
768-        return full_name.strip()
769-
770-    def set_password(self, raw_password):
771-        if raw_password is None:
772-            self.set_unusable_password()
773-        else:
774-            import random
775-            algo = 'sha1'
776-            salt = get_hexdigest(algo, str(random.random()), str(random.random()))[:5]
777-            hsh = get_hexdigest(algo, salt, raw_password)
778-            self.password = '%s$%s$%s' % (algo, salt, hsh)
779-
780-    def check_password(self, raw_password):
781-        """
782-        Returns a boolean of whether the raw_password was correct. Handles
783-        encryption formats behind the scenes.
784-        """
785-        # Backwards-compatibility check. Older passwords won't include the
786-        # algorithm or salt.
787-        if '$' not in self.password:
788-            is_correct = (self.password == get_hexdigest('md5', '', raw_password))
789-            if is_correct:
790-                # Convert the password to the new, more secure format.
791-                self.set_password(raw_password)
792-                self.save()
793-            return is_correct
794-        return check_password(raw_password, self.password)
795-
796-    def set_unusable_password(self):
797-        # Sets a value that will never be a valid hash
798-        self.password = UNUSABLE_PASSWORD
799-
800-    def has_usable_password(self):
801-        if self.password is None \
802-            or self.password == UNUSABLE_PASSWORD:
803-            return False
804-        else:
805-            return True
806-
807-    def get_group_permissions(self, obj=None):
808-        """
809-        Returns a list of permission strings that this user has through
810-        his/her groups. This method queries all available auth backends.
811-        If an object is passed in, only permissions matching this object
812-        are returned.
813-        """
814-        permissions = set()
815-        for backend in auth.get_backends():
816-            if hasattr(backend, "get_group_permissions"):
817-                if obj is not None:
818-                    if backend.supports_object_permissions:
819-                        permissions.update(
820-                            backend.get_group_permissions(self, obj)
821-                        )
822-                else:
823-                    permissions.update(backend.get_group_permissions(self))
824-        return permissions
825-
826-    def get_all_permissions(self, obj=None):
827-        return _user_get_all_permissions(self, obj)
828-
829-    def has_perm(self, perm, obj=None):
830-        """
831-        Returns True if the user has the specified permission. This method
832-        queries all available auth backends, but returns immediately if any
833-        backend returns True. Thus, a user who has permission from a single
834-        auth backend is assumed to have permission in general. If an object
835-        is provided, permissions for this specific object are checked.
836-        """
837-
838-        # Active superusers have all permissions.
839-        if self.is_active and self.is_superuser:
840-            return True
841-
842-        # Otherwise we need to check the backends.
843-        return _user_has_perm(self, perm, obj)
844-
845-    def has_perms(self, perm_list, obj=None):
846-        """
847-        Returns True if the user has each of the specified permissions.
848-        If object is passed, it checks if the user has all required perms
849-        for this object.
850-        """
851-        for perm in perm_list:
852-            if not self.has_perm(perm, obj):
853-                return False
854-        return True
855-
856-    def has_module_perms(self, app_label):
857-        """
858-        Returns True if the user has any permissions in the given app
859-        label. Uses pretty much the same logic as has_perm, above.
860-        """
861-        # Active superusers have all permissions.
862-        if self.is_active and self.is_superuser:
863-            return True
864-
865-        return _user_has_module_perms(self, app_label)
866-
867-    def get_and_delete_messages(self):
868-        messages = []
869-        for m in self.message_set.all():
870-            messages.append(m.message)
871-            m.delete()
872-        return messages
873-
874-    def email_user(self, subject, message, from_email=None):
875-        "Sends an e-mail to this User."
876-        from django.core.mail import send_mail
877-        send_mail(subject, message, from_email, [self.email])
878-
879-    def get_profile(self):
880-        """
881-        Returns site-specific profile for this user. Raises
882-        SiteProfileNotAvailable if this site does not allow profiles.
883-        """
884-        if not hasattr(self, '_profile_cache'):
885-            from django.conf import settings
886-            if not getattr(settings, 'AUTH_PROFILE_MODULE', False):
887-                raise SiteProfileNotAvailable('You need to set AUTH_PROFILE_MO'
888-                                              'DULE in your project settings')
889-            try:
890-                app_label, model_name = settings.AUTH_PROFILE_MODULE.split('.')
891-            except ValueError:
892-                raise SiteProfileNotAvailable('app_label and model_name should'
893-                        ' be separated by a dot in the AUTH_PROFILE_MODULE set'
894-                        'ting')
895-
896-            try:
897-                model = models.get_model(app_label, model_name)
898-                if model is None:
899-                    raise SiteProfileNotAvailable('Unable to load the profile '
900-                        'model, check AUTH_PROFILE_MODULE in your project sett'
901-                        'ings')
902-                self._profile_cache = model._default_manager.using(self._state.db).get(user__id__exact=self.id)
903-                self._profile_cache.user = self
904-            except (ImportError, ImproperlyConfigured):
905-                raise SiteProfileNotAvailable
906-        return self._profile_cache
907-
908-    def _get_message_set(self):
909-        import warnings
910-        warnings.warn('The user messaging API is deprecated. Please update'
911-                      ' your code to use the new messages framework.',
912-                      category=DeprecationWarning)
913-        return self._message_set
914-    message_set = property(_get_message_set)
915-
916-class Message(models.Model):
917-    """
918-    The message system is a lightweight way to queue messages for given
919-    users. A message is associated with a User instance (so it is only
920-    applicable for registered users). There's no concept of expiration or
921-    timestamps. Messages are created by the Django admin after successful
922-    actions. For example, "The poll Foo was created successfully." is a
923-    message.
924-    """
925-    user = models.ForeignKey(User, related_name='_message_set')
926-    message = models.TextField(_('message'))
927-
928-    def __unicode__(self):
929-        return self.message
930-
931-class AnonymousUser(object):
932-    id = None
933-    username = ''
934-    is_staff = False
935-    is_active = False
936-    is_superuser = False
937-    _groups = EmptyManager()
938-    _user_permissions = EmptyManager()
939-
940-    def __init__(self):
941-        pass
942-
943-    def __unicode__(self):
944-        return 'AnonymousUser'
945-
946-    def __str__(self):
947-        return unicode(self).encode('utf-8')
948-
949-    def __eq__(self, other):
950-        return isinstance(other, self.__class__)
951-
952-    def __ne__(self, other):
953-        return not self.__eq__(other)
954-
955-    def __hash__(self):
956-        return 1 # instances always return the same hash value
957-
958-    def save(self):
959-        raise NotImplementedError
960-
961-    def delete(self):
962-        raise NotImplementedError
963-
964-    def set_password(self, raw_password):
965-        raise NotImplementedError
966-
967-    def check_password(self, raw_password):
968-        raise NotImplementedError
969-
970-    def _get_groups(self):
971-        return self._groups
972-    groups = property(_get_groups)
973-
974-    def _get_user_permissions(self):
975-        return self._user_permissions
976-    user_permissions = property(_get_user_permissions)
977-
978-    def get_group_permissions(self, obj=None):
979-        return set()
980-
981-    def get_all_permissions(self, obj=None):
982-        return _user_get_all_permissions(self, obj=obj)
983-
984-    def has_perm(self, perm, obj=None):
985-        return _user_has_perm(self, perm, obj=obj)
986-
987-    def has_perms(self, perm_list, obj=None):
988-        for perm in perm_list:
989-            if not self.has_perm(perm, obj):
990-                return False
991-        return True
992-
993-    def has_module_perms(self, module):
994-        return _user_has_module_perms(self, module)
995-
996-    def get_and_delete_messages(self):
997-        return []
998-
999-    def is_anonymous(self):
1000-        return True
1001 
1002-    def is_authenticated(self):
1003-        return False
1004+if hasattr(settings, 'AUTH_USER_MODULE'):
1005+    try:
1006+        app_label, model_name = settings.AUTH_USER_MODULE.split('.')
1007+    except ValueError:
1008+        raise AuthNotAvailable('app_label and model_name should'
1009+                ' be separated by a dot in the AUTH_USER_MODULE set'
1010+                'ting')
1011+
1012+    try:
1013+        # Grab the AUTH_USER_MODULE path and classname from the settings.
1014+        module = __import__(app_label + '.models', {}, {}, [''])
1015+        # Store the user model so it is accessible with the standard
1016+        # 'from django.contrib.auth.models import User'
1017+        User = getattr(module, model_name, None)
1018+
1019+        if User is None:
1020+            raise AuthNotAvailable('Unable to load the user '
1021+                'model, check AUTH_USER_MODULE in your project sett'
1022+                'ings')
1023+    except (ImportError, ImproperlyConfigured):
1024+        raise AuthNotAvailable
1025+
1026+    # Add te User model to the django models cache
1027+    # These two lines allow the custom auth_user model to play nicely with syncdb
1028+    # and other systems that rely on functions like
1029+    # django.db.models.loading.get_model(...)
1030+    from django.db.models.loading import cache
1031+    cache.register_models('auth', User)
1032+
1033+    # We need to remove whatever we used as the AUTH_USER_MODUlE from the
1034+    # db cache so apps like contenttypes don't think that it's part
1035+    # of contrib.auth
1036+    del cache.app_models['auth'][User.__name__.lower()]
1037+else:
1038+    class User(UserTemplate):
1039+        class Meta:
1040+            verbose_name = _('user')
1041+            verbose_name_plural = _('users')