From 3e7c587b9b10e136f72335afd583f5a776297af3 Mon Sep 17 00:00:00 2001 From: Enrico Zini Date: Sat, 25 Apr 2020 11:49:45 +0200 Subject: [PATCH 1/7] Cleaned up urls, removed old redirects. refs: #12 --- restricted/urls.py | 18 +++++------------- 1 file changed, 5 insertions(+), 13 deletions(-) diff --git a/restricted/urls.py b/restricted/urls.py index 5301008f..3d3fc2cf 100644 --- a/restricted/urls.py +++ b/restricted/urls.py @@ -1,21 +1,13 @@ -from django.conf.urls import url -from django.views.generic import RedirectView +from django.urls import path from . import views urlpatterns = [ # Impersonate a user - url(r'^impersonate/(?P[^/]+)?$', views.Impersonate.as_view(), name="impersonate"), + path('impersonate//', views.Impersonate.as_view(), name="impersonate"), # Export database - url(r'^db-export$', views.DBExport.as_view(), name="restricted_db_export"), + path('db-export/', views.DBExport.as_view(), name="restricted_db_export"), # Mailbox stats - url(r'^mailbox-stats$', views.MailboxStats.as_view(), name="mailbox_stats"), + path('mailbox-stats/', views.MailboxStats.as_view(), name="mailbox_stats"), # Export membership information for salsa - url(r'^salsa-export$', views.SalsaExport.as_view(), name="export_salsa"), - - # Compatibility - url(r'^ammain$', RedirectView.as_view(url="/process/am-dashboard", permanent=True)), - url(r'^amprofile(?:/(?P[^/]+))?$', RedirectView.as_view(url="/person/%(key)s/amprofile", permanent=True)), - url(r'^minechangelogs/(?P[^/]+)?$', RedirectView.as_view(url="/minechangelogs/search/%(key)s", permanent=True)), - url(r'^mail-archive/(?P[^/]+)$', RedirectView.as_view(url="/legacy/mail-archive/%(key)s", permanent=True)), - url(r'^display-mail-archive/(?P[^/]+)$', RedirectView.as_view(url="/legacy/display-mail-archive/%(key)s", permanent=True)), + path('salsa-export/', views.SalsaExport.as_view(), name="export_salsa"), ] -- GitLab From 529cdf7e4fe065600b095c382623fcae7082d5de Mon Sep 17 00:00:00 2001 From: Enrico Zini Date: Sat, 25 Apr 2020 12:03:27 +0200 Subject: [PATCH 2/7] Moved Impersonate view to its own app, activate by POST instead of GET, made view work with any user model. refs: #12 --- backend/mixins.py | 11 +++------- impersonate/__init__.py | 0 impersonate/admin.py | 3 +++ impersonate/apps.py | 5 +++++ impersonate/migrations/__init__.py | 0 impersonate/models.py | 3 +++ impersonate/tests.py | 3 +++ impersonate/urls.py | 8 ++++++++ impersonate/views.py | 32 ++++++++++++++++++++++++++++++ nm2/settings.py | 1 + nm2/urls.py | 3 +-- nmlayout/templates/nm2-base.html | 18 ++++++++++++++--- restricted/urls.py | 2 -- restricted/views.py | 23 --------------------- 14 files changed, 74 insertions(+), 38 deletions(-) create mode 100644 impersonate/__init__.py create mode 100644 impersonate/admin.py create mode 100644 impersonate/apps.py create mode 100644 impersonate/migrations/__init__.py create mode 100644 impersonate/models.py create mode 100644 impersonate/tests.py create mode 100644 impersonate/urls.py create mode 100644 impersonate/views.py diff --git a/backend/mixins.py b/backend/mixins.py index bdde115e..3dbcaee4 100644 --- a/backend/mixins.py +++ b/backend/mixins.py @@ -39,9 +39,9 @@ class VisitorMixin(NM2LayoutMixin): # Implement impersonation if requested in session if self.visitor.is_admin: - key = self.request.session.get("impersonate", None) - if key is not None: - p = bmodels.Person.lookup(key) + pk = self.request.session.get("impersonate", None) + if pk is not None: + p = bmodels.Person.objects.get(pk=pk) if p is not None: self.impersonator = self.visitor self.visitor = p @@ -121,11 +121,6 @@ class VisitPersonMixin(VisitorMixin): if "am_candidate" in self.person.perms: res.append(NavLink( reverse("admin:backend_am_add") + f"?person={self.person.id}", _("Make AM"))) - if self.visitor != self.person: - res.append(NavLink( - reverse("impersonate", kwargs={ - "key": self.person.lookup_key}) + f"?url={self.request.build_absolute_uri()}", - _("Impersonate"), "random")) if self.person.is_dd: res.append(NavLink( reverse("mia_wat_ping", kwargs={"key": self.person.lookup_key}), _("WAT ping"), "heartbeat")) diff --git a/impersonate/__init__.py b/impersonate/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/impersonate/admin.py b/impersonate/admin.py new file mode 100644 index 00000000..8c38f3f3 --- /dev/null +++ b/impersonate/admin.py @@ -0,0 +1,3 @@ +from django.contrib import admin + +# Register your models here. diff --git a/impersonate/apps.py b/impersonate/apps.py new file mode 100644 index 00000000..813354fd --- /dev/null +++ b/impersonate/apps.py @@ -0,0 +1,5 @@ +from django.apps import AppConfig + + +class ImpersonateConfig(AppConfig): + name = 'impersonate' diff --git a/impersonate/migrations/__init__.py b/impersonate/migrations/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/impersonate/models.py b/impersonate/models.py new file mode 100644 index 00000000..71a83623 --- /dev/null +++ b/impersonate/models.py @@ -0,0 +1,3 @@ +from django.db import models + +# Create your models here. diff --git a/impersonate/tests.py b/impersonate/tests.py new file mode 100644 index 00000000..7ce503c2 --- /dev/null +++ b/impersonate/tests.py @@ -0,0 +1,3 @@ +from django.test import TestCase + +# Create your tests here. diff --git a/impersonate/urls.py b/impersonate/urls.py new file mode 100644 index 00000000..ca1f4cee --- /dev/null +++ b/impersonate/urls.py @@ -0,0 +1,8 @@ +from __future__ import annotations +from django.urls import path +from . import views + +urlpatterns = [ + # Impersonate a user + path('impersonate/', views.Impersonate.as_view(), name="impersonate"), +] diff --git a/impersonate/views.py b/impersonate/views.py new file mode 100644 index 00000000..8e940395 --- /dev/null +++ b/impersonate/views.py @@ -0,0 +1,32 @@ +from __future__ import annotations +from django.utils.translation import ugettext as _ +from django.views.generic import View +from django.shortcuts import redirect +from django.core.exceptions import PermissionDenied +from django.contrib import messages +from django.contrib.auth import get_user_model + + +class Impersonate(View): + def post(self, request, *args, **kw): + User = get_user_model() + effective_user = getattr(request, "impersonator", None) + if effective_user is None: + effective_user = request.user + if not effective_user.is_authenticated or not effective_user.is_admin: + raise PermissionDenied + pk = request.POST.get("pk") + if pk is None: + del request.session["impersonate"] + messages.add_message(request, messages.INFO, _("Impersonation canceled")) + user = effective_user + else: + user = User.objects.get(pk=pk) + request.session["impersonate"] = user.pk + messages.info(request, _("Impersonating {}").format(user)) + + url = request.POST.get("next", None) + if url is None: + return redirect(user.get_absolute_url()) + else: + return redirect(url) diff --git a/nm2/settings.py b/nm2/settings.py index 20c9e470..868d1d3f 100644 --- a/nm2/settings.py +++ b/nm2/settings.py @@ -74,6 +74,7 @@ INSTALLED_APPS = [ 'sitechecks', 'deploy', 'signon', + 'impersonate', ] MIDDLEWARE = [ diff --git a/nm2/urls.py b/nm2/urls.py index 1bdc1028..4c27f48e 100644 --- a/nm2/urls.py +++ b/nm2/urls.py @@ -44,10 +44,9 @@ urlpatterns = [ path('minechangelogs/', include("minechangelogs.urls")), path('sitechecks/', include("sitechecks.urls")), path('deploy/', include("deploy.urls")), - path('rest/api/', include(router.urls)), - path('signon/', include("signon.urls")), + path('impersonate/', include("impersonate.urls")), # Uncomment the admin/doc line below to enable admin documentation: path('admin/doc/', include('django.contrib.admindocs.urls')), diff --git a/nmlayout/templates/nm2-base.html b/nmlayout/templates/nm2-base.html index c9c1ffa6..a411d3a6 100644 --- a/nmlayout/templates/nm2-base.html +++ b/nmlayout/templates/nm2-base.html @@ -37,6 +37,13 @@ window.nm2.url_api_people = "{% url 'api_people' %}"; {% for link in navbar.person %} {% if link.icon %} {% endif %}{{link.label}} {% endfor %} + {% if visitor != person %} +
{% csrf_token %} + + + +
+ {% endif %} {% endif %} @@ -72,7 +79,10 @@ window.nm2.url_api_people = "{% url 'api_people' %}";