web/lib/django/contrib/auth/models.py
changeset 29 cc9b7e14412b
parent 0 0d40e90630ef
--- a/web/lib/django/contrib/auth/models.py	Wed May 19 17:43:59 2010 +0200
+++ b/web/lib/django/contrib/auth/models.py	Tue May 25 02:43:45 2010 +0200
@@ -10,13 +10,9 @@
 from django.utils.hashcompat import md5_constructor, sha_constructor
 from django.utils.translation import ugettext_lazy as _
 
+
 UNUSABLE_PASSWORD = '!' # This will never be a valid hash
 
-try:
-    set
-except NameError:
-    from sets import Set as set   # Python 2.3 fallback
-
 def get_hexdigest(algorithm, salt, raw_password):
     """
     Returns a string of the hexdigest of the given plaintext password and salt
@@ -47,6 +43,13 @@
 class SiteProfileNotAvailable(Exception):
     pass
 
+class PermissionManager(models.Manager):
+    def get_by_natural_key(self, codename, app_label, model):
+        return self.get(
+            codename=codename,
+            content_type=ContentType.objects.get_by_natural_key(app_label, model)
+        )
+
 class Permission(models.Model):
     """The permissions system provides a way to assign permissions to specific users and groups of users.
 
@@ -63,12 +66,13 @@
     name = models.CharField(_('name'), max_length=50)
     content_type = models.ForeignKey(ContentType)
     codename = models.CharField(_('codename'), max_length=100)
+    objects = PermissionManager()
 
     class Meta:
         verbose_name = _('permission')
         verbose_name_plural = _('permissions')
         unique_together = (('content_type', 'codename'),)
-        ordering = ('content_type__app_label', 'codename')
+        ordering = ('content_type__app_label', 'content_type__model', 'codename')
 
     def __unicode__(self):
         return u"%s | %s | %s" % (
@@ -76,6 +80,10 @@
             unicode(self.content_type),
             unicode(self.name))
 
+    def natural_key(self):
+        return (self.codename,) + self.content_type.natural_key()
+    natural_key.dependencies = ['contenttypes.contenttype']
+
 class Group(models.Model):
     """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.
 
@@ -95,14 +103,30 @@
 
 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."
+        """
+        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)
+        
+        # Normalize the address by lowercasing the domain part of the email
+        # address.
+        try:
+            email_name, domain_part = email.strip().split('@', 1)
+        except ValueError:
+            pass
+        else:
+            email = '@'.join([email_name, domain_part.lower()])
+
+        user = self.model(username=username, email=email, is_staff=False,
+                         is_active=True, is_superuser=False, last_login=now,
+                         date_joined=now)
+
         if password:
             user.set_password(password)
         else:
             user.set_unusable_password()
-        user.save()
+        user.save(using=self._db)
         return user
 
     def create_superuser(self, username, email, password):
@@ -110,7 +134,7 @@
         u.is_staff = True
         u.is_active = True
         u.is_superuser = True
-        u.save()
+        u.save(using=self._db)
         return u
 
     def make_random_password(self, length=10, allowed_chars='abcdefghjkmnpqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ23456789'):
@@ -120,12 +144,56 @@
         from random import choice
         return ''.join([choice(allowed_chars) for i in range(length)])
 
+
+# A few helper functions for common logic between User and AnonymousUser.
+def _user_get_all_permissions(user, obj):
+    permissions = set()
+    anon = user.is_anonymous()
+    for backend in auth.get_backends():
+        if not anon or backend.supports_anonymous_user:
+            if hasattr(backend, "get_all_permissions"):
+                if obj is not None:
+                    if backend.supports_object_permissions:
+                        permissions.update(
+                            backend.get_all_permissions(user, obj)
+                        )
+                else:
+                    permissions.update(backend.get_all_permissions(user))
+    return permissions
+
+
+def _user_has_perm(user, perm, obj):
+    anon = user.is_anonymous()
+    for backend in auth.get_backends():
+        if not anon or backend.supports_anonymous_user:
+            if hasattr(backend, "has_perm"):
+                if obj is not None:
+                    if (backend.supports_object_permissions and
+                        backend.has_perm(user, perm, obj)):
+                            return True
+                else:
+                    if backend.has_perm(user, perm):
+                        return True
+    return False
+
+
+def _user_has_module_perms(user, app_label):
+    anon = user.is_anonymous()
+    for backend in auth.get_backends():
+        if not anon or backend.supports_anonymous_user:
+            if hasattr(backend, "has_module_perms"):
+                if backend.has_module_perms(user, app_label):
+                    return True
+    return False
+
+
 class User(models.Model):
-    """Users within the Django authentication system are represented by this 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, help_text=_("Required. 30 characters or fewer. Alphanumeric characters only (letters, digits and underscores)."))
+    username = models.CharField(_('username'), max_length=30, unique=True, help_text=_("Required. 30 characters or fewer. Letters, numbers and @/./+/-/_ characters"))
     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)
@@ -151,11 +219,16 @@
         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."
+        """
+        Always returns False. This is a way of comparing User objects to
+        anonymous users.
+        """
         return False
 
     def is_authenticated(self):
-        """Always return True. This is a way to tell if the user has been authenticated in templates.
+        """
+        Always return True. This is a way to tell if the user has been
+        authenticated in templates.
         """
         return True
 
@@ -194,30 +267,35 @@
     def has_usable_password(self):
         return self.password != UNUSABLE_PASSWORD
 
-    def get_group_permissions(self):
+    def get_group_permissions(self, obj=None):
         """
         Returns a list of permission strings that this user has through
         his/her groups. This method queries all available auth backends.
+        If an object is passed in, only permissions matching this object
+        are returned.
         """
         permissions = set()
         for backend in auth.get_backends():
             if hasattr(backend, "get_group_permissions"):
-                permissions.update(backend.get_group_permissions(self))
+                if obj is not None:
+                    if backend.supports_object_permissions:
+                        permissions.update(
+                            backend.get_group_permissions(self, obj)
+                        )
+                else:
+                    permissions.update(backend.get_group_permissions(self))
         return permissions
 
-    def get_all_permissions(self):
-        permissions = set()
-        for backend in auth.get_backends():
-            if hasattr(backend, "get_all_permissions"):
-                permissions.update(backend.get_all_permissions(self))
-        return permissions
+    def get_all_permissions(self, obj=None):
+        return _user_get_all_permissions(self, obj)
 
-    def has_perm(self, perm):
+    def has_perm(self, perm, obj=None):
         """
         Returns True if the user has the specified permission. This method
         queries all available auth backends, but returns immediately if any
         backend returns True. Thus, a user who has permission from a single
-        auth backend is assumed to have permission in general.
+        auth backend is assumed to have permission in general. If an object
+        is provided, permissions for this specific object are checked.
         """
         # Inactive users have no permissions.
         if not self.is_active:
@@ -228,16 +306,16 @@
             return True
 
         # Otherwise we need to check the backends.
-        for backend in auth.get_backends():
-            if hasattr(backend, "has_perm"):
-                if backend.has_perm(self, perm):
-                    return True
-        return False
+        return _user_has_perm(self, perm, obj)
 
-    def has_perms(self, perm_list):
-        """Returns True if the user has each of the specified permissions."""
+    def has_perms(self, perm_list, obj=None):
+        """
+        Returns True if the user has each of the specified permissions.
+        If object is passed, it checks if the user has all required perms
+        for this object.
+        """
         for perm in perm_list:
-            if not self.has_perm(perm):
+            if not self.has_perm(perm, obj):
                 return False
         return True
 
@@ -252,11 +330,7 @@
         if self.is_superuser:
             return True
 
-        for backend in auth.get_backends():
-            if hasattr(backend, "has_module_perms"):
-                if backend.has_module_perms(self, app_label):
-                    return True
-        return False
+        return _user_has_module_perms(self, app_label)
 
     def get_and_delete_messages(self):
         messages = []
@@ -278,16 +352,35 @@
         if not hasattr(self, '_profile_cache'):
             from django.conf import settings
             if not getattr(settings, 'AUTH_PROFILE_MODULE', False):
-                raise SiteProfileNotAvailable
+                raise SiteProfileNotAvailable('You need to set AUTH_PROFILE_MO'
+                                              'DULE in your project settings')
             try:
                 app_label, model_name = settings.AUTH_PROFILE_MODULE.split('.')
+            except ValueError:
+                raise SiteProfileNotAvailable('app_label and model_name should'
+                        ' be separated by a dot in the AUTH_PROFILE_MODULE set'
+                        'ting')
+
+            try:
                 model = models.get_model(app_label, model_name)
-                self._profile_cache = model._default_manager.get(user__id__exact=self.id)
+                if model is None:
+                    raise SiteProfileNotAvailable('Unable to load the profile '
+                        'model, check AUTH_PROFILE_MODULE in your project sett'
+                        'ings')
+                self._profile_cache = model._default_manager.using(self._state.db).get(user__id__exact=self.id)
                 self._profile_cache.user = self
             except (ImportError, ImproperlyConfigured):
                 raise SiteProfileNotAvailable
         return self._profile_cache
 
+    def _get_message_set(self):
+        import warnings
+        warnings.warn('The user messaging API is deprecated. Please update'
+                      ' your code to use the new messages framework.',
+                      category=PendingDeprecationWarning)
+        return self._message_set
+    message_set = property(_get_message_set)
+
 class Message(models.Model):
     """
     The message system is a lightweight way to queue messages for given
@@ -297,7 +390,7 @@
     actions. For example, "The poll Foo was created successfully." is a
     message.
     """
-    user = models.ForeignKey(User)
+    user = models.ForeignKey(User, related_name='_message_set')
     message = models.TextField(_('message'))
 
     def __unicode__(self):
@@ -350,14 +443,23 @@
         return self._user_permissions
     user_permissions = property(_get_user_permissions)
 
-    def has_perm(self, perm):
-        return False
+    def get_group_permissions(self, obj=None):
+        return set()
+
+    def get_all_permissions(self, obj=None):
+        return _user_get_all_permissions(self, obj=obj)
 
-    def has_perms(self, perm_list):
-        return False
+    def has_perm(self, perm, obj=None):
+        return _user_has_perm(self, perm, obj=obj)
+
+    def has_perms(self, perm_list, obj=None):
+        for perm in perm_list:
+            if not self.has_perm(perm, obj):
+                return False
+        return True
 
     def has_module_perms(self, module):
-        return False
+        return _user_has_module_perms(self, module)
 
     def get_and_delete_messages(self):
         return []