google-auth 2.44.0__tar.gz → 2.45.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {google_auth-2.44.0/google_auth.egg-info → google_auth-2.45.0}/PKG-INFO +4 -1
- google_auth-2.45.0/google/auth/_agent_identity_utils.py +281 -0
- {google_auth-2.44.0 → google_auth-2.45.0}/google/auth/_oauth2client.py +1 -1
- {google_auth-2.44.0 → google_auth-2.45.0}/google/auth/compute_engine/_metadata.py +10 -3
- {google_auth-2.44.0 → google_auth-2.45.0}/google/auth/compute_engine/credentials.py +1 -1
- {google_auth-2.44.0 → google_auth-2.45.0}/google/auth/environment_vars.py +9 -0
- {google_auth-2.44.0 → google_auth-2.45.0}/google/auth/external_account.py +11 -4
- {google_auth-2.44.0 → google_auth-2.45.0}/google/auth/identity_pool.py +22 -0
- {google_auth-2.44.0 → google_auth-2.45.0}/google/auth/transport/_mtls_helper.py +29 -2
- {google_auth-2.44.0 → google_auth-2.45.0}/google/auth/transport/requests.py +32 -1
- {google_auth-2.44.0 → google_auth-2.45.0}/google/auth/transport/urllib3.py +40 -0
- {google_auth-2.44.0 → google_auth-2.45.0}/google/auth/version.py +1 -1
- {google_auth-2.44.0 → google_auth-2.45.0/google_auth.egg-info}/PKG-INFO +4 -1
- {google_auth-2.44.0 → google_auth-2.45.0}/google_auth.egg-info/SOURCES.txt +2 -0
- {google_auth-2.44.0 → google_auth-2.45.0}/google_auth.egg-info/requires.txt +6 -0
- {google_auth-2.44.0 → google_auth-2.45.0}/setup.py +1 -0
- {google_auth-2.44.0 → google_auth-2.45.0}/tests/compute_engine/test__metadata.py +100 -0
- {google_auth-2.44.0 → google_auth-2.45.0}/tests/compute_engine/test_credentials.py +72 -0
- google_auth-2.45.0/tests/test_agent_identity_utils.py +337 -0
- {google_auth-2.44.0 → google_auth-2.45.0}/tests/test_external_account.py +18 -0
- {google_auth-2.44.0 → google_auth-2.45.0}/tests/test_identity_pool.py +56 -0
- {google_auth-2.44.0 → google_auth-2.45.0}/tests/transport/test__mtls_helper.py +63 -1
- {google_auth-2.44.0 → google_auth-2.45.0}/tests/transport/test_requests.py +224 -0
- {google_auth-2.44.0 → google_auth-2.45.0}/tests/transport/test_urllib3.py +208 -0
- {google_auth-2.44.0 → google_auth-2.45.0}/LICENSE +0 -0
- {google_auth-2.44.0 → google_auth-2.45.0}/MANIFEST.in +0 -0
- {google_auth-2.44.0 → google_auth-2.45.0}/README.rst +0 -0
- {google_auth-2.44.0 → google_auth-2.45.0}/google/auth/__init__.py +0 -0
- {google_auth-2.44.0 → google_auth-2.45.0}/google/auth/_cloud_sdk.py +0 -0
- {google_auth-2.44.0 → google_auth-2.45.0}/google/auth/_constants.py +0 -0
- {google_auth-2.44.0 → google_auth-2.45.0}/google/auth/_credentials_async.py +0 -0
- {google_auth-2.44.0 → google_auth-2.45.0}/google/auth/_credentials_base.py +0 -0
- {google_auth-2.44.0 → google_auth-2.45.0}/google/auth/_default.py +0 -0
- {google_auth-2.44.0 → google_auth-2.45.0}/google/auth/_default_async.py +0 -0
- {google_auth-2.44.0 → google_auth-2.45.0}/google/auth/_exponential_backoff.py +0 -0
- {google_auth-2.44.0 → google_auth-2.45.0}/google/auth/_helpers.py +0 -0
- {google_auth-2.44.0 → google_auth-2.45.0}/google/auth/_jwt_async.py +0 -0
- {google_auth-2.44.0 → google_auth-2.45.0}/google/auth/_refresh_worker.py +0 -0
- {google_auth-2.44.0 → google_auth-2.45.0}/google/auth/_service_account_info.py +0 -0
- {google_auth-2.44.0 → google_auth-2.45.0}/google/auth/aio/__init__.py +0 -0
- {google_auth-2.44.0 → google_auth-2.45.0}/google/auth/aio/_helpers.py +0 -0
- {google_auth-2.44.0 → google_auth-2.45.0}/google/auth/aio/credentials.py +0 -0
- {google_auth-2.44.0 → google_auth-2.45.0}/google/auth/aio/transport/__init__.py +0 -0
- {google_auth-2.44.0 → google_auth-2.45.0}/google/auth/aio/transport/aiohttp.py +0 -0
- {google_auth-2.44.0 → google_auth-2.45.0}/google/auth/aio/transport/sessions.py +0 -0
- {google_auth-2.44.0 → google_auth-2.45.0}/google/auth/api_key.py +0 -0
- {google_auth-2.44.0 → google_auth-2.45.0}/google/auth/app_engine.py +0 -0
- {google_auth-2.44.0 → google_auth-2.45.0}/google/auth/aws.py +0 -0
- {google_auth-2.44.0 → google_auth-2.45.0}/google/auth/compute_engine/__init__.py +0 -0
- {google_auth-2.44.0 → google_auth-2.45.0}/google/auth/compute_engine/_mtls.py +0 -0
- {google_auth-2.44.0 → google_auth-2.45.0}/google/auth/credentials.py +0 -0
- {google_auth-2.44.0 → google_auth-2.45.0}/google/auth/crypt/__init__.py +0 -0
- {google_auth-2.44.0 → google_auth-2.45.0}/google/auth/crypt/_cryptography_rsa.py +0 -0
- {google_auth-2.44.0 → google_auth-2.45.0}/google/auth/crypt/_helpers.py +0 -0
- {google_auth-2.44.0 → google_auth-2.45.0}/google/auth/crypt/_python_rsa.py +0 -0
- {google_auth-2.44.0 → google_auth-2.45.0}/google/auth/crypt/base.py +0 -0
- {google_auth-2.44.0 → google_auth-2.45.0}/google/auth/crypt/es.py +0 -0
- {google_auth-2.44.0 → google_auth-2.45.0}/google/auth/crypt/es256.py +0 -0
- {google_auth-2.44.0 → google_auth-2.45.0}/google/auth/crypt/rsa.py +0 -0
- {google_auth-2.44.0 → google_auth-2.45.0}/google/auth/downscoped.py +0 -0
- {google_auth-2.44.0 → google_auth-2.45.0}/google/auth/exceptions.py +0 -0
- {google_auth-2.44.0 → google_auth-2.45.0}/google/auth/external_account_authorized_user.py +0 -0
- {google_auth-2.44.0 → google_auth-2.45.0}/google/auth/iam.py +0 -0
- {google_auth-2.44.0 → google_auth-2.45.0}/google/auth/impersonated_credentials.py +0 -0
- {google_auth-2.44.0 → google_auth-2.45.0}/google/auth/jwt.py +0 -0
- {google_auth-2.44.0 → google_auth-2.45.0}/google/auth/metrics.py +0 -0
- {google_auth-2.44.0 → google_auth-2.45.0}/google/auth/pluggable.py +0 -0
- {google_auth-2.44.0 → google_auth-2.45.0}/google/auth/py.typed +0 -0
- {google_auth-2.44.0 → google_auth-2.45.0}/google/auth/transport/__init__.py +0 -0
- {google_auth-2.44.0 → google_auth-2.45.0}/google/auth/transport/_aiohttp_requests.py +0 -0
- {google_auth-2.44.0 → google_auth-2.45.0}/google/auth/transport/_custom_tls_signer.py +0 -0
- {google_auth-2.44.0 → google_auth-2.45.0}/google/auth/transport/_http_client.py +0 -0
- {google_auth-2.44.0 → google_auth-2.45.0}/google/auth/transport/_requests_base.py +0 -0
- {google_auth-2.44.0 → google_auth-2.45.0}/google/auth/transport/grpc.py +0 -0
- {google_auth-2.44.0 → google_auth-2.45.0}/google/auth/transport/mtls.py +0 -0
- {google_auth-2.44.0 → google_auth-2.45.0}/google/oauth2/__init__.py +0 -0
- {google_auth-2.44.0 → google_auth-2.45.0}/google/oauth2/_client.py +0 -0
- {google_auth-2.44.0 → google_auth-2.45.0}/google/oauth2/_client_async.py +0 -0
- {google_auth-2.44.0 → google_auth-2.45.0}/google/oauth2/_credentials_async.py +0 -0
- {google_auth-2.44.0 → google_auth-2.45.0}/google/oauth2/_id_token_async.py +0 -0
- {google_auth-2.44.0 → google_auth-2.45.0}/google/oauth2/_reauth_async.py +0 -0
- {google_auth-2.44.0 → google_auth-2.45.0}/google/oauth2/_service_account_async.py +0 -0
- {google_auth-2.44.0 → google_auth-2.45.0}/google/oauth2/challenges.py +0 -0
- {google_auth-2.44.0 → google_auth-2.45.0}/google/oauth2/credentials.py +0 -0
- {google_auth-2.44.0 → google_auth-2.45.0}/google/oauth2/gdch_credentials.py +0 -0
- {google_auth-2.44.0 → google_auth-2.45.0}/google/oauth2/id_token.py +0 -0
- {google_auth-2.44.0 → google_auth-2.45.0}/google/oauth2/py.typed +0 -0
- {google_auth-2.44.0 → google_auth-2.45.0}/google/oauth2/reauth.py +0 -0
- {google_auth-2.44.0 → google_auth-2.45.0}/google/oauth2/service_account.py +0 -0
- {google_auth-2.44.0 → google_auth-2.45.0}/google/oauth2/sts.py +0 -0
- {google_auth-2.44.0 → google_auth-2.45.0}/google/oauth2/utils.py +0 -0
- {google_auth-2.44.0 → google_auth-2.45.0}/google/oauth2/webauthn_handler.py +0 -0
- {google_auth-2.44.0 → google_auth-2.45.0}/google/oauth2/webauthn_handler_factory.py +0 -0
- {google_auth-2.44.0 → google_auth-2.45.0}/google/oauth2/webauthn_types.py +0 -0
- {google_auth-2.44.0 → google_auth-2.45.0}/google_auth.egg-info/dependency_links.txt +0 -0
- {google_auth-2.44.0 → google_auth-2.45.0}/google_auth.egg-info/top_level.txt +0 -0
- {google_auth-2.44.0 → google_auth-2.45.0}/setup.cfg +0 -0
- {google_auth-2.44.0 → google_auth-2.45.0}/tests/__init__.py +0 -0
- {google_auth-2.44.0 → google_auth-2.45.0}/tests/aio/test__helpers.py +0 -0
- {google_auth-2.44.0 → google_auth-2.45.0}/tests/compute_engine/__init__.py +0 -0
- {google_auth-2.44.0 → google_auth-2.45.0}/tests/compute_engine/data/smbios_product_name +0 -0
- {google_auth-2.44.0 → google_auth-2.45.0}/tests/compute_engine/data/smbios_product_name_non_google +0 -0
- {google_auth-2.44.0 → google_auth-2.45.0}/tests/compute_engine/test__mtls.py +0 -0
- {google_auth-2.44.0 → google_auth-2.45.0}/tests/conftest.py +0 -0
- {google_auth-2.44.0 → google_auth-2.45.0}/tests/crypt/__init__.py +0 -0
- {google_auth-2.44.0 → google_auth-2.45.0}/tests/crypt/test__cryptography_rsa.py +0 -0
- {google_auth-2.44.0 → google_auth-2.45.0}/tests/crypt/test__python_rsa.py +0 -0
- {google_auth-2.44.0 → google_auth-2.45.0}/tests/crypt/test_crypt.py +0 -0
- {google_auth-2.44.0 → google_auth-2.45.0}/tests/crypt/test_es.py +0 -0
- {google_auth-2.44.0 → google_auth-2.45.0}/tests/crypt/test_es256.py +0 -0
- {google_auth-2.44.0 → google_auth-2.45.0}/tests/data/authorized_user.json +0 -0
- {google_auth-2.44.0 → google_auth-2.45.0}/tests/data/authorized_user_cloud_sdk.json +0 -0
- {google_auth-2.44.0 → google_auth-2.45.0}/tests/data/authorized_user_cloud_sdk_with_quota_project_id.json +0 -0
- {google_auth-2.44.0 → google_auth-2.45.0}/tests/data/authorized_user_with_rapt_token.json +0 -0
- {google_auth-2.44.0 → google_auth-2.45.0}/tests/data/client_secrets.json +0 -0
- {google_auth-2.44.0 → google_auth-2.45.0}/tests/data/context_aware_metadata.json +0 -0
- {google_auth-2.44.0 → google_auth-2.45.0}/tests/data/enterprise_cert_invalid.json +0 -0
- {google_auth-2.44.0 → google_auth-2.45.0}/tests/data/enterprise_cert_valid.json +0 -0
- {google_auth-2.44.0 → google_auth-2.45.0}/tests/data/enterprise_cert_valid_provider.json +0 -0
- {google_auth-2.44.0 → google_auth-2.45.0}/tests/data/es256_privatekey.pem +0 -0
- {google_auth-2.44.0 → google_auth-2.45.0}/tests/data/es256_public_cert.pem +0 -0
- {google_auth-2.44.0 → google_auth-2.45.0}/tests/data/es256_publickey.pem +0 -0
- {google_auth-2.44.0 → google_auth-2.45.0}/tests/data/es256_service_account.json +0 -0
- {google_auth-2.44.0 → google_auth-2.45.0}/tests/data/es384_privatekey.pem +0 -0
- {google_auth-2.44.0 → google_auth-2.45.0}/tests/data/es384_public_cert.pem +0 -0
- {google_auth-2.44.0 → google_auth-2.45.0}/tests/data/es384_publickey.pem +0 -0
- {google_auth-2.44.0 → google_auth-2.45.0}/tests/data/es384_service_account.json +0 -0
- {google_auth-2.44.0 → google_auth-2.45.0}/tests/data/external_account_authorized_user.json +0 -0
- {google_auth-2.44.0 → google_auth-2.45.0}/tests/data/external_account_authorized_user_non_gdu.json +0 -0
- {google_auth-2.44.0 → google_auth-2.45.0}/tests/data/external_subject_token.json +0 -0
- {google_auth-2.44.0 → google_auth-2.45.0}/tests/data/external_subject_token.txt +0 -0
- {google_auth-2.44.0 → google_auth-2.45.0}/tests/data/gdch_service_account.json +0 -0
- {google_auth-2.44.0 → google_auth-2.45.0}/tests/data/impersonated_service_account_authorized_user_source.json +0 -0
- {google_auth-2.44.0 → google_auth-2.45.0}/tests/data/impersonated_service_account_external_account_authorized_user_source.json +0 -0
- {google_auth-2.44.0 → google_auth-2.45.0}/tests/data/impersonated_service_account_service_account_source.json +0 -0
- {google_auth-2.44.0 → google_auth-2.45.0}/tests/data/impersonated_service_account_with_quota_project.json +0 -0
- {google_auth-2.44.0 → google_auth-2.45.0}/tests/data/old_oauth_credentials_py3.pickle +0 -0
- {google_auth-2.44.0 → google_auth-2.45.0}/tests/data/other_cert.pem +0 -0
- {google_auth-2.44.0 → google_auth-2.45.0}/tests/data/pem_from_pkcs12.pem +0 -0
- {google_auth-2.44.0 → google_auth-2.45.0}/tests/data/privatekey.p12 +0 -0
- {google_auth-2.44.0 → google_auth-2.45.0}/tests/data/privatekey.pem +0 -0
- {google_auth-2.44.0 → google_auth-2.45.0}/tests/data/privatekey.pub +0 -0
- {google_auth-2.44.0 → google_auth-2.45.0}/tests/data/public_cert.pem +0 -0
- {google_auth-2.44.0 → google_auth-2.45.0}/tests/data/service_account.json +0 -0
- {google_auth-2.44.0 → google_auth-2.45.0}/tests/data/service_account_non_gdu.json +0 -0
- {google_auth-2.44.0 → google_auth-2.45.0}/tests/data/trust_chain_with_leaf.pem +0 -0
- {google_auth-2.44.0 → google_auth-2.45.0}/tests/data/trust_chain_without_leaf.pem +0 -0
- {google_auth-2.44.0 → google_auth-2.45.0}/tests/data/trust_chain_wrong_order.pem +0 -0
- {google_auth-2.44.0 → google_auth-2.45.0}/tests/oauth2/__init__.py +0 -0
- {google_auth-2.44.0 → google_auth-2.45.0}/tests/oauth2/test__client.py +0 -0
- {google_auth-2.44.0 → google_auth-2.45.0}/tests/oauth2/test_challenges.py +0 -0
- {google_auth-2.44.0 → google_auth-2.45.0}/tests/oauth2/test_credentials.py +0 -0
- {google_auth-2.44.0 → google_auth-2.45.0}/tests/oauth2/test_gdch_credentials.py +0 -0
- {google_auth-2.44.0 → google_auth-2.45.0}/tests/oauth2/test_id_token.py +0 -0
- {google_auth-2.44.0 → google_auth-2.45.0}/tests/oauth2/test_reauth.py +0 -0
- {google_auth-2.44.0 → google_auth-2.45.0}/tests/oauth2/test_service_account.py +0 -0
- {google_auth-2.44.0 → google_auth-2.45.0}/tests/oauth2/test_sts.py +0 -0
- {google_auth-2.44.0 → google_auth-2.45.0}/tests/oauth2/test_utils.py +0 -0
- {google_auth-2.44.0 → google_auth-2.45.0}/tests/oauth2/test_webauthn_handler.py +0 -0
- {google_auth-2.44.0 → google_auth-2.45.0}/tests/oauth2/test_webauthn_handler_factory.py +0 -0
- {google_auth-2.44.0 → google_auth-2.45.0}/tests/oauth2/test_webauthn_types.py +0 -0
- {google_auth-2.44.0 → google_auth-2.45.0}/tests/test__cloud_sdk.py +0 -0
- {google_auth-2.44.0 → google_auth-2.45.0}/tests/test__default.py +0 -0
- {google_auth-2.44.0 → google_auth-2.45.0}/tests/test__exponential_backoff.py +0 -0
- {google_auth-2.44.0 → google_auth-2.45.0}/tests/test__helpers.py +0 -0
- {google_auth-2.44.0 → google_auth-2.45.0}/tests/test__oauth2client.py +0 -0
- {google_auth-2.44.0 → google_auth-2.45.0}/tests/test__refresh_worker.py +0 -0
- {google_auth-2.44.0 → google_auth-2.45.0}/tests/test__service_account_info.py +0 -0
- {google_auth-2.44.0 → google_auth-2.45.0}/tests/test_api_key.py +0 -0
- {google_auth-2.44.0 → google_auth-2.45.0}/tests/test_app_engine.py +0 -0
- {google_auth-2.44.0 → google_auth-2.45.0}/tests/test_aws.py +0 -0
- {google_auth-2.44.0 → google_auth-2.45.0}/tests/test_credentials.py +0 -0
- {google_auth-2.44.0 → google_auth-2.45.0}/tests/test_credentials_async.py +0 -0
- {google_auth-2.44.0 → google_auth-2.45.0}/tests/test_downscoped.py +0 -0
- {google_auth-2.44.0 → google_auth-2.45.0}/tests/test_exceptions.py +0 -0
- {google_auth-2.44.0 → google_auth-2.45.0}/tests/test_external_account_authorized_user.py +0 -0
- {google_auth-2.44.0 → google_auth-2.45.0}/tests/test_iam.py +0 -0
- {google_auth-2.44.0 → google_auth-2.45.0}/tests/test_impersonated_credentials.py +0 -0
- {google_auth-2.44.0 → google_auth-2.45.0}/tests/test_jwt.py +0 -0
- {google_auth-2.44.0 → google_auth-2.45.0}/tests/test_metrics.py +0 -0
- {google_auth-2.44.0 → google_auth-2.45.0}/tests/test_packaging.py +0 -0
- {google_auth-2.44.0 → google_auth-2.45.0}/tests/test_pluggable.py +0 -0
- {google_auth-2.44.0 → google_auth-2.45.0}/tests/transport/__init__.py +0 -0
- {google_auth-2.44.0 → google_auth-2.45.0}/tests/transport/aio/test_aiohttp.py +0 -0
- {google_auth-2.44.0 → google_auth-2.45.0}/tests/transport/aio/test_sessions.py +0 -0
- {google_auth-2.44.0 → google_auth-2.45.0}/tests/transport/compliance.py +0 -0
- {google_auth-2.44.0 → google_auth-2.45.0}/tests/transport/test__custom_tls_signer.py +0 -0
- {google_auth-2.44.0 → google_auth-2.45.0}/tests/transport/test__http_client.py +0 -0
- {google_auth-2.44.0 → google_auth-2.45.0}/tests/transport/test_grpc.py +0 -0
- {google_auth-2.44.0 → google_auth-2.45.0}/tests/transport/test_mtls.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: google-auth
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.45.0
|
|
4
4
|
Summary: Google Authentication Library
|
|
5
5
|
Home-page: https://github.com/googleapis/google-auth-library-python
|
|
6
6
|
Author: Google Cloud Platform
|
|
@@ -29,6 +29,9 @@ License-File: LICENSE
|
|
|
29
29
|
Requires-Dist: cachetools<7.0,>=2.0.0
|
|
30
30
|
Requires-Dist: pyasn1-modules>=0.2.1
|
|
31
31
|
Requires-Dist: rsa<5,>=3.1.4
|
|
32
|
+
Provides-Extra: cryptography
|
|
33
|
+
Requires-Dist: cryptography>=38.0.3; extra == "cryptography"
|
|
34
|
+
Requires-Dist: cryptography<39.0.0; python_version < "3.8" and extra == "cryptography"
|
|
32
35
|
Provides-Extra: aiohttp
|
|
33
36
|
Requires-Dist: aiohttp<4.0.0,>=3.6.2; extra == "aiohttp"
|
|
34
37
|
Requires-Dist: requests<3.0.0,>=2.20.0; extra == "aiohttp"
|
|
@@ -0,0 +1,281 @@
|
|
|
1
|
+
# Copyright 2025 Google LLC
|
|
2
|
+
#
|
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
# you may not use this file except in compliance with the License.
|
|
5
|
+
# You may obtain a copy of the License at
|
|
6
|
+
#
|
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
#
|
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
+
# See the License for the specific language governing permissions and
|
|
13
|
+
# limitations under the License.
|
|
14
|
+
|
|
15
|
+
"""Helpers for Agent Identity credentials."""
|
|
16
|
+
|
|
17
|
+
import base64
|
|
18
|
+
import hashlib
|
|
19
|
+
import logging
|
|
20
|
+
import os
|
|
21
|
+
import re
|
|
22
|
+
import time
|
|
23
|
+
from urllib.parse import quote, urlparse
|
|
24
|
+
|
|
25
|
+
from google.auth import environment_vars
|
|
26
|
+
from google.auth import exceptions
|
|
27
|
+
from google.auth.transport import _mtls_helper
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
_LOGGER = logging.getLogger(__name__)
|
|
31
|
+
|
|
32
|
+
CRYPTOGRAPHY_NOT_FOUND_ERROR = (
|
|
33
|
+
"The cryptography library is required for certificate-based authentication."
|
|
34
|
+
"Please install it with `pip install google-auth[cryptography]`."
|
|
35
|
+
)
|
|
36
|
+
|
|
37
|
+
# SPIFFE trust domain patterns for Agent Identities.
|
|
38
|
+
_AGENT_IDENTITY_SPIFFE_TRUST_DOMAIN_PATTERNS = [
|
|
39
|
+
r"^agents\.global\.org-\d+\.system\.id\.goog$",
|
|
40
|
+
r"^agents\.global\.proj-\d+\.system\.id\.goog$",
|
|
41
|
+
]
|
|
42
|
+
|
|
43
|
+
_WELL_KNOWN_CERT_PATH = "/var/run/secrets/workload-spiffe-credentials/certificates.pem"
|
|
44
|
+
|
|
45
|
+
# Constants for polling the certificate file.
|
|
46
|
+
_FAST_POLL_CYCLES = 50
|
|
47
|
+
_FAST_POLL_INTERVAL = 0.1 # 100ms
|
|
48
|
+
_SLOW_POLL_INTERVAL = 0.5 # 500ms
|
|
49
|
+
_TOTAL_TIMEOUT = 30 # seconds
|
|
50
|
+
|
|
51
|
+
# Calculate the number of slow poll cycles based on the total timeout.
|
|
52
|
+
_SLOW_POLL_CYCLES = int(
|
|
53
|
+
(_TOTAL_TIMEOUT - (_FAST_POLL_CYCLES * _FAST_POLL_INTERVAL)) / _SLOW_POLL_INTERVAL
|
|
54
|
+
)
|
|
55
|
+
|
|
56
|
+
_POLLING_INTERVALS = ([_FAST_POLL_INTERVAL] * _FAST_POLL_CYCLES) + (
|
|
57
|
+
[_SLOW_POLL_INTERVAL] * _SLOW_POLL_CYCLES
|
|
58
|
+
)
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def _is_certificate_file_ready(path):
|
|
62
|
+
"""Checks if a file exists and is not empty."""
|
|
63
|
+
return path and os.path.exists(path) and os.path.getsize(path) > 0
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
def get_agent_identity_certificate_path():
|
|
67
|
+
"""Gets the certificate path from the certificate config file.
|
|
68
|
+
|
|
69
|
+
The path to the certificate config file is read from the
|
|
70
|
+
GOOGLE_API_CERTIFICATE_CONFIG environment variable. This function
|
|
71
|
+
implements a retry mechanism to handle cases where the environment
|
|
72
|
+
variable is set before the files are available on the filesystem.
|
|
73
|
+
|
|
74
|
+
Returns:
|
|
75
|
+
str: The path to the leaf certificate file.
|
|
76
|
+
|
|
77
|
+
Raises:
|
|
78
|
+
google.auth.exceptions.RefreshError: If the certificate config file
|
|
79
|
+
or the certificate file cannot be found after retries.
|
|
80
|
+
"""
|
|
81
|
+
import json
|
|
82
|
+
|
|
83
|
+
cert_config_path = os.environ.get(environment_vars.GOOGLE_API_CERTIFICATE_CONFIG)
|
|
84
|
+
if not cert_config_path:
|
|
85
|
+
return None
|
|
86
|
+
|
|
87
|
+
has_logged_warning = False
|
|
88
|
+
|
|
89
|
+
for interval in _POLLING_INTERVALS:
|
|
90
|
+
try:
|
|
91
|
+
with open(cert_config_path, "r") as f:
|
|
92
|
+
cert_config = json.load(f)
|
|
93
|
+
cert_path = (
|
|
94
|
+
cert_config.get("cert_configs", {})
|
|
95
|
+
.get("workload", {})
|
|
96
|
+
.get("cert_path")
|
|
97
|
+
)
|
|
98
|
+
if _is_certificate_file_ready(cert_path):
|
|
99
|
+
return cert_path
|
|
100
|
+
except (IOError, ValueError, KeyError):
|
|
101
|
+
if not has_logged_warning:
|
|
102
|
+
_LOGGER.warning(
|
|
103
|
+
"Certificate config file not found at %s (from %s environment "
|
|
104
|
+
"variable). Retrying for up to %s seconds.",
|
|
105
|
+
cert_config_path,
|
|
106
|
+
environment_vars.GOOGLE_API_CERTIFICATE_CONFIG,
|
|
107
|
+
_TOTAL_TIMEOUT,
|
|
108
|
+
)
|
|
109
|
+
has_logged_warning = True
|
|
110
|
+
pass
|
|
111
|
+
|
|
112
|
+
# As a fallback, check the well-known certificate path.
|
|
113
|
+
if _is_certificate_file_ready(_WELL_KNOWN_CERT_PATH):
|
|
114
|
+
return _WELL_KNOWN_CERT_PATH
|
|
115
|
+
|
|
116
|
+
# A sleep is required in two cases:
|
|
117
|
+
# 1. The config file is not found (the except block).
|
|
118
|
+
# 2. The config file is found, but the certificate is not yet available.
|
|
119
|
+
# In both cases, we need to poll, so we sleep on every iteration
|
|
120
|
+
# that doesn't return a certificate.
|
|
121
|
+
time.sleep(interval)
|
|
122
|
+
|
|
123
|
+
raise exceptions.RefreshError(
|
|
124
|
+
"Certificate config or certificate file not found after multiple retries. "
|
|
125
|
+
f"Token binding protection is failing. You can turn off this protection by setting "
|
|
126
|
+
f"{environment_vars.GOOGLE_API_PREVENT_AGENT_TOKEN_SHARING_FOR_GCP_SERVICES} to false "
|
|
127
|
+
"to fall back to unbound tokens."
|
|
128
|
+
)
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
def get_and_parse_agent_identity_certificate():
|
|
132
|
+
"""Gets and parses the agent identity certificate if not opted out.
|
|
133
|
+
|
|
134
|
+
Checks if the user has opted out of certificate-bound tokens. If not,
|
|
135
|
+
it gets the certificate path, reads the file, and parses it.
|
|
136
|
+
|
|
137
|
+
Returns:
|
|
138
|
+
The parsed certificate object if found and not opted out, otherwise None.
|
|
139
|
+
"""
|
|
140
|
+
# If the user has opted out of cert bound tokens, there is no need to
|
|
141
|
+
# look up the certificate.
|
|
142
|
+
is_opted_out = (
|
|
143
|
+
os.environ.get(
|
|
144
|
+
environment_vars.GOOGLE_API_PREVENT_AGENT_TOKEN_SHARING_FOR_GCP_SERVICES,
|
|
145
|
+
"true",
|
|
146
|
+
).lower()
|
|
147
|
+
== "false"
|
|
148
|
+
)
|
|
149
|
+
if is_opted_out:
|
|
150
|
+
return None
|
|
151
|
+
|
|
152
|
+
cert_path = get_agent_identity_certificate_path()
|
|
153
|
+
if not cert_path:
|
|
154
|
+
return None
|
|
155
|
+
|
|
156
|
+
with open(cert_path, "rb") as cert_file:
|
|
157
|
+
cert_bytes = cert_file.read()
|
|
158
|
+
|
|
159
|
+
return parse_certificate(cert_bytes)
|
|
160
|
+
|
|
161
|
+
|
|
162
|
+
def parse_certificate(cert_bytes):
|
|
163
|
+
"""Parses a PEM-encoded certificate.
|
|
164
|
+
|
|
165
|
+
Args:
|
|
166
|
+
cert_bytes (bytes): The PEM-encoded certificate bytes.
|
|
167
|
+
|
|
168
|
+
Returns:
|
|
169
|
+
cryptography.x509.Certificate: The parsed certificate object.
|
|
170
|
+
"""
|
|
171
|
+
try:
|
|
172
|
+
from cryptography import x509
|
|
173
|
+
|
|
174
|
+
return x509.load_pem_x509_certificate(cert_bytes)
|
|
175
|
+
except ImportError as e:
|
|
176
|
+
raise ImportError(CRYPTOGRAPHY_NOT_FOUND_ERROR) from e
|
|
177
|
+
|
|
178
|
+
|
|
179
|
+
def _is_agent_identity_certificate(cert):
|
|
180
|
+
"""Checks if a certificate is an Agent Identity certificate.
|
|
181
|
+
|
|
182
|
+
This is determined by checking the Subject Alternative Name (SAN) for a
|
|
183
|
+
SPIFFE ID with a trust domain matching Agent Identity patterns.
|
|
184
|
+
|
|
185
|
+
Args:
|
|
186
|
+
cert (cryptography.x509.Certificate): The parsed certificate object.
|
|
187
|
+
|
|
188
|
+
Returns:
|
|
189
|
+
bool: True if the certificate is an Agent Identity certificate,
|
|
190
|
+
False otherwise.
|
|
191
|
+
"""
|
|
192
|
+
try:
|
|
193
|
+
from cryptography import x509
|
|
194
|
+
from cryptography.x509.oid import ExtensionOID
|
|
195
|
+
|
|
196
|
+
try:
|
|
197
|
+
ext = cert.extensions.get_extension_for_oid(
|
|
198
|
+
ExtensionOID.SUBJECT_ALTERNATIVE_NAME
|
|
199
|
+
)
|
|
200
|
+
except x509.ExtensionNotFound:
|
|
201
|
+
return False
|
|
202
|
+
uris = ext.value.get_values_for_type(x509.UniformResourceIdentifier)
|
|
203
|
+
|
|
204
|
+
for uri in uris:
|
|
205
|
+
parsed_uri = urlparse(uri)
|
|
206
|
+
if parsed_uri.scheme == "spiffe":
|
|
207
|
+
trust_domain = parsed_uri.netloc
|
|
208
|
+
for pattern in _AGENT_IDENTITY_SPIFFE_TRUST_DOMAIN_PATTERNS:
|
|
209
|
+
if re.match(pattern, trust_domain):
|
|
210
|
+
return True
|
|
211
|
+
return False
|
|
212
|
+
except ImportError as e:
|
|
213
|
+
raise ImportError(CRYPTOGRAPHY_NOT_FOUND_ERROR) from e
|
|
214
|
+
|
|
215
|
+
|
|
216
|
+
def calculate_certificate_fingerprint(cert):
|
|
217
|
+
"""Calculates the URL-encoded, unpadded, base64-encoded SHA256 hash of a
|
|
218
|
+
DER-encoded certificate.
|
|
219
|
+
|
|
220
|
+
Args:
|
|
221
|
+
cert (cryptography.x509.Certificate): The parsed certificate object.
|
|
222
|
+
|
|
223
|
+
Returns:
|
|
224
|
+
str: The URL-encoded, unpadded, base64-encoded SHA256 fingerprint.
|
|
225
|
+
"""
|
|
226
|
+
try:
|
|
227
|
+
from cryptography.hazmat.primitives import serialization
|
|
228
|
+
|
|
229
|
+
der_cert = cert.public_bytes(serialization.Encoding.DER)
|
|
230
|
+
fingerprint = hashlib.sha256(der_cert).digest()
|
|
231
|
+
# The certificate fingerprint is generated in two steps to align with GFE's
|
|
232
|
+
# expectations and ensure proper URL transmission:
|
|
233
|
+
# 1. Standard base64 encoding is applied, and padding ('=') is removed.
|
|
234
|
+
# 2. The resulting string is then URL-encoded to handle special characters
|
|
235
|
+
# ('+', '/') that would otherwise be misinterpreted in URL parameters.
|
|
236
|
+
base64_fingerprint = base64.b64encode(fingerprint).decode("utf-8")
|
|
237
|
+
unpadded_base64_fingerprint = base64_fingerprint.rstrip("=")
|
|
238
|
+
return quote(unpadded_base64_fingerprint)
|
|
239
|
+
except ImportError as e:
|
|
240
|
+
raise ImportError(CRYPTOGRAPHY_NOT_FOUND_ERROR) from e
|
|
241
|
+
|
|
242
|
+
|
|
243
|
+
def should_request_bound_token(cert):
|
|
244
|
+
"""Determines if a bound token should be requested.
|
|
245
|
+
|
|
246
|
+
This is based on the GOOGLE_API_PREVENT_AGENT_TOKEN_SHARING_FOR_GCP_SERVICES
|
|
247
|
+
environment variable and whether the certificate is an agent identity cert.
|
|
248
|
+
|
|
249
|
+
Args:
|
|
250
|
+
cert (cryptography.x509.Certificate): The parsed certificate object.
|
|
251
|
+
|
|
252
|
+
Returns:
|
|
253
|
+
bool: True if a bound token should be requested, False otherwise.
|
|
254
|
+
"""
|
|
255
|
+
is_agent_cert = _is_agent_identity_certificate(cert)
|
|
256
|
+
is_opted_in = (
|
|
257
|
+
os.environ.get(
|
|
258
|
+
environment_vars.GOOGLE_API_PREVENT_AGENT_TOKEN_SHARING_FOR_GCP_SERVICES,
|
|
259
|
+
"true",
|
|
260
|
+
).lower()
|
|
261
|
+
== "true"
|
|
262
|
+
)
|
|
263
|
+
return is_agent_cert and is_opted_in
|
|
264
|
+
|
|
265
|
+
|
|
266
|
+
def call_client_cert_callback():
|
|
267
|
+
"""Calls the client cert callback and returns the certificate and key."""
|
|
268
|
+
_, cert_bytes, key_bytes, passphrase = _mtls_helper.get_client_ssl_credentials(
|
|
269
|
+
generate_encrypted_key=True
|
|
270
|
+
)
|
|
271
|
+
return cert_bytes, key_bytes
|
|
272
|
+
|
|
273
|
+
|
|
274
|
+
def get_cached_cert_fingerprint(cached_cert):
|
|
275
|
+
"""Returns the fingerprint of the cached certificate."""
|
|
276
|
+
if cached_cert:
|
|
277
|
+
cert_obj = parse_certificate(cached_cert)
|
|
278
|
+
cached_cert_fingerprint = calculate_certificate_fingerprint(cert_obj)
|
|
279
|
+
else:
|
|
280
|
+
raise ValueError("mTLS connection is not configured.")
|
|
281
|
+
return cached_cert_fingerprint
|
|
@@ -127,7 +127,7 @@ _CLASS_CONVERSION_MAP = {
|
|
|
127
127
|
oauth2client.contrib.gce.AppAssertionCredentials: _convert_gce_app_assertion_credentials,
|
|
128
128
|
}
|
|
129
129
|
|
|
130
|
-
if _HAS_APPENGINE:
|
|
130
|
+
if _HAS_APPENGINE: # pragma: no cover
|
|
131
131
|
_CLASS_CONVERSION_MAP[
|
|
132
132
|
oauth2client.contrib.appengine.AppAssertionCredentials
|
|
133
133
|
] = _convert_appengine_app_assertion_credentials
|
|
@@ -451,12 +451,19 @@ def get_service_account_token(request, service_account="default", scopes=None):
|
|
|
451
451
|
google.auth.exceptions.TransportError: if an error occurred while
|
|
452
452
|
retrieving metadata.
|
|
453
453
|
"""
|
|
454
|
+
from google.auth import _agent_identity_utils
|
|
455
|
+
|
|
456
|
+
params = {}
|
|
454
457
|
if scopes:
|
|
455
458
|
if not isinstance(scopes, str):
|
|
456
459
|
scopes = ",".join(scopes)
|
|
457
|
-
params
|
|
458
|
-
|
|
459
|
-
|
|
460
|
+
params["scopes"] = scopes
|
|
461
|
+
|
|
462
|
+
cert = _agent_identity_utils.get_and_parse_agent_identity_certificate()
|
|
463
|
+
if cert:
|
|
464
|
+
if _agent_identity_utils.should_request_bound_token(cert):
|
|
465
|
+
fingerprint = _agent_identity_utils.calculate_certificate_fingerprint(cert)
|
|
466
|
+
params["bindCertificateFingerprint"] = fingerprint
|
|
460
467
|
|
|
461
468
|
metrics_header = {
|
|
462
469
|
metrics.API_CLIENT_HEADER: metrics.token_request_access_token_mds()
|
|
@@ -135,9 +135,9 @@ class Credentials(
|
|
|
135
135
|
service can't be reached if if the instance has not
|
|
136
136
|
credentials.
|
|
137
137
|
"""
|
|
138
|
-
scopes = self._scopes if self._scopes is not None else self._default_scopes
|
|
139
138
|
try:
|
|
140
139
|
self._retrieve_info(request)
|
|
140
|
+
scopes = self._scopes if self._scopes is not None else self._default_scopes
|
|
141
141
|
# Always fetch token with default service account email.
|
|
142
142
|
self.token, self.expiry = _metadata.get_service_account_token(
|
|
143
143
|
request, service_account="default", scopes=scopes
|
|
@@ -92,3 +92,12 @@ AWS_DEFAULT_REGION = "AWS_DEFAULT_REGION"
|
|
|
92
92
|
GOOGLE_AUTH_TRUST_BOUNDARY_ENABLED = "GOOGLE_AUTH_TRUST_BOUNDARY_ENABLED"
|
|
93
93
|
"""Environment variable controlling whether to enable trust boundary feature.
|
|
94
94
|
The default value is false. Users have to explicitly set this value to true."""
|
|
95
|
+
|
|
96
|
+
GOOGLE_API_CERTIFICATE_CONFIG = "GOOGLE_API_CERTIFICATE_CONFIG"
|
|
97
|
+
"""Environment variable defining the location of Google API certificate config
|
|
98
|
+
file."""
|
|
99
|
+
|
|
100
|
+
GOOGLE_API_PREVENT_AGENT_TOKEN_SHARING_FOR_GCP_SERVICES = (
|
|
101
|
+
"GOOGLE_API_PREVENT_AGENT_TOKEN_SHARING_FOR_GCP_SERVICES"
|
|
102
|
+
)
|
|
103
|
+
"""Environment variable to prevent agent token sharing for GCP services."""
|
|
@@ -420,6 +420,9 @@ class Credentials(
|
|
|
420
420
|
credentials, it will refresh the access token and the trust boundary.
|
|
421
421
|
"""
|
|
422
422
|
self._refresh_token(request)
|
|
423
|
+
self._handle_trust_boundary(request)
|
|
424
|
+
|
|
425
|
+
def _handle_trust_boundary(self, request):
|
|
423
426
|
# If we are impersonating, the trust boundary is handled by the
|
|
424
427
|
# impersonated credentials object. We need to get it from there.
|
|
425
428
|
if self._service_account_impersonation_url:
|
|
@@ -428,7 +431,7 @@ class Credentials(
|
|
|
428
431
|
# Otherwise, refresh the trust boundary for the external account.
|
|
429
432
|
self._refresh_trust_boundary(request)
|
|
430
433
|
|
|
431
|
-
def _refresh_token(self, request):
|
|
434
|
+
def _refresh_token(self, request, cert_fingerprint=None):
|
|
432
435
|
scopes = self._scopes if self._scopes is not None else self._default_scopes
|
|
433
436
|
|
|
434
437
|
# Inject client certificate into request.
|
|
@@ -446,11 +449,15 @@ class Credentials(
|
|
|
446
449
|
self.expiry = self._impersonated_credentials.expiry
|
|
447
450
|
else:
|
|
448
451
|
now = _helpers.utcnow()
|
|
449
|
-
additional_options =
|
|
452
|
+
additional_options = {}
|
|
450
453
|
# Do not pass workforce_pool_user_project when client authentication
|
|
451
454
|
# is used. The client ID is sufficient for determining the user project.
|
|
452
455
|
if self._workforce_pool_user_project and not self._client_id:
|
|
453
|
-
additional_options
|
|
456
|
+
additional_options["userProject"] = self._workforce_pool_user_project
|
|
457
|
+
|
|
458
|
+
if cert_fingerprint:
|
|
459
|
+
additional_options["bindCertFingerprint"] = cert_fingerprint
|
|
460
|
+
|
|
454
461
|
additional_headers = {
|
|
455
462
|
metrics.API_CLIENT_HEADER: metrics.byoid_metrics_header(
|
|
456
463
|
self._metrics_options
|
|
@@ -464,7 +471,7 @@ class Credentials(
|
|
|
464
471
|
audience=self._audience,
|
|
465
472
|
scopes=scopes,
|
|
466
473
|
requested_token_type=_STS_REQUESTED_TOKEN_TYPE,
|
|
467
|
-
additional_options=additional_options,
|
|
474
|
+
additional_options=additional_options if additional_options else None,
|
|
468
475
|
additional_headers=additional_headers,
|
|
469
476
|
)
|
|
470
477
|
self.token = response_data.get("access_token")
|
|
@@ -550,3 +550,25 @@ class Credentials(external_account.Credentials):
|
|
|
550
550
|
credentials.
|
|
551
551
|
"""
|
|
552
552
|
return super(Credentials, cls).from_file(filename, **kwargs)
|
|
553
|
+
|
|
554
|
+
def refresh(self, request):
|
|
555
|
+
"""Refreshes the access token.
|
|
556
|
+
|
|
557
|
+
Args:
|
|
558
|
+
request (google.auth.transport.Request): The object used to make
|
|
559
|
+
HTTP requests.
|
|
560
|
+
"""
|
|
561
|
+
from google.auth import _agent_identity_utils
|
|
562
|
+
|
|
563
|
+
cert_fingerprint = None
|
|
564
|
+
# Check if the credential is X.509 based.
|
|
565
|
+
if self._credential_source_certificate is not None:
|
|
566
|
+
cert_bytes = self._get_cert_bytes()
|
|
567
|
+
cert = _agent_identity_utils.parse_certificate(cert_bytes)
|
|
568
|
+
if _agent_identity_utils.should_request_bound_token(cert):
|
|
569
|
+
cert_fingerprint = _agent_identity_utils.calculate_certificate_fingerprint(
|
|
570
|
+
cert
|
|
571
|
+
)
|
|
572
|
+
|
|
573
|
+
self._refresh_token(request, cert_fingerprint=cert_fingerprint)
|
|
574
|
+
self._handle_trust_boundary(request)
|
|
@@ -20,11 +20,12 @@ from os import environ, getenv, path
|
|
|
20
20
|
import re
|
|
21
21
|
import subprocess
|
|
22
22
|
|
|
23
|
+
from google.auth import _agent_identity_utils
|
|
24
|
+
from google.auth import environment_vars
|
|
23
25
|
from google.auth import exceptions
|
|
24
26
|
|
|
25
27
|
CONTEXT_AWARE_METADATA_PATH = "~/.secureConnect/context_aware_metadata.json"
|
|
26
28
|
CERTIFICATE_CONFIGURATION_DEFAULT_PATH = "~/.config/gcloud/certificate_config.json"
|
|
27
|
-
_CERTIFICATE_CONFIGURATION_ENV = "GOOGLE_API_CERTIFICATE_CONFIG"
|
|
28
29
|
_CERT_PROVIDER_COMMAND = "cert_provider_command"
|
|
29
30
|
_CERT_REGEX = re.compile(
|
|
30
31
|
b"-----BEGIN CERTIFICATE-----.+-----END CERTIFICATE-----\r?\n?", re.DOTALL
|
|
@@ -146,7 +147,7 @@ def _get_cert_config_path(certificate_config_path=None):
|
|
|
146
147
|
"""
|
|
147
148
|
|
|
148
149
|
if certificate_config_path is None:
|
|
149
|
-
env_path = environ.get(
|
|
150
|
+
env_path = environ.get(environment_vars.GOOGLE_API_CERTIFICATE_CONFIG, None)
|
|
150
151
|
if env_path is not None and env_path != "":
|
|
151
152
|
certificate_config_path = env_path
|
|
152
153
|
else:
|
|
@@ -474,3 +475,29 @@ def check_use_client_cert():
|
|
|
474
475
|
) as e:
|
|
475
476
|
_LOGGER.debug("error decoding certificate: %s", e)
|
|
476
477
|
return False
|
|
478
|
+
|
|
479
|
+
|
|
480
|
+
def check_parameters_for_unauthorized_response(cached_cert):
|
|
481
|
+
"""Returns the cached and current cert fingerprint for reconfiguring mTLS.
|
|
482
|
+
|
|
483
|
+
Args:
|
|
484
|
+
cached_cert(bytes): The cached client certificate.
|
|
485
|
+
|
|
486
|
+
Returns:
|
|
487
|
+
bytes: The client callback cert bytes.
|
|
488
|
+
bytes: The client callback key bytes.
|
|
489
|
+
str: The base64-encoded SHA256 cached fingerprint.
|
|
490
|
+
str: The base64-encoded SHA256 current cert fingerprint.
|
|
491
|
+
"""
|
|
492
|
+
call_cert_bytes, call_key_bytes = _agent_identity_utils.call_client_cert_callback()
|
|
493
|
+
cert_obj = _agent_identity_utils.parse_certificate(call_cert_bytes)
|
|
494
|
+
current_cert_fingerprint = _agent_identity_utils.calculate_certificate_fingerprint(
|
|
495
|
+
cert_obj
|
|
496
|
+
)
|
|
497
|
+
if cached_cert:
|
|
498
|
+
cached_fingerprint = _agent_identity_utils.get_cached_cert_fingerprint(
|
|
499
|
+
cached_cert
|
|
500
|
+
)
|
|
501
|
+
else:
|
|
502
|
+
cached_fingerprint = current_cert_fingerprint
|
|
503
|
+
return call_cert_bytes, call_key_bytes, cached_fingerprint, current_cert_fingerprint
|
|
@@ -17,6 +17,7 @@
|
|
|
17
17
|
from __future__ import absolute_import
|
|
18
18
|
|
|
19
19
|
import functools
|
|
20
|
+
import http.client as http_client
|
|
20
21
|
import logging
|
|
21
22
|
import numbers
|
|
22
23
|
import time
|
|
@@ -36,6 +37,7 @@ from requests.packages.urllib3.util.ssl_ import ( # type: ignore
|
|
|
36
37
|
from google.auth import _helpers
|
|
37
38
|
from google.auth import exceptions
|
|
38
39
|
from google.auth import transport
|
|
40
|
+
from google.auth.transport import _mtls_helper
|
|
39
41
|
import google.auth.transport._mtls_helper
|
|
40
42
|
from google.oauth2 import service_account
|
|
41
43
|
|
|
@@ -463,6 +465,7 @@ class AuthorizedSession(requests.Session):
|
|
|
463
465
|
|
|
464
466
|
if self._is_mtls:
|
|
465
467
|
mtls_adapter = _MutualTlsAdapter(cert, key)
|
|
468
|
+
self._cached_cert = cert
|
|
466
469
|
self.mount("https://", mtls_adapter)
|
|
467
470
|
except (
|
|
468
471
|
exceptions.ClientCertError,
|
|
@@ -502,6 +505,10 @@ class AuthorizedSession(requests.Session):
|
|
|
502
505
|
itself does not timeout, e.g. if a large file is being
|
|
503
506
|
transmitted. The timout error will be raised after such
|
|
504
507
|
request completes.
|
|
508
|
+
Raises:
|
|
509
|
+
google.auth.exceptions.MutualTLSChannelError: If mutual TLS
|
|
510
|
+
channel creation fails for any reason.
|
|
511
|
+
ValueError: If the client certificate is invalid.
|
|
505
512
|
"""
|
|
506
513
|
# pylint: disable=arguments-differ
|
|
507
514
|
# Requests has a ton of arguments to request, but only two
|
|
@@ -551,7 +558,31 @@ class AuthorizedSession(requests.Session):
|
|
|
551
558
|
response.status_code in self._refresh_status_codes
|
|
552
559
|
and _credential_refresh_attempt < self._max_refresh_attempts
|
|
553
560
|
):
|
|
554
|
-
|
|
561
|
+
# Handle unauthorized permission error(401 status code)
|
|
562
|
+
if response.status_code == http_client.UNAUTHORIZED:
|
|
563
|
+
if self.is_mtls:
|
|
564
|
+
call_cert_bytes, call_key_bytes, cached_fingerprint, current_cert_fingerprint = _mtls_helper.check_parameters_for_unauthorized_response(
|
|
565
|
+
self._cached_cert
|
|
566
|
+
)
|
|
567
|
+
if cached_fingerprint != current_cert_fingerprint:
|
|
568
|
+
try:
|
|
569
|
+
_LOGGER.info(
|
|
570
|
+
"Client certificate has changed, reconfiguring mTLS "
|
|
571
|
+
"channel."
|
|
572
|
+
)
|
|
573
|
+
self.configure_mtls_channel(
|
|
574
|
+
lambda: (call_cert_bytes, call_key_bytes)
|
|
575
|
+
)
|
|
576
|
+
except Exception as e:
|
|
577
|
+
_LOGGER.error("Failed to reconfigure mTLS channel: %s", e)
|
|
578
|
+
raise exceptions.MutualTLSChannelError(
|
|
579
|
+
"Failed to reconfigure mTLS channel"
|
|
580
|
+
) from e
|
|
581
|
+
else:
|
|
582
|
+
_LOGGER.info(
|
|
583
|
+
"Skipping reconfiguration of mTLS channel because the client"
|
|
584
|
+
" certificate has not changed."
|
|
585
|
+
)
|
|
555
586
|
_LOGGER.info(
|
|
556
587
|
"Refreshing credentials due to a %s response. Attempt %s/%s.",
|
|
557
588
|
response.status_code,
|
|
@@ -16,6 +16,7 @@
|
|
|
16
16
|
|
|
17
17
|
from __future__ import absolute_import
|
|
18
18
|
|
|
19
|
+
import http.client as http_client
|
|
19
20
|
import logging
|
|
20
21
|
import warnings
|
|
21
22
|
|
|
@@ -52,6 +53,7 @@ except ImportError as caught_exc: # pragma: NO COVER
|
|
|
52
53
|
from google.auth import _helpers
|
|
53
54
|
from google.auth import exceptions
|
|
54
55
|
from google.auth import transport
|
|
56
|
+
from google.auth.transport import _mtls_helper
|
|
55
57
|
from google.oauth2 import service_account
|
|
56
58
|
|
|
57
59
|
if version.parse(urllib3.__version__) >= version.parse("2.0.0"): # pragma: NO COVER
|
|
@@ -299,6 +301,7 @@ class AuthorizedHttp(RequestMethods): # type: ignore
|
|
|
299
301
|
# Request instance used by internal methods (for example,
|
|
300
302
|
# credentials.refresh).
|
|
301
303
|
self._request = Request(self.http)
|
|
304
|
+
self._is_mtls = False
|
|
302
305
|
|
|
303
306
|
# https://google.aip.dev/auth/4111
|
|
304
307
|
# Attempt to use self-signed JWTs when a service account is used.
|
|
@@ -335,7 +338,10 @@ class AuthorizedHttp(RequestMethods): # type: ignore
|
|
|
335
338
|
"""
|
|
336
339
|
use_client_cert = transport._mtls_helper.check_use_client_cert()
|
|
337
340
|
if not use_client_cert:
|
|
341
|
+
self._is_mtls = False
|
|
338
342
|
return False
|
|
343
|
+
else:
|
|
344
|
+
self._is_mtls = True
|
|
339
345
|
try:
|
|
340
346
|
import OpenSSL
|
|
341
347
|
except ImportError as caught_exc:
|
|
@@ -349,6 +355,7 @@ class AuthorizedHttp(RequestMethods): # type: ignore
|
|
|
349
355
|
|
|
350
356
|
if found_cert_key:
|
|
351
357
|
self.http = _make_mutual_tls_http(cert, key)
|
|
358
|
+
self._cached_cert = cert
|
|
352
359
|
else:
|
|
353
360
|
self.http = _make_default_http()
|
|
354
361
|
except (
|
|
@@ -381,6 +388,11 @@ class AuthorizedHttp(RequestMethods): # type: ignore
|
|
|
381
388
|
if headers is None:
|
|
382
389
|
headers = self.headers
|
|
383
390
|
|
|
391
|
+
use_mtls = False
|
|
392
|
+
if self._is_mtls:
|
|
393
|
+
MTLS_URL_PREFIXES = ["mtls.googleapis.com", "mtls.sandbox.googleapis.com"]
|
|
394
|
+
use_mtls = any([prefix in url for prefix in MTLS_URL_PREFIXES])
|
|
395
|
+
|
|
384
396
|
# Make a copy of the headers. They will be modified by the credentials
|
|
385
397
|
# and we want to pass the original headers if we recurse.
|
|
386
398
|
request_headers = headers.copy()
|
|
@@ -402,6 +414,34 @@ class AuthorizedHttp(RequestMethods): # type: ignore
|
|
|
402
414
|
response.status in self._refresh_status_codes
|
|
403
415
|
and _credential_refresh_attempt < self._max_refresh_attempts
|
|
404
416
|
):
|
|
417
|
+
if response.status == http_client.UNAUTHORIZED:
|
|
418
|
+
if use_mtls:
|
|
419
|
+
call_cert_bytes, call_key_bytes, cached_fingerprint, current_cert_fingerprint = _mtls_helper.check_parameters_for_unauthorized_response(
|
|
420
|
+
self._cached_cert
|
|
421
|
+
)
|
|
422
|
+
if cached_fingerprint != current_cert_fingerprint:
|
|
423
|
+
try:
|
|
424
|
+
_LOGGER.info(
|
|
425
|
+
"Client certificate has changed, reconfiguring mTLS "
|
|
426
|
+
"channel."
|
|
427
|
+
)
|
|
428
|
+
self.configure_mtls_channel(
|
|
429
|
+
client_cert_callback=lambda: (
|
|
430
|
+
call_cert_bytes,
|
|
431
|
+
call_key_bytes,
|
|
432
|
+
)
|
|
433
|
+
)
|
|
434
|
+
except Exception as e:
|
|
435
|
+
_LOGGER.error("Failed to reconfigure mTLS channel: %s", e)
|
|
436
|
+
raise exceptions.MutualTLSChannelError(
|
|
437
|
+
"Failed to reconfigure mTLS channel"
|
|
438
|
+
) from e
|
|
439
|
+
|
|
440
|
+
else:
|
|
441
|
+
_LOGGER.info(
|
|
442
|
+
"Skipping reconfiguration of mTLS channel because the "
|
|
443
|
+
"client certificate has not changed."
|
|
444
|
+
)
|
|
405
445
|
|
|
406
446
|
_LOGGER.info(
|
|
407
447
|
"Refreshing credentials due to a %s response. Attempt %s/%s.",
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: google-auth
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.45.0
|
|
4
4
|
Summary: Google Authentication Library
|
|
5
5
|
Home-page: https://github.com/googleapis/google-auth-library-python
|
|
6
6
|
Author: Google Cloud Platform
|
|
@@ -29,6 +29,9 @@ License-File: LICENSE
|
|
|
29
29
|
Requires-Dist: cachetools<7.0,>=2.0.0
|
|
30
30
|
Requires-Dist: pyasn1-modules>=0.2.1
|
|
31
31
|
Requires-Dist: rsa<5,>=3.1.4
|
|
32
|
+
Provides-Extra: cryptography
|
|
33
|
+
Requires-Dist: cryptography>=38.0.3; extra == "cryptography"
|
|
34
|
+
Requires-Dist: cryptography<39.0.0; python_version < "3.8" and extra == "cryptography"
|
|
32
35
|
Provides-Extra: aiohttp
|
|
33
36
|
Requires-Dist: aiohttp<4.0.0,>=3.6.2; extra == "aiohttp"
|
|
34
37
|
Requires-Dist: requests<3.0.0,>=2.20.0; extra == "aiohttp"
|
|
@@ -4,6 +4,7 @@ README.rst
|
|
|
4
4
|
setup.cfg
|
|
5
5
|
setup.py
|
|
6
6
|
google/auth/__init__.py
|
|
7
|
+
google/auth/_agent_identity_utils.py
|
|
7
8
|
google/auth/_cloud_sdk.py
|
|
8
9
|
google/auth/_constants.py
|
|
9
10
|
google/auth/_credentials_async.py
|
|
@@ -94,6 +95,7 @@ tests/test__helpers.py
|
|
|
94
95
|
tests/test__oauth2client.py
|
|
95
96
|
tests/test__refresh_worker.py
|
|
96
97
|
tests/test__service_account_info.py
|
|
98
|
+
tests/test_agent_identity_utils.py
|
|
97
99
|
tests/test_api_key.py
|
|
98
100
|
tests/test_app_engine.py
|
|
99
101
|
tests/test_aws.py
|