Browse Source

Hide the SSH clone URL if the user is not in one of the group with ssh access

Some deployment of pagure have it set up in such a way that only the
members of certain groups can commit via SSH.
For this reason, there is not need to display the ssh url since they
cannot use it.

Fixes https://pagure.io/pagure-dist-git/issue/97

Signed-off-by: Pierre-Yves Chibon <pingou@pingoured.fr>
Pierre-Yves Chibon 4 years ago
parent
commit
6690c75ba1
4 changed files with 161 additions and 0 deletions
  1. 16 0
      doc/configuration.rst
  2. 2 0
      pagure/templates/repo_master.html
  3. 18 0
      pagure/ui/filters.py
  4. 125 0
      tests/test_pagure_flask_ui_repo.py

+ 16 - 0
doc/configuration.rst

@@ -818,6 +818,22 @@ the default branch of the repo will be the default target of all pull requests.
 
 Defaults to: ``False``.
 
+
+SSH_ACCESS_GROUPS
+~~~~~~~~~~~~~~~~~
+
+Some instances of pagure are deployed in such a way that only the members of
+certain groups are allowed to commit via ssh. This configuration key allows
+to specify which groups have commit access and thus let pagure hide the ssh
+URL from the drop-down "Clone" menu for all the person who are not in one of
+these groups.
+If this configuration key is not defined or left empty, it is assume that there
+is no such group restriction and everyone can commit via ssh (default behavior).
+
+
+Defaults to: ``[]``
+
+
 SMTP configuration
 ~~~~~~~~~~~~~~~~~~
 

+ 2 - 0
pagure/templates/repo_master.html

@@ -6,6 +6,8 @@
     <a href="{{ url_for('ui_ns.user_settings') + '#nav-ssh-tab' }}">
       You need to upload SSH key to be able to clone over SSH
     </a>
+  {% elif not (current_user | user_group_can_ssh_commit) %}
+    Only members of the {{ config["SSH_ACCESS_GROUPS"] | join(", ") }} group(s) can clone via ssh
   {% elif repo.read_only %}
      The permissions on this repository are being updated.
      Cloning over SSH is disabled.

+ 18 - 0
pagure/ui/filters.py

@@ -813,6 +813,24 @@ def user_can_clone_ssh(username):
     return always_render or has_ssh_keys
 
 
+@UI_NS.app_template_filter("user_group_can_ssh_commit")
+def user_group_can_ssh_commit(username):
+    """ Returns whether the user is in a group that has ssh access. """
+    ssh_access_groups = pagure_config.get("SSH_ACCESS_GROUPS") or []
+    if not ssh_access_groups:
+        # ssh access is not restricted to one or more groups
+        return True
+
+    user_obj = pagure.lib.query.search_user(flask.g.session, username=username)
+    if not user_obj:
+        # user not found
+        return False
+
+    user_grps = set(user_obj.groups)
+    req_grps = set(pagure_config.get("SSH_ACCESS_GROUPS"))
+    return len(user_grps.intersection(req_grps)) > 0
+
+
 @UI_NS.app_template_filter("git_url_ssh")
 def get_git_url_ssh(complement=""):
     """ Return the GIT SSH URL to be displayed in the UI based on the

+ 125 - 0
tests/test_pagure_flask_ui_repo.py

@@ -6611,5 +6611,130 @@ class PagureFlaskRepoTestRegenerateGittests(tests.Modeltests):
         self.assertEqual(upgit.call_count, 1)
 
 
+class PagureFlaskRepoTestGitSSHURL(tests.Modeltests):
+    """ Tests the display of the SSH url in the UI """
+
+    def setUp(self):
+        """ Set up the environnment, ran before every tests. """
+        super(PagureFlaskRepoTestGitSSHURL, self).setUp()
+
+        tests.create_projects(self.session)
+        tests.create_projects_git(os.path.join(self.path, "repos"))
+        pingou = pagure.lib.query.get_user(self.session, "pingou")
+
+        # Make the repo not read-only
+        repo = pagure.lib.query._get_project(self.session, "test")
+        pagure.lib.query.update_read_only_mode(
+            self.session, repo, read_only=False
+        )
+        self.session.commit()
+
+        # Add a group and make pingou a member of it
+        item = pagure.lib.model.PagureGroup(
+            group_name="packager",
+            group_type="user",
+            display_name="User group",
+            user_id=1,  # pingou
+        )
+        self.session.add(item)
+        self.session.commit()
+
+        pagure.lib.query.add_user_to_group(
+            self.session, pingou.username, item, pingou.username, True
+        )
+
+        # Add a SSH key for pingou so that he is allowed to push via ssh
+        msg = pagure.lib.query.add_sshkey_to_project_or_user(
+            session=self.session,
+            user=pingou,
+            ssh_key="ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAAAgQDAzBMSIlvPRaEiLOTVInErkRIw9CzQQcnslDekAn1jFnGf+SNa1acvbTiATbCX71AA03giKrPxPH79dxcC7aDXerc6zRcKjJs6MAL9PrCjnbyxCKXRNNZU5U9X/DLaaL1b3caB+WD6OoorhS3LTEtKPX8xyjOzhf3OQSzNjhJp5Q==",
+            pushaccess=True,
+            creator=pingou,
+        )
+        self.session.commit()
+        self.assertEqual(msg, "SSH key added")
+
+    def test_logged_out(self):
+        """ Test the default behavior with the user logged out. """
+
+        output = self.app.get("/test")
+        self.assertEqual(output.status_code, 200)
+        output_text = output.get_data(as_text=True)
+        self.assertIn("<strong>Source Code</strong>", output_text)
+        self.assertIn(
+            '<div class="input-group-prepend"><span class="input-group-text">'
+            "GIT</span></div>",
+            output_text,
+        )
+        self.assertNotIn(
+            '<div class="input-group-prepend"><span class="input-group-text">'
+            "SSH</span></div>",
+            output_text,
+        )
+
+    def test_logged_in(self):
+        """ Test the default behavior with the user logged in. """
+        user = tests.FakeUser(username="pingou")
+        with tests.user_set(self.app.application, user):
+            output = self.app.get("/test")
+            self.assertEqual(output.status_code, 200)
+            output_text = output.get_data(as_text=True)
+            self.assertIn("<strong>Source Code</strong>", output_text)
+            self.assertIn(
+                '<div class="input-group-prepend"><span class="input-group-text">'
+                "GIT</span></div>",
+                output_text,
+            )
+            self.assertIn(
+                '<div class="input-group-prepend"><span class="input-group-text">'
+                "SSH</span></div>",
+                output_text,
+            )
+
+    @patch.dict("pagure.config.config", {"SSH_ACCESS_GROUPS": ["packager"]})
+    def test_ssh_restricted_user_member(self):
+        """ Test when ssh is restricted and the user has access. """
+        user = tests.FakeUser(username="pingou")
+        with tests.user_set(self.app.application, user):
+            output = self.app.get("/test")
+            self.assertEqual(output.status_code, 200)
+            output_text = output.get_data(as_text=True)
+            self.assertIn("<strong>Source Code</strong>", output_text)
+            self.assertIn(
+                '<div class="input-group-prepend"><span class="input-group-text">'
+                "GIT</span></div>",
+                output_text,
+            )
+            self.assertIn(
+                '<div class="input-group-prepend"><span class="input-group-text">'
+                "SSH</span></div>",
+                output_text,
+            )
+
+    @patch.dict("pagure.config.config", {"SSH_ACCESS_GROUPS": ["invalid"]})
+    def test_ssh_restricted_user_non_member(self):
+        """ Test when ssh is restricted and the user does not have access. """
+        user = tests.FakeUser(username="pingou")
+        with tests.user_set(self.app.application, user):
+            output = self.app.get("/test")
+            self.assertEqual(output.status_code, 200)
+            output_text = output.get_data(as_text=True)
+            self.assertIn("<strong>Source Code</strong>", output_text)
+            self.assertIn(
+                '<div class="input-group-prepend"><span class="input-group-text">'
+                "GIT</span></div>",
+                output_text,
+            )
+            self.assertIn(
+                "Only members of the invalid group(s) can clone via ssh",
+                output_text,
+            )
+            self.assertNotIn(
+                '<div class="input-group-prepend"><span class="input-group-text">'
+                "SSH</span></div>",
+                output_text,
+            )
+
+
 if __name__ == "__main__":
     unittest.main(verbosity=2)