Source code for superdjango.contrib.accounts.impersonation.views

# Imports

from django.contrib.auth import get_user_model
from django.core.paginator import Paginator, PageNotAnInteger, EmptyPage
from django.db.models import Q
from django.shortcuts import get_object_or_404
from django.urls import reverse
from django.utils.translation import ugettext_lazy as _
from superdjango.patterns import PATTERN_PK
from superdjango.conf import settings
from superdjango.views import AccessMixin, BreadcrumbsMixin, FormView, RedirectView, TemplateView, ViewSet
from .constants import IMPERSONATION_FOR_ID, IMPERSONATION_PREVIOUS_URL, IMPERSONATION_SESSION_KEY
from .forms import UserImpersonationSearchForm
from .utils import generate_session_key, get_user_impersonation_model, user_is_allowed_to_impersonate

UserModel = get_user_model()
UserImpersonationModel = get_user_impersonation_model()

# Exports

__all__ = (
    "ImpersonationHelp",
    "ImpersonationViewSet",
    "ListUsersForImpersonation",
    "SearchUsersForImpersonation",
    "StartImpersonationRedirect",
    "StopImpersonationRedirect",
)

# Constants

EMAIL_FIELD = getattr(UserModel, "EMAIL_FIELD", "email")
LOGIN_REDIRECT_URL = settings.LOGIN_REDIRECT_URL
REDIRECT_FIELD_NAME = getattr(settings, "REDIRECT_FIELD_NAME", "next")
USERNAME_FIELD = getattr(UserModel, "USERNAME_FIELD", "username")

# Views


[docs]class ImpersonationHelp(BreadcrumbsMixin, TemplateView): login_required = True pattern_name = "impersonation_help" pattern_value = "impersonation/help/" template_name = "accounts/impersonation_help.html"
[docs] def get_breadcrumbs(self): crumbs = super().get_breadcrumbs() crumbs.add(_("User Impersonation"), "") return crumbs
[docs] def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) # noinspection PyPep8Naming if UserImpersonationModel is not None: context['impersonation_history'] = UserImpersonationModel.objects.filter(impersonation_by=self.request.user) else: context['impersonation_history'] = None return context
[docs]class ListUsersForImpersonation(BreadcrumbsMixin, TemplateView): login_required = True pattern_name = "list_users_for_impersonation" pattern_value = 'impersonation/' template_name = "accounts/list_users_for_impersonation.html"
[docs] def get_breadcrumbs(self): crumbs = super().get_breadcrumbs() crumbs.add(_("User Impersonation"), reverse("impersonation_help")) crumbs.add(_("Users"), reverse("list_users_for_impersonation")) return crumbs
[docs] def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) # context['form'] = UserImpersonationSearchForm() context['next_url'] = self.request.GET.get(REDIRECT_FIELD_NAME, None) users = UserModel.objects.filter(is_superuser=False).order_by("email") # Pagination page = self.request.GET.get('page') entries_per_page = 25 paginator = Paginator(users, entries_per_page) try: objects = paginator.page(page) except PageNotAnInteger: objects = paginator.page(1) except EmptyPage: objects = paginator.page(paginator.num_pages) context['users'] = objects return context
[docs]class SearchUsersForImpersonation(BreadcrumbsMixin, FormView): form_class = UserImpersonationSearchForm login_required = True pattern_name = "search_users_for_impersonation" pattern_value = "impersonation/search/" template_name = "accounts/search_users_for_impersonation.html"
[docs] def get_breadcrumbs(self): crumbs = super().get_breadcrumbs() crumbs.add(_("User Impersonation"), "/accounts/impersonation/help/") crumbs.add(_("Users"), reverse("list_users_for_impersonation")) crumbs.add(_("Search"), "") return crumbs
[docs] def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) context['users'] = None term = self.request.GET.get("term", None) context['term'] = term if term is not None: users = UserModel.objects.filter(is_superuser=False).filter( Q(first_name__iexact=term) | Q(last_name__iexact=term) | Q(**{USERNAME_FIELD: term}) | Q(**{EMAIL_FIELD: term}) ) context['users'] = users return context
[docs] def form_valid(self, form): # noinspection PyAttributeOutsideInit self.term = form.cleaned_data['term'] return super().form_valid(form)
[docs] def get_success_url(self): return reverse("search_users_for_impersonation") + "?term=%s" % self.term
[docs]class StartImpersonationRedirect(AccessMixin, RedirectView): login_required = True pattern_name = "start_impersonation_redirect" pattern_value = 'impersonation/start/%s/' % PATTERN_PK
[docs] def get_redirect_url(self, *args, **kwargs): # Get the user to be impersonated or die trying. user = get_object_or_404(UserModel, pk=self.kwargs['pk']) # Make sure the current user is allowed to impersonate another user. if user_is_allowed_to_impersonate(self.request.user): # Generate and save a session key. session_key = generate_session_key() self.request.session[IMPERSONATION_SESSION_KEY] = session_key # Note who is being impersonated. See middleware. self.request.session[IMPERSONATION_FOR_ID] = user.pk # Get the previous path so we can redirect there when impersonation ends. previous_url = self.request.META.get('HTTP_REFERER') if previous_url: self.request.session[IMPERSONATION_PREVIOUS_URL] = self.request.build_absolute_uri(previous_url) else: self.request.session[IMPERSONATION_PREVIOUS_URL] = LOGIN_REDIRECT_URL # TODO: Send a signal to indicate an impersonation session has started? # impersonation_session_started.send( # sender=None, # impersonation_by=self.request.user, # impersonation_for=user, # request=self.request # ) # Optional logging to the database. if UserImpersonationModel is not None: UserImpersonationModel.start(self.request.user, user, session_key) # Make sure the session updates immediately. self.request.session.modified = True # Redirect. next_url = self.request.GET.get(REDIRECT_FIELD_NAME, None) if next_url is None: next_url = LOGIN_REDIRECT_URL return next_url
[docs]class StopImpersonationRedirect(AccessMixin, RedirectView): login_required = True pattern_name = "stop_impersonation_redirect" pattern_value = 'impersonation/stop/'
[docs] def get_redirect_url(self, *args, **kwargs): # There's nothing to do if impersonation is inactive. if IMPERSONATION_SESSION_KEY not in self.request.session: self.messages.warning(_("You don't appear to be impersonating another user.")) return reverse("impersonation_help") # Update the log entry. if UserImpersonationModel is not None: try: criteria = { 'impersonation_by': self.request.user, 'impersonation_for_id': self.request.session[IMPERSONATION_FOR_ID], 'session_key': self.request.session[IMPERSONATION_SESSION_KEY], 'stop_dt__isnull': True, } record = UserImpersonationModel.objects.get(**criteria) record.stop() except UserImpersonationModel.DoesNotExist: pass except UserImpersonationModel.MultipleObjectsReturned: pass # Get the redirect URL and then obliterate session data. redirect_url = self.request.session[IMPERSONATION_PREVIOUS_URL] del self.request.session[IMPERSONATION_FOR_ID] del self.request.session[IMPERSONATION_PREVIOUS_URL] del self.request.session[IMPERSONATION_SESSION_KEY] self.request.session.modified = True # Redirect. return redirect_url
# BaseView Sets
[docs]class ImpersonationViewSet(ViewSet): views = [ ImpersonationHelp, ListUsersForImpersonation, SearchUsersForImpersonation, StartImpersonationRedirect, StopImpersonationRedirect, ]