2007-09-20 00:50:30 +08:00
from django . contrib import auth
2006-05-02 09:31:56 +08:00
from django . core import validators
2006-07-19 20:48:30 +08:00
from django . core . exceptions import ImproperlyConfigured
2007-09-20 00:50:30 +08:00
from django . db import models
2007-09-16 02:01:29 +08:00
from django . db . models . manager import EmptyManager
2006-05-02 09:31:56 +08:00
from django . contrib . contenttypes . models import ContentType
Merged Unicode branch into trunk (r4952:5608). This should be fully
backwards compatible for all practical purposes.
Fixed #2391, #2489, #2996, #3322, #3344, #3370, #3406, #3432, #3454, #3492, #3582, #3690, #3878, #3891, #3937, #4039, #4141, #4227, #4286, #4291, #4300, #4452, #4702
git-svn-id: http://code.djangoproject.com/svn/django/trunk@5609 bcc190cf-cafb-0310-a4f2-bffc1f526a37
2007-07-04 20:11:04 +08:00
from django . utils . encoding import smart_str
from django . utils . translation import ugettext_lazy as _
2006-05-02 09:31:56 +08:00
import datetime
Merged Unicode branch into trunk (r4952:5608). This should be fully
backwards compatible for all practical purposes.
Fixed #2391, #2489, #2996, #3322, #3344, #3370, #3406, #3432, #3454, #3492, #3582, #3690, #3878, #3891, #3937, #4039, #4141, #4227, #4286, #4291, #4300, #4452, #4702
git-svn-id: http://code.djangoproject.com/svn/django/trunk@5609 bcc190cf-cafb-0310-a4f2-bffc1f526a37
2007-07-04 20:11:04 +08:00
import urllib
2006-05-02 09:31:56 +08:00
2007-07-29 02:30:40 +08:00
UNUSABLE_PASSWORD = ' ! ' # This will never be a valid hash
2007-06-23 11:18:22 +08:00
try :
set
except NameError :
from sets import Set as set # Python 2.3 fallback
2007-09-16 03:45:33 +08:00
def get_hexdigest ( algorithm , salt , raw_password ) :
"""
Returns a string of the hexdigest of the given plaintext password and salt
using the given algorithm ( ' md5 ' , ' sha1 ' or ' crypt ' ) .
"""
raw_password , salt = smart_str ( raw_password ) , smart_str ( salt )
if algorithm == ' crypt ' :
try :
import crypt
except ImportError :
raise ValueError ( ' " crypt " password algorithm not supported in this environment ' )
return crypt . crypt ( raw_password , salt )
# The rest of the supported algorithms are supported by hashlib, but
# hashlib is only available in Python 2.5.
try :
import hashlib
except ImportError :
if algorithm == ' md5 ' :
import md5
return md5 . new ( salt + raw_password ) . hexdigest ( )
elif algorithm == ' sha1 ' :
import sha
return sha . new ( salt + raw_password ) . hexdigest ( )
else :
if algorithm == ' md5 ' :
return hashlib . md5 ( salt + raw_password ) . hexdigest ( )
elif algorithm == ' sha1 ' :
return hashlib . sha1 ( salt + raw_password ) . hexdigest ( )
raise ValueError ( " Got unknown password algorithm type in password. " )
2006-06-29 00:37:02 +08:00
def check_password ( raw_password , enc_password ) :
"""
Returns a boolean of whether the raw_password was correct . Handles
encryption formats behind the scenes .
"""
algo , salt , hsh = enc_password . split ( ' $ ' )
2007-09-16 03:45:33 +08:00
return hsh == get_hexdigest ( algo , salt , raw_password )
2006-05-02 09:31:56 +08:00
class SiteProfileNotAvailable ( Exception ) :
pass
class Permission ( models . Model ) :
2006-06-20 12:07:32 +08:00
""" The permissions system provides a way to assign permissions to specific users and groups of users.
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 :
- The " add " permission limits the user ' s ability to view the " add " form and add an object.
- The " change " permission limits a user ' s ability to view the change list, view the " change " form and change an object.
- The " delete " permission limits the ability to delete an object .
2006-06-20 12:14:10 +08:00
2006-06-20 12:07:32 +08:00
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. "
2006-08-31 00:39:18 +08:00
Three basic permissions - - add , change and delete - - are automatically created for each Django model .
2006-06-20 12:07:32 +08:00
"""
2007-08-05 13:14:46 +08:00
name = models . CharField ( _ ( ' name ' ) , max_length = 50 )
2006-05-02 09:31:56 +08:00
content_type = models . ForeignKey ( ContentType )
2007-08-05 13:14:46 +08:00
codename = models . CharField ( _ ( ' codename ' ) , max_length = 100 )
2007-04-07 12:21:31 +08:00
2006-05-02 09:31:56 +08:00
class Meta :
verbose_name = _ ( ' permission ' )
verbose_name_plural = _ ( ' permissions ' )
unique_together = ( ( ' content_type ' , ' codename ' ) , )
2008-08-09 05:24:02 +08:00
ordering = ( ' content_type__app_label ' , ' codename ' )
2006-05-02 09:31:56 +08:00
Merged Unicode branch into trunk (r4952:5608). This should be fully
backwards compatible for all practical purposes.
Fixed #2391, #2489, #2996, #3322, #3344, #3370, #3406, #3432, #3454, #3492, #3582, #3690, #3878, #3891, #3937, #4039, #4141, #4227, #4286, #4291, #4300, #4452, #4702
git-svn-id: http://code.djangoproject.com/svn/django/trunk@5609 bcc190cf-cafb-0310-a4f2-bffc1f526a37
2007-07-04 20:11:04 +08:00
def __unicode__ ( self ) :
2008-08-11 23:13:00 +08:00
return u " %s | %s | %s " % (
unicode ( self . content_type . app_label ) ,
unicode ( self . content_type ) ,
unicode ( self . name ) )
2006-05-02 09:31:56 +08:00
class Group ( models . Model ) :
2006-06-20 12:07:32 +08:00
""" 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.
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 .
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 .
"""
2007-08-05 13:14:46 +08:00
name = models . CharField ( _ ( ' name ' ) , max_length = 80 , unique = True )
2008-07-19 07:54:34 +08:00
permissions = models . ManyToManyField ( Permission , verbose_name = _ ( ' permissions ' ) , blank = True )
2007-04-07 12:21:31 +08:00
2006-05-02 09:31:56 +08:00
class Meta :
verbose_name = _ ( ' group ' )
verbose_name_plural = _ ( ' groups ' )
2008-08-11 23:13:00 +08:00
Merged Unicode branch into trunk (r4952:5608). This should be fully
backwards compatible for all practical purposes.
Fixed #2391, #2489, #2996, #3322, #3344, #3370, #3406, #3432, #3454, #3492, #3582, #3690, #3878, #3891, #3937, #4039, #4141, #4227, #4286, #4291, #4300, #4452, #4702
git-svn-id: http://code.djangoproject.com/svn/django/trunk@5609 bcc190cf-cafb-0310-a4f2-bffc1f526a37
2007-07-04 20:11:04 +08:00
def __unicode__ ( self ) :
2006-05-02 09:31:56 +08:00
return self . name
class UserManager ( models . Manager ) :
2007-07-29 02:30:40 +08:00
def create_user ( self , username , email , password = None ) :
2006-05-02 09:31:56 +08:00
" Creates and saves a User with the given username, e-mail and password. "
now = datetime . datetime . now ( )
user = self . model ( None , username , ' ' , ' ' , email . strip ( ) . lower ( ) , ' placeholder ' , False , True , False , now , now )
2007-07-29 02:30:40 +08:00
if password :
user . set_password ( password )
else :
user . set_unusable_password ( )
2006-05-02 09:31:56 +08:00
user . save ( )
return user
2008-06-08 13:31:16 +08:00
def create_superuser ( self , username , email , password ) :
u = self . create_user ( username , email , password )
u . is_staff = True
u . is_active = True
u . is_superuser = True
u . save ( )
2006-05-02 09:31:56 +08:00
def make_random_password ( self , length = 10 , allowed_chars = ' abcdefghjkmnpqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ23456789 ' ) :
" Generates a random password with the given length and given allowed_chars "
# Note that default value of allowed_chars does not have "I" or letters
# that look like it -- just to avoid confusion.
from random import choice
return ' ' . join ( [ choice ( allowed_chars ) for i in range ( length ) ] )
class User ( models . Model ) :
2006-06-20 12:07:32 +08:00
""" Users within the Django authentication system are represented by this model.
2006-06-20 12:14:10 +08:00
Username and password are required . Other fields are optional .
2006-06-20 12:07:32 +08:00
"""
2008-08-11 12:30:30 +08:00
username = models . CharField ( _ ( ' username ' ) , max_length = 30 , unique = True , help_text = _ ( " Required. 30 characters or fewer. Alphanumeric characters only (letters, digits and underscores). " ) )
2007-08-05 13:14:46 +08:00
first_name = models . CharField ( _ ( ' first name ' ) , max_length = 30 , blank = True )
last_name = models . CharField ( _ ( ' last name ' ) , max_length = 30 , blank = True )
2006-05-02 09:31:56 +08:00
email = models . EmailField ( _ ( ' e-mail address ' ) , blank = True )
2007-08-05 13:14:46 +08:00
password = models . CharField ( _ ( ' password ' ) , max_length = 128 , help_text = _ ( " Use ' [algo]$[salt]$[hexdigest] ' or use the <a href= \" password/ \" >change password form</a>. " ) )
2006-10-25 04:45:28 +08:00
is_staff = models . BooleanField ( _ ( ' staff status ' ) , default = False , help_text = _ ( " Designates whether the user can log into this admin site. " ) )
2008-04-13 09:50:29 +08:00
is_active = models . BooleanField ( _ ( ' active ' ) , default = True , help_text = _ ( " Designates whether this user should be treated as active. Unselect this instead of deleting accounts. " ) )
2006-10-25 04:45:28 +08:00
is_superuser = models . BooleanField ( _ ( ' superuser status ' ) , default = False , help_text = _ ( " Designates that this user has all permissions without explicitly assigning them. " ) )
2007-04-09 21:28:09 +08:00
last_login = models . DateTimeField ( _ ( ' last login ' ) , default = datetime . datetime . now )
date_joined = models . DateTimeField ( _ ( ' date joined ' ) , default = datetime . datetime . now )
2006-05-02 09:31:56 +08:00
groups = models . ManyToManyField ( Group , verbose_name = _ ( ' groups ' ) , blank = True ,
help_text = _ ( " In addition to the permissions manually assigned, this user will also get all permissions granted to each group he/she is in. " ) )
2008-07-19 07:54:34 +08:00
user_permissions = models . ManyToManyField ( Permission , verbose_name = _ ( ' user permissions ' ) , blank = True )
2006-05-02 09:31:56 +08:00
objects = UserManager ( )
2007-04-07 12:21:31 +08:00
2006-05-02 09:31:56 +08:00
class Meta :
verbose_name = _ ( ' user ' )
verbose_name_plural = _ ( ' users ' )
2008-08-11 23:13:00 +08:00
Merged Unicode branch into trunk (r4952:5608). This should be fully
backwards compatible for all practical purposes.
Fixed #2391, #2489, #2996, #3322, #3344, #3370, #3406, #3432, #3454, #3492, #3582, #3690, #3878, #3891, #3937, #4039, #4141, #4227, #4286, #4291, #4300, #4452, #4702
git-svn-id: http://code.djangoproject.com/svn/django/trunk@5609 bcc190cf-cafb-0310-a4f2-bffc1f526a37
2007-07-04 20:11:04 +08:00
def __unicode__ ( self ) :
2006-05-02 09:31:56 +08:00
return self . username
def get_absolute_url ( self ) :
Merged Unicode branch into trunk (r4952:5608). This should be fully
backwards compatible for all practical purposes.
Fixed #2391, #2489, #2996, #3322, #3344, #3370, #3406, #3432, #3454, #3492, #3582, #3690, #3878, #3891, #3937, #4039, #4141, #4227, #4286, #4291, #4300, #4452, #4702
git-svn-id: http://code.djangoproject.com/svn/django/trunk@5609 bcc190cf-cafb-0310-a4f2-bffc1f526a37
2007-07-04 20:11:04 +08:00
return " /users/ %s / " % urllib . quote ( smart_str ( self . username ) )
2006-05-02 09:31:56 +08:00
def is_anonymous ( self ) :
2006-06-20 12:07:32 +08:00
" Always returns False. This is a way of comparing User objects to anonymous users. "
2006-05-02 09:31:56 +08:00
return False
2006-10-25 00:42:03 +08:00
2006-07-19 10:09:26 +08:00
def is_authenticated ( self ) :
""" Always return True. This is a way to tell if the user has been authenticated in templates.
"""
return True
2006-05-02 09:31:56 +08:00
def get_full_name ( self ) :
2006-06-20 12:07:32 +08:00
" Returns the first_name plus the last_name, with a space in between. "
Merged Unicode branch into trunk (r4952:5608). This should be fully
backwards compatible for all practical purposes.
Fixed #2391, #2489, #2996, #3322, #3344, #3370, #3406, #3432, #3454, #3492, #3582, #3690, #3878, #3891, #3937, #4039, #4141, #4227, #4286, #4291, #4300, #4452, #4702
git-svn-id: http://code.djangoproject.com/svn/django/trunk@5609 bcc190cf-cafb-0310-a4f2-bffc1f526a37
2007-07-04 20:11:04 +08:00
full_name = u ' %s %s ' % ( self . first_name , self . last_name )
2006-05-02 09:31:56 +08:00
return full_name . strip ( )
def set_password ( self , raw_password ) :
2007-09-16 03:45:33 +08:00
import random
2006-05-02 09:31:56 +08:00
algo = ' sha1 '
2007-09-16 03:45:33 +08:00
salt = get_hexdigest ( algo , str ( random . random ( ) ) , str ( random . random ( ) ) ) [ : 5 ]
hsh = get_hexdigest ( algo , salt , raw_password )
2006-05-02 09:31:56 +08:00
self . password = ' %s $ %s $ %s ' % ( algo , salt , hsh )
def check_password ( self , raw_password ) :
"""
Returns a boolean of whether the raw_password was correct . Handles
encryption formats behind the scenes .
"""
# Backwards-compatibility check. Older passwords won't include the
# algorithm or salt.
if ' $ ' not in self . password :
2007-09-16 03:45:33 +08:00
is_correct = ( self . password == get_hexdigest ( ' md5 ' , ' ' , raw_password ) )
2006-05-02 09:31:56 +08:00
if is_correct :
# Convert the password to the new, more secure format.
self . set_password ( raw_password )
self . save ( )
return is_correct
2006-06-29 00:37:02 +08:00
return check_password ( raw_password , self . password )
2006-05-02 09:31:56 +08:00
2007-07-29 02:30:40 +08:00
def set_unusable_password ( self ) :
# Sets a value that will never be a valid hash
self . password = UNUSABLE_PASSWORD
def has_usable_password ( self ) :
return self . password != UNUSABLE_PASSWORD
2006-05-02 09:31:56 +08:00
def get_group_permissions ( self ) :
2007-09-20 00:50:30 +08:00
"""
Returns a list of permission strings that this user has through
his / her groups . This method queries all available auth backends .
"""
permissions = set ( )
for backend in auth . get_backends ( ) :
if hasattr ( backend , " get_group_permissions " ) :
permissions . update ( backend . get_group_permissions ( self ) )
return permissions
2006-05-02 09:31:56 +08:00
def get_all_permissions ( self ) :
2007-09-20 00:50:30 +08:00
permissions = set ( )
for backend in auth . get_backends ( ) :
if hasattr ( backend , " get_all_permissions " ) :
permissions . update ( backend . get_all_permissions ( self ) )
2008-04-13 09:50:29 +08:00
return permissions
2006-05-02 09:31:56 +08:00
def has_perm ( self , perm ) :
2007-09-20 00:50:30 +08:00
"""
Returns True if the user has the specified permission . This method
queries all available auth backends , but returns immediately if any
backend returns True . Thus , a user who has permission from a single
auth backend is assumed to have permission in general .
"""
# Inactive users have no permissions.
2006-05-02 09:31:56 +08:00
if not self . is_active :
return False
2008-04-13 09:50:29 +08:00
2007-09-20 00:50:30 +08:00
# Superusers have all permissions.
2006-05-02 09:31:56 +08:00
if self . is_superuser :
return True
2008-04-13 09:50:29 +08:00
2007-09-20 00:50:30 +08:00
# Otherwise we need to check the backends.
for backend in auth . get_backends ( ) :
if hasattr ( backend , " has_perm " ) :
if backend . has_perm ( self , perm ) :
return True
return False
2006-05-02 09:31:56 +08:00
def has_perms ( self , perm_list ) :
2007-09-20 00:50:30 +08:00
""" Returns True if the user has each of the specified permissions. """
2006-05-02 09:31:56 +08:00
for perm in perm_list :
if not self . has_perm ( perm ) :
return False
return True
def has_module_perms ( self , app_label ) :
2007-09-20 00:50:30 +08:00
"""
Returns True if the user has any permissions in the given app
label . Uses pretty much the same logic as has_perm , above .
"""
2006-09-30 09:21:03 +08:00
if not self . is_active :
return False
2007-09-20 00:50:30 +08:00
2006-05-02 09:31:56 +08:00
if self . is_superuser :
return True
2007-09-20 00:50:30 +08:00
for backend in auth . get_backends ( ) :
if hasattr ( backend , " has_module_perms " ) :
if backend . has_module_perms ( self , app_label ) :
return True
return False
2006-05-02 09:31:56 +08:00
def get_and_delete_messages ( self ) :
messages = [ ]
for m in self . message_set . all ( ) :
messages . append ( m . message )
m . delete ( )
return messages
def email_user ( self , subject , message , from_email = None ) :
" Sends an e-mail to this User. "
from django . core . mail import send_mail
send_mail ( subject , message , from_email , [ self . email ] )
def get_profile ( self ) :
"""
Returns site - specific profile for this user . Raises
SiteProfileNotAvailable if this site does not allow profiles .
"""
if not hasattr ( self , ' _profile_cache ' ) :
from django . conf import settings
2008-08-26 00:56:59 +08:00
if not getattr ( settings , ' AUTH_PROFILE_MODULE ' , False ) :
2006-05-02 09:31:56 +08:00
raise SiteProfileNotAvailable
try :
app_label , model_name = settings . AUTH_PROFILE_MODULE . split ( ' . ' )
model = models . get_model ( app_label , model_name )
self . _profile_cache = model . _default_manager . get ( user__id__exact = self . id )
2006-07-11 22:03:24 +08:00
except ( ImportError , ImproperlyConfigured ) :
2006-05-02 09:31:56 +08:00
raise SiteProfileNotAvailable
return self . _profile_cache
class Message ( models . Model ) :
2007-07-29 02:30:40 +08:00
"""
2007-09-20 00:50:30 +08:00
The message system is a lightweight way to queue messages for given
users . A message is associated with a User instance ( so it is only
applicable for registered users ) . There ' s no concept of expiration or
timestamps . Messages are created by the Django admin after successful
actions . For example , " The poll Foo was created successfully. " is a
message .
2006-06-20 12:07:32 +08:00
"""
2006-05-02 09:31:56 +08:00
user = models . ForeignKey ( User )
message = models . TextField ( _ ( ' message ' ) )
Merged Unicode branch into trunk (r4952:5608). This should be fully
backwards compatible for all practical purposes.
Fixed #2391, #2489, #2996, #3322, #3344, #3370, #3406, #3432, #3454, #3492, #3582, #3690, #3878, #3891, #3937, #4039, #4141, #4227, #4286, #4291, #4300, #4452, #4702
git-svn-id: http://code.djangoproject.com/svn/django/trunk@5609 bcc190cf-cafb-0310-a4f2-bffc1f526a37
2007-07-04 20:11:04 +08:00
def __unicode__ ( self ) :
2006-05-02 09:31:56 +08:00
return self . message
class AnonymousUser ( object ) :
id = None
username = ' '
2007-09-16 02:01:29 +08:00
is_staff = False
2007-12-11 14:37:07 +08:00
is_active = False
2007-09-16 02:01:29 +08:00
is_superuser = False
_groups = EmptyManager ( )
_user_permissions = EmptyManager ( )
2006-05-02 09:31:56 +08:00
def __init__ ( self ) :
pass
Merged Unicode branch into trunk (r4952:5608). This should be fully
backwards compatible for all practical purposes.
Fixed #2391, #2489, #2996, #3322, #3344, #3370, #3406, #3432, #3454, #3492, #3582, #3690, #3878, #3891, #3937, #4039, #4141, #4227, #4286, #4291, #4300, #4452, #4702
git-svn-id: http://code.djangoproject.com/svn/django/trunk@5609 bcc190cf-cafb-0310-a4f2-bffc1f526a37
2007-07-04 20:11:04 +08:00
def __unicode__ ( self ) :
2007-07-03 20:24:46 +08:00
return ' AnonymousUser '
2006-05-02 09:31:56 +08:00
Merged Unicode branch into trunk (r4952:5608). This should be fully
backwards compatible for all practical purposes.
Fixed #2391, #2489, #2996, #3322, #3344, #3370, #3406, #3432, #3454, #3492, #3582, #3690, #3878, #3891, #3937, #4039, #4141, #4227, #4286, #4291, #4300, #4452, #4702
git-svn-id: http://code.djangoproject.com/svn/django/trunk@5609 bcc190cf-cafb-0310-a4f2-bffc1f526a37
2007-07-04 20:11:04 +08:00
def __str__ ( self ) :
return unicode ( self ) . encode ( ' utf-8 ' )
2006-10-25 00:42:03 +08:00
def __eq__ ( self , other ) :
return isinstance ( other , self . __class__ )
def __ne__ ( self , other ) :
return not self . __eq__ ( other )
def __hash__ ( self ) :
return 1 # instances always return the same hash value
2006-05-02 09:31:56 +08:00
def save ( self ) :
raise NotImplementedError
def delete ( self ) :
raise NotImplementedError
def set_password ( self , raw_password ) :
raise NotImplementedError
def check_password ( self , raw_password ) :
raise NotImplementedError
def _get_groups ( self ) :
2007-09-16 02:01:29 +08:00
return self . _groups
2006-05-02 09:31:56 +08:00
groups = property ( _get_groups )
def _get_user_permissions ( self ) :
2007-09-16 02:01:29 +08:00
return self . _user_permissions
2006-05-02 09:31:56 +08:00
user_permissions = property ( _get_user_permissions )
def has_perm ( self , perm ) :
return False
2008-07-22 11:05:40 +08:00
def has_perms ( self , perm_list ) :
return False
2006-05-02 09:31:56 +08:00
def has_module_perms ( self , module ) :
return False
def get_and_delete_messages ( self ) :
return [ ]
def is_anonymous ( self ) :
return True
2006-10-25 00:42:03 +08:00
2006-07-19 10:09:26 +08:00
def is_authenticated ( self ) :
return False