Ticket #3011: #3011-extendable_auth_user.diff
File #3011-extendable_auth_user.diff, 40.0 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..30131e0
- + 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 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.") 36 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)) 44 45 def update_last_login(sender, user, **kwargs): 46 """ 47 A signal receiver which updates the last_login date for 48 the user logging in. 49 """ 50 user.last_login = datetime.datetime.now() 51 user.save() 52 user_logged_in.connect(update_last_login) 53 54 class SiteProfileNotAvailable(Exception): 55 pass 56 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.name 114 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 email 123 # address. 124 try: 125 email_name, domain_part = email.strip().split('@', 1) 126 except ValueError: 127 pass 128 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 user 138 139 def create_superuser(self, username, email, password): 140 u = self.create_user(username, email, password) 141 u.is_staff = True 142 u.is_active = True 143 u.is_superuser = True 144 u.save(using=self._db) 145 return u 146 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 letters 150 # that look like it -- just to avoid confusion. 151 from random import choice 152 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 permissions 170 171 172 def _user_has_perm(user, perm, obj): 173 anon = user.is_anonymous() 174 active = user.is_active 175 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 and 181 backend.has_perm(user, perm, obj)): 182 return True 183 else: 184 if backend.has_perm(user, perm): 185 return True 186 return False 187 188 189 def _user_has_module_perms(user, app_label): 190 anon = user.is_anonymous() 191 active = user.is_active 192 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 True 198 return False 199 200 201 class UserTemplate(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 abstract = True 224 225 def __unicode__(self): 226 return self.username 227 228 def get_absolute_url(self): 229 return "/users/%s/" % urllib.quote(smart_str(self.username)) 230 231 def is_anonymous(self): 232 """ 233 Always returns False. This is a way of comparing User objects to 234 anonymous users. 235 """ 236 return False 237 238 def is_authenticated(self): 239 """ 240 Always return True. This is a way to tell if the user has been 241 authenticated in templates. 242 """ 243 return True 244 245 def get_full_name(self): 246 "Returns the first_name plus the last_name, with a space in between." 247 full_name = u'%s %s' % (self.first_name, self.last_name) 248 return full_name.strip() 249 250 def set_password(self, raw_password): 251 if raw_password is None: 252 self.set_unusable_password() 253 else: 254 import random 255 algo = 'sha1' 256 salt = get_hexdigest(algo, str(random.random()), str(random.random()))[:5] 257 hsh = get_hexdigest(algo, salt, raw_password) 258 self.password = '%s$%s$%s' % (algo, salt, hsh) 259 260 def check_password(self, raw_password): 261 """ 262 Returns a boolean of whether the raw_password was correct. Handles 263 encryption formats behind the scenes. 264 """ 265 # Backwards-compatibility check. Older passwords won't include the 266 # algorithm or salt. 267 if '$' not in self.password: 268 is_correct = (self.password == get_hexdigest('md5', '', raw_password)) 269 if is_correct: 270 # Convert the password to the new, more secure format. 271 self.set_password(raw_password) 272 self.save() 273 return is_correct 274 return check_password(raw_password, self.password) 275 276 def set_unusable_password(self): 277 # Sets a value that will never be a valid hash 278 self.password = UNUSABLE_PASSWORD 279 280 def has_usable_password(self): 281 if self.password is None \ 282 or self.password == UNUSABLE_PASSWORD: 283 return False 284 else: 285 return True 286 287 def get_group_permissions(self, obj=None): 288 """ 289 Returns a list of permission strings that this user has through 290 his/her groups. This method queries all available auth backends. 291 If an object is passed in, only permissions matching this object 292 are returned. 293 """ 294 permissions = set() 295 for backend in auth.get_backends(): 296 if hasattr(backend, "get_group_permissions"): 297 if obj is not None: 298 if backend.supports_object_permissions: 299 permissions.update( 300 backend.get_group_permissions(self, obj) 301 ) 302 else: 303 permissions.update(backend.get_group_permissions(self)) 304 return permissions 305 306 def get_all_permissions(self, obj=None): 307 return _user_get_all_permissions(self, obj) 308 309 def has_perm(self, perm, obj=None): 310 """ 311 Returns True if the user has the specified permission. This method 312 queries all available auth backends, but returns immediately if any 313 backend returns True. Thus, a user who has permission from a single 314 auth backend is assumed to have permission in general. If an object 315 is provided, permissions for this specific object are checked. 316 """ 317 318 # Active superusers have all permissions. 319 if self.is_active and self.is_superuser: 320 return True 321 322 # Otherwise we need to check the backends. 323 return _user_has_perm(self, perm, obj) 324 325 def has_perms(self, perm_list, obj=None): 326 """ 327 Returns True if the user has each of the specified permissions. 328 If object is passed, it checks if the user has all required perms 329 for this object. 330 """ 331 for perm in perm_list: 332 if not self.has_perm(perm, obj): 333 return False 334 return True 335 336 def has_module_perms(self, app_label): 337 """ 338 Returns True if the user has any permissions in the given app 339 label. Uses pretty much the same logic as has_perm, above. 340 """ 341 # Active superusers have all permissions. 342 if self.is_active and self.is_superuser: 343 return True 344 345 return _user_has_module_perms(self, app_label) 346 347 def get_and_delete_messages(self): 348 messages = [] 349 for m in self.message_set.all(): 350 messages.append(m.message) 351 m.delete() 352 return messages 353 354 def email_user(self, subject, message, from_email=None): 355 "Sends an e-mail to this User." 356 from django.core.mail import send_mail 357 send_mail(subject, message, from_email, [self.email]) 358 359 def get_profile(self): 360 """ 361 Returns site-specific profile for this user. Raises 362 SiteProfileNotAvailable if this site does not allow profiles. 363 """ 364 if not hasattr(self, '_profile_cache'): 365 from django.conf import settings 366 if not getattr(settings, 'AUTH_PROFILE_MODULE', False): 367 raise SiteProfileNotAvailable('You need to set AUTH_PROFILE_MO' 368 'DULE in your project settings') 369 try: 370 app_label, model_name = settings.AUTH_PROFILE_MODULE.split('.') 371 except ValueError: 372 raise SiteProfileNotAvailable('app_label and model_name should' 373 ' be separated by a dot in the AUTH_PROFILE_MODULE set' 374 'ting') 375 376 try: 377 model = models.get_model(app_label, model_name) 378 if model is None: 379 raise SiteProfileNotAvailable('Unable to load the profile ' 380 'model, check AUTH_PROFILE_MODULE in your project sett' 381 'ings') 382 self._profile_cache = model._default_manager.using(self._state.db).get(user__id__exact=self.id) 383 self._profile_cache.user = self 384 except (ImportError, ImproperlyConfigured): 385 raise SiteProfileNotAvailable 386 return self._profile_cache 387 388 def _get_message_set(self): 389 import warnings 390 warnings.warn('The user messaging API is deprecated. Please update' 391 ' your code to use the new messages framework.', 392 category=DeprecationWarning) 393 return self._message_set 394 message_set = property(_get_message_set) 395 396 397 class AnonymousUser(object): 398 id = None 399 username = '' 400 is_staff = False 401 is_active = False 402 is_superuser = False 403 _groups = EmptyManager() 404 _user_permissions = EmptyManager() 405 406 def __init__(self): 407 pass 408 409 def __unicode__(self): 410 return 'AnonymousUser' 411 412 def __str__(self): 413 return unicode(self).encode('utf-8') 414 415 def __eq__(self, other): 416 return isinstance(other, self.__class__) 417 418 def __ne__(self, other): 419 return not self.__eq__(other) 420 421 def __hash__(self): 422 return 1 # instances always return the same hash value 423 424 def save(self): 425 raise NotImplementedError 426 427 def delete(self): 428 raise NotImplementedError 429 430 def set_password(self, raw_password): 431 raise NotImplementedError 432 433 def check_password(self, raw_password): 434 raise NotImplementedError 435 436 def _get_groups(self): 437 return self._groups 438 groups = property(_get_groups) 439 440 def _get_user_permissions(self): 441 return self._user_permissions 442 user_permissions = property(_get_user_permissions) 443 444 def get_group_permissions(self, obj=None): 445 return set() 446 447 def get_all_permissions(self, obj=None): 448 return _user_get_all_permissions(self, obj=obj) 449 450 def has_perm(self, perm, obj=None): 451 return _user_has_perm(self, perm, obj=obj) 452 453 def has_perms(self, perm_list, obj=None): 454 for perm in perm_list: 455 if not self.has_perm(perm, obj): 456 return False 457 return True 458 459 def has_module_perms(self, module): 460 return _user_has_module_perms(self, module) 461 462 def get_and_delete_messages(self): 463 return [] 464 465 def is_anonymous(self): 466 return True 467 468 def is_authenticated(self): 469 return False 470 No newline at end of file -
django/contrib/auth/models.py
diff --git a/django/contrib/auth/models.py b/django/contrib/auth/models.py index 24195c8..b96ed76 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 1 from django.conf import settings 7 2 from django.db import models 8 from django.db.models.manager import EmptyManager9 from django.contrib.contenttypes.models import ContentType10 from django.utils.encoding import smart_str11 from django.utils.hashcompat import md5_constructor, sha_constructor12 3 from django.utils.translation import ugettext_lazy as _ 13 from django.utils.crypto import constant_time_compare14 15 16 UNUSABLE_PASSWORD = '!' # This will never be a valid hash17 18 def get_hexdigest(algorithm, salt, raw_password):19 """20 Returns a string of the hexdigest of the given plaintext password and salt21 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 crypt27 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.")36 37 def check_password(raw_password, enc_password):38 """39 Returns a boolean of whether the raw_password was correct. Handles40 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))44 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 54 class SiteProfileNotAvailable(Exception):55 pass56 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 4 94 def natural_key(self): 95 return (self.codename,) + self.content_type.natural_key() 96 natural_key.dependencies = ['contenttypes.contenttype'] 5 from django.contrib.auth.base import * 6 7 if hasattr(settings, 'AUTH_USER_MODULE'): 8 # Grab the AUTH_USER_MODULE path and classname from the settings. 9 # auth_user_module_parts[0] = module path 10 # auth_user_module_parts[1] = class name 11 auth_user_module_parts = settings.AUTH_USER_MODULE.rsplit('.', 1) 12 auth_user_module = __import__(auth_user_module_parts[0], {}, {}, [auth_user_module_parts[0]]) 13 # Store the auth_user model so it is accessible with the standard 14 # 'from django.contrib.auth.models import User' 15 User = getattr(auth_user_module, auth_user_module_parts[1]) 16 17 # Add te User model to the django models cache 18 # These two lines allow the custom auth_user model to play nicely with syncdb 19 # and other systems that rely on functions like 20 # django.db.models.loading.get_model(...) 21 from django.db.models.loading import cache 22 cache.register_models('auth', User) 23 24 # We need to remove whatever we used as the AUTH_USER_MODUlE from the 25 # db cache so apps like contenttypes don't think that it's part 26 # of contrib.auth 27 del cache.app_models['auth'][User.__name__.lower()] 28 else: 29 class User(UserTemplate): 30 class Meta: 31 verbose_name = _('user') 32 verbose_name_plural = _('users') 97 33 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 34 397 35 class Message(models.Model): 398 36 """ … … class Message(models.Model): 408 46 409 47 def __unicode__(self): 410 48 return self.message 411 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 483 def is_authenticated(self):484 return False