From 5872c534de8df07b6e7fb3c6f15279f33e957df8 Mon Sep 17 00:00:00 2001 From: Pierre-Yves Chibon Date: Jun 21 2018 10:58:04 +0000 Subject: Include private projects you can access in the project list Fixes https://pagure.io/pagure/issue/3008 Signed-off-by: Pierre-Yves Chibon --- diff --git a/pagure/lib/__init__.py b/pagure/lib/__init__.py index ee260ba..5d0d3da 100644 --- a/pagure/lib/__init__.py +++ b/pagure/lib/__init__.py @@ -2184,7 +2184,6 @@ def search_projects( model.ProjectGroup.access == 'admin', model.ProjectGroup.access == 'commit', ) - ) ) @@ -2207,29 +2206,87 @@ 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, six.string_types) and private != username: - # 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( + # All the public repo + subquery0 = 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.Project.private == True, + model.Project.private == False, # noqa: E712 + ) + sub_q1 = session.query( + sqlalchemy.distinct(model.Project.id) + ).filter( + sqlalchemy.and_( + model.Project.private == True, # noqa: E712 + model.User.id == model.Project.user_id, + model.User.user == private, + ) + ) + sub_q2 = session.query( + model.Project.id + ).filter( + # User got admin or commit right + sqlalchemy.and_( + model.Project.private == True, # noqa: E712 + model.User.user == private, + model.User.id == model.ProjectUser.user_id, + model.ProjectUser.project_id == model.Project.id, + sqlalchemy.or_( + model.ProjectUser.access == 'admin', + model.ProjectUser.access == 'commit', + ) + ) + ) + sub_q3 = session.query( + model.Project.id + ).filter( + # User created a group that has admin or commit right + sqlalchemy.and_( + model.Project.private == True, # noqa: E712 + model.User.user == private, + model.PagureGroup.user_id == model.User.id, + model.PagureGroup.group_type == 'user', + model.PagureGroup.id == model.ProjectGroup.group_id, + model.Project.id == model.ProjectGroup.project_id, + sqlalchemy.or_( + model.ProjectGroup.access == 'admin', + model.ProjectGroup.access == 'commit', + ) + ) + ) + sub_q4 = session.query( + model.Project.id + ).filter( + # User is part of a group that has admin or commit right + sqlalchemy.and_( + model.Project.private == True, # noqa: E712 + model.User.user == private, + model.PagureUserGroup.user_id == model.User.id, + model.PagureUserGroup.group_id == model.PagureGroup.id, + model.PagureGroup.group_type == 'user', + model.PagureGroup.id == model.ProjectGroup.group_id, + model.Project.id == model.ProjectGroup.project_id, + sqlalchemy.or_( + model.ProjectGroup.access == 'admin', + model.ProjectGroup.access == 'commit', ) ) ) + # Exclude projects that the user has accessed via a group that we + # do not want to include + if exclude_groups: + sub_q3 = sub_q3.filter( + model.PagureGroup.group_name.notin_(exclude_groups) + ) + sub_q4 = sub_q4.filter( + model.PagureGroup.group_name.notin_(exclude_groups) + ) + projects = projects.filter( - model.Project.id.in_(subquery) + model.Project.id.in_( + subquery0.union(sub_q1).union(sub_q2).union(sub_q3).union( + sub_q4) + ) ) if fork is not None: diff --git a/pagure/templates/_render_repo.html b/pagure/templates/_render_repo.html index 3efd66c..91a35e0 100644 --- a/pagure/templates/_render_repo.html +++ b/pagure/templates/_render_repo.html @@ -37,6 +37,9 @@ {{ repo.namespace + '/' if repo.namespace }}{{ repo.name }} + {% if repo.private %} + + {% endif %}
@@ -258,7 +261,7 @@ {% endif %}
{{ repo.fullname }} - +
{% if repo.description %} diff --git a/pagure/ui/app.py b/pagure/ui/app.py index 1bd5e60..e2b729f 100644 --- a/pagure/ui/app.py +++ b/pagure/ui/app.py @@ -87,20 +87,26 @@ def index(): limit = pagure_config['ITEM_PER_PAGE'] start = limit * (page - 1) + private = None + if authenticated(): + private = flask.g.fas_user.username + repos = pagure.lib.search_projects( flask.g.session, fork=False, start=start, limit=limit, - sort=sorting) + sort=sorting, + private=private, + ) num_repos = pagure.lib.search_projects( flask.g.session, fork=False, + private=private, count=True) total_page = int(ceil(num_repos / float(limit)) if num_repos > 0 else 1) - return flask.render_template( 'index.html', select="projects", diff --git a/tests/test_pagure_flask_ui_app_browse.py b/tests/test_pagure_flask_ui_app_browse.py new file mode 100644 index 0000000..10072bd --- /dev/null +++ b/tests/test_pagure_flask_ui_app_browse.py @@ -0,0 +1,539 @@ +# -*- coding: utf-8 -*- + +""" + (c) 2018 - Copyright Red Hat Inc + + Authors: + Pierre-Yves Chibon + +""" + +from __future__ import unicode_literals + +__requires__ = ['SQLAlchemy >= 0.8'] +import pkg_resources + +import unittest +import sys +import os + +from mock import patch, MagicMock + +sys.path.insert(0, os.path.join(os.path.dirname( + os.path.abspath(__file__)), '..')) + +import pagure.lib +import tests + + +class PagureFlaskAppBrowsetests(tests.Modeltests): + """ Tests for the browse pages of flask app controller of pagure """ + + def setUp(self): + """ Set up the environnment, ran before every tests. """ + super(PagureFlaskAppBrowsetests, self).setUp() + + tests.create_projects(self.session) + + # Add a 3rd project with a long description + item = pagure.lib.model.Project( + user_id=2, # foo + name='test3', + description='test project #3 with a very long description', + hook_token='aaabbbeeefff', + private=True, + ) + self.session.add(item) + self.session.commit() + + @patch.dict('pagure.config.config', {'PRIVATE_PROJECTS': True}) + def test_browse_project_logged_in_private_project(self): + """ Test the browse project endpoint when logged in with a private + project. """ + + user = tests.FakeUser(username='foo') + with tests.user_set(self.app.application, user): + output = self.app.get('/browse/projects/') + self.assertEqual(output.status_code, 200) + output_text = output.get_data(as_text=True) + self.assertIn( + 'Home - Pagure\n', + output_text) + self.assertIn( + '

All Projects ' + '4

', + output_text) + self.assertIn( + '', + output_text) + self.assertEqual(output_text.count('title="Private project"'), 1) + + @patch.dict('pagure.config.config', {'PRIVATE_PROJECTS': True}) + def test_browse_project_unauth_private_project(self): + """ Test the browse project endpoint when logged out with a private + project. """ + + output = self.app.get('/browse/projects/') + self.assertEqual(output.status_code, 200) + output_text = output.get_data(as_text=True) + self.assertIn( + 'Home - Pagure\n', + output_text) + self.assertIn( + '

All Projects ' + '3

', + output_text) + self.assertNotIn( + '', + output_text) + self.assertEqual(output_text.count('title="Private project"'), 0) + + @patch.dict('pagure.config.config', {'PRIVATE_PROJECTS': True}) + def test_browse_project_logged_in_no_access_private_project(self): + """ Test the browse project endpoint when logged in as an user that + has no access to the private project. """ + + user = tests.FakeUser(username='pingou') + with tests.user_set(self.app.application, user): + output = self.app.get('/browse/projects/') + self.assertEqual(output.status_code, 200) + output_text = output.get_data(as_text=True) + self.assertIn( + 'Home - Pagure\n', + output_text) + self.assertIn( + '

All Projects ' + '3

', + output_text) + self.assertNotIn( + '', + output_text) + self.assertEqual(output_text.count('title="Private project"'), 0) + + @patch.dict('pagure.config.config', {'PRIVATE_PROJECTS': True}) + def test_browse_project_logged_in_ticket_private_project(self): + """ Test the browse project endpoint when logged in as an user that + has no access to the private project. """ + + # Add user 'pingou' with ticket access on repo + repo = pagure.lib._get_project(self.session, 'test3') + msg = pagure.lib.add_user_to_project( + self.session, + repo, + new_user='pingou', + user='foo', + access='ticket', + ) + self.assertEqual(msg, 'User added') + self.session.commit() + + # Ticket access level isn't sufficient to access private projects + user = tests.FakeUser(username='pingou') + with tests.user_set(self.app.application, user): + output = self.app.get('/browse/projects/') + self.assertEqual(output.status_code, 200) + output_text = output.get_data(as_text=True) + self.assertIn( + 'Home - Pagure\n', + output_text) + self.assertIn( + '

All Projects ' + '3

', + output_text) + self.assertNotIn( + '', + output_text) + self.assertEqual(output_text.count('title="Private project"'), 0) + + @patch.dict('pagure.config.config', {'PRIVATE_PROJECTS': True}) + def test_browse_project_logged_in_commit_private_project(self): + """ Test the browse project endpoint when logged in as an user that + has no access to the private project. """ + + # Add user 'pingou' with commit access on repo + repo = pagure.lib._get_project(self.session, 'test3') + msg = pagure.lib.add_user_to_project( + self.session, + repo, + new_user='pingou', + user='foo', + access='commit', + ) + self.assertEqual(msg, 'User added') + self.session.commit() + + user = tests.FakeUser(username='pingou') + with tests.user_set(self.app.application, user): + output = self.app.get('/browse/projects/') + self.assertEqual(output.status_code, 200) + output_text = output.get_data(as_text=True) + self.assertIn( + 'Home - Pagure\n', + output_text) + self.assertIn( + '

All Projects ' + '4

', + output_text) + self.assertIn( + '', + output_text) + self.assertEqual(output_text.count('title="Private project"'), 1) + + @patch.dict('pagure.config.config', {'PRIVATE_PROJECTS': True}) + def test_browse_project_logged_in_admin_private_project(self): + """ Test the browse project endpoint when logged in as an user that + has no access to the private project. """ + + # Add user 'pingou' with admin access on repo + repo = pagure.lib._get_project(self.session, 'test3') + msg = pagure.lib.add_user_to_project( + self.session, + repo, + new_user='pingou', + user='foo', + access='admin', + ) + self.assertEqual(msg, 'User added') + self.session.commit() + + user = tests.FakeUser(username='pingou') + with tests.user_set(self.app.application, user): + output = self.app.get('/browse/projects/') + self.assertEqual(output.status_code, 200) + output_text = output.get_data(as_text=True) + self.assertIn( + 'Home - Pagure\n', + output_text) + self.assertIn( + '

All Projects ' + '4

', + output_text) + self.assertIn( + '', + output_text) + self.assertEqual(output_text.count('title="Private project"'), 1) + +class PagureFlaskAppBrowseGroupAdmintests(tests.Modeltests): + """ Tests for the browse pages of flask app controller of pagure """ + + def setUp(self): + """ Set up the environnment, ran before every tests. """ + super(PagureFlaskAppBrowseGroupAdmintests, self).setUp() + + tests.create_projects(self.session) + + # Add a 3rd project with a long description + item = pagure.lib.model.Project( + user_id=2, # foo + name='test3', + description='test project #3 with a very long description', + hook_token='aaabbbeeefff', + private=True, + ) + self.session.add(item) + self.session.commit() + + # Create a group + msg = pagure.lib.add_group( + self.session, + group_name='JL', + display_name='Justice League', + description='Nope, it\'s not JLA anymore', + group_type='user', + user='foo', + is_admin=False, + blacklist=pagure.config.config.get('BLACKLISTED_PROJECTS') + ) + + self.assertEqual(msg, 'User `foo` added to the group `JL`.') + + # Add the group to project we just created, test3 + # Add it with admin ACL + project = pagure.lib._get_project(self.session, 'test3') + msg = pagure.lib.add_group_to_project( + self.session, + project=project, + new_group='JL', + user='foo', + access='admin' + ) + self.session.commit() + self.assertEqual(msg, 'Group added') + + @patch.dict('pagure.config.config', {'PRIVATE_PROJECTS': True}) + def test_browse_project_user_not_in_group(self): + """ Test the browse project endpoint when logged in as an user that + has no access to the private project via a group as admin. """ + + user = tests.FakeUser(username='pingou') + with tests.user_set(self.app.application, user): + output = self.app.get('/browse/projects/') + self.assertEqual(output.status_code, 200) + output_text = output.get_data(as_text=True) + self.assertIn( + 'Home - Pagure\n', + output_text) + self.assertIn( + '

All Projects ' + '3

', + output_text) + self.assertNotIn( + '', + output_text) + self.assertEqual(output_text.count('title="Private project"'), 0) + + @patch.dict('pagure.config.config', {'PRIVATE_PROJECTS': True}) + def test_browse_project_user_in_group(self): + """ Test the browse project endpoint when logged in as an user that + has no access to the private project via a group as admin. """ + group = pagure.lib.search_groups( + self.session, group_name='JL') + + pagure.lib.add_user_to_group( + session=self.session, + username='pingou', + group=group, + user='foo', + is_admin=False, + ) + self.session.commit() + + user = tests.FakeUser(username='pingou') + with tests.user_set(self.app.application, user): + output = self.app.get('/browse/projects/') + self.assertEqual(output.status_code, 200) + output_text = output.get_data(as_text=True) + self.assertIn( + 'Home - Pagure\n', + output_text) + self.assertIn( + '

All Projects ' + '4

', + output_text) + self.assertIn( + '', + output_text) + self.assertEqual(output_text.count('title="Private project"'), 1) + + +class PagureFlaskAppBrowseGroupCommittests(tests.Modeltests): + """ Tests for the browse pages of flask app controller of pagure """ + + def setUp(self): + """ Set up the environnment, ran before every tests. """ + super(PagureFlaskAppBrowseGroupCommittests, self).setUp() + + tests.create_projects(self.session) + + # Add a 3rd project with a long description + item = pagure.lib.model.Project( + user_id=2, # foo + name='test3', + description='test project #3 with a very long description', + hook_token='aaabbbeeefff', + private=True, + ) + self.session.add(item) + self.session.commit() + + # Create a group + msg = pagure.lib.add_group( + self.session, + group_name='JL', + display_name='Justice League', + description='Nope, it\'s not JLA anymore', + group_type='user', + user='foo', + is_admin=False, + blacklist=pagure.config.config.get('BLACKLISTED_PROJECTS') + ) + + self.assertEqual(msg, 'User `foo` added to the group `JL`.') + + # Add the group to project we just created, test3 + # Add it with commit ACL + project = pagure.lib._get_project(self.session, 'test3') + msg = pagure.lib.add_group_to_project( + self.session, + project=project, + new_group='JL', + user='foo', + access='commit' + ) + self.session.commit() + self.assertEqual(msg, 'Group added') + + @patch.dict('pagure.config.config', {'PRIVATE_PROJECTS': True}) + def test_browse_project_user_not_in_group(self): + """ Test the browse project endpoint when logged in as an user that + has no access to the private project via a group as admin. """ + + user = tests.FakeUser(username='pingou') + with tests.user_set(self.app.application, user): + output = self.app.get('/browse/projects/') + self.assertEqual(output.status_code, 200) + output_text = output.get_data(as_text=True) + self.assertIn( + 'Home - Pagure\n', + output_text) + self.assertIn( + '

All Projects ' + '3

', + output_text) + self.assertNotIn( + '', + output_text) + self.assertEqual(output_text.count('title="Private project"'), 0) + + @patch.dict('pagure.config.config', {'PRIVATE_PROJECTS': True}) + def test_browse_project_user_in_group(self): + """ Test the browse project endpoint when logged in as an user that + has no access to the private project via a group as admin. """ + group = pagure.lib.search_groups( + self.session, group_name='JL') + + pagure.lib.add_user_to_group( + session=self.session, + username='pingou', + group=group, + user='foo', + is_admin=False, + ) + self.session.commit() + + user = tests.FakeUser(username='pingou') + with tests.user_set(self.app.application, user): + output = self.app.get('/browse/projects/') + self.assertEqual(output.status_code, 200) + output_text = output.get_data(as_text=True) + self.assertIn( + 'Home - Pagure\n', + output_text) + self.assertIn( + '

All Projects ' + '4

', + output_text) + self.assertIn( + '', + output_text) + self.assertEqual(output_text.count('title="Private project"'), 1) + + +class PagureFlaskAppBrowseGroupTickettests(tests.Modeltests): + """ Tests for the browse pages of flask app controller of pagure """ + + def setUp(self): + """ Set up the environnment, ran before every tests. """ + super(PagureFlaskAppBrowseGroupTickettests, self).setUp() + + tests.create_projects(self.session) + + # Add a 3rd project with a long description + item = pagure.lib.model.Project( + user_id=2, # foo + name='test3', + description='test project #3 with a very long description', + hook_token='aaabbbeeefff', + private=True, + ) + self.session.add(item) + self.session.commit() + + # Create a group + msg = pagure.lib.add_group( + self.session, + group_name='JL', + display_name='Justice League', + description='Nope, it\'s not JLA anymore', + group_type='user', + user='foo', + is_admin=False, + blacklist=pagure.config.config.get('BLACKLISTED_PROJECTS') + ) + + self.assertEqual(msg, 'User `foo` added to the group `JL`.') + + # Add the group to project we just created, test3 + # Add it with ticket ACL + project = pagure.lib._get_project(self.session, 'test3') + msg = pagure.lib.add_group_to_project( + self.session, + project=project, + new_group='JL', + user='foo', + access='ticket' + ) + self.session.commit() + self.assertEqual(msg, 'Group added') + + @patch.dict('pagure.config.config', {'PRIVATE_PROJECTS': True}) + def test_browse_project_user_not_in_group(self): + """ Test the browse project endpoint when logged in as an user that + has no access to the private project via a group as admin. """ + + user = tests.FakeUser(username='pingou') + with tests.user_set(self.app.application, user): + output = self.app.get('/browse/projects/') + self.assertEqual(output.status_code, 200) + output_text = output.get_data(as_text=True) + self.assertIn( + 'Home - Pagure\n', + output_text) + self.assertIn( + '

All Projects ' + '3

', + output_text) + self.assertNotIn( + '', + output_text) + self.assertEqual(output_text.count('title="Private project"'), 0) + + @patch.dict('pagure.config.config', {'PRIVATE_PROJECTS': True}) + def test_browse_project_user_in_group(self): + """ Test the browse project endpoint when logged in as an user that + has no access to the private project via a group as admin. """ + group = pagure.lib.search_groups( + self.session, group_name='JL') + + pagure.lib.add_user_to_group( + session=self.session, + username='pingou', + group=group, + user='foo', + is_admin=False, + ) + self.session.commit() + + # Ticket ACL isn't enough to grant you access + user = tests.FakeUser(username='pingou') + with tests.user_set(self.app.application, user): + output = self.app.get('/browse/projects/') + self.assertEqual(output.status_code, 200) + output_text = output.get_data(as_text=True) + self.assertIn( + 'Home - Pagure\n', + output_text) + self.assertIn( + '

All Projects ' + '3

', + output_text) + self.assertNotIn( + '', + output_text) + self.assertEqual(output_text.count('title="Private project"'), 0) + + +if __name__ == '__main__': + unittest.main(verbosity=2) diff --git a/tests/test_pagure_flask_ui_app_index.py b/tests/test_pagure_flask_ui_app_index.py index 03734e1..bf8040d 100644 --- a/tests/test_pagure_flask_ui_app_index.py +++ b/tests/test_pagure_flask_ui_app_index.py @@ -497,6 +497,43 @@ class PagureFlaskAppIndextests(tests.Modeltests): r'foo/test3(\s*<[^>]+?>\s*)*?forked from a deleted repository' ) + @patch.dict('pagure.config.config', {'PRIVATE_PROJECTS': True}) + def test_index_logged_in_private_project(self): + """ Test the index endpoint when logged in with a private project. """ + tests.create_projects(self.session) + + # Add a 3rd project with a long description + item = pagure.lib.model.Project( + user_id=2, # foo + name='test3', + description='test project #3 with a very long description', + hook_token='aaabbbeeefff', + private=True, + ) + self.session.add(item) + self.session.commit() + + user = tests.FakeUser(username='foo') + with tests.user_set(self.app.application, user): + output = self.app.get('/') + self.assertEqual(output.status_code, 200) + output_text = output.get_data(as_text=True) + self.assertIn( + 'Projects 1', + output_text) + self.assertIn( + '', + output_text) + self.assertEqual(output_text.count('title="Private project"'), 1) + self.assertIn( + 'Forks 0', + output_text) + self.assertEqual( + output_text.count('

No group found

'), 1) + self.assertEqual( + output_text.count('
'), 6) + if __name__ == '__main__': unittest.main(verbosity=2)