cloud-sql-python-connector 1.22.0__py3-none-any.whl → 1.24.0__py3-none-any.whl

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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: cloud-sql-python-connector
3
- Version: 1.22.0
3
+ Version: 1.24.0
4
4
  Summary: Google Cloud SQL Python Connector library
5
5
  Author-email: Google LLC <googleapis-packages@google.com>
6
6
  License-Expression: Apache-2.0
@@ -12,13 +12,13 @@ Classifier: Development Status :: 5 - Production/Stable
12
12
  Classifier: Intended Audience :: Developers
13
13
  Classifier: Programming Language :: Python
14
14
  Classifier: Programming Language :: Python :: 3
15
- Classifier: Programming Language :: Python :: 3.9
16
- Classifier: Programming Language :: Python :: 3.10
17
15
  Classifier: Programming Language :: Python :: 3.11
18
16
  Classifier: Programming Language :: Python :: 3.12
19
17
  Classifier: Programming Language :: Python :: 3.13
18
+ Classifier: Programming Language :: Python :: 3.14
19
+ Classifier: Programming Language :: Python :: 3.15
20
20
  Classifier: Operating System :: OS Independent
21
- Requires-Python: >=3.9
21
+ Requires-Python: >=3.11
22
22
  Description-Content-Type: text/markdown
23
23
  License-File: LICENSE
24
24
  Requires-Dist: aiohttp
@@ -26,9 +26,7 @@ Requires-Dist: cryptography>=42.0.0
26
26
  Requires-Dist: dnspython>=2.0.0
27
27
  Requires-Dist: Requests
28
28
  Requires-Dist: google-auth>=2.28.0
29
- Requires-Dist: grpcio
30
- Requires-Dist: protobuf
31
- Requires-Dist: googleapis-common-protos
29
+ Requires-Dist: google-cloud-sql>=0.1.1
32
30
  Provides-Extra: pymysql
33
31
  Requires-Dist: PyMySQL>=1.1.0; extra == "pymysql"
34
32
  Provides-Extra: pg8000
@@ -244,7 +242,7 @@ from google.cloud.sql.connector import Connector
244
242
 
245
243
  # Note: all parameters below are optional
246
244
  connector = Connector(
247
- ip_type="public", # can also be "private" or "psc"
245
+ ip_type="public", # can also be "private", "psc", or "sql_data"
248
246
  enable_iam_auth=False,
249
247
  timeout=30,
250
248
  credentials=custom_creds, # google.auth.credentials.Credentials
@@ -322,7 +320,11 @@ with, set the `ip_type` keyword argument when initializing a `Connector()` or wh
322
320
  calling `connector.connect()`.
323
321
 
324
322
  Possible values for `ip_type` are `"public"` (default value),
325
- `"private"`, and `"psc"`.
323
+ `"private"`, `"psc"`, and `"sql_data"`.
324
+
325
+ > [!NOTE]
326
+ >
327
+ > Usage of the `asyncpg` driver does not currently support `ip_type="sql_data"` (`IPTypes.SQL_DATA`).
326
328
 
327
329
  Example:
328
330
 
@@ -788,6 +790,84 @@ async def main():
788
790
  await pool.dispose()
789
791
  ```
790
792
 
793
+ ### Post-Quantum Cryptography (PQC)
794
+
795
+ The Cloud SQL Python Connector establishes mTLS connections over TLS 1.3 using
796
+ Python's standard `ssl` module without restricting key-exchange groups. When
797
+ Python's `ssl` module is linked against **OpenSSL 3.5 or later**, TLS 1.3
798
+ connections automatically offer and negotiate the hybrid post-quantum key
799
+ exchange group **`X25519MLKEM768`** (ML-KEM-768 combined with X25519) whenever
800
+ the Cloud SQL instance supports it. No code changes or connector configuration
801
+ are required.
802
+
803
+ #### Checking your OpenSSL version
804
+
805
+ Post-quantum TLS key exchange depends on the OpenSSL library linked into your
806
+ Python runtime's `ssl` module (not the Python language version or the
807
+ `cryptography` package version alone). Run the following command to check which
808
+ OpenSSL version your Python interpreter uses:
809
+
810
+ ```sh
811
+ python3 -c "import ssl; print(ssl.OPENSSL_VERSION)"
812
+ ```
813
+
814
+ - **OpenSSL 3.5.0 or higher** (for example, `python:3.13-trixie` / Debian 13
815
+ "Trixie" container images; check your runtime with the one-liner above):
816
+ `X25519MLKEM768` is enabled by default on TLS 1.3 connections.
817
+ - **OpenSSL earlier than 3.5.0** (for example, Ubuntu 24.04 LTS, which ships
818
+ system OpenSSL 3.0.x): TLS 1.3 connections continue to use classical elliptic
819
+ curve Diffie-Hellman (such as `X25519` or `P-256`).
820
+
821
+ > [!NOTE]
822
+ >
823
+ > Connections configured with `ip_type="sql_data"` (`IPTypes.SQL_DATA`) stream
824
+ > over gRPC (`grpcio`), which uses its bundled BoringSSL (supporting
825
+ > `X25519MLKEM768` independently of Python's `ssl` module and `OPENSSL_CONF`),
826
+ > whereas its direct-TLS fallback path uses `create_ssl_context()` and Python's
827
+ > linked OpenSSL library as described above.
828
+
829
+ #### Backwards compatibility
830
+
831
+ Hybrid post-quantum key exchange is backwards-compatible. On OpenSSL 3.5+,
832
+ Python's `ssl` module advertises both hybrid (`X25519MLKEM768`) and classical
833
+ (`X25519`, `secp256r1`, etc.) groups in the TLS 1.3 `ClientHello`. If either the
834
+ client runtime (OpenSSL `< 3.5`) or the Cloud SQL instance only supports
835
+ classical groups, the TLS 1.3 handshake automatically falls back to classical
836
+ ECDHE without error.
837
+
838
+ #### Opting out
839
+
840
+ If you need to disable post-quantum key exchange on an OpenSSL 3.5+ client (for
841
+ example, to troubleshoot a network middlebox sensitive to larger TLS
842
+ `ClientHello` records), configure OpenSSL's `Groups` list via an OpenSSL
843
+ configuration file (`OPENSSL_CONF`) to advertise only classical curves.
844
+
845
+ > [!WARNING]
846
+ >
847
+ > Setting `OPENSSL_CONF` replaces the system default OpenSSL configuration for
848
+ > the entire Python process. It affects **all** TLS connections made by the
849
+ > process (not just Cloud SQL connections), and starting from a minimal file
850
+ > omits distribution defaults (such as Debian's `MinProtocol` or
851
+ > `CipherString`). Copy your system's `openssl.cnf` (check `openssl version -d`,
852
+ > typically `/etc/ssl/openssl.cnf`) and add `Groups = X25519:P-256:P-384` under
853
+ > its `[system_default_sect]` section rather than using a bare file from
854
+ > scratch.
855
+
856
+ For example, after copying `/etc/ssl/openssl.cnf`, ensure its
857
+ `[system_default_sect]` (referenced via `ssl_conf` -> `system_default`) sets
858
+ `Groups`:
859
+
860
+ ```ini
861
+ [system_default_sect]
862
+ Groups = X25519:P-256:P-384
863
+ ```
864
+
865
+ Then point `OPENSSL_CONF` to your modified copy when starting your application:
866
+
867
+ ```sh
868
+ OPENSSL_CONF=/path/to/openssl.cnf python3 app.py
869
+ ```
870
+
791
871
  ### Debug Logging
792
872
 
793
873
  The Cloud SQL Python Connector uses the standard [Python logging module][python-logging]
@@ -0,0 +1,28 @@
1
+ cloud_sql_python_connector-1.24.0.dist-info/licenses/LICENSE,sha256=z8d0m5b2O9McPEK1xHG_dWgUBT6EfBDz6wA0F7xSPTA,11358
2
+ google/cloud/sql/connector/__init__.py,sha256=pfa-FD-VRpYI49OUul9p6UFDp7G_yT2f6WS9rZMn8CM,1148
3
+ google/cloud/sql/connector/asyncpg.py,sha256=MpZhwFke0WXvYD3OcEF1Ok0qHl-CU7ZIPy85PCog5QY,2002
4
+ google/cloud/sql/connector/client.py,sha256=GfiZKDDBOxeA0yfqETQuaw3Nhg0BS71oQNZNxq3CooY,14991
5
+ google/cloud/sql/connector/connection_info.py,sha256=NnHSHs1PZ_9WeFICkZ53gbA2hVvlWuzdBdhWizOI6hs,5770
6
+ google/cloud/sql/connector/connection_name.py,sha256=SCCVeWb8YN0f9My09_l9FGTr6iRbi1b_52TJQaS6xzM,2574
7
+ google/cloud/sql/connector/connector.py,sha256=8A4uxTIGJKxnRgG2TqBHQwi4dG5KPO_1vNAQkNrR5vs,41574
8
+ google/cloud/sql/connector/enums.py,sha256=F_mUdwscpW2fAzw4XElSy4BWkWcGQAkBOAUG8bvd3vg,3118
9
+ google/cloud/sql/connector/exceptions.py,sha256=-QgsUq4gZaMrl3MH5KnsfJybEegAKW84-sdRmPKT-Dg,3137
10
+ google/cloud/sql/connector/instance.py,sha256=j3xj3ZuSpV7AcQz6_eYsS6F6bFPySKcGeFYfNOnIEKU,17269
11
+ google/cloud/sql/connector/lazy.py,sha256=9OOSfr13aX1OSA77V72Utp8M8VUQlwLloogRlUm0GDE,6746
12
+ google/cloud/sql/connector/monitored_cache.py,sha256=BarNBg-VsthW5bguGl3aq17IP4QaiF099W0AnrboujM,6919
13
+ google/cloud/sql/connector/pg8000.py,sha256=tbqFSIoSguMX1hTZ4kWeBOKLhLHKX1Vq5SespfA0Y0w,1801
14
+ google/cloud/sql/connector/postgres_connection.py,sha256=NCUCZ_X6svNoJaqSClYDyqXaIArZYPQrPZu7wC-LtjU,7509
15
+ google/cloud/sql/connector/psycopg.py,sha256=UD7OFVJ7C_PPsiIWzpbi1k3YIkqC4rM1pQS08wPeSM0,10950
16
+ google/cloud/sql/connector/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
17
+ google/cloud/sql/connector/pymysql.py,sha256=MOau37nTOTesUR5Hor7lLtEckvvGThzjECEWQYHcM8U,2166
18
+ google/cloud/sql/connector/pytds.py,sha256=e-KBCqv0m3a7cRi9YmFHNuYqClhlJbf-KoF611w-1Ck,2304
19
+ google/cloud/sql/connector/rate_limiter.py,sha256=60k-r9h-nUBqovVIHnIGbW0NMgUGD4exH_rZ02ukNa0,2924
20
+ google/cloud/sql/connector/refresh_utils.py,sha256=Eghd-_uR5ndCGLsPgnyeUOoAvN7Tz4yzTlQFW4QWOjo,5228
21
+ google/cloud/sql/connector/resolver.py,sha256=4U8G0Pxwmy64qoRmP5shLnmIykxcGcGCoFSH_7tr120,6353
22
+ google/cloud/sql/connector/sqldata_client.py,sha256=vov9R3xI_0LBxByMUIBDBsXZOE64cnUADp25Tp4Kvo0,23892
23
+ google/cloud/sql/connector/utils.py,sha256=bGsWj_SupSBmxcnsaMlmREP7hyLV7CcZ4g2_qHzHVoA,4119
24
+ google/cloud/sql/connector/version.py,sha256=juuKD5HYyccQin3zqup25QoyaBFJQnEtQgG7cgdeFIg,598
25
+ cloud_sql_python_connector-1.24.0.dist-info/METADATA,sha256=fLmn8G44gYU1v-gXeu3FvCGScL1xqipmX6lh3UfSB1w,35170
26
+ cloud_sql_python_connector-1.24.0.dist-info/WHEEL,sha256=mOdXR9C3wMVov7cSY8PY-XCzo3SmmWCHvGQr5csJoao,91
27
+ cloud_sql_python_connector-1.24.0.dist-info/top_level.txt,sha256=_1QvSJIhFAGfxb79D6DhB7SUw2X6T4rwnz_LLrbcD3c,7
28
+ cloud_sql_python_connector-1.24.0.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (84.0.0)
2
+ Generator: setuptools (84.2.1)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5
 
@@ -173,9 +173,13 @@ class CloudSQLClient:
173
173
  if psc_dns_names:
174
174
  ip_addresses["PSC"] = psc_dns_names
175
175
 
176
+ server_ca_cert = None
177
+ if "serverCaCert" in ret_dict and "cert" in ret_dict["serverCaCert"]:
178
+ server_ca_cert = ret_dict["serverCaCert"]["cert"]
179
+
176
180
  return {
177
181
  "ip_addresses": ip_addresses,
178
- "server_ca_cert": ret_dict["serverCaCert"]["cert"],
182
+ "server_ca_cert": server_ca_cert,
179
183
  "database_version": ret_dict["databaseVersion"],
180
184
  }
181
185
 
@@ -271,7 +275,15 @@ class CloudSQLClient:
271
275
  finally:
272
276
  resp.raise_for_status()
273
277
 
274
- ephemeral_cert: str = ret_dict["ephemeralCert"]["cert"]
278
+ try:
279
+ ephemeral_cert: str = ret_dict["ephemeralCert"]["cert"]
280
+ except KeyError as e:
281
+ logger.error(
282
+ "KeyError in _get_ephemeral parsing generateEphemeralCert: %s. Response dict: %s",
283
+ e,
284
+ ret_dict,
285
+ )
286
+ raise
275
287
 
276
288
  # decode cert to read expiration
277
289
  x509 = load_pem_x509_certificate(
@@ -285,7 +297,7 @@ class CloudSQLClient:
285
297
  # google.auth library strips timezone info for backwards compatibality
286
298
  # reasons with Python 2. Add it back to allow timezone aware datetimes.
287
299
  # Ref: https://github.com/googleapis/google-auth-library-python/blob/49a5ff7411a2ae4d32a7d11700f9f961c55406a9/google/auth/_helpers.py#L93-L99
288
- token_expiration = token_expiration.replace(tzinfo=datetime.timezone.utc)
300
+ token_expiration = token_expiration.replace(tzinfo=datetime.UTC)
289
301
 
290
302
  expiration = min(expiration, token_expiration)
291
303
  return ephemeral_cert, expiration
@@ -21,6 +21,7 @@ import ssl
21
21
  from typing import Any, TYPE_CHECKING
22
22
 
23
23
  from google.cloud.sql.connector.connection_name import ConnectionName
24
+ from google.cloud.sql.connector.exceptions import CloudSQLConnectionError
24
25
  from google.cloud.sql.connector.exceptions import CloudSQLIPTypeError
25
26
  from google.cloud.sql.connector.exceptions import TLSVersionError
26
27
  from google.cloud.sql.connector.utils import AsyncTemporaryDirectory
@@ -45,6 +46,10 @@ class ConnectionInfoCache(abc.ABC):
45
46
  async def force_refresh(self) -> None:
46
47
  pass
47
48
 
49
+ @abc.abstractmethod
50
+ def record_principal(self, user: str, database: str) -> None:
51
+ pass
52
+
48
53
  @abc.abstractmethod
49
54
  async def close(self) -> None:
50
55
  pass
@@ -62,7 +67,7 @@ class ConnectionInfo:
62
67
 
63
68
  conn_name: ConnectionName
64
69
  client_cert: str
65
- server_ca_cert: str
70
+ server_ca_cert: str | None
66
71
  private_key: bytes
67
72
  ip_addrs: dict[str, Any]
68
73
  database_version: str
@@ -78,6 +83,12 @@ class ConnectionInfo:
78
83
  # if SSL context is cached, use it
79
84
  if self.context is not None:
80
85
  return self.context
86
+
87
+ if self.server_ca_cert is None:
88
+ raise CloudSQLConnectionError(
89
+ "Cannot create SSL context: server CA certificate is missing."
90
+ )
91
+
81
92
  context = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT)
82
93
 
83
94
  # update ssl.PROTOCOL_TLS_CLIENT default