Adding READ_ONLY impersonation mode support. Closes #58
5 files changed, 72 insertions(+), 10 deletions(-)

M README.md
M README.rst
M impersonate/middleware.py
M impersonate/settings.py
M impersonate/tests.py
M README.md +11 -3
@@ 237,6 237,14 @@ impersonate another user. If this is not
 LOGIN_REDIRECT_URL setting and fall back to '/' if neither is present.
 Value should be a string containing the redirect path.
 
+    READ_ONLY
+
+A boolean that if set to `True` any requests that are not either `GET` or
+`HEAD` will result in a "Bad Request" response (status code 405). Use this if
+you want to limit your impersonating users to read only impersonation sessions.
+
+Value should be a boolean, defaults to `False`
+
     USE_HTTP_REFERER
 
 If this is set to `True`, then the app will attempt to be redirect you to

          
@@ 441,15 449,15 @@ need tox.:
 
 And you should see:
 
-    py36-django1.11: commands succeeded
     py36-django2.2: commands succeeded
     py36-django3.0: commands succeeded
-    py37-django1.11: commands succeeded
+    py36-django3.1: commands succeeded
     py37-django2.2: commands succeeded
     py37-django3.0: commands succeeded
-    py38-django1.11: commands succeeded
+    py37-django3.1: commands succeeded
     py38-django2.2: commands succeeded
     py38-django3.0: commands succeeded
+    py38-django3.1: commands succeeded
     congratulations :)
 
 # Contributing

          
M README.rst +49 -7
@@ 4,7 4,7 @@ django-impersonate |nlshield|
 Simple application to allow superusers to "impersonate" other
 non-superuser accounts.
 
-**Version:** 1.5.1
+**Version:** 1.6
 
 **Project Links:**
 `Issues <https://todo.code.netlandish.com/~petersanchez/django-impersonate>`__

          
@@ 17,7 17,7 @@ List <https://lists.code.netlandish.com/
 Python / Django Support
 =======================
 
--  Python 3.6+ for Django versions 1.11, 2.2 and 3.0
+-  Python 3.6+ for Django versions 2.2+
 
 **Note:** As of version 1.4 we are only officially supporting Python
 3.6+ and following the Django support schedule. Meaning we can only

          
@@ 32,6 32,9 @@ Dependencies
 
 **NOTE:**
 
+-  **Version 1.6** has officially removed support for the old settings
+   format. Please see the `settings <#settings>`__ section for how
+   settings should be configured.
 -  **Version 1.5 is now only officially supporting Django's 1.11, 2.2,
    and 3.0**
 -  **Version 1.4 is now officially supporting Python 3.6+ and Django

          
@@ 264,6 267,17 @@ Value should be a string containing the 
 
 ::
 
+    READ_ONLY
+
+A boolean that if set to ``True`` any requests that are not either
+``GET`` or ``HEAD`` will result in a "Bad Request" response (status code
+405). Use this if you want to limit your impersonating users to read
+only impersonation sessions.
+
+Value should be a boolean, defaults to ``False``
+
+::
+
     USE_HTTP_REFERER
 
 If this is set to ``True``, then the app will attempt to be redirect you

          
@@ 417,11 431,39 @@ It is optional, and defaults to False (i
     MAX_FILTER_SIZE
 
 The max number of items acceptable in the admin list filters. If the
-number of items exceeds this, then the filter is removed (just shows
-all). This is used by the "Filter by impersonator" filter.
+number of items exceeds this, then the filter list is the size of the
+settings value. This is used by the "Filter by impersonator" filter.
 
 It is optional, and defaults to 100.
 
+::
+
+    ADMIN_DELETE_PERMISSION
+
+A boolean to enable/disable deletion of impersonation logs in the Django
+admin.
+
+Default is ``False``
+
+::
+
+    ADMIN_ADD_PERMISSION
+
+A boolean to enable/disable ability to add impersonation logs in the
+Django admin.
+
+Default is ``False``
+
+::
+
+    ADMIN_READ_ONLY
+
+A boolean to enable/disable "read only" mode of impersonation logs in
+the Django admin. Generally you want to leave this enabled otherwise
+admin users can alter logs within the Django admin area.
+
+Default is ``True``
+
 Admin
 =====
 

          
@@ 486,15 528,15 @@ And you should see:
 
 ::
 
-    py36-django1.11: commands succeeded
     py36-django2.2: commands succeeded
     py36-django3.0: commands succeeded
-    py37-django1.11: commands succeeded
+    py36-django3.1: commands succeeded
     py37-django2.2: commands succeeded
     py37-django3.0: commands succeeded
-    py38-django1.11: commands succeeded
+    py37-django3.1: commands succeeded
     py38-django2.2: commands succeeded
     py38-django3.0: commands succeeded
+    py38-django3.1: commands succeeded
     congratulations :)
 
 Contributing

          
M impersonate/middleware.py +5 -0
@@ 1,9 1,11 @@ 
 # -*- coding: utf-8 -*-
+from django.http import HttpResponseNotAllowed
 from django.utils.deprecation import MiddlewareMixin
 
 from .helpers import (
     User, check_allow_for_uri, check_allow_for_user, is_authenticated,
 )
+from .settings import settings
 
 
 class ImpersonateMiddleware(MiddlewareMixin):

          
@@ 25,6 27,9 @@ class ImpersonateMiddleware(MiddlewareMi
             except User.DoesNotExist:
                 return
 
+            if settings.READ_ONLY and request.method not in ['GET', 'HEAD']:
+                return HttpResponseNotAllowed(['GET', 'HEAD'])
+
             if check_allow_for_user(request, new_user) and check_allow_for_uri(
                 request.path
             ):

          
M impersonate/settings.py +1 -0
@@ 19,6 19,7 @@ username_field = getattr(User, 'USERNAME
     'LOOKUP_TYPE': 'icontains',
     'SEARCH_FIELDS': [username_field, 'first_name', 'last_name', 'email'],
     'REDIRECT_URL': getattr(django_settings, 'LOGIN_REDIRECT_URL', u'/'),
+    'READ_ONLY': False,
     'ADMIN_DELETE_PERMISSION': False,
     'ADMIN_ADD_PERMISSION': False,
     'ADMIN_READ_ONLY': True,

          
M impersonate/tests.py +6 -0
@@ 757,3 757,9 @@ class TestImpersonation(TestCase):
             self.assertTrue(model_admin.has_change_permission(request))
             request.method = 'POST'
             self.assertTrue(model_admin.has_change_permission(request))
+
+    @override_settings(IMPERSONATE={'READ_ONLY': True})
+    def test_impersonate_read_only(self):
+        self._impersonate_helper('user1', 'foobar', 4)
+        resp = self.client.post('/not/real/url/')
+        self.assertEqual(resp.status_code, 405)