From 5ff1e3548f9f1429667acdec7410ab68a634f80c Mon Sep 17 00:00:00 2001 From: Arun Babu Neelicattu Date: Tue, 10 May 2022 01:08:13 +0200 Subject: [PATCH] ensure git repository authn uses exact urls Since git repository authentication is a special case of repository configuration, the existing assumptions around path matching do not apply. In order to prevent unexpected behaviour due to similar path matching, git authentication will use exact url matching. --- src/poetry/utils/authenticator.py | 20 +++++++++++++++--- src/poetry/vcs/git/backend.py | 2 +- tests/utils/test_authenticator.py | 35 +++++++++++++++++++++++++++++++ 3 files changed, 53 insertions(+), 4 deletions(-) diff --git a/src/poetry/utils/authenticator.py b/src/poetry/utils/authenticator.py index 22703be8977..070e503acd8 100644 --- a/src/poetry/utils/authenticator.py +++ b/src/poetry/utils/authenticator.py @@ -245,8 +245,10 @@ def _get_credentials_for_repository( return self._credentials[key] - def _get_credentials_for_url(self, url: str) -> HTTPAuthCredential: - repository = self.get_repository_config_for_url(url) + def _get_credentials_for_url( + self, url: str, exact_match: bool = False + ) -> HTTPAuthCredential: + repository = self.get_repository_config_for_url(url, exact_match) credential = ( self._get_credentials_for_repository(repository=repository) @@ -267,6 +269,14 @@ def _get_credentials_for_url(self, url: str) -> HTTPAuthCredential: return credential + def get_credentials_for_git_url(self, url: str) -> HTTPAuthCredential: + key = f"git+{url}" + + if key not in self._credentials: + self._credentials[key] = self._get_credentials_for_url(url, True) + + return self._credentials[key] + def get_credentials_for_url(self, url: str) -> HTTPAuthCredential: parsed_url = urllib.parse.urlsplit(url) netloc = parsed_url.netloc @@ -338,13 +348,17 @@ def get_certs_for_url(self, url: str) -> dict[str, Path | None]: @functools.lru_cache(maxsize=None) def get_repository_config_for_url( - self, url: str + self, url: str, exact_match: bool = False ) -> AuthenticatorRepositoryConfig | None: parsed_url = urllib.parse.urlsplit(url) candidates_netloc_only = [] candidates_path_match = [] for repository in self.configured_repositories.values(): + if exact_match: + if parsed_url.path == repository.path: + return repository + continue if repository.netloc == parsed_url.netloc: if parsed_url.path.startswith(repository.path) or commonprefix( diff --git a/src/poetry/vcs/git/backend.py b/src/poetry/vcs/git/backend.py index c43493febdb..a1b34436b41 100644 --- a/src/poetry/vcs/git/backend.py +++ b/src/poetry/vcs/git/backend.py @@ -186,7 +186,7 @@ def _fetch_remote_refs(cls, url: str, local: Repo) -> FetchPackResult: client: GitClient path: str - credentials = get_default_authenticator().get_credentials_for_url(url=url) + credentials = get_default_authenticator().get_credentials_for_git_url(url=url) client, path = get_transport_and_path( # type: ignore[no-untyped-call] url, username=credentials.username, password=credentials.password ) diff --git a/tests/utils/test_authenticator.py b/tests/utils/test_authenticator.py index dd1666036ab..7b0c858fc42 100644 --- a/tests/utils/test_authenticator.py +++ b/tests/utils/test_authenticator.py @@ -560,3 +560,38 @@ def test_authenticator_add_repository( basic_auth = base64.b64encode(b"foo:bar").decode() assert request.headers["Authorization"] == f"Basic {basic_auth}" + + +def test_authenticator_git_repositories( + config: Config, + mock_remote: None, + http: type[httpretty.httpretty], + with_simple_keyring: None, + dummy_keyring: DummyBackend, +): + config.merge( + { + "repositories": { + "one": {"url": "https://foo.bar/org/one.git"}, + "two": {"url": "https://foo.bar/org/two.git"}, + }, + "http-basic": { + "one": {"username": "foo", "password": "bar"}, + "two": {"username": "baz", "password": "qux"}, + }, + } + ) + + authenticator = Authenticator(config, NullIO()) + + one = authenticator.get_credentials_for_git_url("https://foo.bar/org/one.git") + assert one.username == "foo" + assert one.password == "bar" + + two = authenticator.get_credentials_for_git_url("https://foo.bar/org/two.git") + assert two.username == "baz" + assert two.password == "qux" + + three = authenticator.get_credentials_for_git_url("https://foo.bar/org/three.git") + assert not three.username + assert not three.password