diff --git a/README.rst b/README.rst index 7c42092..858d3e5 100644 --- a/README.rst +++ b/README.rst @@ -61,6 +61,7 @@ Rosetta can be configured via the following parameters, to be defined in your pr * ``ROSETTA_REQUIRES_AUTH``: Require authentication for all Rosetta views. Defaults to ``True``. * ``ROSETTA_POFILE_WRAP_WIDTH``: Sets the line-length of the edited PO file. Set this to ``0`` to mimic ``makemessage``'s ``--no-wrap`` option. Defaults to ``78``. * ``ROSETTA_STORAGE_CLASS``: See the note below on Storages. Defaults to ``rosetta.storage.CacheRosettaStorage`` +* ``ROSETTA_ACCESS_CONTROL_FUNCTION``: An alternative function that determines if a given user can access the translation views. ******** Storages diff --git a/rosetta/access.py b/rosetta/access.py new file mode 100644 index 0000000..b3641bc --- /dev/null +++ b/rosetta/access.py @@ -0,0 +1,36 @@ +from django.conf import settings +from django.utils import importlib + + +def can_translate(user): + return get_access_control_function()(user) + + +def get_access_control_function(): + """ + Return a predicate for determining if a user can access the Rosetta views + """ + fn_path = getattr(settings, 'ROSETTA_ACCESS_CONTROL_FUNCTION', None) + if fn_path is None: + return is_superuser_staff_or_in_translators_group + # Dynamically load a permissions function + perm_module, perm_func = fn_path.rsplit('.', 1) + perm_module = importlib.import_module(perm_module) + return getattr(perm_module, perm_func) + + +# Default access control test +def is_superuser_staff_or_in_translators_group(user): + if not getattr(settings, 'ROSETTA_REQUIRES_AUTH', True): + return True + if not user.is_authenticated(): + return False + elif user.is_superuser and user.is_staff: + return True + else: + try: + from django.contrib.auth.models import Group + translators = Group.objects.get(name='translators') + return translators in user.groups.all() + except Group.DoesNotExist: + return False diff --git a/rosetta/conf/settings.py b/rosetta/conf/settings.py index 967afaf..7cd73d6 100644 --- a/rosetta/conf/settings.py +++ b/rosetta/conf/settings.py @@ -16,6 +16,9 @@ MAIN_LANGUAGE = getattr(settings, 'ROSETTA_MAIN_LANGUAGE', None) MESSAGES_SOURCE_LANGUAGE_CODE = getattr(settings, 'ROSETTA_MESSAGES_SOURCE_LANGUAGE_CODE', 'en') MESSAGES_SOURCE_LANGUAGE_NAME = getattr(settings, 'ROSETTA_MESSAGES_SOURCE_LANGUAGE_NAME', 'English') +ACCESS_CONTROL_FUNCTION = getattr( + settings, 'ROSETTA_ACCESS_CONTROL_FUNCTION', None) + """ When running WSGI daemon mode, using mod_wsgi 2.0c5 or later, this setting diff --git a/rosetta/tests/__init__.py b/rosetta/tests/__init__.py index 2a96c6b..4a316b5 100644 --- a/rosetta/tests/__init__.py +++ b/rosetta/tests/__init__.py @@ -537,3 +537,22 @@ class RosettaTestCase(TestCase): r = self.client.post(reverse('rosetta-home'), dict(m_e48f149a8b2e8baa81b816c0edf93890='Hello, world', _next='_next')) r = self.client.get(reverse('rosetta-home')) self.assertTrue('Progress: 25.00%' in str(r.content)) + + def test_24_replace_access_control(self): + # Test default access control allows access + url = reverse('rosetta-home') + response = self.client.get(url) + self.assertEqual(200, response.status_code) + + # Now replace access control, and check we get redirected + settings.ROSETTA_ACCESS_CONTROL_FUNCTION = 'rosetta.tests.no_access' + response = self.client.get(url) + self.assertEqual(302, response.status_code) + + # Restore setting to default + settings.ROSETTA_ACCESS_CONTROL_FUNCTION = None + + +# Stubbed access control function +def no_access(user): + return False diff --git a/rosetta/views.py b/rosetta/views.py index 80fd4c6..ce3b9b4 100644 --- a/rosetta/views.py +++ b/rosetta/views.py @@ -13,6 +13,7 @@ from rosetta.polib import pofile from rosetta.poutil import find_pos, pagination_range, timestamp_with_timezone from rosetta.signals import entry_changed, post_save from rosetta.storage import get_storage +from rosetta.access import can_translate import re import rosetta import unicodedata @@ -392,20 +393,3 @@ def lang_sel(request, langid, idx): storage.set('rosetta_i18n_write', False) return HttpResponseRedirect(reverse('rosetta-home')) - - -def can_translate(user): - if not getattr(settings, 'ROSETTA_REQUIRES_AUTH', True): - return True - if not user.is_authenticated(): - return False - elif user.is_superuser and user.is_staff: - return True - else: - try: - from django.contrib.auth.models import Group - translators = Group.objects.get(name='translators') - return translators in user.groups.all() - except Group.DoesNotExist: - return False -