diff --git a/django/contrib/admin/forms.py b/django/contrib/admin/forms.py index e790e2e3b6..4e828697e8 100644 --- a/django/contrib/admin/forms.py +++ b/django/contrib/admin/forms.py @@ -9,6 +9,7 @@ from django.utils.translation import ugettext_lazy, ugettext as _ ERROR_MESSAGE = ugettext_lazy("Please enter the correct username and password " "for a staff account. Note that both fields are case-sensitive.") + class AdminAuthenticationForm(AuthenticationForm): """ A custom authentication form used in the admin app. diff --git a/django/contrib/admin/sites.py b/django/contrib/admin/sites.py index 4bb6440877..e57eafe256 100644 --- a/django/contrib/admin/sites.py +++ b/django/contrib/admin/sites.py @@ -2,7 +2,7 @@ from functools import update_wrapper from django.http import Http404, HttpResponseRedirect from django.contrib.admin import ModelAdmin, actions from django.contrib.admin.forms import AdminAuthenticationForm -from django.contrib.auth import REDIRECT_FIELD_NAME +from django.contrib.auth import REDIRECT_FIELD_NAME, get_user_model from django.contrib.contenttypes import views as contenttype_views from django.views.decorators.csrf import csrf_protect from django.db.models.base import ModelBase @@ -79,20 +79,23 @@ class AdminSite(object): if model in self._registry: raise AlreadyRegistered('The model %s is already registered' % model.__name__) - # If we got **options then dynamically construct a subclass of - # admin_class with those **options. - if options: - # For reasons I don't quite understand, without a __module__ - # the created class appears to "live" in the wrong place, - # which causes issues later on. - options['__module__'] = __name__ - admin_class = type("%sAdmin" % model.__name__, (admin_class,), options) + # Ignore the registration if the model has been + # swapped out. + if not model._meta.swapped: + # If we got **options then dynamically construct a subclass of + # admin_class with those **options. + if options: + # For reasons I don't quite understand, without a __module__ + # the created class appears to "live" in the wrong place, + # which causes issues later on. + options['__module__'] = __name__ + admin_class = type("%sAdmin" % model.__name__, (admin_class,), options) - # Validate (which might be a no-op) - validate(admin_class, model) + # Validate (which might be a no-op) + validate(admin_class, model) - # Instantiate the admin class to save in the registry - self._registry[model] = admin_class(model, self) + # Instantiate the admin class to save in the registry + self._registry[model] = admin_class(model, self) def unregister(self, model_or_iterable): """ @@ -317,6 +320,7 @@ class AdminSite(object): REDIRECT_FIELD_NAME: request.get_full_path(), } context.update(extra_context or {}) + defaults = { 'extra_context': context, 'current_app': self.name, diff --git a/django/contrib/admin/templates/admin/base.html b/django/contrib/admin/templates/admin/base.html index caa26744d4..3d2a07eba2 100644 --- a/django/contrib/admin/templates/admin/base.html +++ b/django/contrib/admin/templates/admin/base.html @@ -26,7 +26,7 @@ {% if user.is_active and user.is_staff %}
{% trans 'Welcome,' %} - {% filter force_escape %}{% firstof user.first_name user.username %}{% endfilter %}. + {% filter force_escape %}{% firstof user.get_short_name user.username %}{% endfilter %}. {% block userlinks %} {% url 'django-admindocs-docroot' as docsroot %} {% if docsroot %} diff --git a/django/contrib/auth/__init__.py b/django/contrib/auth/__init__.py index 0b3ccf7d8c..d5d4430dc0 100644 --- a/django/contrib/auth/__init__.py +++ b/django/contrib/auth/__init__.py @@ -6,9 +6,10 @@ SESSION_KEY = '_auth_user_id' BACKEND_SESSION_KEY = '_auth_user_backend' REDIRECT_FIELD_NAME = 'next' + def load_backend(path): i = path.rfind('.') - module, attr = path[:i], path[i+1:] + module, attr = path[:i], path[i + 1:] try: mod = import_module(module) except ImportError as e: @@ -21,6 +22,7 @@ def load_backend(path): raise ImproperlyConfigured('Module "%s" does not define a "%s" authentication backend' % (module, attr)) return cls() + def get_backends(): from django.conf import settings backends = [] @@ -30,6 +32,7 @@ def get_backends(): raise ImproperlyConfigured('No authentication backends have been defined. Does AUTHENTICATION_BACKENDS contain anything?') return backends + def authenticate(**credentials): """ If the given credentials are valid, return a User object. @@ -46,6 +49,7 @@ def authenticate(**credentials): user.backend = "%s.%s" % (backend.__module__, backend.__class__.__name__) return user + def login(request, user): """ Persist a user id and a backend in the request. This way a user doesn't @@ -69,6 +73,7 @@ def login(request, user): request.user = user user_logged_in.send(sender=user.__class__, request=request, user=user) + def logout(request): """ Removes the authenticated user's ID from the request and flushes their @@ -86,6 +91,16 @@ def logout(request): from django.contrib.auth.models import AnonymousUser request.user = AnonymousUser() + +def get_user_model(): + "Return the User model that is active in this project" + from django.conf import settings + from django.db.models import get_model + + app_label, model_name = settings.AUTH_USER_MODEL.split('.') + return get_model(app_label, model_name) + + def get_user(request): from django.contrib.auth.models import AnonymousUser try: diff --git a/django/contrib/auth/backends.py b/django/contrib/auth/backends.py index 04fbef4788..da4c9c727a 100644 --- a/django/contrib/auth/backends.py +++ b/django/contrib/auth/backends.py @@ -1,4 +1,5 @@ -from django.contrib.auth.models import User, Permission +from django.contrib.auth import get_user_model +from django.contrib.auth.models import Permission class ModelBackend(object): @@ -10,10 +11,13 @@ class ModelBackend(object): # configurable. def authenticate(self, username=None, password=None): try: - user = User.objects.get(username=username) + UserModel = get_user_model() + user = UserModel.objects.get(**{ + getattr(UserModel, 'USERNAME_FIELD', 'username'): username + }) if user.check_password(password): return user - except User.DoesNotExist: + except UserModel.DoesNotExist: return None def get_group_permissions(self, user_obj, obj=None): @@ -58,8 +62,9 @@ class ModelBackend(object): def get_user(self, user_id): try: - return User.objects.get(pk=user_id) - except User.DoesNotExist: + UserModel = get_user_model() + return UserModel.objects.get(pk=user_id) + except UserModel.DoesNotExist: return None @@ -92,17 +97,23 @@ class RemoteUserBackend(ModelBackend): user = None username = self.clean_username(remote_user) + UserModel = get_user_model() + # Note that this could be accomplished in one try-except clause, but # instead we use get_or_create when creating unknown users since it has # built-in safeguards for multiple threads. if self.create_unknown_user: - user, created = User.objects.get_or_create(username=username) + user, created = UserModel.objects.get_or_create(**{ + getattr(UserModel, 'USERNAME_FIELD', 'username'): username + }) if created: user = self.configure_user(user) else: try: - user = User.objects.get(username=username) - except User.DoesNotExist: + user = UserModel.objects.get(**{ + getattr(UserModel, 'USERNAME_FIELD', 'username'): username + }) + except UserModel.DoesNotExist: pass return user diff --git a/django/contrib/auth/management/__init__.py b/django/contrib/auth/management/__init__.py index 8287c97d58..2ae4084416 100644 --- a/django/contrib/auth/management/__init__.py +++ b/django/contrib/auth/management/__init__.py @@ -5,10 +5,9 @@ import getpass import locale import unicodedata -from django.contrib.auth import models as auth_app +from django.contrib.auth import models as auth_app, get_user_model from django.core import exceptions from django.db.models import get_models, signals -from django.contrib.auth.models import User, get_user_model def _get_permission_codename(action, opts): @@ -106,7 +105,7 @@ def get_default_username(check_db=True): """ # If the User model has been swapped out, we can't make any assumptions # about the default user name. - if User._meta.swapped: + if auth_app.User._meta.swapped: return '' default_username = get_system_username() @@ -118,15 +117,15 @@ def get_default_username(check_db=True): # Run the username validator try: - User._meta.get_field('username').run_validators(default_username) + auth_app.User._meta.get_field('username').run_validators(default_username) except exceptions.ValidationError: return '' # Don't return the default username if it is already taken. if check_db and default_username: try: - User.objects.get(username=default_username) - except User.DoesNotExist: + auth_app.User.objects.get(username=default_username) + except auth_app.User.DoesNotExist: pass else: return '' diff --git a/django/contrib/auth/management/commands/changepassword.py b/django/contrib/auth/management/commands/changepassword.py index 2a8a19e6cb..1a2387442c 100644 --- a/django/contrib/auth/management/commands/changepassword.py +++ b/django/contrib/auth/management/commands/changepassword.py @@ -1,8 +1,8 @@ import getpass from optparse import make_option +from django.contrib.auth import get_user_model from django.core.management.base import BaseCommand, CommandError -from django.contrib.auth.models import get_user_model from django.db import DEFAULT_DB_ALIAS diff --git a/django/contrib/auth/management/commands/createsuperuser.py b/django/contrib/auth/management/commands/createsuperuser.py index ffe248753a..3db1b877e1 100644 --- a/django/contrib/auth/management/commands/createsuperuser.py +++ b/django/contrib/auth/management/commands/createsuperuser.py @@ -6,8 +6,8 @@ import getpass import sys from optparse import make_option +from django.contrib.auth import get_user_model from django.contrib.auth.management import get_default_username -from django.contrib.auth.models import get_user_model from django.core import exceptions from django.core.management.base import BaseCommand, CommandError from django.db import DEFAULT_DB_ALIAS diff --git a/django/contrib/auth/models.py b/django/contrib/auth/models.py index 62bf68afbc..6604423512 100644 --- a/django/contrib/auth/models.py +++ b/django/contrib/auth/models.py @@ -1,7 +1,6 @@ import re import urllib -from django.conf import settings from django.core.exceptions import ImproperlyConfigured from django.core.mail import send_mail from django.core import validators @@ -271,12 +270,6 @@ class AbstractBaseUser(models.Model): raise NotImplementedError() -def get_user_model(): - "Return the User model that is active in this project" - app_label, model_name = settings.AUTH_USER_MODEL.split('.') - return models.get_model(app_label, model_name) - - class User(AbstractBaseUser): """ Users within the Django authentication system are represented by this diff --git a/django/contrib/auth/views.py b/django/contrib/auth/views.py index c86ef53595..d32d2180b9 100644 --- a/django/contrib/auth/views.py +++ b/django/contrib/auth/views.py @@ -70,6 +70,7 @@ def login(request, template_name='registration/login.html', return TemplateResponse(request, template_name, context, current_app=current_app) + def logout(request, next_page=None, template_name='registration/logged_out.html', redirect_field_name=REDIRECT_FIELD_NAME, @@ -100,6 +101,7 @@ def logout(request, next_page=None, # Redirect to this page until the session has been cleared. return HttpResponseRedirect(next_page or request.path) + def logout_then_login(request, login_url=None, current_app=None, extra_context=None): """ Logs out the user if he is logged in. Then redirects to the log-in page. @@ -108,6 +110,7 @@ def logout_then_login(request, login_url=None, current_app=None, extra_context=N login_url = settings.LOGIN_URL return logout(request, login_url, current_app=current_app, extra_context=extra_context) + def redirect_to_login(next, login_url=None, redirect_field_name=REDIRECT_FIELD_NAME): """ @@ -124,6 +127,7 @@ def redirect_to_login(next, login_url=None, return HttpResponseRedirect(urlparse.urlunparse(login_url_parts)) + # 4 views for password reset: # - password_reset sends the mail # - password_reset_done shows a success message for the above @@ -169,6 +173,7 @@ def password_reset(request, is_admin_site=False, return TemplateResponse(request, template_name, context, current_app=current_app) + def password_reset_done(request, template_name='registration/password_reset_done.html', current_app=None, extra_context=None): @@ -178,6 +183,7 @@ def password_reset_done(request, return TemplateResponse(request, template_name, context, current_app=current_app) + # Doesn't need csrf_protect since no-one can guess the URL @sensitive_post_parameters() @never_cache @@ -191,7 +197,7 @@ def password_reset_confirm(request, uidb36=None, token=None, View that checks the hash in a password reset link and presents a form for entering a new password. """ - assert uidb36 is not None and token is not None # checked by URLconf + assert uidb36 is not None and token is not None # checked by URLconf if post_reset_redirect is None: post_reset_redirect = reverse('django.contrib.auth.views.password_reset_complete') try: @@ -221,6 +227,7 @@ def password_reset_confirm(request, uidb36=None, token=None, return TemplateResponse(request, template_name, context, current_app=current_app) + def password_reset_complete(request, template_name='registration/password_reset_complete.html', current_app=None, extra_context=None): @@ -232,6 +239,7 @@ def password_reset_complete(request, return TemplateResponse(request, template_name, context, current_app=current_app) + @sensitive_post_parameters() @csrf_protect @login_required @@ -257,6 +265,7 @@ def password_change(request, return TemplateResponse(request, template_name, context, current_app=current_app) + @login_required def password_change_done(request, template_name='registration/password_change_done.html',