# HG changeset patch # User Peter Sanchez # Date 1601685858 25200 # Fri Oct 02 17:44:18 2020 -0700 # Node ID dc72819a06e814a01c5ccb5cdbb1458466797bb8 # Parent ab452693bae81c139a4533b2d5c65f1f547e022b Adding support to enable/disable admin add/change/delete of ImpersonationLog's. Closes #41 #42 diff --git a/README.md b/README.md --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@ Simple application to allow superusers to "impersonate" other non-superuser accounts. -**Version:** 1.5.1 +**Version:** 1.6 **Project Links:** [Issues][issues] - [Mailing List][pinbox] - [Contributing](#contributing) @@ -16,7 +16,7 @@ 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 @@ -31,6 +31,9 @@ **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 @@ -363,6 +366,27 @@ 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 ===== diff --git a/impersonate/admin.py b/impersonate/admin.py --- a/impersonate/admin.py +++ b/impersonate/admin.py @@ -165,5 +165,18 @@ def _impersonating(self, obj): return friendly_name(obj.impersonating) + def has_add_permission(self, request): + return settings.ADMIN_ADD_PERMISSION + + def has_delete_permission(self, request, obj=None): + return settings.ADMIN_DELETE_PERMISSION + + # hack to show records but limit updating. + # `return False` hides impersonates module in admin page + def has_change_permission(self, request, obj=None): + if settings.ADMIN_READ_ONLY: + return request.method in ['GET', 'HEAD'] + return True + admin.site.register(ImpersonationLog, ImpersonationLogAdmin) diff --git a/impersonate/settings.py b/impersonate/settings.py --- a/impersonate/settings.py +++ b/impersonate/settings.py @@ -1,10 +1,6 @@ # -*- coding: utf-8 -*- -from __future__ import unicode_literals - -import warnings +from django.conf import settings as django_settings from django.contrib.auth import get_user_model -from django.conf import settings as django_settings - User = get_user_model() username_field = getattr(User, 'USERNAME_FIELD', 'username') @@ -23,29 +19,16 @@ 'LOOKUP_TYPE': 'icontains', 'SEARCH_FIELDS': [username_field, 'first_name', 'last_name', 'email'], 'REDIRECT_URL': getattr(django_settings, 'LOGIN_REDIRECT_URL', u'/'), + 'ADMIN_DELETE_PERMISSION': False, + 'ADMIN_ADD_PERMISSION': False, + 'ADMIN_READ_ONLY': True, } -def deprecate_settings(name): - # Silly reset of specific fields, needed for tests - _settings['REDIRECT_URL'] = \ - getattr(django_settings, 'LOGIN_REDIRECT_URL', u'/') - - old_settings_name = 'IMPERSONATE_{0}'.format(name) - if hasattr(django_settings, old_settings_name): - warnings.warn( - ('The IMPERSONATE_* settings are now deprecated and will be ' - 'removed in an upcoming release. Please use the IMPERSONATE ' - 'dictionary setting.') - ) - return getattr(django_settings, old_settings_name) - return _settings.get(name) - - class Settings(object): def __getattribute__(self, name): sdict = getattr(django_settings, 'IMPERSONATE', {}) - return sdict.get(name, deprecate_settings(name)) + return sdict.get(name, _settings.get(name)) settings = Settings() diff --git a/impersonate/tests.py b/impersonate/tests.py --- a/impersonate/tests.py +++ b/impersonate/tests.py @@ -203,17 +203,17 @@ self.assertEqual(User.objects.count(), 4) def test_dont_impersonate_superuser(self): - response = self._impersonate_helper('user1', 'foobar', 2) + self._impersonate_helper('user1', 'foobar', 2) self.assertEqual(self.client.session.get('_impersonate'), None) self.client.logout() # Try again with normal staff user - response = self._impersonate_helper('user3', 'foobar', 2) + self._impersonate_helper('user3', 'foobar', 2) self.assertEqual(self.client.session.get('_impersonate'), None) self.client.logout() def test_successful_impersonation(self): - response = self._impersonate_helper('user1', 'foobar', 4) + self._impersonate_helper('user1', 'foobar', 4) self.assertEqual(self.client.session['_impersonate'], 4) self.client.get(reverse('impersonate-stop')) self.assertEqual(self.client.session.get('_impersonate'), None) @@ -246,7 +246,7 @@ session_end.connect(on_session_end) # start the impersonation and check that the _begin signal is sent - response = self._impersonate_helper('user1', 'foobar', 4) + self._impersonate_helper('user1', 'foobar', 4) self.assertEqual(self.client.session['_impersonate'], 4) self.assertTrue(self.session_begin_fired) self.assertFalse(self.session_end_fired) @@ -263,7 +263,7 @@ self.assertFalse(self.session_end_fired) # Start impersonation - response = self._impersonate_helper('user1', 'foobar', 4) + self._impersonate_helper('user1', 'foobar', 4) self.assertTrue(self.session_begin_fired) self.assertFalse(self.session_end_fired) @@ -278,7 +278,7 @@ session_end.disconnect(on_session_end) def test_successsful_impersonation_by_staff(self): - response = self._impersonate_helper('user3', 'foobar', 4) + self._impersonate_helper('user3', 'foobar', 4) self.assertEqual(self.client.session['_impersonate'], 4) self.client.get(reverse('impersonate-stop')) self.assertEqual(self.client.session.get('_impersonate'), None) @@ -286,7 +286,7 @@ @override_settings(IMPERSONATE={'ALLOW_SUPERUSER': True}) def test_successful_impersonation_of_superuser(self): - response = self._impersonate_helper('user1', 'foobar', 2) + self._impersonate_helper('user1', 'foobar', 2) self.assertEqual(self.client.session.get('_impersonate'), 2) user = User.objects.get(pk=self.client.session.get('_impersonate')) self.assertTrue(user.is_superuser) @@ -296,18 +296,18 @@ @override_settings(IMPERSONATE={'REQUIRE_SUPERUSER': True}) def test_unsuccessful_impersonation_by_staff(self): - response = self._impersonate_helper('user3', 'foobar', 4) + self._impersonate_helper('user3', 'foobar', 4) self.assertEqual(self.client.session.get('_impersonate'), None) self.client.logout() @override_settings(IMPERSONATE={'ALLOW_SUPERUSER': False}) def test_unsuccessful_impersonation_of_superuser(self): - response = self._impersonate_helper('user1', 'foobar', 2) + self._impersonate_helper('user1', 'foobar', 2) self.assertEqual(self.client.session.get('_impersonate'), None) self.client.logout() def test_unsuccessful_impersonation(self): - response = self._impersonate_helper('user4', 'foobar', 3) + self._impersonate_helper('user4', 'foobar', 3) self.assertEqual(self.client.session.get('_impersonate'), None) self.client.logout() @@ -316,7 +316,7 @@ self.assertEqual(self.client.session['_impersonate'], 4) # Don't allow impersonated users to use impersonate views - with self.settings(LOGIN_REDIRECT_URL='/test-redirect/'): + with self.settings(IMPERSONATE={'REDIRECT_URL': '/test-redirect/'}): response = self.client.get(reverse('impersonate-list')) self._redirect_check(response, '/test-redirect/') @@ -359,7 +359,7 @@ self.assertEqual(self.client.session.get('_impersonate'), None) self.client.logout() - @override_settings(LOGIN_REDIRECT_URL='/test-redirect-2/') + @override_settings(IMPERSONATE={'REDIRECT_URL': '/test-redirect-2/'}) def test_successful_impersonation_login_redirect_url(self): response = self._impersonate_helper('user1', 'foobar', 4) self.assertEqual(self.client.session['_impersonate'], 4) @@ -586,13 +586,13 @@ def test_disable_impersonatelog_logging(self): self.assertFalse(ImpersonationLog.objects.exists()) - response = self._impersonate_helper('user1', 'foobar', 4) + self._impersonate_helper('user1', 'foobar', 4) self.assertFalse(ImpersonationLog.objects.exists()) @override_settings(IMPERSONATE={'DISABLE_LOGGING': False}) def test_signals_session_begin_impersonatelog(self): self.assertFalse(ImpersonationLog.objects.exists()) - response = self._impersonate_helper('user1', 'foobar', 4) + self._impersonate_helper('user1', 'foobar', 4) log = ImpersonationLog.objects.get() session_key = self.client.session.get('_impersonate_session_id') self.assertEqual(log.impersonator.id, 1) @@ -604,7 +604,7 @@ @override_settings(IMPERSONATE={'DISABLE_LOGGING': False}) def test_signals_session_end_impersonatelog(self): self.assertFalse(ImpersonationLog.objects.exists()) - response = self._impersonate_helper('user1', 'foobar', 4) + self._impersonate_helper('user1', 'foobar', 4) session_key = self.client.session.get('_impersonate_session_id') self.client.get(reverse('impersonate-stop')) none_session_key = self.client.session.get('_impersonate_session_id') @@ -721,3 +721,39 @@ (_id, name) for _id, name in _filter.lookups(None, model_admin) ] self.assertEqual(len(opts), 0) + + def test_impersonatelog_admin_add_delete_permissions(self): + model_admin = ImpersonationLogAdmin(ImpersonationLog, AdminSite()) + self.assertFalse(model_admin.has_add_permission(None)) + self.assertFalse(model_admin.has_delete_permission(None)) + + # Custom perms + with self.settings( + IMPERSONATE={ + 'ADMIN_DELETE_PERMISSION': True, + 'ADMIN_ADD_PERMISSION': True, + } + ): + model_admin = ImpersonationLogAdmin(ImpersonationLog, AdminSite()) + self.assertTrue(model_admin.has_add_permission(None)) + self.assertTrue(model_admin.has_delete_permission(None)) + + def test_impersonatelog_admin_change_permissions(self): + class FakeRequest: + method = 'GET' + + request = FakeRequest() + model_admin = ImpersonationLogAdmin(ImpersonationLog, AdminSite()) + self.assertTrue(model_admin.has_change_permission(request)) + request.method = 'HEAD' + self.assertTrue(model_admin.has_change_permission(request)) + request.method = 'POST' + self.assertFalse(model_admin.has_change_permission(request)) + + # Custom perms + with self.settings(IMPERSONATE={'ADMIN_READ_ONLY': False}): + model_admin = ImpersonationLogAdmin(ImpersonationLog, AdminSite()) + request.method = 'GET' + self.assertTrue(model_admin.has_change_permission(request)) + request.method = 'POST' + self.assertTrue(model_admin.has_change_permission(request))