From f871d80a7ef17687e688bf8b2adfa7d565287ded Mon Sep 17 00:00:00 2001 From: Robert Iannucci Date: Tue, 11 Jun 2024 23:03:31 +0000 Subject: [PATCH] [gerrit_util] Add dogfoodable luci-auth Authenticator. Inspired by https://chromium-review.googlesource.com/c/5577744. This implementation allows toggling the entire new-auth-stack with the git configuration: [depot-tools] useNewAuthStack = 1 Additionally, you can set: [depot-tools] newAuthSkipSSO = 1 To intentionally skip SSOAuthenticator for now while doing local evaluation of these auth methods. This CL was uploaded without gitcookies using the new luci-auth Authenticator. Subsequent CLs will adjust creds-check and EnsureAuthenticated to work correctly with the new auth stack. R=ayatane@google.com Bug: 336351842, 336652327 Change-Id: I0eb6d82ca106ddd114b74f63d8cda4c5a7b70c86 Reviewed-on: https://chromium-review.googlesource.com/c/chromium/tools/depot_tools/+/5590324 Reviewed-by: Yiwei Zhang Reviewed-by: Scott Lee Commit-Queue: Allen Li --- auth.py | 4 ++-- gerrit_util.py | 65 ++++++++++++++++++++++++++++++++++---------------- 2 files changed, 46 insertions(+), 23 deletions(-) diff --git a/auth.py b/auth.py index de9ecfd415..b879a746d8 100644 --- a/auth.py +++ b/auth.py @@ -107,7 +107,7 @@ class Authenticator(object): return self._access_token # Nope, still expired. Needs user interaction. - logging.error('Failed to create access token') + logging.debug('Failed to create access token') raise LoginRequiredError(self._scopes) def get_id_token(self): @@ -127,7 +127,7 @@ class Authenticator(object): return self._id_token # Nope, still expired. Needs user interaction. - logging.error('Failed to create id token') + logging.debug('Failed to create id token') raise LoginRequiredError() def authorize(self, http, use_id_token=False): diff --git a/gerrit_util.py b/gerrit_util.py index 4b104e66cf..52cfcb0a62 100644 --- a/gerrit_util.py +++ b/gerrit_util.py @@ -177,24 +177,42 @@ class Authenticator(object): Probes the local system and its environment and identifies the Authenticator instance to use. """ - authenticators: List[Type[Authenticator]] = [ - SSOAuthenticator, - - # LUCI Context takes priority since it's normally present only on bots, - # which then must use it. - LuciContextAuthenticator, - - # TODO(crbug.com/1059384): Automatically detect when running on - # cloudtop, and use CookiesAuthenticator instead. - GceAuthenticator, - CookiesAuthenticator, - ] + use_new_auth = scm.GIT.GetConfig(os.getcwd(), + 'depot-tools.usenewauthstack') == '1' + + # Allow skipping SSOAuthenticator for local testing purposes. + skip_sso = scm.GIT.GetConfig(os.getcwd(), + 'depot-tools.newauthskipsso') == '1' + + authenticators: List[Type[Authenticator]] + + if use_new_auth: + LOGGER.debug('Authenticator.get: using new auth stack.') + authenticators = [ + SSOAuthenticator, + LuciContextAuthenticator, + GceAuthenticator, + LuciAuthAuthenticator, + ] + if skip_sso: + LOGGER.debug('Authenticator.get: skipping SSOAuthenticator.') + authenticators = authenticators[1:] + else: + authenticators = [ + LuciContextAuthenticator, + GceAuthenticator, + CookiesAuthenticator, + ] + for candidate in authenticators: if candidate.is_applicable(): + LOGGER.debug('Authenticator.get: Selected %s.', + candidate.__name__) return candidate() + auth_names = ', '.join(a.__name__ for a in authenticators) raise ValueError( - f"Could not find suitable authenticator, tried: {authenticators}") + f"Could not find suitable authenticator, tried: [{auth_names}].") class SSOAuthenticator(Authenticator): @@ -234,14 +252,7 @@ class SSOAuthenticator(Authenticator): def is_applicable(cls) -> bool: """If the git-remote-sso binary is in $PATH, we consider this authenticator to be applicable.""" - if scm.GIT.GetConfig(os.getcwd(), 'depot-tools.usenewauthstack') != '1': - LOGGER.debug('SSOAuthenticator: skipping due to missing opt-in.') - return False - - pth = cls._resolve_sso_binary_path() - if pth: - LOGGER.debug('SSOAuthenticator: enabled %r.', pth) - return bool(pth) + return bool(cls._resolve_sso_binary_path()) @classmethod def _parse_config(cls, config: str) -> SSOInfo: @@ -636,6 +647,18 @@ class LuciContextAuthenticator(Authenticator): return '' +class LuciAuthAuthenticator(LuciContextAuthenticator): + """Authenticator implementation that uses `luci-auth` credentials. + + This is the same as LuciContextAuthenticator, except that it is for local + non-google.com developer credentials. + """ + + @staticmethod + def is_applicable(): + return True + + class ReqParams(TypedDict): uri: str method: str