test: Create BYOID Integration tests (#719)


diff --git a/system_tests/noxfile.py b/system_tests/noxfile.py
index 6e2e1b8..f177dcd 100644
--- a/system_tests/noxfile.py
+++ b/system_tests/noxfile.py
@@ -354,9 +354,14 @@
     )
 
 
-# ASYNC SYSTEM TESTS
+@nox.session(python=PYTHON_VERSIONS_SYNC)
+def external_accounts(session):
+    session.install(*TEST_DEPENDENCIES_SYNC, "google-auth", "google-api-python-client", "enum34")
+    default(session, "system_tests_sync/test_external_accounts.py")
 
 
+# ASYNC SYSTEM TESTS
+
 @nox.session(python=PYTHON_VERSIONS_ASYNC)
 def service_account_async(session):
     session.install(*(TEST_DEPENDENCIES_SYNC + TEST_DEPENDENCIES_ASYNC))
@@ -374,7 +379,7 @@
     session.install(LIBRARY_DIR)
     default(
         session,
-        "system_tests_async/test_default.py", 
+        "system_tests_async/test_default.py",
         "system_tests_async/test_id_token.py",
     )
 
diff --git a/system_tests/system_tests_sync/test_external_accounts.py b/system_tests/system_tests_sync/test_external_accounts.py
new file mode 100644
index 0000000..0e8ad16
--- /dev/null
+++ b/system_tests/system_tests_sync/test_external_accounts.py
@@ -0,0 +1,138 @@
+# Copyright 2021 Google LLC
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Prerequisites:
+# Make sure to run the setup in scripts/setup_external_accounts.sh
+# and copy the logged constant strings (_AUDIENCE_OIDC, _AUDIENCE_AWS)
+# into this file before running this test suite.
+# Once that is done, this test can be run indefinitely.
+#
+# The only requirement for this test suite to run is to set the environment
+# variable GOOGLE_APPLICATION_CREDENTIALS to point to the expected service
+# account keys whose email is referred to in the setup script.
+#
+# This script follows the following logic.
+# OIDC provider (file-sourced and url-sourced credentials):
+# Use the service account keys to generate a Google ID token using the
+# iamcredentials generateIdToken API, using the default STS audience.
+# This will use the service account client ID as the sub field of the token.
+# This OIDC token will be used as the external subject token to be exchanged
+# for a Google access token via GCP STS endpoint and then to impersonate the
+# original service account key.
+
+
+import json
+import os
+from tempfile import NamedTemporaryFile
+
+import sys
+import google.auth
+from googleapiclient import discovery
+from google.oauth2 import service_account
+import pytest
+from mock import patch
+
+# Populate values from the output of scripts/setup_external_accounts.sh.
+_AUDIENCE_OIDC = "//iam.googleapis.com/projects/79992041559/locations/global/workloadIdentityPools/pool-73wslmxn/providers/oidc-73wslmxn"
+
+
+def dns_access_direct(request, project_id):
+    # First, get the default credentials.
+    credentials, _ = google.auth.default(
+        scopes=["https://www.googleapis.com/auth/cloud-platform.read-only"],
+        request=request,
+    )
+
+    # Apply the default credentials to the headers to make the request.
+    headers = {}
+    credentials.apply(headers)
+    response = request(
+        url="https://dns.googleapis.com/dns/v1/projects/{}".format(project_id),
+        headers=headers,
+    )
+
+    if response.status == 200:
+        return response.data
+
+
+def dns_access_client_library(_, project_id):
+    service = discovery.build("dns", "v1")
+    request = service.projects().get(project=project_id)
+    return request.execute()
+
+
+@pytest.fixture(params=[dns_access_direct, dns_access_client_library])
+def dns_access(request, http_request, service_account_info):
+    # Fill in the fixtures on the functions,
+    # so that we don't have to fill in the parameters manually.
+    def wrapper():
+        return request.param(http_request, service_account_info["project_id"])
+
+    yield wrapper
+
+
+@pytest.fixture
+def oidc_credentials(service_account_file, http_request):
+    result = service_account.IDTokenCredentials.from_service_account_file(
+        service_account_file, target_audience=_AUDIENCE_OIDC
+    )
+    result.refresh(http_request)
+    yield result
+
+
+@pytest.fixture
+def service_account_info(service_account_file):
+    with open(service_account_file) as f:
+        yield json.load(f)
+
+
+# Our external accounts tests involve setting up some preconditions, setting a
+# credential file, and then making sure that our client libraries can work with
+# the set credentials.
+def get_project_dns(dns_access, credential_data):
+    with NamedTemporaryFile() as credfile:
+        credfile.write(json.dumps(credential_data).encode("utf-8"))
+        credfile.flush()
+        old_credentials = os.environ.get("GOOGLE_APPLICATION_CREDENTIALS")
+
+        with patch.dict(os.environ, {"GOOGLE_APPLICATION_CREDENTIALS": credfile.name}):
+            # If our setup and credential file are correct,
+            # discovery.build should be able to establish these as the default credentials.
+            return dns_access()
+
+
+# This test makes sure that setting an accesible credential file
+# works to allow access to Google resources.
+def test_file_based_external_account(
+    oidc_credentials, service_account_info, dns_access
+):
+    with NamedTemporaryFile() as tmpfile:
+        tmpfile.write(oidc_credentials.token.encode("utf-8"))
+        tmpfile.flush()
+
+        assert get_project_dns(
+            dns_access,
+            {
+                "type": "external_account",
+                "audience": _AUDIENCE_OIDC,
+                "subject_token_type": "urn:ietf:params:oauth:token-type:jwt",
+                "token_url": "https://sts.googleapis.com/v1/token",
+                "service_account_impersonation_url": "https://iamcredentials.googleapis.com/v1/projects/-/serviceAccounts/{}:generateAccessToken".format(
+                    oidc_credentials.service_account_email
+                ),
+                "credential_source": {
+                    "file": tmpfile.name,
+                },
+            },
+        )