Ticket #3011: #3011-extendable_auth_user-1.3.diff
File #3011-extendable_auth_user-1.3.diff, 41.5 KB (added by , 13 years ago) |
---|
-
django/contrib/admin/sites.py
diff --git a/django/contrib/admin/sites.py b/django/contrib/admin/sites.py index b03bce4..be60e96 100644
1 1 import re 2 2 from django import http, template 3 3 from django.contrib.admin import ModelAdmin, actions 4 from django.contrib.admin.forms import AdminAuthenticationForm5 4 from django.contrib.auth import REDIRECT_FIELD_NAME 6 5 from django.contrib.contenttypes import views as contenttype_views 7 6 from django.views.decorators.csrf import csrf_protect … … class AdminSite(object): 315 314 Displays the login form for the given HttpRequest. 316 315 """ 317 316 from django.contrib.auth.views import login 317 from django.contrib.admin.forms import AdminAuthenticationForm 318 318 context = { 319 319 'title': _('Log in'), 320 320 'root_path': self.root_path, -
new file django/contrib/auth/base.py
diff --git a/django/contrib/auth/base.py b/django/contrib/auth/base.py new file mode 100644 index 0000000..3465c64
- + 1 import datetime 2 import urllib 3 4 from django.contrib import auth 5 from django.contrib.auth.signals import user_logged_in 6 from django.core.exceptions import ImproperlyConfigured 7 from django.db import models 8 from django.db.models.manager import EmptyManager 9 from django.contrib.contenttypes.models import ContentType 10 from django.utils.encoding import smart_str 11 from django.utils.hashcompat import md5_constructor, sha_constructor 12 from django.utils.translation import ugettext_lazy as _ 13 from django.utils.crypto import constant_time_compare 14 from django.conf import settings 15 16 17 UNUSABLE_PASSWORD = '!' # This will never be a valid hash 18 19 def get_hexdigest(algorithm, salt, raw_password): 20 """ 21 Returns a string of the hexdigest of the given plaintext password and salt 22 using the given algorithm ('md5', 'sha1' or 'crypt'). 23 """ 24 raw_password, salt = smart_str(raw_password), smart_str(salt) 25 if algorithm == 'crypt': 26 try: 27 import crypt 28 except ImportError: 29 raise ValueError('"crypt" password algorithm not supported in this environment') 30 return crypt.crypt(raw_password, salt) 31 32 if algorithm == 'md5': 33 return md5_constructor(salt + raw_password).hexdigest() 34 elif algorithm == 'sha1': 35 return sha_constructor(salt + raw_password).hexdigest() 36 raise ValueError("Got unknown password algorithm type in password.") 37 38 def check_password(raw_password, enc_password): 39 """ 40 Returns a boolean of whether the raw_password was correct. Handles 41 encryption formats behind the scenes. 42 """ 43 algo, salt, hsh = enc_password.split('$') 44 return constant_time_compare(hsh, get_hexdigest(algo, salt, raw_password)) 45 46 def update_last_login(sender, user, **kwargs): 47 """ 48 A signal receiver which updates the last_login date for 49 the user logging in. 50 """ 51 user.last_login = datetime.datetime.now() 52 user.save() 53 user_logged_in.connect(update_last_login) 54 55 class SiteProfileNotAvailable(Exception): 56 pass 57 58 class PermissionManager(models.Manager): 59 def get_by_natural_key(self, codename, app_label, model): 60 return self.get( 61 codename=codename, 62 content_type=ContentType.objects.get_by_natural_key(app_label, model) 63 ) 64 65 class Permission(models.Model): 66 """The permissions system provides a way to assign permissions to specific users and groups of users. 67 68 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: 69 70 - The "add" permission limits the user's ability to view the "add" form and add an object. 71 - The "change" permission limits a user's ability to view the change list, view the "change" form and change an object. 72 - The "delete" permission limits the ability to delete an object. 73 74 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." 75 76 Three basic permissions -- add, change and delete -- are automatically created for each Django model. 77 """ 78 name = models.CharField(_('name'), max_length=50) 79 content_type = models.ForeignKey(ContentType) 80 codename = models.CharField(_('codename'), max_length=100) 81 objects = PermissionManager() 82 83 class Meta: 84 verbose_name = _('permission') 85 verbose_name_plural = _('permissions') 86 unique_together = (('content_type', 'codename'),) 87 ordering = ('content_type__app_label', 'content_type__model', 'codename') 88 89 def __unicode__(self): 90 return u"%s | %s | %s" % ( 91 unicode(self.content_type.app_label), 92 unicode(self.content_type), 93 unicode(self.name)) 94 95 def natural_key(self): 96 return (self.codename,) + self.content_type.natural_key() 97 natural_key.dependencies = ['contenttypes.contenttype'] 98 99 class Group(models.Model): 100 """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. 101 102 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. 103 104 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. 105 """ 106 name = models.CharField(_('name'), max_length=80, unique=True) 107 permissions = models.ManyToManyField(Permission, verbose_name=_('permissions'), blank=True) 108 109 class Meta: 110 verbose_name = _('group') 111 verbose_name_plural = _('groups') 112 113 def __unicode__(self): 114 return self.name 115 116 class UserManager(models.Manager): 117 def create_user(self, username, email, password=None): 118 """ 119 Creates and saves a User with the given username, e-mail and password. 120 """ 121 now = datetime.datetime.now() 122 123 # Normalize the address by lowercasing the domain part of the email 124 # address. 125 try: 126 email_name, domain_part = email.strip().split('@', 1) 127 except ValueError: 128 pass 129 else: 130 email = '@'.join([email_name, domain_part.lower()]) 131 132 user = self.model(username=username, email=email, is_staff=False, 133 is_active=True, is_superuser=False, last_login=now, 134 date_joined=now) 135 136 user.set_password(password) 137 user.save(using=self._db) 138 return user 139 140 def create_superuser(self, username, email, password): 141 u = self.create_user(username, email, password) 142 u.is_staff = True 143 u.is_active = True 144 u.is_superuser = True 145 u.save(using=self._db) 146 return u 147 148 def make_random_password(self, length=10, allowed_chars='abcdefghjkmnpqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ23456789'): 149 "Generates a random password with the given length and given allowed_chars" 150 # Note that default value of allowed_chars does not have "I" or letters 151 # that look like it -- just to avoid confusion. 152 from random import choice 153 return ''.join([choice(allowed_chars) for i in range(length)]) 154 155 156 # A few helper functions for common logic between User and AnonymousUser. 157 def _user_get_all_permissions(user, obj): 158 permissions = set() 159 anon = user.is_anonymous() 160 for backend in auth.get_backends(): 161 if not anon or backend.supports_anonymous_user: 162 if hasattr(backend, "get_all_permissions"): 163 if obj is not None: 164 if backend.supports_object_permissions: 165 permissions.update( 166 backend.get_all_permissions(user, obj) 167 ) 168 else: 169 permissions.update(backend.get_all_permissions(user)) 170 return permissions 171 172 173 def _user_has_perm(user, perm, obj): 174 anon = user.is_anonymous() 175 active = user.is_active 176 for backend in auth.get_backends(): 177 if (not active and not anon and backend.supports_inactive_user) or \ 178 (not anon or backend.supports_anonymous_user): 179 if hasattr(backend, "has_perm"): 180 if obj is not None: 181 if (backend.supports_object_permissions and 182 backend.has_perm(user, perm, obj)): 183 return True 184 else: 185 if backend.has_perm(user, perm): 186 return True 187 return False 188 189 190 def _user_has_module_perms(user, app_label): 191 anon = user.is_anonymous() 192 active = user.is_active 193 for backend in auth.get_backends(): 194 if (not active and not anon and backend.supports_inactive_user) or \ 195 (not anon or backend.supports_anonymous_user): 196 if hasattr(backend, "has_module_perms"): 197 if backend.has_module_perms(user, app_label): 198 return True 199 return False 200 201 202 class UserTemplate(models.Model): 203 """ 204 Users within the Django authentication system are represented by this model. 205 206 Username and password are required. Other fields are optional. 207 """ 208 username = models.CharField(_('username'), max_length=30, unique=True, help_text=_("Required. 30 characters or fewer. Letters, numbers and @/./+/-/_ characters")) 209 first_name = models.CharField(_('first name'), max_length=30, blank=True) 210 last_name = models.CharField(_('last name'), max_length=30, blank=True) 211 email = models.EmailField(_('e-mail address'), blank=True) 212 password = models.CharField(_('password'), max_length=128, help_text=_("Use '[algo]$[salt]$[hexdigest]' or use the <a href=\"password/\">change password form</a>.")) 213 is_staff = models.BooleanField(_('staff status'), default=False, help_text=_("Designates whether the user can log into this admin site.")) 214 is_active = models.BooleanField(_('active'), default=True, help_text=_("Designates whether this user should be treated as active. Unselect this instead of deleting accounts.")) 215 is_superuser = models.BooleanField(_('superuser status'), default=False, help_text=_("Designates that this user has all permissions without explicitly assigning them.")) 216 last_login = models.DateTimeField(_('last login'), default=datetime.datetime.now) 217 date_joined = models.DateTimeField(_('date joined'), default=datetime.datetime.now) 218 groups = models.ManyToManyField(Group, verbose_name=_('groups'), blank=True, 219 help_text=_("In addition to the permissions manually assigned, this user will also get all permissions granted to each group he/she is in.")) 220 user_permissions = models.ManyToManyField(Permission, verbose_name=_('user permissions'), blank=True) 221 objects = UserManager() 222 223 class Meta: 224 abstract = True 225 226 def __unicode__(self): 227 return self.username 228 229 def get_absolute_url(self): 230 return "/users/%s/" % urllib.quote(smart_str(self.username)) 231 232 def is_anonymous(self): 233 """ 234 Always returns False. This is a way of comparing User objects to 235 anonymous users. 236 """ 237 return False 238 239 def is_authenticated(self): 240 """ 241 Always return True. This is a way to tell if the user has been 242 authenticated in templates. 243 """ 244 return True 245 246 def get_full_name(self): 247 "Returns the first_name plus the last_name, with a space in between." 248 full_name = u'%s %s' % (self.first_name, self.last_name) 249 return full_name.strip() 250 251 def set_password(self, raw_password): 252 if raw_password is None: 253 self.set_unusable_password() 254 else: 255 import random 256 algo = 'sha1' 257 salt = get_hexdigest(algo, str(random.random()), str(random.random()))[:5] 258 hsh = get_hexdigest(algo, salt, raw_password) 259 self.password = '%s$%s$%s' % (algo, salt, hsh) 260 261 def check_password(self, raw_password): 262 """ 263 Returns a boolean of whether the raw_password was correct. Handles 264 encryption formats behind the scenes. 265 """ 266 # Backwards-compatibility check. Older passwords won't include the 267 # algorithm or salt. 268 if '$' not in self.password: 269 is_correct = (self.password == get_hexdigest('md5', '', raw_password)) 270 if is_correct: 271 # Convert the password to the new, more secure format. 272 self.set_password(raw_password) 273 self.save() 274 return is_correct 275 return check_password(raw_password, self.password) 276 277 def set_unusable_password(self): 278 # Sets a value that will never be a valid hash 279 self.password = UNUSABLE_PASSWORD 280 281 def has_usable_password(self): 282 if self.password is None \ 283 or self.password == UNUSABLE_PASSWORD: 284 return False 285 else: 286 return True 287 288 def get_group_permissions(self, obj=None): 289 """ 290 Returns a list of permission strings that this user has through 291 his/her groups. This method queries all available auth backends. 292 If an object is passed in, only permissions matching this object 293 are returned. 294 """ 295 permissions = set() 296 for backend in auth.get_backends(): 297 if hasattr(backend, "get_group_permissions"): 298 if obj is not None: 299 if backend.supports_object_permissions: 300 permissions.update( 301 backend.get_group_permissions(self, obj) 302 ) 303 else: 304 permissions.update(backend.get_group_permissions(self)) 305 return permissions 306 307 def get_all_permissions(self, obj=None): 308 return _user_get_all_permissions(self, obj) 309 310 def has_perm(self, perm, obj=None): 311 """ 312 Returns True if the user has the specified permission. This method 313 queries all available auth backends, but returns immediately if any 314 backend returns True. Thus, a user who has permission from a single 315 auth backend is assumed to have permission in general. If an object 316 is provided, permissions for this specific object are checked. 317 """ 318 319 # Active superusers have all permissions. 320 if self.is_active and self.is_superuser: 321 return True 322 323 # Otherwise we need to check the backends. 324 return _user_has_perm(self, perm, obj) 325 326 def has_perms(self, perm_list, obj=None): 327 """ 328 Returns True if the user has each of the specified permissions. 329 If object is passed, it checks if the user has all required perms 330 for this object. 331 """ 332 for perm in perm_list: 333 if not self.has_perm(perm, obj): 334 return False 335 return True 336 337 def has_module_perms(self, app_label): 338 """ 339 Returns True if the user has any permissions in the given app 340 label. Uses pretty much the same logic as has_perm, above. 341 """ 342 # Active superusers have all permissions. 343 if self.is_active and self.is_superuser: 344 return True 345 346 return _user_has_module_perms(self, app_label) 347 348 def get_and_delete_messages(self): 349 messages = [] 350 for m in self.message_set.all(): 351 messages.append(m.message) 352 m.delete() 353 return messages 354 355 def email_user(self, subject, message, from_email=None): 356 "Sends an e-mail to this User." 357 from django.core.mail import send_mail 358 send_mail(subject, message, from_email, [self.email]) 359 360 def get_profile(self): 361 """ 362 Returns site-specific profile for this user. Raises 363 SiteProfileNotAvailable if this site does not allow profiles. 364 """ 365 if not hasattr(self, '_profile_cache'): 366 from django.conf import settings 367 if not getattr(settings, 'AUTH_PROFILE_MODULE', False): 368 raise SiteProfileNotAvailable('You need to set AUTH_PROFILE_MO' 369 'DULE in your project settings') 370 try: 371 app_label, model_name = settings.AUTH_PROFILE_MODULE.split('.') 372 except ValueError: 373 raise SiteProfileNotAvailable('app_label and model_name should' 374 ' be separated by a dot in the AUTH_PROFILE_MODULE set' 375 'ting') 376 377 try: 378 model = models.get_model(app_label, model_name) 379 if model is None: 380 raise SiteProfileNotAvailable('Unable to load the profile ' 381 'model, check AUTH_PROFILE_MODULE in your project sett' 382 'ings') 383 self._profile_cache = model._default_manager.using(self._state.db).get(user__id__exact=self.id) 384 self._profile_cache.user = self 385 except (ImportError, ImproperlyConfigured): 386 raise SiteProfileNotAvailable 387 return self._profile_cache 388 389 def _get_message_set(self): 390 import warnings 391 warnings.warn('The user messaging API is deprecated. Please update' 392 ' your code to use the new messages framework.', 393 category=DeprecationWarning) 394 return self._message_set 395 message_set = property(_get_message_set) 396 397 class Message(models.Model): 398 """ 399 The message system is a lightweight way to queue messages for given 400 users. A message is associated with a User instance (so it is only 401 applicable for registered users). There's no concept of expiration or 402 timestamps. Messages are created by the Django admin after successful 403 actions. For example, "The poll Foo was created successfully." is a 404 message. 405 """ 406 user = models.ForeignKey(getattr(settings, 'AUTH_USER_MODULE', 'auth.User'), related_name='_message_set') 407 message = models.TextField(_('message')) 408 409 def __unicode__(self): 410 return self.message 411 412 class AnonymousUser(object): 413 id = None 414 username = '' 415 is_staff = False 416 is_active = False 417 is_superuser = False 418 _groups = EmptyManager() 419 _user_permissions = EmptyManager() 420 421 def __init__(self): 422 pass 423 424 def __unicode__(self): 425 return 'AnonymousUser' 426 427 def __str__(self): 428 return unicode(self).encode('utf-8') 429 430 def __eq__(self, other): 431 return isinstance(other, self.__class__) 432 433 def __ne__(self, other): 434 return not self.__eq__(other) 435 436 def __hash__(self): 437 return 1 # instances always return the same hash value 438 439 def save(self): 440 raise NotImplementedError 441 442 def delete(self): 443 raise NotImplementedError 444 445 def set_password(self, raw_password): 446 raise NotImplementedError 447 448 def check_password(self, raw_password): 449 raise NotImplementedError 450 451 def _get_groups(self): 452 return self._groups 453 groups = property(_get_groups) 454 455 def _get_user_permissions(self): 456 return self._user_permissions 457 user_permissions = property(_get_user_permissions) 458 459 def get_group_permissions(self, obj=None): 460 return set() 461 462 def get_all_permissions(self, obj=None): 463 return _user_get_all_permissions(self, obj=obj) 464 465 def has_perm(self, perm, obj=None): 466 return _user_has_perm(self, perm, obj=obj) 467 468 def has_perms(self, perm_list, obj=None): 469 for perm in perm_list: 470 if not self.has_perm(perm, obj): 471 return False 472 return True 473 474 def has_module_perms(self, module): 475 return _user_has_module_perms(self, module) 476 477 def get_and_delete_messages(self): 478 return [] 479 480 def is_anonymous(self): 481 return True 482 483 def is_authenticated(self): 484 return False -
django/contrib/auth/models.py
diff --git a/django/contrib/auth/models.py b/django/contrib/auth/models.py index 24195c8..6291350 100644
1 import datetime 2 import urllib 3 4 from django.contrib import auth 5 from django.contrib.auth.signals import user_logged_in 6 from django.core.exceptions import ImproperlyConfigured 7 from django.db import models 8 from django.db.models.manager import EmptyManager 9 from django.contrib.contenttypes.models import ContentType 10 from django.utils.encoding import smart_str 11 from django.utils.hashcompat import md5_constructor, sha_constructor 1 from django.conf import settings 12 2 from django.utils.translation import ugettext_lazy as _ 13 from django.utils.crypto import constant_time_compare 14 15 16 UNUSABLE_PASSWORD = '!' # This will never be a valid hash 17 18 def 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 31 if algorithm == 'md5': 32 return md5_constructor(salt + raw_password).hexdigest() 33 elif algorithm == 'sha1': 34 return sha_constructor(salt + raw_password).hexdigest() 35 raise ValueError("Got unknown password algorithm type in password.") 3 from django.core.exceptions import ImproperlyConfigured 36 4 37 def check_password(raw_password, enc_password): 38 """ 39 Returns a boolean of whether the raw_password was correct. Handles 40 encryption formats behind the scenes. 41 """ 42 algo, salt, hsh = enc_password.split('$') 43 return constant_time_compare(hsh, get_hexdigest(algo, salt, raw_password)) 5 from django.contrib.auth.base import * 44 6 45 def update_last_login(sender, user, **kwargs):46 """47 A signal receiver which updates the last_login date for48 the user logging in.49 """50 user.last_login = datetime.datetime.now()51 user.save()52 user_logged_in.connect(update_last_login)53 7 54 class SiteProfileNotAvailable(Exception):8 class AuthNotAvailable(Exception): 55 9 pass 56 10 57 class PermissionManager(models.Manager):58 def get_by_natural_key(self, codename, app_label, model):59 return self.get(60 codename=codename,61 content_type=ContentType.objects.get_by_natural_key(app_label, model)62 )63 64 class Permission(models.Model):65 """The permissions system provides a way to assign permissions to specific users and groups of users.66 67 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:68 69 - The "add" permission limits the user's ability to view the "add" form and add an object.70 - The "change" permission limits a user's ability to view the change list, view the "change" form and change an object.71 - The "delete" permission limits the ability to delete an object.72 73 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."74 75 Three basic permissions -- add, change and delete -- are automatically created for each Django model.76 """77 name = models.CharField(_('name'), max_length=50)78 content_type = models.ForeignKey(ContentType)79 codename = models.CharField(_('codename'), max_length=100)80 objects = PermissionManager()81 82 class Meta:83 verbose_name = _('permission')84 verbose_name_plural = _('permissions')85 unique_together = (('content_type', 'codename'),)86 ordering = ('content_type__app_label', 'content_type__model', 'codename')87 88 def __unicode__(self):89 return u"%s | %s | %s" % (90 unicode(self.content_type.app_label),91 unicode(self.content_type),92 unicode(self.name))93 94 def natural_key(self):95 return (self.codename,) + self.content_type.natural_key()96 natural_key.dependencies = ['contenttypes.contenttype']97 98 class Group(models.Model):99 """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.100 101 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.102 103 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.104 """105 name = models.CharField(_('name'), max_length=80, unique=True)106 permissions = models.ManyToManyField(Permission, verbose_name=_('permissions'), blank=True)107 108 class Meta:109 verbose_name = _('group')110 verbose_name_plural = _('groups')111 112 def __unicode__(self):113 return self.name114 115 class UserManager(models.Manager):116 def create_user(self, username, email, password=None):117 """118 Creates and saves a User with the given username, e-mail and password.119 """120 now = datetime.datetime.now()121 122 # Normalize the address by lowercasing the domain part of the email123 # address.124 try:125 email_name, domain_part = email.strip().split('@', 1)126 except ValueError:127 pass128 else:129 email = '@'.join([email_name, domain_part.lower()])130 131 user = self.model(username=username, email=email, is_staff=False,132 is_active=True, is_superuser=False, last_login=now,133 date_joined=now)134 135 user.set_password(password)136 user.save(using=self._db)137 return user138 139 def create_superuser(self, username, email, password):140 u = self.create_user(username, email, password)141 u.is_staff = True142 u.is_active = True143 u.is_superuser = True144 u.save(using=self._db)145 return u146 147 def make_random_password(self, length=10, allowed_chars='abcdefghjkmnpqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ23456789'):148 "Generates a random password with the given length and given allowed_chars"149 # Note that default value of allowed_chars does not have "I" or letters150 # that look like it -- just to avoid confusion.151 from random import choice152 return ''.join([choice(allowed_chars) for i in range(length)])153 154 155 # A few helper functions for common logic between User and AnonymousUser.156 def _user_get_all_permissions(user, obj):157 permissions = set()158 anon = user.is_anonymous()159 for backend in auth.get_backends():160 if not anon or backend.supports_anonymous_user:161 if hasattr(backend, "get_all_permissions"):162 if obj is not None:163 if backend.supports_object_permissions:164 permissions.update(165 backend.get_all_permissions(user, obj)166 )167 else:168 permissions.update(backend.get_all_permissions(user))169 return permissions170 171 172 def _user_has_perm(user, perm, obj):173 anon = user.is_anonymous()174 active = user.is_active175 for backend in auth.get_backends():176 if (not active and not anon and backend.supports_inactive_user) or \177 (not anon or backend.supports_anonymous_user):178 if hasattr(backend, "has_perm"):179 if obj is not None:180 if (backend.supports_object_permissions and181 backend.has_perm(user, perm, obj)):182 return True183 else:184 if backend.has_perm(user, perm):185 return True186 return False187 188 189 def _user_has_module_perms(user, app_label):190 anon = user.is_anonymous()191 active = user.is_active192 for backend in auth.get_backends():193 if (not active and not anon and backend.supports_inactive_user) or \194 (not anon or backend.supports_anonymous_user):195 if hasattr(backend, "has_module_perms"):196 if backend.has_module_perms(user, app_label):197 return True198 return False199 200 201 class User(models.Model):202 """203 Users within the Django authentication system are represented by this model.204 205 Username and password are required. Other fields are optional.206 """207 username = models.CharField(_('username'), max_length=30, unique=True, help_text=_("Required. 30 characters or fewer. Letters, numbers and @/./+/-/_ characters"))208 first_name = models.CharField(_('first name'), max_length=60, blank=True)209 last_name = models.CharField(_('last name'), max_length=60, blank=True)210 email = models.EmailField(_('e-mail address'), max_length=255, blank=True)211 password = models.CharField(_('password'), max_length=128, help_text=_("Use '[algo]$[salt]$[hexdigest]' or use the <a href=\"password/\">change password form</a>."))212 is_staff = models.BooleanField(_('staff status'), default=False, help_text=_("Designates whether the user can log into this admin site."))213 is_active = models.BooleanField(_('active'), default=True, help_text=_("Designates whether this user should be treated as active. Unselect this instead of deleting accounts."))214 is_superuser = models.BooleanField(_('superuser status'), default=False, help_text=_("Designates that this user has all permissions without explicitly assigning them."))215 last_login = models.DateTimeField(_('last login'), default=datetime.datetime.now)216 date_joined = models.DateTimeField(_('date joined'), default=datetime.datetime.now)217 groups = models.ManyToManyField(Group, verbose_name=_('groups'), blank=True,218 help_text=_("In addition to the permissions manually assigned, this user will also get all permissions granted to each group he/she is in."))219 user_permissions = models.ManyToManyField(Permission, verbose_name=_('user permissions'), blank=True)220 objects = UserManager()221 222 class Meta:223 verbose_name = _('user')224 verbose_name_plural = _('users')225 226 def __unicode__(self):227 return self.username228 229 def get_absolute_url(self):230 return "/users/%s/" % urllib.quote(smart_str(self.username))231 232 def is_anonymous(self):233 """234 Always returns False. This is a way of comparing User objects to235 anonymous users.236 """237 return False238 239 def is_authenticated(self):240 """241 Always return True. This is a way to tell if the user has been242 authenticated in templates.243 """244 return True245 246 def get_full_name(self):247 "Returns the first_name plus the last_name, with a space in between."248 full_name = u'%s %s' % (self.first_name, self.last_name)249 return full_name.strip()250 251 def set_password(self, raw_password):252 if raw_password is None:253 self.set_unusable_password()254 else:255 import random256 algo = 'sha1'257 salt = get_hexdigest(algo, str(random.random()), str(random.random()))[:5]258 hsh = get_hexdigest(algo, salt, raw_password)259 self.password = '%s$%s$%s' % (algo, salt, hsh)260 261 def check_password(self, raw_password):262 """263 Returns a boolean of whether the raw_password was correct. Handles264 encryption formats behind the scenes.265 """266 # Backwards-compatibility check. Older passwords won't include the267 # algorithm or salt.268 if '$' not in self.password:269 is_correct = (self.password == get_hexdigest('md5', '', raw_password))270 if is_correct:271 # Convert the password to the new, more secure format.272 self.set_password(raw_password)273 self.save()274 return is_correct275 return check_password(raw_password, self.password)276 277 def set_unusable_password(self):278 # Sets a value that will never be a valid hash279 self.password = UNUSABLE_PASSWORD280 281 def has_usable_password(self):282 if self.password is None \283 or self.password == UNUSABLE_PASSWORD:284 return False285 else:286 return True287 288 def get_group_permissions(self, obj=None):289 """290 Returns a list of permission strings that this user has through291 his/her groups. This method queries all available auth backends.292 If an object is passed in, only permissions matching this object293 are returned.294 """295 permissions = set()296 for backend in auth.get_backends():297 if hasattr(backend, "get_group_permissions"):298 if obj is not None:299 if backend.supports_object_permissions:300 permissions.update(301 backend.get_group_permissions(self, obj)302 )303 else:304 permissions.update(backend.get_group_permissions(self))305 return permissions306 307 def get_all_permissions(self, obj=None):308 return _user_get_all_permissions(self, obj)309 310 def has_perm(self, perm, obj=None):311 """312 Returns True if the user has the specified permission. This method313 queries all available auth backends, but returns immediately if any314 backend returns True. Thus, a user who has permission from a single315 auth backend is assumed to have permission in general. If an object316 is provided, permissions for this specific object are checked.317 """318 319 # Active superusers have all permissions.320 if self.is_active and self.is_superuser:321 return True322 323 # Otherwise we need to check the backends.324 return _user_has_perm(self, perm, obj)325 326 def has_perms(self, perm_list, obj=None):327 """328 Returns True if the user has each of the specified permissions.329 If object is passed, it checks if the user has all required perms330 for this object.331 """332 for perm in perm_list:333 if not self.has_perm(perm, obj):334 return False335 return True336 337 def has_module_perms(self, app_label):338 """339 Returns True if the user has any permissions in the given app340 label. Uses pretty much the same logic as has_perm, above.341 """342 # Active superusers have all permissions.343 if self.is_active and self.is_superuser:344 return True345 346 return _user_has_module_perms(self, app_label)347 348 def get_and_delete_messages(self):349 messages = []350 for m in self.message_set.all():351 messages.append(m.message)352 m.delete()353 return messages354 355 def email_user(self, subject, message, from_email=None):356 "Sends an e-mail to this User."357 from django.core.mail import send_mail358 send_mail(subject, message, from_email, [self.email])359 360 def get_profile(self):361 """362 Returns site-specific profile for this user. Raises363 SiteProfileNotAvailable if this site does not allow profiles.364 """365 if not hasattr(self, '_profile_cache'):366 from django.conf import settings367 if not getattr(settings, 'AUTH_PROFILE_MODULE', False):368 raise SiteProfileNotAvailable('You need to set AUTH_PROFILE_MO'369 'DULE in your project settings')370 try:371 app_label, model_name = settings.AUTH_PROFILE_MODULE.split('.')372 except ValueError:373 raise SiteProfileNotAvailable('app_label and model_name should'374 ' be separated by a dot in the AUTH_PROFILE_MODULE set'375 'ting')376 377 try:378 model = models.get_model(app_label, model_name)379 if model is None:380 raise SiteProfileNotAvailable('Unable to load the profile '381 'model, check AUTH_PROFILE_MODULE in your project sett'382 'ings')383 self._profile_cache = model._default_manager.using(self._state.db).get(user__id__exact=self.id)384 self._profile_cache.user = self385 except (ImportError, ImproperlyConfigured):386 raise SiteProfileNotAvailable387 return self._profile_cache388 389 def _get_message_set(self):390 import warnings391 warnings.warn('The user messaging API is deprecated. Please update'392 ' your code to use the new messages framework.',393 category=DeprecationWarning)394 return self._message_set395 message_set = property(_get_message_set)396 397 class Message(models.Model):398 """399 The message system is a lightweight way to queue messages for given400 users. A message is associated with a User instance (so it is only401 applicable for registered users). There's no concept of expiration or402 timestamps. Messages are created by the Django admin after successful403 actions. For example, "The poll Foo was created successfully." is a404 message.405 """406 user = models.ForeignKey(User, related_name='_message_set')407 message = models.TextField(_('message'))408 409 def __unicode__(self):410 return self.message411 412 class AnonymousUser(object):413 id = None414 username = ''415 is_staff = False416 is_active = False417 is_superuser = False418 _groups = EmptyManager()419 _user_permissions = EmptyManager()420 421 def __init__(self):422 pass423 424 def __unicode__(self):425 return 'AnonymousUser'426 427 def __str__(self):428 return unicode(self).encode('utf-8')429 430 def __eq__(self, other):431 return isinstance(other, self.__class__)432 433 def __ne__(self, other):434 return not self.__eq__(other)435 436 def __hash__(self):437 return 1 # instances always return the same hash value438 439 def save(self):440 raise NotImplementedError441 442 def delete(self):443 raise NotImplementedError444 445 def set_password(self, raw_password):446 raise NotImplementedError447 448 def check_password(self, raw_password):449 raise NotImplementedError450 451 def _get_groups(self):452 return self._groups453 groups = property(_get_groups)454 455 def _get_user_permissions(self):456 return self._user_permissions457 user_permissions = property(_get_user_permissions)458 459 def get_group_permissions(self, obj=None):460 return set()461 462 def get_all_permissions(self, obj=None):463 return _user_get_all_permissions(self, obj=obj)464 465 def has_perm(self, perm, obj=None):466 return _user_has_perm(self, perm, obj=obj)467 468 def has_perms(self, perm_list, obj=None):469 for perm in perm_list:470 if not self.has_perm(perm, obj):471 return False472 return True473 474 def has_module_perms(self, module):475 return _user_has_module_perms(self, module)476 477 def get_and_delete_messages(self):478 return []479 480 def is_anonymous(self):481 return True482 11 483 def is_authenticated(self): 484 return False 12 if hasattr(settings, 'AUTH_USER_MODULE'): 13 try: 14 app_label, model_name = settings.AUTH_USER_MODULE.split('.') 15 except ValueError: 16 raise AuthNotAvailable('app_label and model_name should' 17 ' be separated by a dot in the AUTH_USER_MODULE set' 18 'ting') 19 20 try: 21 # Grab the AUTH_USER_MODULE path and classname from the settings. 22 module = __import__(app_label + '.models', {}, {}, ['']) 23 # Store the user model so it is accessible with the standard 24 # 'from django.contrib.auth.models import User' 25 User = getattr(module, model_name, None) 26 27 if User is None: 28 raise AuthNotAvailable('Unable to load the user ' 29 'model, check AUTH_USER_MODULE in your project sett' 30 'ings') 31 except (ImportError, ImproperlyConfigured): 32 raise AuthNotAvailable 33 34 # Add te User model to the django models cache 35 # These two lines allow the custom auth_user model to play nicely with syncdb 36 # and other systems that rely on functions like 37 # django.db.models.loading.get_model(...) 38 from django.db.models.loading import cache 39 cache.register_models('auth', User) 40 41 # We need to remove whatever we used as the AUTH_USER_MODUlE from the 42 # db cache so apps like contenttypes don't think that it's part 43 # of contrib.auth 44 del cache.app_models['auth'][User.__name__.lower()] 45 else: 46 class User(UserTemplate): 47 class Meta: 48 verbose_name = _('user') 49 verbose_name_plural = _('users')