From 3a8ee847435d89bec6b73363ebba20cf90896cf8 Mon Sep 17 00:00:00 2001
From: Pierre-Yves Chibon <pingou@pingoured.fr>
Date: Jun 21 2018 10:58:04 +0000
Subject: [PATCH 1/4] Indicate that private issues are private


Signed-off-by: Pierre-Yves Chibon <pingou@pingoured.fr>

---

diff --git a/pagure/templates/_render_issues.html b/pagure/templates/_render_issues.html
index e9a9af6..49d7e3f 100644
--- a/pagure/templates/_render_issues.html
+++ b/pagure/templates/_render_issues.html
@@ -20,7 +20,9 @@
                         {{issue.title}}
                         </a>
                     </span>
-
+                    {% if issue.private %}
+                    <span title="Private ticket" class="text-danger fa fa-fw fa-lock"></span>
+                    {% endif %}
                 </div>
                 <div>
                     <small>Opened <a href="#" data-toggle="tooltip" title="{{
diff --git a/pagure/templates/issue.html b/pagure/templates/issue.html
index 572d663..10d511d 100644
--- a/pagure/templates/issue.html
+++ b/pagure/templates/issue.html
@@ -26,7 +26,7 @@ namespace=repo.namespace, repo=repo.name, issueid=issueid)
     <span class="issueid badge badge-secondary">#{{ issueid }}</span>
     <span id="issuetitle">{{issue.title | noJS("img") | safe }}</span>
     {% if issue.private %}
-      <span class="oi red-icon" data-glyph="lock-locked" title="Private issue"></span>
+      <span title="Private ticket" class="text-danger fa fa-fw fa-lock"></span>
     {% endif %}
   </h2>
 
diff --git a/tests/test_pagure_flask_ui_issues.py b/tests/test_pagure_flask_ui_issues.py
index 658e689..46f2dae 100644
--- a/tests/test_pagure_flask_ui_issues.py
+++ b/tests/test_pagure_flask_ui_issues.py
@@ -1273,8 +1273,8 @@ class PagureFlaskIssuestests(tests.Modeltests):
                 '<title>Issue #2: Test issue - test - Pagure</title>',
                 output_text)
             self.assertIn(
-                '<span class="oi red-icon" data-glyph="lock-locked" '
-                'title="Private issue"></span>', output_text)
+                '<span title="Private ticket" class="text-danger fa fa-fw '
+                'fa-lock"></span>', output_text)
             self.assertIn(
                 '<a class="btn btn-primary btn-sm" '
                 'href="/test/issue/2/edit" title="Edit this issue">',
diff --git a/tests/test_pagure_flask_ui_issues_acl_checks.py b/tests/test_pagure_flask_ui_issues_acl_checks.py
index 47b9c41..8332513 100644
--- a/tests/test_pagure_flask_ui_issues_acl_checks.py
+++ b/tests/test_pagure_flask_ui_issues_acl_checks.py
@@ -285,8 +285,8 @@ class PagureFlaskIssuesACLtests(tests.Modeltests):
                 '<title>Issue #2: Test issue - test - Pagure</title>',
                 output_text)
             self.assertIn(
-                '<span class="oi red-icon" data-glyph="lock-locked" '
-                'title="Private issue"></span>', output_text)
+                '<span title="Private ticket" class="text-danger '
+                'fa fa-fw fa-lock"></span>', output_text)
             self.assertIn(
                 '<a class="btn btn-primary btn-sm" '
                 'href="/test/issue/2/edit" title="Edit this issue">',
@@ -551,8 +551,8 @@ class PagureFlaskIssuesACLtests(tests.Modeltests):
                 '<title>Issue #2: Test issue - test - Pagure</title>',
                 output_text)
             self.assertIn(
-                '<span class="oi red-icon" data-glyph="lock-locked" '
-                'title="Private issue"></span>', output_text)
+                '<span title="Private ticket" class="text-danger '
+                'fa fa-fw fa-lock"></span>', output_text)
             self.assertIn(
                 '<a class="btn btn-primary btn-sm" '
                 'href="/test/issue/2/edit" title="Edit this issue">',
@@ -816,8 +816,8 @@ class PagureFlaskIssuesACLtests(tests.Modeltests):
                 '<title>Issue #2: Test issue - test - Pagure</title>',
                 output_text)
             self.assertIn(
-                '<span class="oi red-icon" data-glyph="lock-locked" '
-                'title="Private issue"></span>', output_text)
+                '<span title="Private ticket" class="text-danger '
+                'fa fa-fw fa-lock"></span>', output_text)
             self.assertIn(
                 '<a class="btn btn-primary btn-sm" '
                 'href="/test/issue/2/edit" title="Edit this issue">',
@@ -1081,8 +1081,8 @@ class PagureFlaskIssuesACLtests(tests.Modeltests):
                 '<title>Issue #2: Test issue - test - Pagure</title>',
                 output_text)
             self.assertIn(
-                '<span class="oi red-icon" data-glyph="lock-locked" '
-                'title="Private issue"></span>', output_text)
+                '<span title="Private ticket" class="text-danger '
+                'fa fa-fw fa-lock"></span>', output_text)
             self.assertIn(
                 '<a class="btn btn-primary btn-sm" '
                 'href="/test/issue/2/edit" title="Edit this issue">',

From a3319816229af9d76d174f04855580bd5afb5533 Mon Sep 17 00:00:00 2001
From: Pierre-Yves Chibon <pingou@pingoured.fr>
Date: Jun 21 2018 10:58:04 +0000
Subject: [PATCH 2/4] Indicate that private projects are private


Signed-off-by: Pierre-Yves Chibon <pingou@pingoured.fr>

---

diff --git a/pagure/templates/index_auth.html b/pagure/templates/index_auth.html
index 46c75da..b6a9878 100644
--- a/pagure/templates/index_auth.html
+++ b/pagure/templates/index_auth.html
@@ -54,6 +54,9 @@
                   {% endif %}
                   <strong>{{ repo.namespace + '/' if repo.namespace }}{{ repo.name }}</strong>
                 </a>
+                {% if repo.private %}
+                  <span title="Private project" class="text-danger fa fa-fw fa-lock"></span>
+                {% endif %}
                   <div class="float-right">
                     {% if config.get('ENABLE_TICKETS', True) and repo.settings.get('issue_tracker', True) %}
                       {% if repo.open_tickets_public == 0 %}

From f70191019f6da6246e03ee901ee983deccab7e32 Mon Sep 17 00:00:00 2001
From: Pierre-Yves Chibon <pingou@pingoured.fr>
Date: Jun 21 2018 10:58:04 +0000
Subject: [PATCH 3/4] If we're showing the front page, no need to query the DB, redirect


Signed-off-by: Pierre-Yves Chibon <pingou@pingoured.fr>

---

diff --git a/pagure/ui/app.py b/pagure/ui/app.py
index 90195ed..1bd5e60 100644
--- a/pagure/ui/app.py
+++ b/pagure/ui/app.py
@@ -72,6 +72,9 @@ def _filter_acls(repos, acl, user):
 def index():
     """ Front page of the application.
     """
+    if authenticated() and flask.request.path == '/':
+        return index_auth()
+
     sorting = flask.request.args.get('sorting') or None
     page = flask.request.args.get('page', 1)
     try:
@@ -97,8 +100,6 @@ def index():
         count=True)
     total_page = int(ceil(num_repos / float(limit)) if num_repos > 0 else 1)
 
-    if authenticated() and flask.request.path == '/':
-        return index_auth()
 
     return flask.render_template(
         'index.html',

From 5872c534de8df07b6e7fb3c6f15279f33e957df8 Mon Sep 17 00:00:00 2001
From: Pierre-Yves Chibon <pingou@pingoured.fr>
Date: Jun 21 2018 10:58:04 +0000
Subject: [PATCH 4/4] Include private projects you can access in the project list


Fixes https://pagure.io/pagure/issue/3008

Signed-off-by: Pierre-Yves Chibon <pingou@pingoured.fr>

---

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 @@
           <strong>
             {{ repo.namespace + '/' if repo.namespace }}{{ repo.name }}
           </strong>
+          {% if repo.private %}
+            <span title="Private project" class="text-danger fa fa-fw fa-lock"></span>
+          {% endif %}
         </div>
       </a>
       <div class="repo_desc" title="{{ repo.description }}" data-toggle="tooltip">
@@ -258,7 +261,7 @@
                   {% endif %}
                   <div class="media-body align-self-center">
                     <a class="font-weight-bold" href="{{ url }}">{{ repo.fullname }}</a>
-        
+
                     <div title="{{ repo.description }}" data-toggle="tooltip" class="repo_descripton">
                       <small>
                         {% 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 <pingou@pingoured.fr>
+
+"""
+
+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(
+                '<title>Home - Pagure</title>\n',
+                output_text)
+            self.assertIn(
+                '<h3 class="m-0 font-weight-bold">All Projects '
+                '<span class="badge badge-secondary">4</span></h3>',
+                output_text)
+            self.assertIn(
+                '<span title="Private project" class="text-danger '
+                'fa fa-fw fa-lock"></span>',
+                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(
+            '<title>Home - Pagure</title>\n',
+            output_text)
+        self.assertIn(
+            '<h3 class="m-0 font-weight-bold">All Projects '
+            '<span class="badge badge-secondary">3</span></h3>',
+            output_text)
+        self.assertNotIn(
+            '<span title="Private project" class="text-danger '
+            'fa fa-fw fa-lock"></span>',
+            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(
+                '<title>Home - Pagure</title>\n',
+                output_text)
+            self.assertIn(
+                '<h3 class="m-0 font-weight-bold">All Projects '
+                '<span class="badge badge-secondary">3</span></h3>',
+                output_text)
+            self.assertNotIn(
+                '<span title="Private project" class="text-danger '
+                'fa fa-fw fa-lock"></span>',
+                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(
+                '<title>Home - Pagure</title>\n',
+                output_text)
+            self.assertIn(
+                '<h3 class="m-0 font-weight-bold">All Projects '
+                '<span class="badge badge-secondary">3</span></h3>',
+                output_text)
+            self.assertNotIn(
+                '<span title="Private project" class="text-danger '
+                'fa fa-fw fa-lock"></span>',
+                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(
+                '<title>Home - Pagure</title>\n',
+                output_text)
+            self.assertIn(
+                '<h3 class="m-0 font-weight-bold">All Projects '
+                '<span class="badge badge-secondary">4</span></h3>',
+                output_text)
+            self.assertIn(
+                '<span title="Private project" class="text-danger '
+                'fa fa-fw fa-lock"></span>',
+                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(
+                '<title>Home - Pagure</title>\n',
+                output_text)
+            self.assertIn(
+                '<h3 class="m-0 font-weight-bold">All Projects '
+                '<span class="badge badge-secondary">4</span></h3>',
+                output_text)
+            self.assertIn(
+                '<span title="Private project" class="text-danger '
+                'fa fa-fw fa-lock"></span>',
+                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(
+                '<title>Home - Pagure</title>\n',
+                output_text)
+            self.assertIn(
+                '<h3 class="m-0 font-weight-bold">All Projects '
+                '<span class="badge badge-secondary">3</span></h3>',
+                output_text)
+            self.assertNotIn(
+                '<span title="Private project" class="text-danger '
+                'fa fa-fw fa-lock"></span>',
+                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(
+                '<title>Home - Pagure</title>\n',
+                output_text)
+            self.assertIn(
+                '<h3 class="m-0 font-weight-bold">All Projects '
+                '<span class="badge badge-secondary">4</span></h3>',
+                output_text)
+            self.assertIn(
+                '<span title="Private project" class="text-danger '
+                'fa fa-fw fa-lock"></span>',
+                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(
+                '<title>Home - Pagure</title>\n',
+                output_text)
+            self.assertIn(
+                '<h3 class="m-0 font-weight-bold">All Projects '
+                '<span class="badge badge-secondary">3</span></h3>',
+                output_text)
+            self.assertNotIn(
+                '<span title="Private project" class="text-danger '
+                'fa fa-fw fa-lock"></span>',
+                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(
+                '<title>Home - Pagure</title>\n',
+                output_text)
+            self.assertIn(
+                '<h3 class="m-0 font-weight-bold">All Projects '
+                '<span class="badge badge-secondary">4</span></h3>',
+                output_text)
+            self.assertIn(
+                '<span title="Private project" class="text-danger '
+                'fa fa-fw fa-lock"></span>',
+                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(
+                '<title>Home - Pagure</title>\n',
+                output_text)
+            self.assertIn(
+                '<h3 class="m-0 font-weight-bold">All Projects '
+                '<span class="badge badge-secondary">3</span></h3>',
+                output_text)
+            self.assertNotIn(
+                '<span title="Private project" class="text-danger '
+                'fa fa-fw fa-lock"></span>',
+                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(
+                '<title>Home - Pagure</title>\n',
+                output_text)
+            self.assertIn(
+                '<h3 class="m-0 font-weight-bold">All Projects '
+                '<span class="badge badge-secondary">3</span></h3>',
+                output_text)
+            self.assertNotIn(
+                '<span title="Private project" class="text-danger '
+                'fa fa-fw fa-lock"></span>',
+                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 <span class="badge badge-secondary">1</span>',
+                output_text)
+            self.assertIn(
+                '<span title="Private project" class="text-danger '
+                'fa fa-fw fa-lock"></span>',
+                output_text)
+            self.assertEqual(output_text.count('title="Private project"'), 1)
+            self.assertIn(
+                'Forks <span class="badge badge-secondary">0</span>',
+                output_text)
+            self.assertEqual(
+                output_text.count('<p>No group found</p>'), 1)
+            self.assertEqual(
+                output_text.count('<div class="card-header">'), 6)
+
 
 if __name__ == '__main__':
     unittest.main(verbosity=2)

