Script 'mail_helper' called by obssrc
Hello community,

here is the log from the commit of package python-google-auth for 
openSUSE:Factory checked in at 2026-06-02 16:01:02
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/python-google-auth (Old)
 and      /work/SRC/openSUSE:Factory/.python-google-auth.new.1937 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "python-google-auth"

Tue Jun  2 16:01:02 2026 rev:68 rq:1356350 version:2.53.0

Changes:
--------
--- /work/SRC/openSUSE:Factory/python-google-auth/python-google-auth.changes    
2026-05-24 19:35:22.190580323 +0200
+++ 
/work/SRC/openSUSE:Factory/.python-google-auth.new.1937/python-google-auth.changes
  2026-06-02 16:01:27.075793136 +0200
@@ -1,0 +2,8 @@
+Mon Jun  1 09:53:55 UTC 2026 - John Paul Adrian Glaubitz 
<[email protected]>
+
+- Update to version 2.53.0
+  * allowlist agents-nonprod trust domains for agent identity (#17155)
+  * fail-fast on invalid or non-workload certificate configs in agent
+    identity discovery (#17116)
+
+-------------------------------------------------------------------

Old:
----
  google_auth-2.52.0.tar.gz

New:
----
  google_auth-2.53.0.tar.gz

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Other differences:
------------------
++++++ python-google-auth.spec ++++++
--- /var/tmp/diff_new_pack.7fJxaP/_old  2026-06-02 16:01:28.051833621 +0200
+++ /var/tmp/diff_new_pack.7fJxaP/_new  2026-06-02 16:01:28.051833621 +0200
@@ -18,7 +18,7 @@
 
 %{?sle15_python_module_pythons}
 Name:           python-google-auth
-Version:        2.52.0
+Version:        2.53.0
 Release:        0
 Summary:        Google Authentication Library
 License:        Apache-2.0

++++++ google_auth-2.52.0.tar.gz -> google_auth-2.53.0.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/google_auth-2.52.0/PKG-INFO 
new/google_auth-2.53.0/PKG-INFO
--- old/google_auth-2.52.0/PKG-INFO     2026-05-07 21:27:57.138338600 +0200
+++ new/google_auth-2.53.0/PKG-INFO     2026-05-15 22:31:47.528021600 +0200
@@ -1,6 +1,6 @@
 Metadata-Version: 2.4
 Name: google-auth
-Version: 2.52.0
+Version: 2.53.0
 Summary: Google Authentication Library
 Home-page: https://github.com/googleapis/google-auth-library-python
 Author: Google Cloud Platform
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/google_auth-2.52.0/google/auth/_agent_identity_utils.py 
new/google_auth-2.53.0/google/auth/_agent_identity_utils.py
--- old/google_auth-2.52.0/google/auth/_agent_identity_utils.py 2026-05-07 
21:26:34.000000000 +0200
+++ new/google_auth-2.53.0/google/auth/_agent_identity_utils.py 2026-05-15 
22:31:13.000000000 +0200
@@ -22,9 +22,7 @@
 import time
 from urllib.parse import quote, urlparse
 
-from google.auth import environment_vars
-from google.auth import exceptions
-
+from google.auth import environment_vars, exceptions
 
 _LOGGER = logging.getLogger(__name__)
 
@@ -37,6 +35,8 @@
 _AGENT_IDENTITY_SPIFFE_TRUST_DOMAIN_PATTERNS = [
     r"^agents\.global\.org-\d+\.system\.id\.goog$",
     r"^agents\.global\.proj-\d+\.system\.id\.goog$",
+    r"^agents-nonprod\.global\.org-\d+\.system\.id\.goog$",
+    r"^agents-nonprod\.global\.proj-\d+\.system\.id\.goog$",
 ]
 
 _WELL_KNOWN_CERT_PATH = 
"/var/run/secrets/workload-spiffe-credentials/certificates.pem"
@@ -80,41 +80,86 @@
     import json
 
     cert_config_path = 
os.environ.get(environment_vars.GOOGLE_API_CERTIFICATE_CONFIG)
-    if not cert_config_path:
+
+    # Check if the well-known workload directory is mounted.
+    well_known_dir = os.path.dirname(_WELL_KNOWN_CERT_PATH)
+    has_well_known_dir = os.path.exists(well_known_dir)
+
+    # If we have neither a config path nor a well-known mount directory, exit 
immediately.
+    if not cert_config_path and not has_well_known_dir:
         return None
 
-    has_logged_warning = False
+    has_logged_config_warning = False
+    has_logged_cert_warning = False
 
     for interval in _POLLING_INTERVALS:
         try:
-            with open(cert_config_path, "r") as f:
-                cert_config = json.load(f)
-                cert_path = (
-                    cert_config.get("cert_configs", {})
-                    .get("workload", {})
-                    .get("cert_path")
+            # Path A: Config file is explicitly set
+            if cert_config_path:
+                with open(cert_config_path, "r") as f:
+                    cert_config = json.load(f)
+
+                cert_configs = (
+                    cert_config.get("cert_configs")
+                    if isinstance(cert_config, dict)
+                    else None
+                )
+                workload_config = (
+                    cert_configs.get("workload")
+                    if isinstance(cert_configs, dict)
+                    else None
                 )
+
+                if (
+                    not isinstance(workload_config, dict)
+                    or "cert_path" not in workload_config
+                ):
+                    return None
+
+                cert_path = workload_config["cert_path"]
                 if _is_certificate_file_ready(cert_path):
                     return cert_path
-        except (IOError, ValueError, KeyError):
-            if not has_logged_warning:
+
+                # The config was parsed, but the cert file is not ready yet
+                target_path = cert_path
+
+            # Path B: Config is NOT set, fallback to the well-known path
+            else:
+                if _is_certificate_file_ready(_WELL_KNOWN_CERT_PATH):
+                    return _WELL_KNOWN_CERT_PATH
+
+                # The well-known cert file is not ready yet
+                target_path = _WELL_KNOWN_CERT_PATH
+
+            # Log a warning on the first failed attempt to load the 
certificate file
+            if not has_logged_cert_warning:
+                _LOGGER.warning(
+                    "Certificate file not ready at %s. Retrying until startup 
timeout (up to %s seconds total)...",
+                    target_path,
+                    _TOTAL_TIMEOUT,
+                )
+                has_logged_cert_warning = True
+
+        except (IOError, ValueError, KeyError) as e:
+            if cert_config_path and os.path.exists(cert_config_path):
+                # If the file exists but has invalid JSON or is unreadable,
+                # we assume it is in its final format and fail-fast by 
returning None.
+                return None
+
+            if not has_logged_config_warning and cert_config_path:
                 _LOGGER.warning(
-                    "Certificate config file not found at %s (from %s 
environment "
-                    "variable). Retrying for up to %s seconds.",
-                    cert_config_path,
+                    "Certificate config file not found or incomplete: %s (from 
%s "
+                    "environment variable). Retrying until startup timeout (up 
to %s seconds total)...",
+                    e,
                     environment_vars.GOOGLE_API_CERTIFICATE_CONFIG,
                     _TOTAL_TIMEOUT,
                 )
-                has_logged_warning = True
+                has_logged_config_warning = True
             pass
 
-        # As a fallback, check the well-known certificate path.
-        if _is_certificate_file_ready(_WELL_KNOWN_CERT_PATH):
-            return _WELL_KNOWN_CERT_PATH
-
         # A sleep is required in two cases:
         # 1. The config file is not found (the except block).
-        # 2. The config file is found, but the certificate is not yet 
available.
+        # 2. The config file/well-known path is found, but the certificate is 
not yet available.
         # In both cases, we need to poll, so we sleep on every iteration
         # that doesn't return a certificate.
         time.sleep(interval)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/google_auth-2.52.0/google/auth/version.py 
new/google_auth-2.53.0/google/auth/version.py
--- old/google_auth-2.52.0/google/auth/version.py       2026-05-07 
21:26:31.000000000 +0200
+++ new/google_auth-2.53.0/google/auth/version.py       2026-05-15 
22:31:12.000000000 +0200
@@ -12,4 +12,4 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-__version__ = "2.52.0"
+__version__ = "2.53.0"
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/google_auth-2.52.0/google_auth.egg-info/PKG-INFO 
new/google_auth-2.53.0/google_auth.egg-info/PKG-INFO
--- old/google_auth-2.52.0/google_auth.egg-info/PKG-INFO        2026-05-07 
21:27:57.000000000 +0200
+++ new/google_auth-2.53.0/google_auth.egg-info/PKG-INFO        2026-05-15 
22:31:47.000000000 +0200
@@ -1,6 +1,6 @@
 Metadata-Version: 2.4
 Name: google-auth
-Version: 2.52.0
+Version: 2.53.0
 Summary: Google Authentication Library
 Home-page: https://github.com/googleapis/google-auth-library-python
 Author: Google Cloud Platform
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/google_auth-2.52.0/tests/test_agent_identity_utils.py 
new/google_auth-2.53.0/tests/test_agent_identity_utils.py
--- old/google_auth-2.52.0/tests/test_agent_identity_utils.py   2026-05-07 
21:26:34.000000000 +0200
+++ new/google_auth-2.53.0/tests/test_agent_identity_utils.py   2026-05-15 
22:31:11.000000000 +0200
@@ -21,9 +21,7 @@
 from cryptography import x509
 import pytest
 
-from google.auth import _agent_identity_utils
-from google.auth import environment_vars
-from google.auth import exceptions
+from google.auth import _agent_identity_utils, environment_vars, exceptions
 
 # A mock PEM-encoded certificate without an Agent Identity SPIFFE ID.
 NON_AGENT_IDENTITY_CERT_BYTES = (
@@ -60,15 +58,22 @@
         cert = 
_agent_identity_utils.parse_certificate(NON_AGENT_IDENTITY_CERT_BYTES)
         assert not _agent_identity_utils._is_agent_identity_certificate(cert)
 
-    def test__is_agent_identity_certificate_valid_spiffe(self):
+    @pytest.mark.parametrize(
+        "spiffe_id",
+        [
+            "spiffe://agents.global.proj-12345.system.id.goog/workload",
+            "spiffe://agents.global.org-12345.system.id.goog/workload",
+            
"spiffe://agents-nonprod.global.proj-12345.system.id.goog/workload",
+            "spiffe://agents-nonprod.global.org-12345.system.id.goog/workload",
+        ],
+    )
+    def test__is_agent_identity_certificate_valid_spiffe(self, spiffe_id):
         mock_cert = mock.MagicMock()
         mock_ext = mock.MagicMock()
         mock_san_value = mock.MagicMock()
         mock_cert.extensions.get_extension_for_oid.return_value = mock_ext
         mock_ext.value = mock_san_value
-        mock_san_value.get_values_for_type.return_value = [
-            "spiffe://agents.global.proj-12345.system.id.goog/workload"
-        ]
+        mock_san_value.get_values_for_type.return_value = [spiffe_id]
         assert _agent_identity_utils._is_agent_identity_certificate(mock_cert)
 
     def test__is_agent_identity_certificate_non_matching_spiffe(self):
@@ -172,7 +177,7 @@
         with pytest.raises(exceptions.RefreshError):
             _agent_identity_utils.get_agent_identity_certificate_path()
 
-        assert mock_sleep.call_count == 100
+        assert mock_sleep.call_count == 
len(_agent_identity_utils._POLLING_INTERVALS)
 
     @mock.patch("time.sleep")
     def test_get_agent_identity_certificate_path_failure(
@@ -191,7 +196,7 @@
             
environment_vars.GOOGLE_API_PREVENT_AGENT_TOKEN_SHARING_FOR_GCP_SERVICES
             in str(excinfo.value)
         )
-        assert mock_sleep.call_count == 100
+        assert mock_sleep.call_count == 
len(_agent_identity_utils._POLLING_INTERVALS)
 
     @mock.patch("time.sleep")
     @mock.patch("os.path.exists")
@@ -215,7 +220,149 @@
         with pytest.raises(exceptions.RefreshError):
             _agent_identity_utils.get_agent_identity_certificate_path()
 
-        assert mock_sleep.call_count == 100
+        assert mock_sleep.call_count == 
len(_agent_identity_utils._POLLING_INTERVALS)
+
+    @mock.patch("time.sleep")
+    def test_get_agent_identity_certificate_path_non_workload_config(
+        self, mock_sleep, tmpdir, monkeypatch
+    ):
+        config_path = tmpdir.join("config.json")
+        config_path.write(
+            json.dumps({"cert_configs": {"pkcs11": {"module": "some_module"}}})
+        )
+        monkeypatch.setenv(
+            environment_vars.GOOGLE_API_CERTIFICATE_CONFIG, str(config_path)
+        )
+
+        result = _agent_identity_utils.get_agent_identity_certificate_path()
+
+        # Should return None immediately without polling
+        assert result is None
+        mock_sleep.assert_not_called()
+
+    @mock.patch("time.sleep")
+    def test_get_agent_identity_certificate_path_invalid_json(
+        self, mock_sleep, tmpdir, monkeypatch
+    ):
+        config_path = tmpdir.join("config.json")
+        config_path.write("{invalid_json: true}")  # Invalid JSON missing 
quotes
+        monkeypatch.setenv(
+            environment_vars.GOOGLE_API_CERTIFICATE_CONFIG, str(config_path)
+        )
+
+        result = _agent_identity_utils.get_agent_identity_certificate_path()
+
+        # Should return None immediately without polling/sleeping
+        assert result is None
+        mock_sleep.assert_not_called()
+
+    @mock.patch("time.sleep")
+    def test_get_agent_identity_certificate_path_non_dict_json(
+        self, mock_sleep, tmpdir, monkeypatch
+    ):
+        config_path = tmpdir.join("config.json")
+        config_path.write(json.dumps(["not", "a", "dict"]))  # Valid JSON list
+        monkeypatch.setenv(
+            environment_vars.GOOGLE_API_CERTIFICATE_CONFIG, str(config_path)
+        )
+
+        result = _agent_identity_utils.get_agent_identity_certificate_path()
+
+        # Should fail fast immediately and return None without polling
+        assert result is None
+        mock_sleep.assert_not_called()
+
+    @mock.patch("time.sleep")
+    def 
test_get_agent_identity_certificate_path_workload_config_missing_cert_path(
+        self, mock_sleep, tmpdir, monkeypatch
+    ):
+        config_path = tmpdir.join("config.json")
+        config_path.write(json.dumps({"cert_configs": {"workload": {}}}))
+        monkeypatch.setenv(
+            environment_vars.GOOGLE_API_CERTIFICATE_CONFIG, str(config_path)
+        )
+
+        result = _agent_identity_utils.get_agent_identity_certificate_path()
+
+        # Should return None immediately without polling
+        assert result is None
+        mock_sleep.assert_not_called()
+
+    @mock.patch("time.sleep")
+    @mock.patch("os.path.exists")
+    @mock.patch("google.auth._agent_identity_utils._is_certificate_file_ready")
+    def 
test_get_agent_identity_certificate_path_no_config_but_has_well_known_dir(
+        self, mock_is_ready, mock_exists, mock_sleep, monkeypatch
+    ):
+        monkeypatch.delenv(
+            environment_vars.GOOGLE_API_CERTIFICATE_CONFIG, raising=False
+        )
+
+        # Simulate that the well-known workload mount directory exists, and 
the cert is ready
+        mock_exists.return_value = True
+        mock_is_ready.return_value = True
+
+        result = _agent_identity_utils.get_agent_identity_certificate_path()
+
+        # Should return the well-known path immediately
+        assert result == _agent_identity_utils._WELL_KNOWN_CERT_PATH
+        mock_sleep.assert_not_called()
+
+    @mock.patch("time.sleep")
+    @mock.patch("os.path.exists")
+    def test_get_agent_identity_certificate_path_no_config_no_well_known_dir(
+        self, mock_exists, mock_sleep, monkeypatch
+    ):
+        monkeypatch.delenv(
+            environment_vars.GOOGLE_API_CERTIFICATE_CONFIG, raising=False
+        )
+
+        # Simulate that the well-known mount directory does NOT exist
+        mock_exists.return_value = False
+
+        result = _agent_identity_utils.get_agent_identity_certificate_path()
+
+        # Should return None immediately without polling
+        assert result is None
+        mock_sleep.assert_not_called()
+
+    @mock.patch("time.sleep")
+    @mock.patch("os.path.exists")
+    @mock.patch("google.auth._agent_identity_utils._is_certificate_file_ready")
+    def 
test_get_agent_identity_certificate_path_no_config_well_known_polling_success(
+        self, mock_is_ready, mock_exists, mock_sleep, monkeypatch
+    ):
+        monkeypatch.delenv(
+            environment_vars.GOOGLE_API_CERTIFICATE_CONFIG, raising=False
+        )
+
+        # Simulate that the directory exists, file appears on 2nd try
+        mock_exists.return_value = True
+        mock_is_ready.side_effect = [False, True]
+
+        result = _agent_identity_utils.get_agent_identity_certificate_path()
+
+        assert result == _agent_identity_utils._WELL_KNOWN_CERT_PATH
+        assert mock_sleep.call_count == 1
+
+    @mock.patch("time.sleep")
+    @mock.patch("os.path.exists")
+    @mock.patch("google.auth._agent_identity_utils._is_certificate_file_ready")
+    def 
test_get_agent_identity_certificate_path_no_config_well_known_polling_timeout(
+        self, mock_is_ready, mock_exists, mock_sleep, monkeypatch
+    ):
+        monkeypatch.delenv(
+            environment_vars.GOOGLE_API_CERTIFICATE_CONFIG, raising=False
+        )
+
+        # Simulate that the directory exists, but file never appears
+        mock_exists.return_value = True
+        mock_is_ready.return_value = False
+
+        with pytest.raises(exceptions.RefreshError):
+            _agent_identity_utils.get_agent_identity_certificate_path()
+
+        assert mock_sleep.call_count == 
len(_agent_identity_utils._POLLING_INTERVALS)
 
     
@mock.patch("google.auth._agent_identity_utils.get_agent_identity_certificate_path")
     def test_get_and_parse_agent_identity_certificate_opted_out(
@@ -261,27 +408,6 @@
         mock_parse_certificate.assert_called_once_with(b"cert_bytes")
         assert result == mock_parse_certificate.return_value
 
-    @mock.patch("time.sleep", return_value=None)
-    @mock.patch("google.auth._agent_identity_utils._is_certificate_file_ready")
-    def test_get_agent_identity_certificate_path_fallback_to_well_known_path(
-        self, mock_is_ready, mock_sleep, monkeypatch
-    ):
-        # Set a dummy config path that won't be found.
-        monkeypatch.setenv(
-            environment_vars.GOOGLE_API_CERTIFICATE_CONFIG, 
"/dummy/config.json"
-        )
-
-        # First, the primary path from the (mocked) config is not ready.
-        # Then, the fallback well-known path is ready.
-        mock_is_ready.side_effect = [False, True]
-
-        result = _agent_identity_utils.get_agent_identity_certificate_path()
-
-        assert result == _agent_identity_utils._WELL_KNOWN_CERT_PATH
-        # The sleep should have been called once before the fallback is 
checked.
-        mock_sleep.assert_called_once()
-        assert mock_is_ready.call_count == 2
-
     def test_get_cached_cert_fingerprint_no_cert(self):
         with pytest.raises(ValueError, match="mTLS connection is not 
configured."):
             _agent_identity_utils.get_cached_cert_fingerprint(None)

Reply via email to