Repository: akvo/akvo-rsr

Issue:
Signing in from RSR Up fails with error 'Unable to authorize:'
Login for a user who has `rsr.change_project` permissions for a lot of projects, but is not an `rsr_admin` can timeout. This piece of code seems like the [culprit code](https://github.com/akvo/akvo-rsr/blob/5a07b31d1c272f2014e5eb3209f23506aa6e91f1/akvo/rsr/views/account.py#L273). 


Discussion:

Resolving patch:
diff --git a/akvo/rsr/views/account.py b/akvo/rsr/views/account.py
--- a/akvo/rsr/views/account.py
+++ b/akvo/rsr/views/account.py
@@ -270,7 +270,8 @@ def api_key_json_response(user, orgs):
 
     # Editable projects
     perm = 'rsr.change_project'
-    response_data["allow_edit_projects"] = [p.id for p in projects if user.has_perm(perm, p)]
+    perm_filter = user.get_permission_filter(perm, '')
+    response_data["allow_edit_projects"] = list(projects.filter(perm_filter).values_list('id', flat=True))
 
     return json.dumps(response_data)
 


Test patch:
diff --git a/akvo/rsr/tests/views/test_account.py b/akvo/rsr/tests/views/test_account.py
new file mode 100644
--- /dev/null
+++ b/akvo/rsr/tests/views/test_account.py
@@ -0,0 +1,142 @@
+# -*- coding: utf-8 -*-
+
+"""
+Akvo RSR is covered by the GNU Affero General Public License.
+
+See more details in the license.txt file located at the root folder of the Akvo RSR module.
+For additional details on the GNU license please see < http://www.gnu.org/licenses/agpl.html >.
+"""
+
+from __future__ import print_function
+
+import json
+
+from django.conf import settings
+from django.contrib.auth.models import Group
+from django.test import TestCase, Client
+
+from akvo.rsr.models import Employment, Organisation, Partnership, Project, User
+from akvo.utils import check_auth_groups
+
+
+class AccountTestCase(TestCase):
+    """Test account views."""
+
+    def setUp(self):
+        check_auth_groups(settings.REQUIRED_AUTH_GROUPS)
+        self.admin_group = Group.objects.get(name='Admins')
+        self.user_group = Group.objects.get(name='Users')
+        self.username = 'user@example.com'
+        self.password = 'password'
+        self.org1 = Organisation.objects.create(name='akvo', long_name='akvo foundation')
+        self.org2 = Organisation.objects.create(name='icco', long_name='icco foundation')
+        for org in (self.org1, self.org2):
+            project = Project.objects.create()
+            project.publishingstatus.status = 'published'
+            project.publishingstatus.save()
+            Partnership.objects.create(project=project, organisation=org)
+        self.c = Client(HTTP_HOST=settings.RSR_DOMAIN)
+
+    def tearDown(self):
+        Group.objects.all().delete()
+        Organisation.objects.all().delete()
+        Project.objects.all().delete()
+        User.objects.all().delete()
+
+    def test_api_key_for_plain_user(self):
+        # Given
+        user = self._create_user(self.username, self.password, is_admin=False)
+        Employment.objects.create(user=user, organisation=self.org1, group=self.user_group, is_approved=True)
+        data = {
+            'username': self.username,
+            'password': self.password,
+        }
+
+        # When
+        response = self.c.post('/auth/token/?format=json', data=data)
+
+        # Then
+        self.assertEqual(response.status_code, 200)
+        edit_projects = json.loads(response.content)['allow_edit_projects']
+        self.assertEqual([], edit_projects)
+
+    def test_api_key_for_admin_group_user(self):
+        # Given
+        user = self._create_user(self.username, self.password, is_admin=False)
+        Employment.objects.create(
+            user=user, organisation=self.org1, group=self.admin_group, is_approved=True
+        )
+        data = {
+            'username': self.username,
+            'password': self.password,
+        }
+
+        # When
+        response = self.c.post('/auth/token/?format=json', data=data)
+
+        # Then
+        self.assertEqual(response.status_code, 200)
+        edit_projects = json.loads(response.content)['allow_edit_projects']
+        self.assertEqual(
+            set(self.org1.projects.published().values_list('id', flat=True)),
+            set(edit_projects)
+        )
+
+    def test_api_key_for_rsr_admin_user(self):
+        # Given
+        user = self._create_user(self.username, self.password, is_admin=True)
+        Employment.objects.create(
+            user=user, organisation=self.org1, group=self.admin_group, is_approved=True
+        )
+        data = {
+            'username': self.username,
+            'password': self.password,
+        }
+
+        # When
+        response = self.c.post('/auth/token/?format=json', data=data)
+
+        # Then
+        self.assertEqual(response.status_code, 200)
+        edit_projects = json.loads(response.content)['allow_edit_projects']
+        self.assertEqual(
+            set(self.org1.projects.published().values_list('id', flat=True)),
+            set(edit_projects)
+        )
+
+    def test_api_key_for_rsr_admin_all_orgs_user(self):
+        # Given
+        user = self._create_user(self.username, self.password, is_admin=True)
+        for org in (self.org1, self.org2):
+            Employment.objects.create(
+                user=user, organisation=org, group=self.admin_group, is_approved=True
+            )
+        data = {
+            'username': self.username,
+            'password': self.password,
+        }
+
+        # When
+        response = self.c.post('/auth/token/?format=json', data=data)
+
+        # Then
+        self.assertEqual(response.status_code, 200)
+        edit_projects = json.loads(response.content)['allow_edit_projects']
+        self.assertEqual(
+            set(Project.objects.filter(publishingstatus__status='published').values_list('id', flat=True)),
+            set(edit_projects)
+        )
+
+    def _create_user(self, email, password, is_active=True, is_admin=False):
+        """Create a user with the given email and password."""
+
+        user = User.objects.create(
+            email=email,
+            username=email,
+            is_active=is_active,
+            is_admin=is_admin
+        )
+        user.set_password(password)
+        user.save()
+
+        return user