- 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
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"))
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:
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
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
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
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
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)
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
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)