Ticket #9788: models.py

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

django/contrib/auth/models.py

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