Skip to content

Commit b8de693

Browse files
committed
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.
1 parent 8e126ff commit b8de693

File tree

3 files changed

+53
-4
lines changed

3 files changed

+53
-4
lines changed

src/poetry/utils/authenticator.py

+17-3
Original file line numberDiff line numberDiff line change
@@ -245,8 +245,10 @@ def _get_credentials_for_repository(
245245

246246
return self._credentials[key]
247247

248-
def _get_credentials_for_url(self, url: str) -> HTTPAuthCredential:
249-
repository = self.get_repository_config_for_url(url)
248+
def _get_credentials_for_url(
249+
self, url: str, exact_match: bool = False
250+
) -> HTTPAuthCredential:
251+
repository = self.get_repository_config_for_url(url, exact_match)
250252

251253
credential = (
252254
self._get_credentials_for_repository(repository=repository)
@@ -267,6 +269,14 @@ def _get_credentials_for_url(self, url: str) -> HTTPAuthCredential:
267269

268270
return credential
269271

272+
def get_credentials_for_git_url(self, url: str) -> HTTPAuthCredential:
273+
key = f"git+{url}"
274+
275+
if key not in self._credentials:
276+
self._credentials[key] = self._get_credentials_for_url(url, True)
277+
278+
return self._credentials[key]
279+
270280
def get_credentials_for_url(self, url: str) -> HTTPAuthCredential:
271281
parsed_url = urllib.parse.urlsplit(url)
272282
netloc = parsed_url.netloc
@@ -338,13 +348,17 @@ def get_certs_for_url(self, url: str) -> dict[str, Path | None]:
338348

339349
@functools.lru_cache(maxsize=None)
340350
def get_repository_config_for_url(
341-
self, url: str
351+
self, url: str, exact_match: bool = False
342352
) -> AuthenticatorRepositoryConfig | None:
343353
parsed_url = urllib.parse.urlsplit(url)
344354
candidates_netloc_only = []
345355
candidates_path_match = []
346356

347357
for repository in self.configured_repositories.values():
358+
if exact_match:
359+
if parsed_url.path == repository.path:
360+
return repository
361+
continue
348362

349363
if repository.netloc == parsed_url.netloc:
350364
if parsed_url.path.startswith(repository.path) or commonprefix(

src/poetry/vcs/git/backend.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -186,7 +186,7 @@ def _fetch_remote_refs(cls, url: str, local: Repo) -> FetchPackResult:
186186
client: GitClient
187187
path: str
188188

189-
credentials = get_default_authenticator().get_credentials_for_url(url=url)
189+
credentials = get_default_authenticator().get_credentials_for_git_url(url=url)
190190
client, path = get_transport_and_path( # type: ignore[no-untyped-call]
191191
url, username=credentials.username, password=credentials.password
192192
)

tests/utils/test_authenticator.py

+35
Original file line numberDiff line numberDiff line change
@@ -560,3 +560,38 @@ def test_authenticator_add_repository(
560560

561561
basic_auth = base64.b64encode(b"foo:bar").decode()
562562
assert request.headers["Authorization"] == f"Basic {basic_auth}"
563+
564+
565+
def test_authenticator_git_repositories(
566+
config: Config,
567+
mock_remote: None,
568+
http: type[httpretty.httpretty],
569+
with_simple_keyring: None,
570+
dummy_keyring: DummyBackend,
571+
):
572+
config.merge(
573+
{
574+
"repositories": {
575+
"one": {"url": "https://foo.bar/org/one.git"},
576+
"two": {"url": "https://foo.bar/org/two.git"},
577+
},
578+
"http-basic": {
579+
"one": {"username": "foo", "password": "bar"},
580+
"two": {"username": "baz", "password": "qux"},
581+
},
582+
}
583+
)
584+
585+
authenticator = Authenticator(config, NullIO())
586+
587+
one = authenticator.get_credentials_for_git_url("https://foo.bar/org/one.git")
588+
assert one.username == "foo"
589+
assert one.password == "bar"
590+
591+
two = authenticator.get_credentials_for_git_url("https://foo.bar/org/two.git")
592+
assert two.username == "baz"
593+
assert two.password == "qux"
594+
595+
three = authenticator.get_credentials_for_git_url("https://foo.bar/org/three.git")
596+
assert not three.username
597+
assert not three.password

0 commit comments

Comments
 (0)