Commit b2b8c859 by Arun Babu Neelicattu Committed by Bjorn Neergaard

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.
parent 404ab141
......@@ -245,8 +245,10 @@ class Authenticator:
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 @@ class Authenticator:
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 @@ class Authenticator:
@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(
......
......@@ -186,7 +186,7 @@ class Git:
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
)
......
......@@ -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
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment