diff --git a/docs/repositories.md b/docs/repositories.md index 31122036dfe..499c5ec59e2 100644 --- a/docs/repositories.md +++ b/docs/repositories.md @@ -511,6 +511,17 @@ You can prevent this by adding double dashes to prevent any following argument f poetry config -- http-basic.pypi myUsername -myPasswordStartingWithDash ``` +{{% note %}} +In some cases like that of [Gemfury](https://gemfury.com/help/errors/repo-url-password/) repositories, it might be +required to set an empty password. This is supported by Poetry. + +```bash +poetry config http-basic.foo "" +``` + +**Note:** Usernames cannot be empty. Attempting to use an empty username can result in an unpredictable failure. +{{% /note %}} + ## Certificates ### Custom certificate authority and mutual TLS authentication diff --git a/src/poetry/utils/password_manager.py b/src/poetry/utils/password_manager.py index f40ac4d1355..6300373b6b6 100644 --- a/src/poetry/utils/password_manager.py +++ b/src/poetry/utils/password_manager.py @@ -196,23 +196,14 @@ def get_http_auth(self, repo_name: str) -> dict[str, str | None] | None: username = self._config.get(f"http-basic.{repo_name}.username") password = self._config.get(f"http-basic.{repo_name}.password") - # we only return None if both values are None or "" - # password can be None at this stage with the username "" - if (username is password is None) or (username == password == ""): + if not username: return None - if not password: - if self.use_keyring: - password = self.keyring.get_password(repo_name, username) - elif not username: - # at this tage if username is "" or None, auth is invalid - return None - - if not username and not password: - return None + if not password and self.use_keyring: + password = self.keyring.get_password(repo_name, username) return { - "username": username or "", + "username": username, "password": password or "", } diff --git a/tests/utils/test_authenticator.py b/tests/utils/test_authenticator.py index bd25702e296..12d91ab62e7 100644 --- a/tests/utils/test_authenticator.py +++ b/tests/utils/test_authenticator.py @@ -153,7 +153,7 @@ def test_authenticator_uses_empty_strings_as_default_password( assert request.headers["Authorization"] == f"Basic {basic_auth}" -def test_authenticator_uses_empty_strings_as_default_username( +def test_authenticator_ignores_empty_strings_as_default_username( config: Config, mock_remote: None, repo: dict[str, dict[str, str]], @@ -170,8 +170,7 @@ def test_authenticator_uses_empty_strings_as_default_username( authenticator.request("get", "https://foo.bar/files/foo-0.1.0.tar.gz") request = http.last_request() - basic_auth = base64.b64encode(b":bar").decode() - assert request.headers["Authorization"] == f"Basic {basic_auth}" + assert request.headers["Authorization"] is None def test_authenticator_falls_back_to_keyring_url( diff --git a/tests/utils/test_password_manager.py b/tests/utils/test_password_manager.py index 00058e46513..53b8909075b 100644 --- a/tests/utils/test_password_manager.py +++ b/tests/utils/test_password_manager.py @@ -40,7 +40,7 @@ def test_set_http_password( ("username", "password", "is_valid"), [ ("bar", "baz", True), - ("", "baz", True), + ("", "baz", False), ("bar", "", True), ("", "", False), ], @@ -134,7 +134,7 @@ def test_set_http_password_with_unavailable_backend( ("username", "password", "is_valid"), [ ("bar", "baz", True), - ("", "baz", True), + ("", "baz", False), ("bar", "", True), ("", "", False), ],