[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 <yiwzhang@google.com>
Reviewed-by: Scott Lee <ddoman@chromium.org>
Commit-Queue: Allen Li <ayatane@chromium.org>
changes/24/5590324/15
Robert Iannucci 1 year ago committed by LUCI CQ
parent 165fdee7e6
commit f871d80a7e

@ -107,7 +107,7 @@ class Authenticator(object):
return self._access_token return self._access_token
# Nope, still expired. Needs user interaction. # Nope, still expired. Needs user interaction.
logging.error('Failed to create access token') logging.debug('Failed to create access token')
raise LoginRequiredError(self._scopes) raise LoginRequiredError(self._scopes)
def get_id_token(self): def get_id_token(self):
@ -127,7 +127,7 @@ class Authenticator(object):
return self._id_token return self._id_token
# Nope, still expired. Needs user interaction. # Nope, still expired. Needs user interaction.
logging.error('Failed to create id token') logging.debug('Failed to create id token')
raise LoginRequiredError() raise LoginRequiredError()
def authorize(self, http, use_id_token=False): def authorize(self, http, use_id_token=False):

@ -177,24 +177,42 @@ class Authenticator(object):
Probes the local system and its environment and identifies the Probes the local system and its environment and identifies the
Authenticator instance to use. Authenticator instance to use.
""" """
authenticators: List[Type[Authenticator]] = [ use_new_auth = scm.GIT.GetConfig(os.getcwd(),
SSOAuthenticator, 'depot-tools.usenewauthstack') == '1'
# LUCI Context takes priority since it's normally present only on bots, # Allow skipping SSOAuthenticator for local testing purposes.
# which then must use it. skip_sso = scm.GIT.GetConfig(os.getcwd(),
LuciContextAuthenticator, 'depot-tools.newauthskipsso') == '1'
# TODO(crbug.com/1059384): Automatically detect when running on authenticators: List[Type[Authenticator]]
# cloudtop, and use CookiesAuthenticator instead.
GceAuthenticator, if use_new_auth:
CookiesAuthenticator, 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: for candidate in authenticators:
if candidate.is_applicable(): if candidate.is_applicable():
LOGGER.debug('Authenticator.get: Selected %s.',
candidate.__name__)
return candidate() return candidate()
auth_names = ', '.join(a.__name__ for a in authenticators)
raise ValueError( raise ValueError(
f"Could not find suitable authenticator, tried: {authenticators}") f"Could not find suitable authenticator, tried: [{auth_names}].")
class SSOAuthenticator(Authenticator): class SSOAuthenticator(Authenticator):
@ -234,14 +252,7 @@ class SSOAuthenticator(Authenticator):
def is_applicable(cls) -> bool: def is_applicable(cls) -> bool:
"""If the git-remote-sso binary is in $PATH, we consider this """If the git-remote-sso binary is in $PATH, we consider this
authenticator to be applicable.""" authenticator to be applicable."""
if scm.GIT.GetConfig(os.getcwd(), 'depot-tools.usenewauthstack') != '1': return bool(cls._resolve_sso_binary_path())
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)
@classmethod @classmethod
def _parse_config(cls, config: str) -> SSOInfo: def _parse_config(cls, config: str) -> SSOInfo:
@ -636,6 +647,18 @@ class LuciContextAuthenticator(Authenticator):
return '' 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): class ReqParams(TypedDict):
uri: str uri: str
method: str method: str

Loading…
Cancel
Save