Ticket #16724: models.py

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