Repository: translate/pootle

Issue:
Redirect loop when accessing project's languages admin
If users with project administration rights (i.e. non-superusers) try to access a project's languages admin page, they will be stuck in a redirect loop, making it impossible to access such page.

git bisect points to 14e2326, although it might as well be any of the surrounding commits (they don't seem to work individually).



Discussion:
hi @julen afaict here https://github.com/translate/pootle/blob/master/pootle/apps/pootle_app/views/admin/languages.py#L19 only superusers have access to this view.

Not sure if im looking at the wrong view

That view is actually the list of all languages. I think the view @julen is talking about is https://github.com/translate/pootle/blob/master/pootle/apps/pootle_project/views.py#L179

thanks @unho 

@julen after further investigation it seems there are 2 problems here. The first is that the view doesnt have the correct permisions, i will fix that and close this ticket.

The second problem is that although django expects to be able to redirect a logged in user to the login page if they request a resource for which they dont have permission, the login widget appears to automatically redirect users back to the `next` page as they are logged in. I will open a separate ticket for that issue.


Resolving patch:
diff --git a/pootle/core/views.py b/pootle/core/views.py
--- a/pootle/core/views.py
+++ b/pootle/core/views.py
@@ -14,7 +14,7 @@
 from django.forms import ValidationError
 from django.conf import settings
 from django.contrib.auth import get_user_model
-from django.contrib.auth.decorators import login_required, user_passes_test
+from django.contrib.auth.decorators import login_required
 from django.core.exceptions import PermissionDenied
 from django.core.urlresolvers import reverse
 from django.db.models import ObjectDoesNotExist, ProtectedError, Q
@@ -445,11 +445,16 @@ def server_error(request):
 
 class PootleAdminView(DetailView):
 
-    @method_decorator(user_passes_test(lambda u: u.is_superuser))
+    @set_permissions
+    @requires_permission("administrate")
     def dispatch(self, request, *args, **kwargs):
         return super(
             PootleAdminView, self).dispatch(request, *args, **kwargs)
 
+    @property
+    def permission_context(self):
+        return self.get_object().directory
+
     def post(self, *args, **kwargs):
         return self.get(*args, **kwargs)
 


Test patch:
diff --git a/tests/views/admin.py b/tests/views/admin.py
--- a/tests/views/admin.py
+++ b/tests/views/admin.py
@@ -16,6 +16,7 @@
 
 from pootle.core.paginator import paginate
 from pootle.core.url_helpers import split_pootle_path
+from pootle_app.models import PermissionSet
 from pootle_app.views.admin.util import form_set_as_table
 from pootle_project.models import Project
 from pootle_translationproject.models import TranslationProject
@@ -119,17 +120,30 @@ def test_admin_view_projects(client, request_users):
     response = _admin_view_get(client, project)
 
     if not user.is_superuser:
-        assert response.status_code == 302
-        request = response.wsgi_request
-        redirect = (
-            "http://testserver%s?next=%s"
-            % (reverse("account_login"),
-               request.get_full_path()))
-        assert response.get("location") == redirect
+        assert response.status_code == 403
         return
     _test_admin_view(response, project)
 
 
+@pytest.mark.django_db
+def test_admin_view_projects_manager(client, member, administrate):
+    project = Project.objects.get(code="project0")
+    criteria = {
+        'user': member,
+        'directory': project.directory}
+    ps = PermissionSet.objects.create(**criteria)
+    ps.positive_permissions = [administrate]
+    client.login(
+        username=member.username,
+        password=TEST_USERS[member.username]["password"])
+    response = _admin_view_get(client, project)
+    assert response.status_code == 200
+    _test_admin_view(response, project)
+    response = _admin_view_post(client, project)
+    assert response.status_code == 200
+    _test_admin_view(response, project)
+
+
 @pytest.mark.django_db
 def test_admin_view_projects_post(client, request_users):
 
@@ -143,13 +157,7 @@ def test_admin_view_projects_post(client, request_users):
     if user.is_superuser:
         return
     response = _admin_view_post(client, project)
-    request = response.wsgi_request
-    assert response.status_code == 302
-    redirect = (
-        "http://testserver%s?next=%s"
-        % (reverse("account_login"),
-           request.get_full_path()))
-    assert response.get("location") == redirect
+    assert response.status_code == 403
 
 
 @pytest.mark.django_db