This is an automated email from the ASF dual-hosted git repository. akitouni pushed a commit to branch abderrahim/docker-auth in repository https://gitbox.apache.org/repos/asf/buildstream-plugins.git
commit a71ce8470fbca4f05b804ce4cde194fb4d111e7f Author: Abderrahim Kitouni <[email protected]> AuthorDate: Tue Oct 24 10:53:07 2023 +0100 docker: fix authentication when using .netrc From the requests documentation > The netrc file overrides raw HTTP authentication headers set with headers=. Which means that when the .netrc file contains the credentials for a docker registry, it will override the bearer token authentication that the plugin is setting using a raw "Authorization" header. Instead, we create a new BearerAuth class and pass it to requests to use for authenticating to the registry. --- src/buildstream_plugins/sources/docker.py | 39 +++++++++++++++++++------------ 1 file changed, 24 insertions(+), 15 deletions(-) diff --git a/src/buildstream_plugins/sources/docker.py b/src/buildstream_plugins/sources/docker.py index 45628fa..188e817 100644 --- a/src/buildstream_plugins/sources/docker.py +++ b/src/buildstream_plugins/sources/docker.py @@ -121,6 +121,27 @@ def urljoin(url, *args): return url +# Handles authentication with a bearer token +class BearerAuth(requests.auth.AuthBase): + def __init__(self, api_timeout=3): + self.token = None + self.api_timeout = api_timeout + + def __call__(self, r): + if self.token: + r.headers["Authorization"] = "Bearer {}".format(self.token) + return r + + def refresh_token(self, auth_challenge): + auth_vars = parse_bearer_authorization_challenge(auth_challenge) + # Respond to an Www-Authenticate challenge by requesting the necessary + # token from the 'realm' (endpoint) that we were given in the challenge. + request_url = "{realm}?service={service}&scope={scope}".format(**auth_vars) + response = requests.get(request_url, timeout=self.api_timeout) + response.raise_for_status() + self.token = response.json()["token"] + + # DockerManifestError # # Raised if something goes wrong while querying an image manifest from a remote @@ -137,7 +158,7 @@ class DockerRegistryV2Client: self.endpoint = endpoint self.api_timeout = api_timeout - self.token = None + self.auth = BearerAuth(api_timeout) def _request(self, subpath, extra_headers=None, stream=False, _reauthorized=False): if not extra_headers: @@ -146,32 +167,20 @@ class DockerRegistryV2Client: headers = {"content-type": "application/json"} headers.update(extra_headers) - if self.token: - headers["Authorization"] = "Bearer {}".format(self.token) - url = urljoin(self.endpoint, "v2", subpath) - response = requests.get(url, headers=headers, stream=stream, timeout=self.api_timeout) + response = requests.get(url, headers=headers, stream=stream, timeout=self.api_timeout, auth=self.auth) if response.status_code == requests.codes["unauthorized"] and not _reauthorized: # This request requires (re)authorization. See: # https://docs.docker.com/registry/spec/auth/token/ auth_challenge = response.headers["Www-Authenticate"] - auth_vars = parse_bearer_authorization_challenge(auth_challenge) - self._auth(auth_vars["realm"], auth_vars["service"], auth_vars["scope"]) + self.auth.refresh_token(auth_challenge) return self._request(subpath, extra_headers=extra_headers, _reauthorized=True) else: response.raise_for_status() return response - def _auth(self, realm, service, scope): - # Respond to an Www-Authenticate challenge by requesting the necessary - # token from the 'realm' (endpoint) that we were given in the challenge. - request_url = "{}?service={}&scope={}".format(realm, service, scope) - response = requests.get(request_url, timeout=self.api_timeout) - response.raise_for_status() - self.token = response.json()["token"] - # digest(): # # Calculate a Docker-compatible digest of an arbitrary string of bytes.
