]> git.ipfire.org Git - thirdparty/psycopg.git/commitdiff
feat: raise a warning if a GSS connection is obtained using the libpq default 1137/head
authorDaniele Varrazzo <daniele.varrazzo@gmail.com>
Tue, 26 Aug 2025 01:28:16 +0000 (03:28 +0200)
committerDaniele Varrazzo <daniele.varrazzo@gmail.com>
Fri, 5 Sep 2025 21:57:07 +0000 (23:57 +0200)
Suggest the user to specify a 'gssencmode' setting explicitly so that,
in the future, we will be able to change the setting default to 'disable'.

See #1136

.flake8
docs/news.rst
psycopg/psycopg/_conninfo_utils.py
psycopg/psycopg/connection.py
psycopg/psycopg/connection_async.py
tests/test_connection.py
tests/test_connection_async.py

diff --git a/.flake8 b/.flake8
index 79e029388048c5ca75725c6bd2ad3353bacc472f..0cc6c5ff58e135ab1c63295c8cdf378b3530ff8e 100644 (file)
--- a/.flake8
+++ b/.flake8
@@ -12,6 +12,7 @@ per-file-ignores =
     psycopg/psycopg/errors.py: E125, E128, E302
 
     # Allow concatenated string literals from async_to_sync
+    psycopg/psycopg/connection.py: E501
     psycopg_pool/psycopg_pool/pool.py: E501
 
     # Pytest's importorskip() getting in the way
index d923b53e1a743edc5f3eeb6721e7bc8afd5083d2..8b2717b47a6e0f4901e63eb1c58221c61f031359 100644 (file)
@@ -17,6 +17,16 @@ Psycopg 3.2.10 (unreleased)
   (:ticket:`#1108`).
 - Add support for Python 3.14 (:ticket:`#1053`).
 - Fix `psycopg_binary.__version__`.
+- Raise a warning if a GSSAPI connection is obtained using the
+  ``gssencmode=prefer`` libpq default (see :ticket:`#1136`).
+
+  .. warning::
+
+    In a future Psycopg version the default in the binary package will be
+    changed to ``disable``. If you need to interact with the GSSAPI reliably
+    you should explicitly set the ``gssencmode`` parameter in the connection
+    string or the :envvar:`PGGSSENCMODE` environment variable to ``prefer`` or
+    ``require``.
 
 
 Current release
index e959bbf155b32a3fe7aefbd01e9bba872146f0ca..cc5774e232d2ce502673f0290eff852fc3c58f1f 100644 (file)
@@ -120,3 +120,8 @@ def is_ip_address(s: str) -> bool:
     except ValueError:
         return False
     return True
+
+
+def gssapi_requested(params: ConnDict) -> bool:
+    """Return `true` if `gssencmode` was specified explicitly."""
+    return bool(get_param(params, "gssencmode"))
index 6d89114eed66ac245560494d2189f25a6ce49b97..ec149efcc7d1ca2717e087435cde0a7039ae1eb9 100644 (file)
@@ -10,6 +10,7 @@ Psycopg connection object (sync version)
 from __future__ import annotations
 
 import logging
+import warnings
 from time import monotonic
 from types import TracebackType
 from typing import TYPE_CHECKING, Any, Generator, Iterator, cast, overload
@@ -32,6 +33,7 @@ from .generators import notifies
 from .transaction import Transaction
 from ._capabilities import capabilities
 from ._server_cursor import ServerCursor
+from ._conninfo_utils import gssapi_requested
 from ._connection_base import BaseConnection, CursorRow, Notify
 
 if TYPE_CHECKING:
@@ -124,6 +126,16 @@ class Connection(BaseConnection[Row]):
             lines.extend((f"- {descr}: {error}" for error, descr in conn_errors))
             raise type(last_ex)("\n".join(lines)).with_traceback(None)
 
+        if (
+            pq.version() >= 160000
+            and rv.pgconn.used_gssapi
+            and (not gssapi_requested(params))
+        ):
+            warnings.warn(
+                "the connection was obtained using the GSSAPI relying on the 'gssencmode=prefer' libpq default. In a future psycopg[binary] version this default will be changed to 'disable'. If you wish to interact with the GSSAPI reliably please set the 'gssencmode' parameter in the connection string or the 'PGGSSENCMODE' environment variable to 'prefer' or 'require'",
+                DeprecationWarning,
+            )
+
         rv._autocommit = bool(autocommit)
         if row_factory:
             rv.row_factory = row_factory
index be7fda4e5295408e1f9ed50b9481b189fba58d4b..43dc8c0e86471dcce5436965aa911b4947e064a6 100644 (file)
@@ -7,6 +7,7 @@ Psycopg connection object (async version)
 from __future__ import annotations
 
 import logging
+import warnings
 from time import monotonic
 from types import TracebackType
 from typing import TYPE_CHECKING, Any, AsyncGenerator, AsyncIterator, cast, overload
@@ -27,6 +28,7 @@ from .generators import notifies
 from .transaction import AsyncTransaction
 from .cursor_async import AsyncCursor
 from ._capabilities import capabilities
+from ._conninfo_utils import gssapi_requested
 from ._connection_base import BaseConnection, CursorRow, Notify
 from ._server_cursor_async import AsyncServerCursor
 
@@ -144,6 +146,21 @@ class AsyncConnection(BaseConnection[Row]):
             lines.extend(f"- {descr}: {error}" for error, descr in conn_errors)
             raise type(last_ex)("\n".join(lines)).with_traceback(None)
 
+        if (
+            pq.version() >= 160000
+            and rv.pgconn.used_gssapi
+            and not gssapi_requested(params)
+        ):
+            warnings.warn(
+                "the connection was obtained using the GSSAPI relying on the"
+                " 'gssencmode=prefer' libpq default. In a future psycopg[binary]"
+                " version this default will be changed to 'disable'."
+                " If you wish to interact with the GSSAPI reliably please set the"
+                " 'gssencmode' parameter in the connection string or the"
+                " 'PGGSSENCMODE' environment variable to 'prefer' or 'require'",
+                DeprecationWarning,
+            )
+
         rv._autocommit = bool(autocommit)
         if row_factory:
             rv.row_factory = row_factory
index 240bba0f96fc772e5fe48195120b203a86468a3a..8115cba0b1f11411d247eacfe74184dc2b7408c2 100644 (file)
@@ -16,6 +16,7 @@ from psycopg import errors as e
 from psycopg import pq
 from psycopg.rows import tuple_row
 from psycopg.conninfo import conninfo_to_dict, timeout_from_conninfo
+from psycopg._conninfo_utils import get_param
 
 from .acompat import skip_async, skip_sync, sleep
 from .fix_crdb import crdb_anydb
@@ -961,3 +962,16 @@ def test_connect_tsa_bad(conn_cls, dsn, mode):
     params = conninfo_to_dict(dsn, target_session_attrs=mode)
     with pytest.raises(psycopg.OperationalError, match=mode):
         conn_cls.connect(**params)
+
+
+@pytest.mark.libpq(">= 16")
+@pytest.mark.skipif(pq.__impl__ != "python", reason="can't monkeypatch C module")
+def test_implicit_gssapi_warning(conn_cls, dsn, recwarn, monkeypatch):
+    if get_param(conninfo_to_dict(dsn), "gssencmode"):
+        pytest.skip("gssencmode parameter explicitly set in test connection string")
+
+    monkeypatch.setattr(pq.PGconn, "used_gssapi", lambda: True)
+    with conn_cls.connect(dsn):
+        pass
+
+    assert "gssencmode" in str(recwarn.pop(DeprecationWarning).message)
index 5583821fae2a1aae2dbe5a95e046bbf7e338bcf2..a62a7e62737392716fb8a30c2f6ff90728b5b0c4 100644 (file)
@@ -13,6 +13,7 @@ from psycopg import errors as e
 from psycopg import pq
 from psycopg.rows import tuple_row
 from psycopg.conninfo import conninfo_to_dict, timeout_from_conninfo
+from psycopg._conninfo_utils import get_param
 
 from .acompat import asleep, skip_async, skip_sync
 from .fix_crdb import crdb_anydb
@@ -966,3 +967,16 @@ async def test_connect_tsa_bad(aconn_cls, dsn, mode):
     params = conninfo_to_dict(dsn, target_session_attrs=mode)
     with pytest.raises(psycopg.OperationalError, match=mode):
         await aconn_cls.connect(**params)
+
+
+@pytest.mark.libpq(">= 16")
+@pytest.mark.skipif(pq.__impl__ != "python", reason="can't monkeypatch C module")
+async def test_implicit_gssapi_warning(aconn_cls, dsn, recwarn, monkeypatch):
+    if get_param(conninfo_to_dict(dsn), "gssencmode"):
+        pytest.skip("gssencmode parameter explicitly set in test connection string")
+
+    monkeypatch.setattr(pq.PGconn, "used_gssapi", lambda: True)
+    async with await aconn_cls.connect(dsn):
+        pass
+
+    assert "gssencmode" in str(recwarn.pop(DeprecationWarning).message)