Code

Ticket #3011: 3011.6.diff

File 3011.6.diff, 15.7 KB (added by Michael Richardson <michael@…>, 6 years ago)

Patch against [7832] and added a quick fix so that apps like contenttypes wouldn't think that the imported module was part of contrib.auth.

Line 
1Index: django/conf/global_settings.py
2===================================================================
3--- django/conf/global_settings.py      (revision 7832)
4+++ django/conf/global_settings.py      (working copy)
5@@ -363,6 +363,9 @@
6 
7 LOGIN_REDIRECT_URL = '/accounts/profile/'
8 
9+# The class to use as the default AUTH_USER for the authentication system.
10+AUTH_USER_MODULE = 'django.contrib.auth.default_user.DefaultUser'
11+
12 ###########
13 # TESTING #
14 ###########
15Index: django/contrib/auth/default_user.py
16===================================================================
17--- django/contrib/auth/default_user.py (revision 0)
18+++ django/contrib/auth/default_user.py (revision 0)
19@@ -0,0 +1,114 @@
20+from django.db import models
21+from django.core import validators
22+from django.utils.translation import gettext_lazy as _
23+
24+from django.contrib.auth.models import Group, Permission, UserTemplate
25+
26+import datetime
27+
28+
29+class DefaultUserManager(models.Manager):
30+    def create_user(self, username, email, password=None):
31+        "Creates and saves a User with the given username, e-mail and password."
32+        now = datetime.datetime.now()
33+        user = self.model(None, username, '', '', email.strip().lower(), 'placeholder', False, True, False, now, now)
34+        if password:
35+            user.set_password(password)
36+        else:
37+            user.set_unusable_password()
38+        user.save()
39+        return user
40+
41+    def create_superuser(self, username, email, password):
42+        u = self.create_user(username, email, password)
43+        u.is_staff = True
44+        u.is_active = True
45+        u.is_superuser = True
46+        u.save()
47+
48+    def make_random_password(self, length=10, allowed_chars='abcdefghjkmnpqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ23456789'):
49+        "Generates a random password with the given length and given allowed_chars"
50+        # Note that default value of allowed_chars does not have "I" or letters
51+        # that look like it -- just to avoid confusion.
52+        from random import choice
53+        return ''.join([choice(allowed_chars) for i in range(length)])
54+
55+
56+class DefaultUser(UserTemplate):
57+    """Users within the Django authentication system are represented by this model.
58+
59+    Username and password are required. Other fields are optional.
60+    """
61+    username = models.CharField(_('username'), max_length=30, unique=True, validator_list=[validators.isAlphaNumeric], help_text=_("Required. 30 characters or fewer. Alphanumeric characters only (letters, digits and underscores)."))
62+    first_name = models.CharField(_('first name'), max_length=30, blank=True)
63+    last_name = models.CharField(_('last name'), max_length=30, blank=True)
64+    email = models.EmailField(_('e-mail address'), blank=True)
65+    password = models.CharField(_('password'), max_length=128, help_text=_("Use '[algo]$[salt]$[hexdigest]' or use the <a href=\"password/\">change password form</a>."))
66+    is_staff = models.BooleanField(_('staff status'), default=False, help_text=_("Designates whether the user can log into this admin site."))
67+    is_active = models.BooleanField(_('active'), default=True, help_text=_("Designates whether this user should be treated as active. Unselect this instead of deleting accounts."))
68+    is_superuser = models.BooleanField(_('superuser status'), default=False, help_text=_("Designates that this user has all permissions without explicitly assigning them."))
69+    last_login = models.DateTimeField(_('last login'), default=datetime.datetime.now)
70+    date_joined = models.DateTimeField(_('date joined'), default=datetime.datetime.now)
71+    groups = models.ManyToManyField(Group, verbose_name=_('groups'), blank=True,
72+        help_text=_("In addition to the permissions manually assigned, this user will also get all permissions granted to each group he/she is in."))
73+    user_permissions = models.ManyToManyField(Permission, verbose_name=_('user permissions'), blank=True, filter_interface=models.HORIZONTAL)
74+    objects = DefaultUserManager()
75+
76+    class Admin:
77+        fields = (
78+            (None, {'fields': ('username', 'password')}),
79+            (_('Personal info'), {'fields': ('first_name', 'last_name', 'email')}),
80+            (_('Permissions'), {'fields': ('is_staff', 'is_active', 'is_superuser', 'user_permissions')}),
81+            (_('Important dates'), {'fields': ('last_login', 'date_joined')}),
82+            (_('Groups'), {'fields': ('groups',)}),
83+        )
84+        list_display = ('username', 'email', 'first_name', 'last_name', 'is_staff')
85+        list_filter = ('is_staff', 'is_superuser')
86+        search_fields = ('username', 'first_name', 'last_name', 'email')
87+        ordering = ('username',)
88+
89+    def __unicode__(self):
90+        return self.username
91+
92+    def get_absolute_url(self):
93+        return "/users/%s/" % urllib.quote(smart_str(self.username))
94+
95+    def get_full_name(self):
96+        "Returns the first_name plus the last_name, with a space in between."
97+        full_name = u'%s %s' % (self.first_name, self.last_name)
98+        return full_name.strip()
99+
100+    def set_password(self, raw_password):
101+        import random
102+        algo = 'sha1'
103+        salt = get_hexdigest(algo, str(random.random()), str(random.random()))[:5]
104+        hsh = get_hexdigest(algo, salt, raw_password)
105+        self.password = '%s$%s$%s' % (algo, salt, hsh)
106+
107+    def check_password(self, raw_password):
108+        """
109+        Returns a boolean of whether the raw_password was correct. Handles
110+        encryption formats behind the scenes.
111+        """
112+        # Backwards-compatibility check. Older passwords won't include the
113+        # algorithm or salt.
114+        if '$' not in self.password:
115+            is_correct = (self.password == get_hexdigest('md5', '', raw_password))
116+            if is_correct:
117+                # Convert the password to the new, more secure format.
118+                self.set_password(raw_password)
119+                self.save()
120+            return is_correct
121+        return check_password(raw_password, self.password)
122+
123+    def set_unusable_password(self):
124+        # Sets a value that will never be a valid hash
125+        self.password = UNUSABLE_PASSWORD
126+
127+    def has_usable_password(self):
128+        return self.password != UNUSABLE_PASSWORD
129+
130+    def email_user(self, subject, message, from_email=None):
131+        "Sends an e-mail to this User."
132+        from django.core.mail import send_mail
133+        send_mail(subject, message, from_email, [self.email])
134\ No newline at end of file
135Index: django/contrib/auth/models.py
136===================================================================
137--- django/contrib/auth/models.py       (revision 7832)
138+++ django/contrib/auth/models.py       (working copy)
139@@ -6,6 +6,7 @@
140 from django.contrib.contenttypes.models import ContentType
141 from django.utils.encoding import smart_str
142 from django.utils.translation import ugettext_lazy as _
143+from django.conf import settings
144 import datetime
145 import urllib
146 
147@@ -104,75 +105,13 @@
148     def __unicode__(self):
149         return self.name
150 
151-class UserManager(models.Manager):
152-    def create_user(self, username, email, password=None):
153-        "Creates and saves a User with the given username, e-mail and password."
154-        now = datetime.datetime.now()
155-        user = self.model(None, username, '', '', email.strip().lower(), 'placeholder', False, True, False, now, now)
156-        if password:
157-            user.set_password(password)
158-        else:
159-            user.set_unusable_password()
160-        user.save()
161-        return user
162+class UserTemplate(models.Model):
163+    """ Base class from which all User models inherit.  """
164 
165-    def create_superuser(self, username, email, password):
166-        u = self.create_user(username, email, password)
167-        u.is_staff = True
168-        u.is_active = True
169-        u.is_superuser = True
170-        u.save()
171-
172-    def make_random_password(self, length=10, allowed_chars='abcdefghjkmnpqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ23456789'):
173-        "Generates a random password with the given length and given allowed_chars"
174-        # Note that default value of allowed_chars does not have "I" or letters
175-        # that look like it -- just to avoid confusion.
176-        from random import choice
177-        return ''.join([choice(allowed_chars) for i in range(length)])
178-
179-class User(models.Model):
180-    """Users within the Django authentication system are represented by this model.
181-
182-    Username and password are required. Other fields are optional.
183-    """
184-    username = models.CharField(_('username'), max_length=30, unique=True, validator_list=[validators.isAlphaNumeric], help_text=_("Required. 30 characters or fewer. Alphanumeric characters only (letters, digits and underscores)."))
185-    first_name = models.CharField(_('first name'), max_length=30, blank=True)
186-    last_name = models.CharField(_('last name'), max_length=30, blank=True)
187-    email = models.EmailField(_('e-mail address'), blank=True)
188-    password = models.CharField(_('password'), max_length=128, help_text=_("Use '[algo]$[salt]$[hexdigest]' or use the <a href=\"password/\">change password form</a>."))
189-    is_staff = models.BooleanField(_('staff status'), default=False, help_text=_("Designates whether the user can log into this admin site."))
190-    is_active = models.BooleanField(_('active'), default=True, help_text=_("Designates whether this user should be treated as active. Unselect this instead of deleting accounts."))
191-    is_superuser = models.BooleanField(_('superuser status'), default=False, help_text=_("Designates that this user has all permissions without explicitly assigning them."))
192-    last_login = models.DateTimeField(_('last login'), default=datetime.datetime.now)
193-    date_joined = models.DateTimeField(_('date joined'), default=datetime.datetime.now)
194-    groups = models.ManyToManyField(Group, verbose_name=_('groups'), blank=True,
195-        help_text=_("In addition to the permissions manually assigned, this user will also get all permissions granted to each group he/she is in."))
196-    user_permissions = models.ManyToManyField(Permission, verbose_name=_('user permissions'), blank=True, filter_interface=models.HORIZONTAL)
197-    objects = UserManager()
198-
199     class Meta:
200         verbose_name = _('user')
201         verbose_name_plural = _('users')
202 
203-    class Admin:
204-        fields = (
205-            (None, {'fields': ('username', 'password')}),
206-            (_('Personal info'), {'fields': ('first_name', 'last_name', 'email')}),
207-            (_('Permissions'), {'fields': ('is_staff', 'is_active', 'is_superuser', 'user_permissions')}),
208-            (_('Important dates'), {'fields': ('last_login', 'date_joined')}),
209-            (_('Groups'), {'fields': ('groups',)}),
210-        )
211-        list_display = ('username', 'email', 'first_name', 'last_name', 'is_staff')
212-        list_filter = ('is_staff', 'is_superuser')
213-        search_fields = ('username', 'first_name', 'last_name', 'email')
214-        ordering = ('username',)
215-
216-    def __unicode__(self):
217-        return self.username
218-
219-    def get_absolute_url(self):
220-        return "/users/%s/" % urllib.quote(smart_str(self.username))
221-
222     def is_anonymous(self):
223         "Always returns False. This is a way of comparing User objects to anonymous users."
224         return False
225@@ -181,42 +120,7 @@
226         """Always return True. This is a way to tell if the user has been authenticated in templates.
227         """
228         return True
229-
230-    def get_full_name(self):
231-        "Returns the first_name plus the last_name, with a space in between."
232-        full_name = u'%s %s' % (self.first_name, self.last_name)
233-        return full_name.strip()
234-
235-    def set_password(self, raw_password):
236-        import random
237-        algo = 'sha1'
238-        salt = get_hexdigest(algo, str(random.random()), str(random.random()))[:5]
239-        hsh = get_hexdigest(algo, salt, raw_password)
240-        self.password = '%s$%s$%s' % (algo, salt, hsh)
241-
242-    def check_password(self, raw_password):
243-        """
244-        Returns a boolean of whether the raw_password was correct. Handles
245-        encryption formats behind the scenes.
246-        """
247-        # Backwards-compatibility check. Older passwords won't include the
248-        # algorithm or salt.
249-        if '$' not in self.password:
250-            is_correct = (self.password == get_hexdigest('md5', '', raw_password))
251-            if is_correct:
252-                # Convert the password to the new, more secure format.
253-                self.set_password(raw_password)
254-                self.save()
255-            return is_correct
256-        return check_password(raw_password, self.password)
257-
258-    def set_unusable_password(self):
259-        # Sets a value that will never be a valid hash
260-        self.password = UNUSABLE_PASSWORD
261-
262-    def has_usable_password(self):
263-        return self.password != UNUSABLE_PASSWORD
264-
265+   
266     def get_group_permissions(self):
267         """
268         Returns a list of permission strings that this user has through
269@@ -288,11 +192,6 @@
270             m.delete()
271         return messages
272 
273-    def email_user(self, subject, message, from_email=None):
274-        "Sends an e-mail to this User."
275-        from django.core.mail import send_mail
276-        send_mail(subject, message, from_email, [self.email])
277-
278     def get_profile(self):
279         """
280         Returns site-specific profile for this user. Raises
281@@ -310,6 +209,28 @@
282                 raise SiteProfileNotAvailable
283         return self._profile_cache
284 
285+
286+# Grab the AUTH_USER_MODULE path and classname from the settings.
287+# auth_user_module_parts[0] = module path
288+# auth_user_module_parts[1] = class name
289+auth_user_module_parts = settings.AUTH_USER_MODULE.rsplit('.', 1)
290+auth_user_module = __import__(auth_user_module_parts[0], {}, {}, [auth_user_module_parts[0]])
291+# Store the auth_user model so it is accessible with the standard
292+# 'from django.contrib.auth.models import User'
293+User = getattr(auth_user_module, auth_user_module_parts[1])
294+
295+# Add the User model to the django models cache
296+# These two lines allow the custom auth_user model to play nicely with syncdb
297+# and other systems that rely on functions like
298+# django.db.models.loading.get_model(...)
299+from django.db.models.loading import cache
300+cache.register_models('auth', User)
301+
302+# We need to remove whatever we used as the AUTH_USER_MODUlE from the
303+# db cache so apps like contenttypes don't think that it's part
304+# of contrib.auth
305+del cache.app_models['auth'][auth_user_module_parts[1].lower()]
306+
307 class Message(models.Model):
308     """
309     The message system is a lightweight way to queue messages for given
310Index: docs/authentication.txt
311===================================================================
312--- docs/authentication.txt     (revision 7832)
313+++ docs/authentication.txt     (working copy)
314@@ -1164,3 +1164,13 @@
315 the ``auth_permission`` table most of the time.
316 
317 .. _django/contrib/auth/backends.py: http://code.djangoproject.com/browser/django/trunk/django/contrib/auth/backends.py
318+
319+Overriding the Default Auth User Module
320+=======================================
321+
322+If the default User model doesn't work for you, you can specify your own through
323+the AUTH_USER_MODULE variable in settings.py.
324+
325+Simply import ``django.contrib.auth.UserTemplate`` and have your user model inherit
326+from that.  You'll also have to specify your own authentication backend and any
327+specific manager methods you'll need, such as create_user.
328Index: docs/settings.txt
329===================================================================
330--- docs/settings.txt   (revision 7832)
331+++ docs/settings.txt   (working copy)
332@@ -249,6 +249,15 @@
333 
334 .. _documentation on user profile models: ../authentication/#storing-additional-information-about-users
335 
336+AUTH_USER_MODULE
337+----------------
338+
339+Default: ``'django.contrib.auth.default_user.User'``
340+
341+The user model used by this site.  See the `documentation on user models`_ for details.
342+
343+.. _documentation on user models: ../authentication/#overriding-default-user-model
344+
345 CACHE_BACKEND
346 -------------
347