From b4b6061d36ee978bafea5095825adabd0ebc4d1a Mon Sep 17 00:00:00 2001 From: Simon Gerber Date: Wed, 9 Oct 2024 15:32:53 +0200 Subject: [PATCH] Refresh expired API token before pushing compile metadata to Lieutenant --- commodore/cluster.py | 6 ++++++ commodore/config.py | 34 ++++++++++++++++++++++------------ commodore/login.py | 2 +- 3 files changed, 29 insertions(+), 13 deletions(-) diff --git a/commodore/cluster.py b/commodore/cluster.py index c9ba9108e..8a6cbb33e 100644 --- a/commodore/cluster.py +++ b/commodore/cluster.py @@ -20,6 +20,7 @@ from .component import component_parameters_key, Component from .config import Config from .inventory import Inventory +from .login import login class Cluster: @@ -333,6 +334,11 @@ def report_compile_metadata( ) if report: + if cfg.api_token is None: + # Re-login to ensure we have a valid API token. This assumes that the only case where we + # call `report_compile_metadata()` and the api_token is None is when a short-lived OIDC + # token expired while we were compiling the catalog. + login(cfg) lieutenant_post( cfg.api_url, cfg.api_token, diff --git a/commodore/config.py b/commodore/config.py index 8da8adefa..cb2e2608a 100644 --- a/commodore/config.py +++ b/commodore/config.py @@ -202,18 +202,28 @@ def api_token(self): if self._api_token is None and self.api_url: tokens = tokencache.get(self.api_url) token = tokens.get("id_token") - if token is not None: - # We don't verify the signature, we just want to know if the token is expired - # lieutenant will decide if it's valid - try: - t = jwt.decode( - token, algorithms=["RS256"], options={"verify_signature": False} - ) - if "exp" in t and t["exp"] < time.time() + 10: - return None - except jwt.exceptions.InvalidTokenError: - return None - self._api_token = token + self._api_token = token + + if self._api_token: + # Clear cached token if it's expired. + # + # NOTE(sg): This assumes that users of this property call `login.login()` if they see + # that the property is None. Callers that don't do so must expect failed API operations + # when Commodore is invoked with a short-lived OIDC token. + try: + # We don't verify the signature, we just want to know if the token is expired. + t = jwt.decode( + self._api_token, + algorithms=["RS256"], + options={"verify_signature": False}, + ) + if "exp" in t and t["exp"] < time.time() + 10: + self._api_token = None + # Here: tokens without 'exp' don't expire + except jwt.exceptions.InvalidTokenError: + # Assume that unparseable tokens are long-lived. + pass + return self._api_token @api_token.setter diff --git a/commodore/login.py b/commodore/login.py index 1473ccfff..a88fab5fc 100644 --- a/commodore/login.py +++ b/commodore/login.py @@ -276,7 +276,7 @@ def login(config: Config): raise click.ClickException("Required OIDC discovery URL not set") if config.api_token: - # Short-circuit if we already have a valid API token + # Short-circuit if we have a valid API token return client = WebApplicationClient(config.oidc_client)