Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions news/10269.bugfix.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
Fix the auth credential cache to allow for the case in which
the index url contains the username, but the password comes
from an external source, such as keyring.
13 changes: 10 additions & 3 deletions src/pip/_internal/network/auth.py
Original file line number Diff line number Diff line change
Expand Up @@ -179,9 +179,16 @@ def _get_url_and_credentials(
# Try to get credentials from original url
username, password = self._get_new_credentials(original_url)

# If credentials not found, use any stored credentials for this netloc
if username is None and password is None:
username, password = self.passwords.get(netloc, (None, None))
# If credentials not found, use any stored credentials for this netloc.
# Do this if either the username or the password is missing.
# This accounts for the situation in which the user has specified
# the username in the index url, but the password comes from keyring.
if (username is None or password is None) and netloc in self.passwords:
un, pw = self.passwords[netloc]
# It is possible that the cached credentials are for a different username,
# in which case the cache should be ignored.
if username is None or username == un:
username, password = un, pw

if username is not None or password is not None:
# Convert the username and password if they're None, so that
Expand Down
9 changes: 9 additions & 0 deletions tests/unit/test_network_auth.py
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,15 @@ def test_get_credentials_uses_cached_credentials():
assert got == expected


def test_get_credentials_uses_cached_credentials_only_username():
auth = MultiDomainBasicAuth()
auth.passwords["example.com"] = ("user", "pass")

got = auth._get_url_and_credentials("http://[email protected]/path")
expected = ("http://example.com/path", "user", "pass")
assert got == expected


def test_get_index_url_credentials():
auth = MultiDomainBasicAuth(index_urls=["http://foo:[email protected]/path"])
get = functools.partial(
Expand Down