Kallithea issues archive

Issue #379: Cannot clone hg repos with web interface

Reported by: rockin
State: new
Created on: 2020-07-07 19:32
Updated on: 2020-07-13 14:31

Description

I cannot clone hg repos with the add repo feature in Kallithea. It fails with a 500 error. The bug seems to be an attempt match of a regex to a bytes object. Maybe it was introduced when Kallithea was ported to Python3

Attachments

Comments

Comment by Thomas De Schampheleire, on 2020-07-08 07:48

Thanks for reporting, it seems this issue occurs only when username/password auth is present in the URL.

Below patch should fix your problem, could you test in your context?

Thanks

# HG changeset patch
# User Thomas De Schampheleire <thomas.de_schampheleire@nokia.com>
# Date 1594192757 -7200
#      Wed Jul 08 09:19:17 2020 +0200
# Branch stable
# Node ID c56c87b03fdd4e6f5b59cd51792741a51d528f74
# Parent  0bca9e828db247730518cbb4fd9225ea42592faf
vcs: fix cloning remote repository with HTTP authentication

Using a remote clone URI of
   http://user:pass@host/...
triggered an exception:

    ...
    E     File ".../kallithea/lib/utils.py", line 256, in is_valid_repo_uri
    E       GitRepository._check_url(url)
    E     File ".../kallithea/lib/vcs/backends/git/repository.py", line 183, in _check_url
    E       passmgr.add_password(*authinfo)
    E     File "/usr/lib/python3.7/urllib/request.py", line 848, in add_password
    E       self.reduce_uri(u, default_port) for u in uri)
    E     File "/usr/lib/python3.7/urllib/request.py", line 848, in <genexpr>
    E       self.reduce_uri(u, default_port) for u in uri)
    E     File "/usr/lib/python3.7/urllib/request.py", line 875, in reduce_uri
    E       host, port = splitport(authority)
    E     File "/usr/lib/python3.7/urllib/parse.py", line 1022, in splitport
    E       match = _portprog.fullmatch(host)
    E   TypeError: cannot use a string pattern on a bytes-like object


The authinfo tuple is obtained via mercurial.util.url, which unfortunately
returns a tuple of bytes whereas urllib expects strings.
It seems that mercurial internally has some more hacking around urllib as
urllibcompat.py, which we don't use.

Therefore, transform the bytes into strings before passing authinfo to
urllib.

diff --git a/kallithea/lib/vcs/backends/git/repository.py b/kallithea/lib/vcs/backends/git/repository.py
--- a/kallithea/lib/vcs/backends/git/repository.py
+++ b/kallithea/lib/vcs/backends/git/repository.py
@@ -178,6 +178,16 @@ class GitRepository(BaseRepository):
         cleaned_uri = str(url_obj)

         if authinfo:
+            # mercurial.util.url returns authinfo as bytes, but urllib expects strings
+            #(realm, uris, user, password)
+            #(None, (b'http://127.0.0.1/repo', b'127.0.0.1'), b'user', b'pass')
+            authinfo = (
+                safe_str(authinfo[0]),
+                tuple(safe_str(x) for x in authinfo[1]),
+                safe_str(authinfo[2]),
+                safe_str(authinfo[3]),
+            )
+
             # create a password manager
             passmgr = urllib.request.HTTPPasswordMgrWithDefaultRealm()
             passmgr.add_password(*authinfo)
diff --git a/kallithea/lib/vcs/backends/hg/repository.py b/kallithea/lib/vcs/backends/hg/repository.py
--- a/kallithea/lib/vcs/backends/hg/repository.py
+++ b/kallithea/lib/vcs/backends/hg/repository.py
@@ -315,6 +315,16 @@ class MercurialRepository(BaseRepository
         cleaned_uri = str(url_obj)

         if authinfo:
+            # mercurial.util.url returns authinfo as bytes, but urllib expects strings
+            #(realm, uris, user, password)
+            #(None, (b'http://127.0.0.1/repo', b'127.0.0.1'), b'user', b'pass')
+            authinfo = (
+                safe_str(authinfo[0]),
+                tuple(safe_str(x) for x in authinfo[1]),
+                safe_str(authinfo[2]),
+                safe_str(authinfo[3]),
+            )
+
             # create a password manager
             passmgr = urllib.request.HTTPPasswordMgrWithDefaultRealm()
             passmgr.add_password(*authinfo)
diff --git a/kallithea/tests/functional/test_admin_repos.py b/kallithea/tests/functional/test_admin_repos.py
--- a/kallithea/tests/functional/test_admin_repos.py
+++ b/kallithea/tests/functional/test_admin_repos.py
@@ -344,6 +344,19 @@ class _BaseTestCase(base.TestController)
                                                 _session_csrf_secret_token=self.session_csrf_secret_token()))
         response.mustcontain('Invalid repository URL')

+    def test_create_remote_repo_wrong_clone_uri_http_auth(self):
+        self.log_user()
+        repo_name = self.NEW_REPO
+        description = 'description for newly created repo'
+        response = self.app.post(base.url('repos'),
+                        fixture._get_repo_create_params(repo_private=False,
+                                                repo_name=repo_name,
+                                                repo_type=self.REPO_TYPE,
+                                                repo_description=description,
+                                                clone_uri='http://user:pass@127.0.0.1/repo',
+                                                _session_csrf_secret_token=self.session_csrf_secret_token()))
+        response.mustcontain('Invalid repository URL')
+
     def test_delete(self):
         self.log_user()
         repo_name = 'vcs_test_new_to_delete_%s' % self.REPO_TYPE

Comment by rockin, on 2020-07-09 14:36

It fixes the 500 error when I put auth informations in the clone_url. However, it seems Kallithea doesn’t validate correctly the clone_url. I had this problem before your patch.

Comment by Thomas De Schampheleire, on 2020-07-09 14:43

Can you give more details? Do you get an error? Which one? Please paste the relevant parts of the log.