Преглед изворни кода

Revert^2 "autoninja.py: disallow external account from corp machine"

This reverts commit ed596dadf949912c91d2632ef422426d7db85ac6.

Reason for revert:
Ignore exception when getting account for application default
credentials.

Original change's description:
> Revert "autoninja.py: disallow external account from corp machine"
>
> This reverts commit 0d0f28a4fc7b8493532e731d873475ad927043d8.
>
> Reason for revert:
> http://b/309720176#comment7
>
> Original change's description:
> > autoninja.py: disallow external account from corp machine
> >
> > This disallows Googler to use non-google account with reclient and
> > siso from corp machine.
> >
> > Bug: b/309720176
> > Change-Id: I8077eff8bbf47f579462e1fefa3609a5d492a013
> > Reviewed-on: https://chromium-review.googlesource.com/c/chromium/tools/depot_tools/+/5035266
> > Reviewed-by: Philipp Wollermann <philwo@chromium.org>
> > Reviewed-by: Josip Sokcevic <sokcevic@chromium.org>
> > Commit-Queue: Takuto Ikuta <tikuta@chromium.org>
>
> Bug: b/309720176
> Change-Id: I62b4891b1e2e2554d088d162057982b95c8adcd7
> No-Presubmit: true
> No-Tree-Checks: true
> No-Try: true
> Reviewed-on: https://chromium-review.googlesource.com/c/chromium/tools/depot_tools/+/5065849
> Reviewed-by: Philipp Wollermann <philwo@chromium.org>
> Owners-Override: Takuto Ikuta <tikuta@chromium.org>
> Commit-Queue: Takuto Ikuta <tikuta@chromium.org>
> Auto-Submit: Takuto Ikuta <tikuta@chromium.org>
> Bot-Commit: Rubber Stamper <rubber-stamper@appspot.gserviceaccount.com>

Bug: b/309720176
Change-Id: Ib9aef1b7fa8e3c9bb20ac8d6b461c6bcff72dc83
No-Presubmit: true
No-Tree-Checks: true
No-Try: true
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/tools/depot_tools/+/5065052
Reviewed-by: Gavin Mak <gavinmak@google.com>
Commit-Queue: Takuto Ikuta <tikuta@chromium.org>
Takuto Ikuta пре 1 година
родитељ
комит
cccca95d8f
5 измењених фајлова са 143 додато и 4 уклоњено
  1. 30 0
      .vpython3
  2. 1 1
      autoninja
  3. 1 1
      autoninja.bat
  4. 85 1
      autoninja.py
  5. 26 1
      tests/autoninja_test.py

+ 30 - 0
.vpython3

@@ -65,3 +65,33 @@ wheel: <
   name: "infra/python/wheels/certifi-py2_py3"
   name: "infra/python/wheels/certifi-py2_py3"
   version: "version:2021.5.30"
   version: "version:2021.5.30"
 >
 >
+
+# Used by:
+#   autoninja.py
+wheel: <
+  name: "infra/python/wheels/google-auth-py3"
+  version: "version:2.16.2"
+>
+wheel: <
+  name: "infra/python/wheels/cachetools-py3"
+  version: "version:4.2.2"
+>
+wheel: <
+  name: "infra/python/wheels/pyasn1_modules-py2_py3"
+  version: "version:0.2.8"
+>
+wheel: <
+  name: "infra/python/wheels/rsa-py3"
+  version: "version:4.7.2"
+>
+wheel: <
+  name: "infra/python/wheels/pyasn1-py2_py3"
+  version: "version:0.4.8"
+>
+
+# Used by:
+#   tests/autoninja_test.py
+wheel: <
+  name: "infra/python/wheels/parameterized-py2_py3"
+  version: "version:0.8.1"
+>

+ 1 - 1
autoninja

@@ -20,7 +20,7 @@ fi
 
 
 # Execute whatever is printed by autoninja.py.
 # Execute whatever is printed by autoninja.py.
 # Also print it to reassure that the right settings are being used.
 # Also print it to reassure that the right settings are being used.
-python3 "$(dirname -- "$0")/autoninja.py" "$@"
+vpython3 "$(dirname -- "$0")/autoninja.py" "$@"
 retval=$?
 retval=$?
 
 
 if [ "$retval" == "0" ] && [ "$NINJA_SUMMARIZE_BUILD" == "1" ]; then
 if [ "$retval" == "0" ] && [ "$NINJA_SUMMARIZE_BUILD" == "1" ]; then

+ 1 - 1
autoninja.bat

@@ -30,7 +30,7 @@ if "%NINJA_SUMMARIZE_BUILD%" == "1" set "NINJA_STATUS=[%%r processes, %%f/%%t @
 :: should be consistent between autoninja.bat and the autoninja script used by
 :: should be consistent between autoninja.bat and the autoninja script used by
 :: git bash.
 :: git bash.
 
 
-@call %scriptdir%python-bin\python3.bat %scriptdir%autoninja.py "%%*"
+@call %scriptdir%\vpython3.bat %scriptdir%autoninja.py "%%*"
 @if errorlevel 1 goto buildfailure
 @if errorlevel 1 goto buildfailure
 
 
 :: Use call to invoke python script here, because we use python via python3.bat.
 :: Use call to invoke python script here, because we use python via python3.bat.

+ 85 - 1
autoninja.py

@@ -1,4 +1,4 @@
-#!/usr/bin/env python3
+#!/usr/bin/env vpython3
 # Copyright (c) 2017 The Chromium Authors. All rights reserved.
 # Copyright (c) 2017 The Chromium Authors. All rights reserved.
 # Use of this source code is governed by a BSD-style license that can be
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 # found in the LICENSE file.
@@ -14,13 +14,19 @@ does handle import statements, but it can't handle conditional setting of build
 settings.
 settings.
 """
 """
 
 
+import json
 import multiprocessing
 import multiprocessing
 import os
 import os
 import platform
 import platform
 import re
 import re
 import shlex
 import shlex
+import shutil
 import subprocess
 import subprocess
 import sys
 import sys
+import warnings
+
+import google.auth
+from google.auth.transport.requests import AuthorizedSession
 
 
 import autosiso
 import autosiso
 import ninja
 import ninja
@@ -43,6 +49,72 @@ _UNSAFE_FOR_CMD = set("^<>&|()%")
 _ALL_META_CHARS = _UNSAFE_FOR_CMD.union(set('"'))
 _ALL_META_CHARS = _UNSAFE_FOR_CMD.union(set('"'))
 
 
 
 
+def _adc_account():
+    """Returns account used to authenticate with GCP application default credentials."""
+
+    try:
+        # Suppress warnings from google.auth.default.
+        # https://github.com/googleapis/google-auth-library-python/issues/271
+        warnings.filterwarnings(
+            "ignore",
+            "Your application has authenticated using end user credentials from"
+            " Google Cloud SDK without a quota project.",
+        )
+        credentials, _ = google.auth.default(
+            scopes=["https://www.googleapis.com/auth/userinfo.email"])
+    except google.auth.exceptions.DefaultCredentialsError:
+        # Application Default Crendetials is not configured.
+        return None
+    finally:
+        warnings.resetwarnings()
+
+    with AuthorizedSession(credentials) as session:
+        try:
+            response = session.get(
+                "https://www.googleapis.com/oauth2/v1/userinfo")
+        except Exception:
+            # Ignore exception.
+            return None
+
+    return response.json().get("email")
+
+
+def _gcloud_auth_account():
+    """Returns active account authenticated with `gcloud auth login`."""
+    if shutil.which("gcloud") is None:
+        return None
+
+    accounts = json.loads(
+        subprocess.check_output("gcloud auth list --format=json",
+                                shell=True,
+                                text=True))
+    for account in accounts:
+        if account["status"] == "ACTIVE":
+            return account["account"]
+    return None
+
+
+def _is_google_corp_machine():
+    """This assumes that corp machine has gcert binary in known location."""
+    return shutil.which("gcert") is not None
+
+
+def _is_google_corp_machine_using_external_account():
+    if not _is_google_corp_machine():
+        return False
+
+    account = _adc_account()
+    if account and not account.endswith("@google.com"):
+        return True
+
+    account = _gcloud_auth_account()
+    if not account:
+        return False
+    # Handle service account and google account as internal account.
+    return not (account.endswith("@google.com")
+                or account.endswith("gserviceaccount.com"))
+
+
 def _quote_for_cmd(arg):
 def _quote_for_cmd(arg):
     # First, escape the arg so that CommandLineToArgvW will parse it properly.
     # First, escape the arg so that CommandLineToArgvW will parse it properly.
     if arg == "" or " " in arg or '"' in arg:
     if arg == "" or " " in arg or '"' in arg:
@@ -163,6 +235,18 @@ def main(args):
                 use_siso = True
                 use_siso = True
                 continue
                 continue
 
 
+        if use_remoteexec:
+            if _is_google_corp_machine_using_external_account():
+                print(
+                    "You can't use a non-@google.com account (%s and/or %s) on"
+                    " a corp machine.\n"
+                    "Please login via `gcloud auth login --update-adc` with"
+                    " your @google.com account instead.\n" %
+                    (_adc_account(), _gcloud_auth_account()),
+                    file=sys.stderr,
+                )
+                return 1
+
         siso_marker = os.path.join(output_dir, ".siso_deps")
         siso_marker = os.path.join(output_dir, ".siso_deps")
         if use_siso:
         if use_siso:
             # autosiso generates a .ninja_log file so the mere existence of a
             # autosiso generates a .ninja_log file so the mere existence of a

+ 26 - 1
tests/autoninja_test.py

@@ -1,4 +1,4 @@
-#!/usr/bin/env python3
+#!/usr/bin/env vpython3
 # Copyright (c) 2022 The Chromium Authors. All rights reserved.
 # Copyright (c) 2022 The Chromium Authors. All rights reserved.
 # Use of this source code is governed by a BSD-style license that can be
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 # found in the LICENSE file.
@@ -12,6 +12,8 @@ import unittest
 import contextlib
 import contextlib
 from unittest import mock
 from unittest import mock
 
 
+from parameterized import parameterized
+
 ROOT_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
 ROOT_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
 sys.path.insert(0, ROOT_DIR)
 sys.path.insert(0, ROOT_DIR)
 
 
@@ -152,6 +154,29 @@ class AutoninjaTest(trial_dir.TestCase):
         self.assertIn('-C', args)
         self.assertIn('-C', args)
         self.assertEqual(args[args.index('-C') + 1], out_dir)
         self.assertEqual(args[args.index('-C') + 1], out_dir)
 
 
+    @parameterized.expand([
+        ("non corp machine", False, None, None, False),
+        ("non corp adc account", True, "foo@chromium.org", None, True),
+        ("corp adc account", True, "foo@google.com", None, False),
+        ("non corp gcloud auth account", True, None, "foo@chromium.org", True),
+        ("corp gcloud auth account", True, None, "foo@google.com", False),
+    ])
+    def test_is_corp_machine_using_external_account(self, _, is_corp,
+                                                    adc_account,
+                                                    gcloud_auth_account,
+                                                    expected):
+        with mock.patch('autoninja._is_google_corp_machine',
+                        return_value=is_corp), mock.patch(
+                            'autoninja._adc_account',
+                            return_value=adc_account), mock.patch(
+                                'autoninja._gcloud_auth_account',
+                                return_value=gcloud_auth_account):
+            self.assertEqual(
+                bool(
+                    # pylint: disable=line-too-long
+                    autoninja._is_google_corp_machine_using_external_account()),
+                expected)
+
     def test_gn_lines(self):
     def test_gn_lines(self):
         out_dir = os.path.join('out', 'dir')
         out_dir = os.path.join('out', 'dir')
         # Make sure nested import directives work. This is based on the
         # Make sure nested import directives work. This is based on the