]> git.ipfire.org Git - thirdparty/psycopg.git/commitdiff
feat: raise a warning if a GSS connection is obtained using the libpq default
authorDaniele Varrazzo <daniele.varrazzo@gmail.com>
Tue, 26 Aug 2025 01:28:16 +0000 (03:28 +0200)
committerDaniele Varrazzo <daniele.varrazzo@gmail.com>
Sun, 7 Sep 2025 14:06:51 +0000 (16:06 +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'.

Note: the warning in psycopg 3.3 is a runtime warning; in 3.2.x was a
deprectation warning.

See #1136

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

index 7ec03e50d2caa7bd84c6dc62692bb05cc9f2f506..322a8766fcfb2f673bc914eb00e88cfe1318d18a 100644 (file)
@@ -65,6 +65,16 @@ Psycopg 3.2.10 (unreleased)
 
 - 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 00d23d6ecaf12072f612dd7a1c7325f6f4332c77..8c2b32dbe5ed2ac57070e5c3fbbe005417414973 100644 (file)
@@ -34,6 +34,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:
@@ -126,6 +127,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 (
+            capabilities.has_used_gssapi()
+            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. The value for this default might be 'disable' instead, in certain psycopg[binary] implementations. 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'",
+                RuntimeWarning,
+            )
+
         rv._autocommit = bool(autocommit)
         if row_factory:
             rv.row_factory = row_factory
index dabdab036f7ec1b589becdbb784033aee61f2bb2..804f3e0f5391ee510e9297f683296aad9d8573ce 100644 (file)
@@ -30,6 +30,7 @@ from .transaction import AsyncTransaction
 from .cursor_async import AsyncCursor
 from ._capabilities import capabilities
 from ._pipeline_async import AsyncPipeline
+from ._conninfo_utils import gssapi_requested
 from ._connection_base import BaseConnection, CursorRow, Notify
 from ._server_cursor_async import AsyncServerCursor
 
@@ -142,6 +143,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 (
+            capabilities.has_used_gssapi()
+            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. The value for this default might"
+                " be 'disable' instead, in certain psycopg[binary] implementations."
+                " 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'",
+                RuntimeWarning,
+            )
+
         rv._autocommit = bool(autocommit)
         if row_factory:
             rv.row_factory = row_factory
index 240bba0f96fc772e5fe48195120b203a86468a3a..25b17119fa9c9b7e5acae551116a81b716dbe7dc 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(RuntimeWarning).message)
index 5583821fae2a1aae2dbe5a95e046bbf7e338bcf2..080909f694ebb1fbeee76c0369a0a1a962541c73 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(RuntimeWarning).message)