depot_tools: Remove depot-tools-auth
Users must use luci-auth now.
Bug: 1001756
Change-Id: I04cab6bdbfbd958f386a4cab761dfe4d34073afc
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/tools/depot_tools/+/1849810
Commit-Queue: Edward Lesmes <ehmaldonado@chromium.org>
Reviewed-by: Anthony Polito <apolito@google.com>
diff --git a/auth.py b/auth.py
index 0da997c..f717be1 100644
--- a/auth.py
+++ b/auth.py
@@ -50,13 +50,6 @@
# Deprecated. Use OAUTH_SCOPE_EMAIL instead.
OAUTH_SCOPES = OAUTH_SCOPE_EMAIL
-# Path to a file with cached OAuth2 credentials used by default relative to the
-# home dir (see _get_token_cache_path). It should be a safe location accessible
-# only to a current user: knowing content of this file is roughly equivalent to
-# knowing account password. Single file can hold multiple independent tokens
-# identified by token_cache_key (see Authenticator).
-OAUTH_TOKENS_CACHE = '.depot_tools_oauth2_tokens'
-
# Authentication configuration extracted from command line options.
# See doc string for 'make_auth_config' for meaning of fields.
@@ -381,79 +374,38 @@
return opts
-def get_authenticator_for_host(hostname, config, scopes=OAUTH_SCOPE_EMAIL):
+def get_authenticator(config, scopes=OAUTH_SCOPE_EMAIL):
"""Returns Authenticator instance to access given host.
Args:
- hostname: a naked hostname or http(s)://<hostname>[/] URL. Used to derive
- a cache key for token cache.
config: AuthConfig instance.
scopes: space separated oauth scopes. Defaults to OAUTH_SCOPE_EMAIL.
Returns:
Authenticator object.
-
- Raises:
- AuthenticationError if hostname is invalid.
"""
- hostname = hostname.lower().rstrip('/')
- # Append some scheme, otherwise urlparse puts hostname into parsed.path.
- if '://' not in hostname:
- hostname = 'https://' + hostname
- parsed = urlparse.urlparse(hostname)
-
- if parsed.path or parsed.params or parsed.query or parsed.fragment:
- raise AuthenticationError(
- 'Expecting a hostname or root host URL, got %s instead' % hostname)
- return Authenticator(parsed.netloc, config, scopes)
+ return Authenticator(config, scopes)
class Authenticator(object):
"""Object that knows how to refresh access tokens when needed.
Args:
- token_cache_key: string key of a section of the token cache file to use
- to keep the tokens. See hostname_to_token_cache_key.
config: AuthConfig object that holds authentication configuration.
"""
- def __init__(self, token_cache_key, config, scopes):
+ def __init__(self, config, scopes):
assert isinstance(config, AuthConfig)
assert config.use_oauth2
self._access_token = None
self._config = config
self._lock = threading.Lock()
- self._token_cache_key = token_cache_key
self._external_token = None
self._scopes = scopes
if config.refresh_token_json:
self._external_token = _read_refresh_token_json(config.refresh_token_json)
logging.debug('Using auth config %r', config)
- def login(self):
- """Performs interactive login flow if necessary.
-
- Raises:
- AuthenticationError on error or if interrupted.
- """
- if self._external_token:
- raise AuthenticationError(
- 'Can\'t run login flow when using --auth-refresh-token-json.')
- return self.get_access_token(
- force_refresh=True, allow_user_interaction=True)
-
- def logout(self):
- """Revokes the refresh token and deletes it from the cache.
-
- Returns True if had some credentials cached.
- """
- with self._lock:
- self._access_token = None
- had_creds = bool(_get_luci_auth_credentials(self._scopes))
- subprocess2.check_call(
- ['luci-auth', 'logout', '-scopes', self._scopes])
- return had_creds
-
def has_cached_credentials(self):
"""Returns True if long term credentials (refresh token) are in cache.
@@ -526,16 +478,6 @@
return self._access_token
- def get_token_info(self):
- """Returns a result of /oauth2/v2/tokeninfo call with token info."""
- access_token = self.get_access_token()
- resp, content = httplib2.Http().request(
- uri='https://www.googleapis.com/oauth2/v2/tokeninfo?%s' % (
- urllib.urlencode({'access_token': access_token.token})))
- if resp.status == 200:
- return json.loads(content)
- raise AuthenticationError('Failed to fetch the token info: %r' % content)
-
def authorize(self, http):
"""Monkey patches authentication logic of httplib2.Http instance.
@@ -684,20 +626,6 @@
## Private functions.
-def _get_token_cache_path():
- # On non Win just use HOME.
- if sys.platform != 'win32':
- return os.path.join(os.path.expanduser('~'), OAUTH_TOKENS_CACHE)
- # Prefer USERPROFILE over HOME, since HOME is overridden in
- # git-..._bin/cmd/git.cmd to point to depot_tools. depot-tools-auth.py script
- # (and all other scripts) doesn't use this override and thus uses another
- # value for HOME. git.cmd doesn't touch USERPROFILE though, and usually
- # USERPROFILE == HOME on Windows.
- if 'USERPROFILE' in os.environ:
- return os.path.join(os.environ['USERPROFILE'], OAUTH_TOKENS_CACHE)
- return os.path.join(os.path.expanduser('~'), OAUTH_TOKENS_CACHE)
-
-
def _is_headless():
"""True if machine doesn't seem to have a display."""
return sys.platform == 'linux2' and not os.environ.get('DISPLAY')
@@ -750,6 +678,7 @@
user_agent=None,
revoke_uri=None)
+
def _run_oauth_dance(scopes):
"""Perform full 3-legged OAuth2 flow with the browser.
diff --git a/depot-tools-auth b/depot-tools-auth
deleted file mode 100755
index 9233c92..0000000
--- a/depot-tools-auth
+++ /dev/null
@@ -1,8 +0,0 @@
-#!/usr/bin/env bash
-# Copyright 2015 The Chromium Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-
-base_dir=$(dirname "$0")
-
-PYTHONDONTWRITEBYTECODE=1 exec python "$base_dir/depot-tools-auth.py" "$@"
diff --git a/depot-tools-auth.bat b/depot-tools-auth.bat
deleted file mode 100644
index 37280b7..0000000
--- a/depot-tools-auth.bat
+++ /dev/null
@@ -1,12 +0,0 @@
-@echo off
-:: Copyright 2015 The Chromium Authors. All rights reserved.
-:: Use of this source code is governed by a BSD-style license that can be
-:: found in the LICENSE file.
-setlocal
-
-:: Ensure that "depot_tools" is somewhere in PATH so this tool can be used
-:: standalone, but allow other PATH manipulations to take priority.
-set PATH=%PATH%;%~dp0
-
-:: Defer control.
-python "%~dp0\depot-tools-auth.py" %*
diff --git a/depot-tools-auth.py b/depot-tools-auth.py
deleted file mode 100755
index 17be1e4..0000000
--- a/depot-tools-auth.py
+++ /dev/null
@@ -1,104 +0,0 @@
-#!/usr/bin/env python
-# Copyright 2015 The Chromium Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-
-"""Manages cached OAuth2 tokens used by other depot_tools scripts.
-
-Usage:
- depot-tools-auth login codereview.chromium.org
- depot-tools-auth info codereview.chromium.org
- depot-tools-auth logout codereview.chromium.org
-"""
-
-from __future__ import print_function
-
-import logging
-import optparse
-import sys
-import os
-
-import auth
-import setup_color
-import subcommand
-
-__version__ = '1.0'
-
-
-@subcommand.usage('<hostname>')
-def CMDlogin(parser, args):
- """Performs interactive login and caches authentication token."""
- # Forcefully relogin, revoking previous token.
- hostname, authenticator = parser.parse_args(args)
- authenticator.logout()
- authenticator.login()
- print_token_info(hostname, authenticator)
- return 0
-
-
-@subcommand.usage('<hostname>')
-def CMDlogout(parser, args):
- """Revokes cached authentication token and removes it from disk."""
- _, authenticator = parser.parse_args(args)
- done = authenticator.logout()
- print('Done.' if done else 'Already logged out.')
- return 0
-
-
-@subcommand.usage('<hostname>')
-def CMDinfo(parser, args):
- """Shows email associated with a cached authentication token."""
- # If no token is cached, AuthenticationError will be caught in 'main'.
- hostname, authenticator = parser.parse_args(args)
- print_token_info(hostname, authenticator)
- return 0
-
-
-def print_token_info(hostname, authenticator):
- token_info = authenticator.get_token_info()
- print('Logged in to %s as %s.' % (hostname, token_info['email']))
- print('')
- print('To login with a different email run:')
- print(' depot-tools-auth login %s' % hostname)
- print('To logout and purge the authentication token run:')
- print(' depot-tools-auth logout %s' % hostname)
-
-
-class OptionParser(optparse.OptionParser):
- def __init__(self, *args, **kwargs):
- optparse.OptionParser.__init__(
- self, *args, prog='depot-tools-auth', version=__version__, **kwargs)
- self.add_option(
- '-v', '--verbose', action='count', default=0,
- help='Use 2 times for more debugging info')
- auth.add_auth_options(self, auth.make_auth_config(use_oauth2=True))
-
- def parse_args(self, args=None, values=None):
- """Parses options and returns (hostname, auth.Authenticator object)."""
- options, args = optparse.OptionParser.parse_args(self, args, values)
- levels = [logging.WARNING, logging.INFO, logging.DEBUG]
- logging.basicConfig(level=levels[min(options.verbose, len(levels) - 1)])
- auth_config = auth.extract_auth_config_from_options(options)
- if len(args) != 1:
- self.error('Expecting single argument (hostname).')
- if not auth_config.use_oauth2:
- self.error('This command is only usable with OAuth2 authentication')
- return args[0], auth.get_authenticator_for_host(args[0], auth_config)
-
-
-def main(argv):
- dispatcher = subcommand.CommandDispatcher(__name__)
- try:
- return dispatcher.execute(OptionParser(), argv)
- except auth.AuthenticationError as e:
- print(e, file=sys.stderr)
- return 1
-
-
-if __name__ == '__main__':
- setup_color.init()
- try:
- sys.exit(main(sys.argv[1:]))
- except KeyboardInterrupt:
- sys.stderr.write('interrupted\n')
- sys.exit(1)
diff --git a/git_cl.py b/git_cl.py
index 0b1d915..ff6d5b3 100755
--- a/git_cl.py
+++ b/git_cl.py
@@ -465,11 +465,7 @@
if not requests:
return
- codereview_url = changelist.GetCodereviewServer()
- codereview_host = urlparse.urlparse(codereview_url).hostname
-
- authenticator = auth.get_authenticator_for_host(codereview_host, auth_config)
- http = authenticator.authorize(httplib2.Http())
+ http = auth.get_authenticator(auth_config).authorize(httplib2.Http())
http.force_exception_to_status_code = True
batch_request = {'requests': requests}
@@ -544,10 +540,7 @@
'fields': ','.join('builds.*.' + field for field in fields),
}
- codereview_url = changelist.GetCodereviewServer()
- codereview_host = urlparse.urlparse(codereview_url).hostname
-
- authenticator = auth.get_authenticator_for_host(codereview_host, auth_config)
+ authenticator = auth.get_authenticator(auth_config)
if authenticator.has_cached_credentials():
http = authenticator.authorize(httplib2.Http())
else:
diff --git a/my_activity.py b/my_activity.py
index 5e76cc1..81b901d 100755
--- a/my_activity.py
+++ b/my_activity.py
@@ -293,8 +293,7 @@
def monorail_get_auth_http(self):
auth_config = auth.extract_auth_config_from_options(self.options)
- authenticator = auth.get_authenticator_for_host(
- 'bugs.chromium.org', auth_config)
+ authenticator = auth.get_authenticator(auth_config)
# Manually use a long timeout (10m); for some users who have a
# long history on the issue tracker, whatever the default timeout
# is is reached.
diff --git a/presubmit_canned_checks.py b/presubmit_canned_checks.py
index 4cd3a84..2b559af 100644
--- a/presubmit_canned_checks.py
+++ b/presubmit_canned_checks.py
@@ -1417,8 +1417,7 @@
# authentication
try:
- authenticator = auth.get_authenticator_for_host(
- LUCI_CONFIG_HOST_NAME, auth.make_auth_config())
+ authenticator = auth.get_authenticator(auth.make_auth_config())
acc_tkn = authenticator.get_access_token()
except auth.AuthenticationError as e:
return [output_api.PresubmitError(
diff --git a/tests/git_cl_test.py b/tests/git_cl_test.py
index 1a2612f..917024a 100755
--- a/tests/git_cl_test.py
+++ b/tests/git_cl_test.py
@@ -636,7 +636,7 @@
self._mocked_call('write_json', path, contents))
self.mock(git_cl.presubmit_support, 'DoPresubmitChecks', PresubmitMock)
self.mock(git_cl.watchlists, 'Watchlists', WatchlistsMock)
- self.mock(git_cl.auth, 'get_authenticator_for_host', AuthenticatorMock)
+ self.mock(git_cl.auth, 'get_authenticator', AuthenticatorMock)
self.mock(git_cl.gerrit_util, 'GetChangeDetail',
lambda *args, **kwargs: self._mocked_call(
'GetChangeDetail', *args, **kwargs))
@@ -3021,7 +3021,8 @@
return_value='https://chromium-review.googlesource.com').start()
mock.patch('git_cl.Changelist.GetMostRecentPatchset',
return_value=7).start()
- mock.patch('git_cl.auth.get_authenticator_for_host', AuthenticatorMock())
+ mock.patch('git_cl.auth.get_authenticator',
+ return_value=AuthenticatorMock()).start()
mock.patch('git_cl.Changelist._GetChangeDetail',
return_value=self._CHANGE_DETAIL).start()
mock.patch('git_cl._call_buildbucket',
diff --git a/tests/presubmit_unittest.py b/tests/presubmit_unittest.py
index 8c476db..af7acd3 100755
--- a/tests/presubmit_unittest.py
+++ b/tests/presubmit_unittest.py
@@ -1642,8 +1642,8 @@
presubmit.OutputApi.PresubmitPromptWarning)
@mock.patch('git_cl.Changelist')
- @mock.patch('auth.get_authenticator_for_host')
- def testCannedCheckChangedLUCIConfigs(self, mockGAFH, mockChangelist):
+ @mock.patch('auth.get_authenticator')
+ def testCannedCheckChangedLUCIConfigs(self, mockGetAuth, mockChangelist):
affected_file1 = mock.MagicMock(presubmit.GitAffectedFile)
affected_file1.LocalPath.return_value = 'foo.cfg'
affected_file1.NewContents.return_value = ['test', 'foo']
@@ -1651,7 +1651,7 @@
affected_file2.LocalPath.return_value = 'bar.cfg'
affected_file2.NewContents.return_value = ['test', 'bar']
- mockGAFH().get_access_token().token = 123
+ mockGetAuth().get_access_token().token = 123
host = 'https://host.com'
branch = 'branch'