]> git.ipfire.org Git - thirdparty/psycopg.git/commitdiff
feat(pq): add libpq OAuth support structures and functions pg18-oauth 1116/head
authorDaniele Varrazzo <daniele.varrazzo@gmail.com>
Sat, 5 Jul 2025 21:58:41 +0000 (23:58 +0200)
committerDaniele Varrazzo <daniele.varrazzo@gmail.com>
Sat, 5 Jul 2025 22:24:05 +0000 (00:24 +0200)
psycopg/psycopg/pq/_enums.py
psycopg/psycopg/pq/_pq_ctypes.py
psycopg/psycopg/pq/_pq_ctypes.pyi

index 3f82c489e89b517da95e2c035db414606f2ced9a..54ea621160e5be7b21a736055f43a2dd1810be07 100644 (file)
@@ -257,3 +257,15 @@ class Trace(IntFlag):
 
     REGRESS_MODE = 2
     """Redact some fields, e.g. OIDs, from messages."""
+
+
+class AuthData(IntEnum):
+    """
+    Enum to represent the available OAuth hook types.
+    """
+
+    PROMPT_OAUTH_DEVICE = 0
+    """user must visit a device-authorization URL."""
+
+    OAUTH_BEARER_TOKEN = auto()
+    """Server requests an OAuth Bearer token."""
index 846b29559d909da78c88445b36c6241e91e3888c..549192e154719eea0149e9eb2d5c00658891f60b 100644 (file)
@@ -759,6 +759,66 @@ PQinitOpenSSL.argtypes = [c_int, c_int]
 PQinitOpenSSL.restype = None
 
 
+# 32.20. OAuth support
+
+
+class PGpromptOAuthDevice_struct(Structure):
+    _fields_ = [
+        ("verification_uri", c_char_p),
+        ("user_code", c_char_p),
+        ("verification_uri_complete", c_char_p),
+        ("expires_in", c_int),
+    ]
+
+
+class PGoauthBearerRequest_struct(Structure):
+    # Struct forward declaration to allow to define the callbacks
+    pass
+
+
+PGpromptOAuthDevice_ptr = POINTER(PGpromptOAuthDevice_struct)
+PGoauthBearerRequest_ptr = POINTER(PGoauthBearerRequest_struct)
+
+
+# TODO: not sure - uintptr_t on win32 in libpq source
+SOCKTYPE = c_void_p if sys.platform == "win32" else c_int
+
+PGoauthBearerRequestAsyncCB = CFUNCTYPE(
+    c_int, PGconn_ptr, PGoauthBearerRequest_ptr, POINTER(SOCKTYPE)
+)
+PGoauthBearerRequestCleanupCB = CFUNCTYPE(
+    None, PGconn_ptr, POINTER(PGoauthBearerRequest_struct)
+)
+
+PGoauthBearerRequest_struct._fields_ = [
+    ("openid_configuration", c_char_p),
+    ("scope", c_char_p),
+    ("async", PGoauthBearerRequestAsyncCB),
+    ("cleanup", PGoauthBearerRequestCleanupCB),
+    ("token", c_char_p),
+    ("user", c_void_p),
+]
+
+PQauthDataHook_type = CFUNCTYPE(c_int, c_int, PGconn_ptr, c_void_p)
+
+if libpq_version >= 180000:
+    PQsetAuthDataHook = pq.PQsetAuthDataHook
+    PQsetAuthDataHook.argtypes = [PQauthDataHook_type]
+    PQsetAuthDataHook.restype = None
+
+    PQgetAuthDataHook = pq.PQgetAuthDataHook
+    PQgetAuthDataHook.argtypes = []
+    PQgetAuthDataHook.restype = PQauthDataHook_type
+
+    PQdefaultAuthDataHook = pq.PQdefaultAuthDataHook
+    PQdefaultAuthDataHook.argtypes = [c_int, PGconn_ptr, c_void_p]
+    PQdefaultAuthDataHook.restype = c_int
+else:
+    PQsetAuthDataHook = not_supported_before("PQsetAuthDataHook", 180000)
+    PQgetAuthDataHook = not_supported_before("PQgetAuthDataHook", 180000)
+    PQdefaultAuthDataHook = not_supported_before("PQdefaultAuthDataHook", 180000)
+
+
 def generate_stub() -> None:
     import re
     from ctypes import _CFuncPtr  # type: ignore[attr-defined]
index 3936d08db53fcdb26a5964edc98d893468f5ade1..ebb42808cc4096675c242f7d4150dfdcc8e725e6 100644 (file)
@@ -134,6 +134,13 @@ def PQenterPipelineMode(pgconn: PGconn_struct | None) -> int: ...
 def PQexitPipelineMode(pgconn: PGconn_struct | None) -> int: ...
 def PQpipelineSync(pgconn: PGconn_struct | None) -> int: ...
 def PQsendFlushRequest(pgconn: PGconn_struct | None) -> int: ...
+def PQsetAuthDataHook(
+    hook: Callable[[int, PGconn_struct | None, Any], int],
+) -> None: ...
+def PQgetAuthDataHook() -> Callable[[int, PGconn_struct | None, Any], int]: ...
+def PQdefaultAuthDataHook(
+    type: int, pgconn: PGconn_struct | None, data: Any
+) -> int: ...
 
 # Autogenerated section.
 # In order to refresh, run: