Repository: django-hijack/django-hijack

Issue:
Django 2.0 Support



Discussion:

Resolving patch:
diff --git a/hijack/helpers.py b/hijack/helpers.py
--- a/hijack/helpers.py
+++ b/hijack/helpers.py
@@ -1,4 +1,5 @@
 # -*- coding: utf-8 -*-
+import contextlib
 from django.core.exceptions import PermissionDenied
 from django.contrib.auth.models import update_last_login
 from django.contrib.auth.signals import user_logged_in
@@ -14,6 +15,25 @@
 from hijack.signals import hijack_started, hijack_ended
 
 
+@contextlib.contextmanager
+def no_update_last_login():
+    """
+    Disconnect any signals to update_last_login() for the scope of the context
+    manager, then restore.
+    """
+    kw = {'receiver': update_last_login}
+    kw_id = {'receiver': update_last_login, 'dispatch_uid': 'update_last_login'}
+
+    was_connected = user_logged_in.disconnect(**kw)
+    was_connected_id = not was_connected and user_logged_in.disconnect(**kw_id)
+    yield
+    # Restore signal if needed
+    if was_connected:
+        user_logged_in.connect(**kw)
+    elif was_connected_id:
+        user_logged_in.connect(**kw_id)
+
+
 def get_used_backend(request):
     backend_str = request.session[BACKEND_SESSION_KEY]
     backend = load_backend(backend_str)
@@ -34,7 +54,8 @@ def release_hijack(request):
         hijacker = get_object_or_404(get_user_model(), pk=user_pk)
         backend = get_used_backend(request)
         hijacker.backend = "%s.%s" % (backend.__module__, backend.__class__.__name__)
-        login(request, hijacker)
+        with no_update_last_login():
+            login(request, hijacker)
     if hijack_history:
         request.session['hijack_history'] = hijack_history
         request.session['is_hijacked_user'] = True
@@ -102,15 +123,9 @@ def login_user(request, hijacked):
     backend = get_used_backend(request)
     hijacked.backend = "%s.%s" % (backend.__module__, backend.__class__.__name__)
 
-    # Prevent update of hijacked user last_login
-    signal_was_connected = user_logged_in.disconnect(update_last_login)
-
-    # Actually log user in
-    login(request, hijacked)
-
-    # Restore signal if needed
-    if signal_was_connected:
-        user_logged_in.connect(update_last_login)
+    with no_update_last_login():
+        # Actually log user in
+        login(request, hijacked)
 
     hijack_started.send(sender=None, hijacker_id=hijacker.pk, hijacked_id=hijacked.pk, request=request)  # Send official, documented signal
     request.session['hijack_history'] = hijack_history
diff --git a/hijack/urls.py b/hijack/urls.py
--- a/hijack/urls.py
+++ b/hijack/urls.py
@@ -3,6 +3,7 @@
 from hijack import settings as hijack_settings
 from hijack import views
 
+app_name = 'hijack'
 urlpatterns = [
     url(
         r'^release-hijack/$',


Test patch:
diff --git a/hijack/tests/test_app/urls.py b/hijack/tests/test_app/urls.py
--- a/hijack/tests/test_app/urls.py
+++ b/hijack/tests/test_app/urls.py
@@ -1,6 +1,7 @@
 from compat import url
 from django.views.generic import TemplateView
 
+app_name = 'test_app'
 urlpatterns = [
     url(r'^filter/$', TemplateView.as_view(template_name='hello_filter.html'), name='hello_filter'),
     url(r'^$', TemplateView.as_view(template_name='hello.html'), name='hello'),
diff --git a/hijack/tests/test_hijack.py b/hijack/tests/test_hijack.py
--- a/hijack/tests/test_hijack.py
+++ b/hijack/tests/test_hijack.py
@@ -1,7 +1,6 @@
 # -*- coding: utf-8 -*-
 from django.conf import settings
 from django.contrib.auth.models import User
-from django.core.urlresolvers import reverse
 from django.test import TestCase, Client
 from compat import import_string, unquote_plus
 
@@ -11,6 +10,11 @@
 from hijack.templatetags.hijack_tags import can_hijack
 from hijack.tests.utils import SettingsOverride
 
+try:
+    from django.urls import reverse
+except ImportError:
+    from django.core.urlresolvers import reverse
+
 
 class BaseHijackTests(TestCase):
 
@@ -76,14 +80,14 @@ def assertHijackPermissionDenied(self, response):
         self.assertFalse('hijacked-warning' in str(response.content))
 
     def test_hijack_urls(self):
-        self.assertEqual('/hijack/disable-hijack-warning/', reverse('disable_hijack_warning'))
-        self.assertEqual('/hijack/release-hijack/', reverse('release_hijack'))
-        self.assertEqual('/hijack/1/', reverse('login_with_id', args=[1]))
-        self.assertEqual('/hijack/2/', reverse('login_with_id', kwargs={'user_id': 2}))
-        self.assertEqual('/hijack/username/bob/', reverse('login_with_username', args=['bob']))
-        self.assertEqual('/hijack/username/bob_too/', reverse('login_with_username', kwargs={'username': 'bob_too'}))
-        self.assertEqual('/hijack/email/bob@bobsburgers.com/', unquote_plus(reverse('login_with_email', args=['bob@bobsburgers.com'])))
-        self.assertEqual('/hijack/email/bob_too@bobsburgers.com/', unquote_plus(reverse('login_with_email', kwargs={'email': 'bob_too@bobsburgers.com'})))
+        self.assertEqual('/hijack/disable-hijack-warning/', reverse('hijack:disable_hijack_warning'))
+        self.assertEqual('/hijack/release-hijack/', reverse('hijack:release_hijack'))
+        self.assertEqual('/hijack/1/', reverse('hijack:login_with_id', args=[1]))
+        self.assertEqual('/hijack/2/', reverse('hijack:login_with_id', kwargs={'user_id': 2}))
+        self.assertEqual('/hijack/username/bob/', reverse('hijack:login_with_username', args=['bob']))
+        self.assertEqual('/hijack/username/bob_too/', reverse('hijack:login_with_username', kwargs={'username': 'bob_too'}))
+        self.assertEqual('/hijack/email/bob@bobsburgers.com/', unquote_plus(reverse('hijack:login_with_email', args=['bob@bobsburgers.com'])))
+        self.assertEqual('/hijack/email/bob_too@bobsburgers.com/', unquote_plus(reverse('hijack:login_with_email', kwargs={'email': 'bob_too@bobsburgers.com'})))
 
     def test_hijack_url_user_id(self):
         response = self.client.post('/hijack/%d/' % self.user.id, follow=True)
@@ -124,15 +128,22 @@ def test_release_before_hijack(self):
 
     def test_last_login_time_not_changed(self):
         self.client.logout()
+        # user login to set user.last_login
         self.client.login(username=self.user_username, password=self.user_password)
         self.client.logout()
-        last_non_hijack_login = User.objects.get(id=self.user.id).last_login
-        self.assertIsNotNone(last_non_hijack_login)
+        user_last_login = User.objects.get(id=self.user.id).last_login
+        self.assertIsNotNone(user_last_login)
+        # super-user login to hijack
         self.client.login(username=self.superuser_username, password=self.superuser_password)
+        su_last_login = User.objects.get(id=self.superuser.id).last_login
+        self.assertIsNotNone(su_last_login)
+        # this shall not update user last_login
         response = self._hijack(self.user)
         self.assertHijackSuccess(response)
+        # this shall not update super-user last_login
         self._release_hijack()
-        self.assertEqual(User.objects.get(id=self.user.id).last_login, last_non_hijack_login)
+        self.assertEqual(User.objects.get(id=self.user.id).last_login, user_last_login)
+        self.assertEqual(User.objects.get(id=self.superuser.id).last_login, su_last_login)
 
     def test_disable_hijack_warning(self):
         response = self._hijack(self.user)
diff --git a/hijack/tests/test_settings.py b/hijack/tests/test_settings.py
--- a/hijack/tests/test_settings.py
+++ b/hijack/tests/test_settings.py
@@ -34,14 +34,18 @@
                     'django.contrib.auth.hashers.MD5PasswordHasher',
                     'django.contrib.auth.hashers.CryptPasswordHasher', )
 
-MIDDLEWARE_CLASSES = (
+MIDDLEWARE = (
+    'django.middleware.security.SecurityMiddleware',
     'django.contrib.sessions.middleware.SessionMiddleware',
     'django.middleware.common.CommonMiddleware',
     'django.middleware.csrf.CsrfViewMiddleware',
     'django.contrib.auth.middleware.AuthenticationMiddleware',
-    # 'django.contrib.auth.middleware.SessionAuthenticationMiddleware',
+    'django.contrib.messages.middleware.MessageMiddleware',
+    'django.middleware.clickjacking.XFrameOptionsMiddleware',
 )
 
+MIDDLEWARE_CLASSES = MIDDLEWARE
+
 TEMPLATE_CONTEXT_PROCESSORS = (
     "django.contrib.auth.context_processors.auth",
     "django.core.context_processors.debug",
diff --git a/hijack/tests/urls.py b/hijack/tests/urls.py
--- a/hijack/tests/urls.py
+++ b/hijack/tests/urls.py
@@ -3,8 +3,7 @@
 from django.contrib import admin
 
 urlpatterns = [
-    url(r'^admin/', include(admin.site.urls)),
-    url(r'^hijack/', include('hijack.urls')),
-    url(r'^hello/', include('hijack.tests.test_app.urls')),
+    url(r'^admin/', admin.site.urls),
+    url(r'^hijack/', include('hijack.urls', namespace='hijack')),
+    url(r'^hello/', include('hijack.tests.test_app.urls', 'test_app')),
 ]
-