diff --git a/docs/settings.rst b/docs/settings.rst index 710abbeb..ea57a3ba 100644 --- a/docs/settings.rst +++ b/docs/settings.rst @@ -61,7 +61,7 @@ These changes are visible throughout django-helpdesk **Default:** ``HELPDESK_FOLLOWUP_MOD = False`` - **HELPDESK_AUTO_SUBSCRIBE_ON_TICKET_RESPONSE** Auto-subscribe user to ticket as a 'CC' if (s)he responds to a ticket? - + **Default:** ``HELPDESK_AUTO_SUBSCRIBE_ON_TICKET_RESPONSE = False`` - **HELPDESK_EMAIL_SUBJECT_TEMPLATE** Subject template for templated emails. ``%(subject)s`` represents the subject wording from the email template (e.g. "(Closed)"). @@ -94,7 +94,9 @@ Options that change ticket updates - **HELPDESK_CUSTOM_STAFF_FILTER_CALLBACK** Apply a custom authorisation logic when defining 'staff_member_required' in staff.py. If set, `HELPDESK_ALLOW_NON_STAFF_TICKET_UPDATE` will be ignored when determining staff access. The value should be a function accepting the active user as a parameter and returning True if the user is considered helpdesk - staff. + staff, e.g. + + lambda u: u.is_authenticated() and u.is_active and u.groups.filter(name='helpdesk_staff').exists())) **Default:** ``HELPDESK_ALLOW_NON_STAFF_TICKET_UPDATE = None`` @@ -172,7 +174,7 @@ Discontinued Settings The following settings were defined in previous versions and are no longer supported. -- **HELPDESK_CUSTOM_WELCOME** +- **HELPDESK_CUSTOM_WELCOME** - **HELDPESK_KB_ENABLED_STAFF** Now always True diff --git a/helpdesk/tests/navigation.py b/helpdesk/tests/navigation.py index 08b049b3..835ff23e 100644 --- a/helpdesk/tests/navigation.py +++ b/helpdesk/tests/navigation.py @@ -1,22 +1,20 @@ # -*- coding: utf-8 -*- +import sys from django.core.urlresolvers import reverse from django.test import TestCase -from helpdesk.tests.helpers import get_staff_user, reload_urlconf +from helpdesk import settings +from helpdesk.tests.helpers import get_staff_user, reload_urlconf, User -class TestKBDisabled(TestCase): +class KBDisabledTestCase(TestCase): def setUp(self): - from helpdesk import settings - self.HELPDESK_KB_ENABLED = settings.HELPDESK_KB_ENABLED if self.HELPDESK_KB_ENABLED: settings.HELPDESK_KB_ENABLED = False reload_urlconf() def tearDown(self): - from helpdesk import settings - if self.HELPDESK_KB_ENABLED: settings.HELPDESK_KB_ENABLED = True reload_urlconf() @@ -36,3 +34,91 @@ class TestKBDisabled(TestCase): raise else: self.assertEqual(response.status_code, 200) + + +class StaffUserTestCaseMixin(object): + HELPDESK_ALLOW_NON_STAFF_TICKET_UPDATE = False + HELPDESK_CUSTOM_STAFF_FILTER_CALLBACK = None + expected_login_template = 'admin/login.html' + + def setUp(self): + self.old_settings = settings.HELPDESK_ALLOW_NON_STAFF_TICKET_UPDATE, settings.HELPDESK_CUSTOM_STAFF_FILTER_CALLBACK + settings.HELPDESK_ALLOW_NON_STAFF_TICKET_UPDATE = self.HELPDESK_ALLOW_NON_STAFF_TICKET_UPDATE + settings.HELPDESK_CUSTOM_STAFF_FILTER_CALLBACK = self.HELPDESK_CUSTOM_STAFF_FILTER_CALLBACK + self.reload_views() + + def tearDown(self): + settings.HELPDESK_ALLOW_NON_STAFF_TICKET_UPDATE, settings.HELPDESK_CUSTOM_STAFF_FILTER_CALLBACK = self.old_settings + self.reload_views() + + def reload_views(self): + try: + reload(sys.modules['helpdesk.views.staff']) + reload_urlconf() + except KeyError: + pass + + def test_anonymous_user(self): + """Access to the dashboard always requires a login""" + response = self.client.get(reverse('helpdesk_dashboard'), follow=True) + self.assertTemplateUsed(response, self.expected_login_template) + + +class NonStaffUsersAllowedTestCase(StaffUserTestCaseMixin, TestCase): + HELPDESK_ALLOW_NON_STAFF_TICKET_UPDATE = True + HELPDESK_CUSTOM_STAFF_FILTER_CALLBACK = None + expected_login_template = 'helpdesk/registration/login.html' + + def test_non_staff_allowed(self): + """If HELPDESK_ALLOW_NON_STAFF_TICKET_UPDATE is True, + authenticated, non-staff users should be able to access + the dashboard. + """ + user = User.objects.create_user(username='henry.wensleydale', password='gouda', email='wensleydale@example.com') + + self.client.login(username=user.username, password='gouda') + response = self.client.get(reverse('helpdesk_dashboard'), follow=True) + self.assertTemplateUsed(response, 'helpdesk/dashboard.html') + + +class StaffUsersOnlyTestCase(StaffUserTestCaseMixin, TestCase): + # Use default values + HELPDESK_ALLOW_NON_STAFF_TICKET_UPDATE = False + HELPDESK_CUSTOM_STAFF_FILTER_CALLBACK = None + + def test_staff_only(self): + """If HELPDESK_ALLOW_NON_STAFF_TICKET_UPDATE is False, + only staff users should be able to access the dashboard. + """ + user = get_staff_user() + + self.client.login(username=user.username, password='password') + response = self.client.get(reverse('helpdesk_dashboard'), follow=True) + self.assertTemplateUsed(response, 'helpdesk/dashboard.html') + + +class CustomStaffUserTestCase(StaffUserTestCaseMixin, TestCase): + HELPDESK_ALLOW_NON_STAFF_TICKET_UPDATE = False + expected_login_template = 'helpdesk/registration/login.html' + + @staticmethod + def HELPDESK_CUSTOM_STAFF_FILTER_CALLBACK(user): + """Arbitrary user validation function""" + return user.is_authenticated() and user.is_active and user.username.lower().endswith('wensleydale') + + def test_custom_staff_pass(self): + """If HELPDESK_CUSTOM_STAFF_FILTER_CALLBACK is not None, + a custom access rule is applied. + """ + user = User.objects.create_user(username='henry.wensleydale', password='gouda', email='wensleydale@example.com') + + self.client.login(username=user.username, password='gouda') + response = self.client.get(reverse('helpdesk_dashboard'), follow=True) + self.assertTemplateUsed(response, 'helpdesk/dashboard.html') + + def test_custom_staff_fail(self): + user = User.objects.create_user(username='terry.milton', password='frog', email='milton@example.com') + + self.client.login(username=user.username, password='frog') + response = self.client.get(reverse('helpdesk_dashboard'), follow=True) + self.assertTemplateUsed(response, self.expected_login_template) \ No newline at end of file diff --git a/helpdesk/views/staff.py b/helpdesk/views/staff.py index aad7ae7a..527c08ee 100644 --- a/helpdesk/views/staff.py +++ b/helpdesk/views/staff.py @@ -42,6 +42,7 @@ from helpdesk.models import Ticket, Queue, FollowUp, TicketChange, PreSetReply, from helpdesk import settings as helpdesk_settings if helpdesk_settings.HELPDESK_CUSTOM_STAFF_FILTER_CALLBACK: + # apply a custom user validation condition staff_member_required = user_passes_test(helpdesk_settings.HELPDESK_CUSTOM_STAFF_FILTER_CALLBACK) elif helpdesk_settings.HELPDESK_ALLOW_NON_STAFF_TICKET_UPDATE: # treat 'normal' users like 'staff' diff --git a/quicktest.py b/quicktest.py index f6aa9af6..3d04ace0 100644 --- a/quicktest.py +++ b/quicktest.py @@ -65,8 +65,8 @@ class QuickDjangoTest(object): Fire up the Django test suite developed for version 1.2 """ settings.configure( - DEBUG = True, - DATABASES = { + DEBUG=True, + DATABASES={ 'default': { 'ENGINE': 'django.db.backends.sqlite3', 'NAME': os.path.join(self.DIRNAME, 'database.db'), @@ -76,8 +76,10 @@ class QuickDjangoTest(object): 'PORT': '', } }, - INSTALLED_APPS = self.INSTALLED_APPS + self.apps, - ROOT_URLCONF = self.apps[0] + '.urls', + INSTALLED_APPS=self.INSTALLED_APPS + self.apps, + ROOT_URLCONF=self.apps[0] + '.urls', + STATIC_URL='/static/', + LOGIN_URL='login', ) from django.test.simple import DjangoTestSuiteRunner failures = DjangoTestSuiteRunner().run_tests(self.apps, verbosity=1)