From 4b298126b04227051e60166c5a205f3a0d5dc7c4 Mon Sep 17 00:00:00 2001 From: Pierre-Yves Chibon Date: Apr 26 2018 12:53:04 +0000 Subject: Fix browsing projects in a namespace when logged in. If all the project in the database have only one admin and no other members, the user_projects table is empty making the part of the query filtering for public projects or projects that the user has access to returning an empty table all the time. To fix this, Slavek Kabrda and I changed the code from using regular WHERE statements in the query to using a subquery. The tests have also been found to be broken as they contained no private repo and were hitting this bug but without saying so. They have been adjusted that they are now producing the expected behavior. Merges https://pagure.io/pagure/pull-request/3184 Signed-off-by: Pierre-Yves Chibon --- diff --git a/pagure/lib/__init__.py b/pagure/lib/__init__.py index 3ba29c3..39db198 100644 --- a/pagure/lib/__init__.py +++ b/pagure/lib/__init__.py @@ -2195,18 +2195,31 @@ def search_projects( # No filtering is done if private == username i.e if the owner of the # project is viewing the project elif isinstance(private, basestring) and private != username: - projects = projects.filter( + # if we use the sqlalchemy.or_ in projects.filter, it will + # fail to find any projects assuming there are no rows in + # the ProjectUser table due to the way sqlalchemy creates + # nested selects from conditions; therefore we create the + # subquery explicitly on our own and do it right + subquery = session.query( + sqlalchemy.distinct(model.Project.id) + ).outerjoin( + model.ProjectUser + ).outerjoin( + model.User + ).filter( sqlalchemy.or_( model.Project.private == False, # noqa: E712 sqlalchemy.and_( model.User.user == private, - model.User.id == model.ProjectUser.user_id, - model.ProjectUser.project_id == model.Project.id, model.Project.private == True, ) ) ) + projects = projects.filter( + model.Project.id.in_(subquery) + ) + if fork is not None: if fork is True: projects = projects.filter( diff --git a/tests/test_pagure_lib.py b/tests/test_pagure_lib.py index 6bb5800..857c7f2 100644 --- a/tests/test_pagure_lib.py +++ b/tests/test_pagure_lib.py @@ -281,27 +281,31 @@ class PagureLibtests_search_projects(tests.Modeltests): name='private_test', description='Private test project #1', hook_token='aaabbbcccpp', + private=True, ) self.session.add(item) self.session.commit() + # non authenticated projects = pagure.lib.search_projects(self.session) - self.assertEqual(len(projects), 4) + self.assertEqual(len(projects), 3) self.assertEqual( [p.path for p in projects], - ['private_test.git', 'test.git', 'test2.git', + ['test.git', 'test2.git', 'somenamespace/test3.git'] ) + # non authenticated projects = pagure.lib.search_projects( self.session, username='pingou') - self.assertEqual(len(projects), 4) + self.assertEqual(len(projects), 3) self.assertEqual( [p.path for p in projects], - ['private_test.git', 'test.git', 'test2.git', + ['test.git', 'test2.git', 'somenamespace/test3.git'] ) + # authenticated as pingou projects = pagure.lib.search_projects( self.session, username='pingou', private='pingou') self.assertEqual(len(projects), 4) @@ -311,9 +315,15 @@ class PagureLibtests_search_projects(tests.Modeltests): 'somenamespace/test3.git'] ) + # authenticated as foo projects = pagure.lib.search_projects( self.session, username='pingou', private='foo') - self.assertEqual(len(projects), 0) + self.assertEqual(len(projects), 3) + self.assertEqual( + [p.path for p in projects], + ['test.git', 'test2.git', + 'somenamespace/test3.git'] + ) def test_search_projects_tags(self): """