Ticket #9788: models.py

File models.py, 15.1 KB (added by jonathan@…, 7 years ago)

django/contrib/auth/models.py

Line 
1from django.contrib import auth
2from django.core.exceptions import ImproperlyConfigured
3from django.db import models
4from django.db.models.manager import EmptyManager
5from django.contrib.contenttypes.models import ContentType
6from django.utils.encoding import smart_str
7from django.utils.translation import ugettext_lazy as _
8import datetime
9import urllib
10
11UNUSABLE_PASSWORD = '!' # This will never be a valid hash
12
13try:
14    set
15except NameError:
16    from sets import Set as set   # Python 2.3 fallback
17
18def get_hexdigest(algorithm, salt, raw_password):
19    """
20    Returns a string of the hexdigest of the given plaintext password and salt
21    using the given algorithm ('md5', 'sha1' or 'crypt').
22    """
23    raw_password, salt = smart_str(raw_password), smart_str(salt)
24    if algorithm == 'crypt':
25        try:
26            import crypt
27        except ImportError:
28            raise ValueError('"crypt" password algorithm not supported in this environment')
29        return crypt.crypt(raw_password, salt)
30    # The rest of the supported algorithms are supported by hashlib, but
31    # hashlib is only available in Python 2.5.
32    try:
33        import hashlib
34    except ImportError:
35        if algorithm == 'md5':
36            import md5
37            return md5.new(salt + raw_password).hexdigest()
38        elif algorithm == 'sha1':
39            import sha
40            return sha.new(salt + raw_password).hexdigest()
41    else:
42        if algorithm == 'md5':
43            return hashlib.md5(salt + raw_password).hexdigest()
44        elif algorithm == 'sha1':
45            return hashlib.sha1(salt + raw_password).hexdigest()
46    raise ValueError("Got unknown password algorithm type in password.")
47
48def check_password(raw_password, enc_password):
49    """
50    Returns a boolean of whether the raw_password was correct. Handles
51    encryption formats behind the scenes.
52    """
53    algo, salt, hsh = enc_password.split('$')
54    return hsh == get_hexdigest(algo, salt, raw_password)
55
56class SiteProfileNotAvailable(Exception):
57    pass
58
59class Permission(models.Model):
60    """The permissions system provides a way to assign permissions to specific users and groups of users.
61
62    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:
63
64        - The "add" permission limits the user's ability to view the "add" form and add an object.
65        - The "change" permission limits a user's ability to view the change list, view the "change" form and change an object.
66        - The "delete" permission limits the ability to delete an object.
67
68    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."
69
70    Three basic permissions -- add, change and delete -- are automatically created for each Django model.
71    """
72    name = models.CharField(_('name'), max_length=50)
73    content_type = models.ForeignKey(ContentType)
74    codename = models.CharField(_('codename'), max_length=100)
75
76    class Meta:
77        verbose_name = _('permission')
78        verbose_name_plural = _('permissions')
79        unique_together = (('content_type', 'codename'),)
80        ordering = ('content_type__app_label', 'codename')
81
82    def __unicode__(self):
83        return u"%s | %s | %s" % (
84            unicode(self.content_type.app_label),
85            unicode(self.content_type),
86            unicode(self.name))
87
88class Group(models.Model):
89    """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.
90
91    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.
92
93    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.
94    """
95    name = models.CharField(_('name'), max_length=80, unique=True)
96    permissions = models.ManyToManyField(Permission, verbose_name=_('permissions'), blank=True)
97
98    class Meta:
99        verbose_name = _('group')
100        verbose_name_plural = _('groups')
101
102    def __unicode__(self):
103        return self.name
104
105class UserManager(models.Manager):
106    def create_user(self, username, email, password=None):
107        "Creates and saves a User with the given username, e-mail and password."
108        now = datetime.datetime.now()
109        user = self.model(None, username, '', '', email.strip().lower(), 'placeholder', False, True, False, now, now)
110        if password:
111            user.set_password(password)
112        else:
113            user.set_unusable_password()
114        user.save()
115        return user
116
117    def create_superuser(self, username, email, password):
118        u = self.create_user(username, email, password)
119        u.is_staff = True
120        u.is_active = True
121        u.is_superuser = True
122        u.save()
123
124    def make_random_password(self, length=10, allowed_chars='abcdefghjkmnpqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ23456789'):
125        "Generates a random password with the given length and given allowed_chars"
126        # Note that default value of allowed_chars does not have "I" or letters
127        # that look like it -- just to avoid confusion.
128        from random import choice
129        return ''.join([choice(allowed_chars) for i in range(length)])
130
131class User(models.Model):
132    """Users within the Django authentication system are represented by this model.
133
134    Username and password are required. Other fields are optional.
135    """
136    username = models.CharField(_('username'), max_length=30, unique=True, help_text=_("Required. 30 characters or fewer. Alphanumeric characters only (letters, digits and underscores)."))
137    first_name = models.CharField(_('first name'), max_length=30, blank=True)
138    last_name = models.CharField(_('last name'), max_length=30, blank=True)
139    email = models.EmailField(_('e-mail address'), blank=True)
140    password = models.CharField(_('password'), max_length=128, help_text=_("Use '[algo]$[salt]$[hexdigest]' or use the <a href=\"password/\">change password form</a>."))
141    is_staff = models.BooleanField(_('staff status'), default=False, help_text=_("Designates whether the user can log into this admin site."))
142    is_active = models.BooleanField(_('active'), default=True, help_text=_("Designates whether this user should be treated as active. Unselect this instead of deleting accounts."))
143    is_superuser = models.BooleanField(_('superuser status'), default=False, help_text=_("Designates that this user has all permissions without explicitly assigning them."))
144    last_login = models.DateTimeField(_('last login'), default=datetime.datetime.now)
145    date_joined = models.DateTimeField(_('date joined'), default=datetime.datetime.now)
146    groups = models.ManyToManyField(Group, verbose_name=_('groups'), blank=True,
147        help_text=_("In addition to the permissions manually assigned, this user will also get all permissions granted to each group he/she is in."))
148    user_permissions = models.ManyToManyField(Permission, verbose_name=_('user permissions'), blank=True)
149    objects = UserManager()
150
151    class Meta:
152        verbose_name = _('user')
153        verbose_name_plural = _('users')
154
155    def __unicode__(self):
156        return self.username
157
158    def get_absolute_url(self):
159        return "/users/%s/" % urllib.quote(smart_str(self.username))
160
161    def is_anonymous(self):
162        "Always returns False. This is a way of comparing User objects to anonymous users."
163        return False
164
165    def is_authenticated(self):
166        """Always return True. This is a way to tell if the user has been authenticated in templates.
167        """
168        return True
169
170    def get_full_name(self):
171        "Returns the first_name plus the last_name, with a space in between."
172        full_name = u'%s %s' % (self.first_name, self.last_name)
173        return full_name.strip()
174
175    def set_password(self, raw_password):
176        import random
177        algo = 'sha1'
178        salt = get_hexdigest(algo, str(random.random()), str(random.random()))[:5]
179        hsh = get_hexdigest(algo, salt, raw_password)
180        self.password = '%s$%s$%s' % (algo, salt, hsh)
181
182    def check_password(self, raw_password):
183        """
184        Returns a boolean of whether the raw_password was correct. Handles
185        encryption formats behind the scenes.
186        """
187        # Backwards-compatibility check. Older passwords won't include the
188        # algorithm or salt.
189        if '$' not in self.password:
190            is_correct = (self.password == get_hexdigest('md5', '', raw_password))
191            if is_correct:
192                # Convert the password to the new, more secure format.
193                self.set_password(raw_password)
194                self.save()
195            return is_correct
196        return check_password(raw_password, self.password)
197
198    def set_unusable_password(self):
199        # Sets a value that will never be a valid hash
200        self.password = UNUSABLE_PASSWORD
201
202    def has_usable_password(self):
203        return self.password != UNUSABLE_PASSWORD
204
205    def get_group_permissions(self):
206        """
207        Returns a list of permission strings that this user has through
208        his/her groups. This method queries all available auth backends.
209        """
210        permissions = set()
211        for backend in auth.get_backends():
212            if hasattr(backend, "get_group_permissions"):
213                permissions.update(backend.get_group_permissions(self))
214        return permissions
215
216    def get_all_permissions(self):
217        permissions = set()
218        for backend in auth.get_backends():
219            if hasattr(backend, "get_all_permissions"):
220                permissions.update(backend.get_all_permissions(self))
221        return permissions
222
223    def has_perm(self, perm):
224        """
225        Returns True if the user has the specified permission. This method
226        queries all available auth backends, but returns immediately if any
227        backend returns True. Thus, a user who has permission from a single
228        auth backend is assumed to have permission in general.
229        """
230        # Inactive users have no permissions.
231        if not self.is_active:
232            return False
233
234        # Superusers have all permissions.
235        if self.is_superuser:
236            return True
237
238        # Otherwise we need to check the backends.
239        for backend in auth.get_backends():
240            if hasattr(backend, "has_perm"):
241                if backend.has_perm(self, perm):
242                    return True
243        return False
244
245    def has_perms(self, perm_list):
246        """Returns True if the user has each of the specified permissions."""
247        for perm in perm_list:
248            if not self.has_perm(perm):
249                return False
250        return True
251
252    def has_module_perms(self, app_label):
253        """
254        Returns True if the user has any permissions in the given app
255        label. Uses pretty much the same logic as has_perm, above.
256        """
257        if not self.is_active:
258            return False
259
260        if self.is_superuser:
261            return True
262
263        for backend in auth.get_backends():
264            if hasattr(backend, "has_module_perms"):
265                if backend.has_module_perms(self, app_label):
266                    return True
267        return False
268
269    def get_and_delete_messages(self):
270        messages = []
271        for m in self.message_set.all():
272            messages.append(m.message)
273            m.delete()
274        return messages
275
276    def email_user(self, subject, message, from_email=None):
277        "Sends an e-mail to this User."
278        from django.core.mail import send_mail
279        send_mail(subject, message, from_email, [self.email])
280
281    @property
282    def profile(self):
283        return self.get_profile
284   
285    def get_profile(self):
286        """
287        Returns site-specific profile for this user. Raises
288        SiteProfileNotAvailable if this site does not allow profiles.
289        """
290        if not hasattr(self, '_profile_cache'):
291            from django.conf import settings
292            if not getattr(settings, 'AUTH_PROFILE_MODULE', False):
293                raise SiteProfileNotAvailable
294            try:
295                app_label, model_name = settings.AUTH_PROFILE_MODULE.split('.')
296                model = models.get_model(app_label, model_name)
297                self._profile_cache = model._default_manager.get(user__id__exact=self.id)
298            except (ImportError, ImproperlyConfigured):
299                raise SiteProfileNotAvailable
300        return self._profile_cache
301
302class Message(models.Model):
303    """
304    The message system is a lightweight way to queue messages for given
305    users. A message is associated with a User instance (so it is only
306    applicable for registered users). There's no concept of expiration or
307    timestamps. Messages are created by the Django admin after successful
308    actions. For example, "The poll Foo was created successfully." is a
309    message.
310    """
311    user = models.ForeignKey(User)
312    message = models.TextField(_('message'))
313
314    def __unicode__(self):
315        return self.message
316
317class AnonymousUser(object):
318    id = None
319    username = ''
320    is_staff = False
321    is_active = False
322    is_superuser = False
323    _groups = EmptyManager()
324    _user_permissions = EmptyManager()
325
326    def __init__(self):
327        pass
328
329    def __unicode__(self):
330        return 'AnonymousUser'
331
332    def __str__(self):
333        return unicode(self).encode('utf-8')
334
335    def __eq__(self, other):
336        return isinstance(other, self.__class__)
337
338    def __ne__(self, other):
339        return not self.__eq__(other)
340
341    def __hash__(self):
342        return 1 # instances always return the same hash value
343
344    def save(self):
345        raise NotImplementedError
346
347    def delete(self):
348        raise NotImplementedError
349
350    def set_password(self, raw_password):
351        raise NotImplementedError
352
353    def check_password(self, raw_password):
354        raise NotImplementedError
355
356    def _get_groups(self):
357        return self._groups
358    groups = property(_get_groups)
359
360    def _get_user_permissions(self):
361        return self._user_permissions
362    user_permissions = property(_get_user_permissions)
363
364    def has_perm(self, perm):
365        return False
366
367    def has_perms(self, perm_list):
368        return False
369
370    def has_module_perms(self, module):
371        return False
372
373    def get_and_delete_messages(self):
374        return []
375
376    def is_anonymous(self):
377        return True
378
379    def is_authenticated(self):
380        return False
Back to Top