since-v37.diff.txt
text/plain
Filename: since-v37.diff.txt
Type: text/plain
Part: 0
1: 16f1b8fc02 ! 1: 785add8015 Add OAUTHBEARER SASL mechanism
@@ .cirrus.tasks.yml: task:
--buildtype=debug \
-Dcassert=true -Dinjection_points=true \
-Duuid=bsd -Dtcl_version=tcl86 -Ddtrace=auto \
-+ -Dbuiltin_oauth=curl \
++ -Dlibcurl=enabled \
-Dextra_lib_dirs=/usr/local/lib -Dextra_include_dirs=/usr/local/include/ \
build
EOF
@@ .cirrus.tasks.yml: LINUX_CONFIGURE_FEATURES: &LINUX_CONFIGURE_FEATURES >-
+ --with-gssapi
+ --with-icu
+ --with-ldap
++ --with-libcurl
+ --with-libxml
--with-libxslt
--with-llvm
- --with-lz4
-+ --with-builtin-oauth=curl
- --with-pam
- --with-perl
- --with-python
@@ .cirrus.tasks.yml: LINUX_CONFIGURE_FEATURES: &LINUX_CONFIGURE_FEATURES >-
+ --with-zstd
LINUX_MESON_FEATURES: &LINUX_MESON_FEATURES >-
++ -Dlibcurl=enabled
-Dllvm=enabled
-+ -Dbuiltin_oauth=curl
-Duuid=e2fs
-
@@ .cirrus.tasks.yml: task:
EOF
@@ config/programs.m4: if test "$pgac_cv_ldap_safe" != yes; then
*** also uses LDAP will crash on exit.])
fi])
-+# PGAC_CHECK_LIBCURL
-+# ------------------
-+# Check for libcurl 7.61.0 or higher (corresponding to RHEL8 and the ability to
-+# explicitly set TLS 1.3 ciphersuites).
-
-+AC_DEFUN([PGAC_CHECK_LIBCURL],
-+[AC_CACHE_CHECK([for compatible libcurl], [pgac_cv_check_libcurl],
-+[AC_COMPILE_IFELSE([AC_LANG_PROGRAM(
-+[#include <curl/curlver.h>
-+#if LIBCURL_VERSION_MAJOR < 7 || (LIBCURL_VERSION_MAJOR == 7 && LIBCURL_VERSION_MINOR < 61)
-+choke me
-+#endif], [])],
-+[pgac_cv_check_libcurl=yes],
-+[pgac_cv_check_libcurl=no])])
-+
-+if test "$pgac_cv_check_libcurl" != yes; then
-+ AC_MSG_ERROR([
-+*** The installed version of libcurl is too old to use with PostgreSQL.
-+*** libcurl version 7.61.0 or later is required.])
-+fi])
-
+-
+-
# PGAC_CHECK_READLINE
# -------------------
+ # Check for the readline library and dependent libraries, either
## configure ##
-@@ configure: with_uuid
+@@ configure: XML2_LIBS
+ XML2_CFLAGS
+ XML2_CONFIG
+ with_libxml
++LIBCURL_LIBS
++LIBCURL_CFLAGS
++with_libcurl
+ with_uuid
with_readline
with_systemd
- with_selinux
-+with_builtin_oauth
- with_ldap
- with_krb_srvnam
- krb_srvtab
-@@ configure: with_krb_srvnam
- with_pam
- with_bsd_auth
- with_ldap
-+with_builtin_oauth
- with_bonjour
- with_selinux
- with_systemd
+@@ configure: with_readline
+ with_libedit_preferred
+ with_uuid
+ with_ossp_uuid
++with_libcurl
+ with_libxml
+ with_libxslt
+ with_system_tzdata
+@@ configure: PKG_CONFIG_PATH
+ PKG_CONFIG_LIBDIR
+ ICU_CFLAGS
+ ICU_LIBS
++LIBCURL_CFLAGS
++LIBCURL_LIBS
+ XML2_CONFIG
+ XML2_CFLAGS
+ XML2_LIBS
@@ configure: Optional Packages:
- --with-pam build with PAM support
- --with-bsd-auth build with BSD Authentication support
- --with-ldap build with LDAP support
-+ --with-builtin-oauth=LIB
-+ use LIB for built-in OAuth 2.0 client flows (curl)
- --with-bonjour build with Bonjour support
- --with-selinux build with SELinux support
- --with-systemd build with systemd support
-@@ configure: $as_echo "$with_ldap" >&6; }
+ prefer BSD Libedit over GNU Readline
+ --with-uuid=LIB build contrib/uuid-ossp using LIB (bsd,e2fs,ossp)
+ --with-ossp-uuid obsolete spelling of --with-uuid=ossp
++ --with-libcurl build with libcurl support for OAuth client flows
+ --with-libxml build with XML support
+ --with-libxslt use XSLT support when building contrib/xml2
+ --with-system-tzdata=DIR
+@@ configure: Some influential environment variables:
+ path overriding pkg-config's built-in search path
+ ICU_CFLAGS C compiler flags for ICU, overriding pkg-config
+ ICU_LIBS linker flags for ICU, overriding pkg-config
++ LIBCURL_CFLAGS
++ C compiler flags for LIBCURL, overriding pkg-config
++ LIBCURL_LIBS
++ linker flags for LIBCURL, overriding pkg-config
+ XML2_CONFIG path to xml2-config utility
+ XML2_CFLAGS C compiler flags for XML2, overriding pkg-config
+ XML2_LIBS linker flags for XML2, overriding pkg-config
+@@ configure: fi
+#
-+# OAuth 2.0
++# libcurl
+#
-+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether to build with built-in OAuth client support" >&5
-+$as_echo_n "checking whether to build with built-in OAuth client support... " >&6; }
++{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether to build with libcurl support for OAuth client flows" >&5
++$as_echo_n "checking whether to build with libcurl support for OAuth client flows... " >&6; }
+
+
+
-+# Check whether --with-builtin-oauth was given.
-+if test "${with_builtin_oauth+set}" = set; then :
-+ withval=$with_builtin_oauth;
++# Check whether --with-libcurl was given.
++if test "${with_libcurl+set}" = set; then :
++ withval=$with_libcurl;
+ case $withval in
+ yes)
-+ as_fn_error $? "argument required for --with-builtin-oauth option" "$LINENO" 5
++
++$as_echo "#define USE_LIBCURL 1" >>confdefs.h
++
+ ;;
+ no)
-+ as_fn_error $? "argument required for --with-builtin-oauth option" "$LINENO" 5
++ :
+ ;;
+ *)
-+
++ as_fn_error $? "no argument expected for --with-libcurl option" "$LINENO" 5
+ ;;
+ esac
+
++else
++ with_libcurl=no
++
+fi
+
+
-+if test x"$with_builtin_oauth" = x"" ; then
-+ with_builtin_oauth=no
++{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $with_libcurl" >&5
++$as_echo "$with_libcurl" >&6; }
++
++
++if test "$with_libcurl" = yes ; then
++ # Check for libcurl 7.61.0 or higher (corresponding to RHEL8 and the ability
++ # to explicitly set TLS 1.3 ciphersuites).
++
++pkg_failed=no
++{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for libcurl >= 7.61.0" >&5
++$as_echo_n "checking for libcurl >= 7.61.0... " >&6; }
++
++if test -n "$LIBCURL_CFLAGS"; then
++ pkg_cv_LIBCURL_CFLAGS="$LIBCURL_CFLAGS"
++ elif test -n "$PKG_CONFIG"; then
++ if test -n "$PKG_CONFIG" && \
++ { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"libcurl >= 7.61.0\""; } >&5
++ ($PKG_CONFIG --exists --print-errors "libcurl >= 7.61.0") 2>&5
++ ac_status=$?
++ $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
++ test $ac_status = 0; }; then
++ pkg_cv_LIBCURL_CFLAGS=`$PKG_CONFIG --cflags "libcurl >= 7.61.0" 2>/dev/null`
++ test "x$?" != "x0" && pkg_failed=yes
++else
++ pkg_failed=yes
++fi
++ else
++ pkg_failed=untried
++fi
++if test -n "$LIBCURL_LIBS"; then
++ pkg_cv_LIBCURL_LIBS="$LIBCURL_LIBS"
++ elif test -n "$PKG_CONFIG"; then
++ if test -n "$PKG_CONFIG" && \
++ { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"libcurl >= 7.61.0\""; } >&5
++ ($PKG_CONFIG --exists --print-errors "libcurl >= 7.61.0") 2>&5
++ ac_status=$?
++ $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
++ test $ac_status = 0; }; then
++ pkg_cv_LIBCURL_LIBS=`$PKG_CONFIG --libs "libcurl >= 7.61.0" 2>/dev/null`
++ test "x$?" != "x0" && pkg_failed=yes
++else
++ pkg_failed=yes
+fi
++ else
++ pkg_failed=untried
++fi
++
+
-+if test x"$with_builtin_oauth" = x"curl"; then
+
-+$as_echo "#define USE_BUILTIN_OAUTH 1" >>confdefs.h
++if test $pkg_failed = yes; then
++ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
++$as_echo "no" >&6; }
+
++if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then
++ _pkg_short_errors_supported=yes
++else
++ _pkg_short_errors_supported=no
++fi
++ if test $_pkg_short_errors_supported = yes; then
++ LIBCURL_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "libcurl >= 7.61.0" 2>&1`
++ else
++ LIBCURL_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "libcurl >= 7.61.0" 2>&1`
++ fi
++ # Put the nasty error message in config.log where it belongs
++ echo "$LIBCURL_PKG_ERRORS" >&5
++
++ as_fn_error $? "Package requirements (libcurl >= 7.61.0) were not met:
++
++$LIBCURL_PKG_ERRORS
++
++Consider adjusting the PKG_CONFIG_PATH environment variable if you
++installed software in a non-standard prefix.
++
++Alternatively, you may set the environment variables LIBCURL_CFLAGS
++and LIBCURL_LIBS to avoid the need to call pkg-config.
++See the pkg-config man page for more details." "$LINENO" 5
++elif test $pkg_failed = untried; then
++ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
++$as_echo "no" >&6; }
++ { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
++$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
++as_fn_error $? "The pkg-config script could not be found or is too old. Make sure it
++is in your PATH or set the PKG_CONFIG environment variable to the full
++path to pkg-config.
++
++Alternatively, you may set the environment variables LIBCURL_CFLAGS
++and LIBCURL_LIBS to avoid the need to call pkg-config.
++See the pkg-config man page for more details.
++
++To get pkg-config, see <http://pkg-config.freedesktop.org/>.
++See \`config.log' for more details" "$LINENO" 5; }
++else
++ LIBCURL_CFLAGS=$pkg_cv_LIBCURL_CFLAGS
++ LIBCURL_LIBS=$pkg_cv_LIBCURL_LIBS
++ { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
++$as_echo "yes" >&6; }
+
-+$as_echo "#define USE_OAUTH_CURL 1" >>confdefs.h
++fi
+
+ # OAuth requires python for testing
+ if test "$with_python" != yes; then
-+ { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: *** OAuth support tests requires --with-python to run" >&5
-+$as_echo "$as_me: WARNING: *** OAuth support tests requires --with-python to run" >&2;}
++ { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: *** OAuth support tests require --with-python to run" >&5
++$as_echo "$as_me: WARNING: *** OAuth support tests require --with-python to run" >&2;}
+ fi
-+elif test x"$with_builtin_oauth" != x"no"; then
-+ as_fn_error $? "--with-builtin-oauth must specify curl" "$LINENO" 5
+fi
+
-+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $with_builtin_oauth" >&5
-+$as_echo "$with_builtin_oauth" >&6; }
-+
-+
+
#
- # Bonjour
+ # XML
#
@@ configure: fi
@@ configure: fi
+# XXX libcurl must link after libgssapi_krb5 on FreeBSD to avoid segfaults
+# during gss_acquire_cred(). This is possibly related to Curl's Heimdal
+# dependency on that platform?
-+if test "$with_builtin_oauth" = curl ; then
++if test "$with_libcurl" = yes ; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for curl_multi_init in -lcurl" >&5
+$as_echo_n "checking for curl_multi_init in -lcurl... " >&6; }
+if ${ac_cv_lib_curl_curl_multi_init+:} false; then :
@@ configure: fi
+ LIBS="-lcurl $LIBS"
+
+else
-+ as_fn_error $? "library 'curl' is required for --with-builtin-oauth=curl" "$LINENO" 5
-+fi
-+
-+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for compatible libcurl" >&5
-+$as_echo_n "checking for compatible libcurl... " >&6; }
-+if ${pgac_cv_check_libcurl+:} false; then :
-+ $as_echo_n "(cached) " >&6
-+else
-+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
-+/* end confdefs.h. */
-+#include <curl/curlver.h>
-+#if LIBCURL_VERSION_MAJOR < 7 || (LIBCURL_VERSION_MAJOR == 7 && LIBCURL_VERSION_MINOR < 61)
-+choke me
-+#endif
-+int
-+main ()
-+{
-+
-+ ;
-+ return 0;
-+}
-+_ACEOF
-+if ac_fn_c_try_compile "$LINENO"; then :
-+ pgac_cv_check_libcurl=yes
-+else
-+ pgac_cv_check_libcurl=no
-+fi
-+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
++ as_fn_error $? "library 'curl' is required for --with-libcurl" "$LINENO" 5
+fi
-+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $pgac_cv_check_libcurl" >&5
-+$as_echo "$pgac_cv_check_libcurl" >&6; }
+
-+if test "$pgac_cv_check_libcurl" != yes; then
-+ as_fn_error $? "
-+*** The installed version of libcurl is too old to use with PostgreSQL.
-+*** libcurl version 7.61.0 or later is required." "$LINENO" 5
-+fi
+fi
+
if test "$with_gssapi" = yes ; then
@@ configure: fi
+fi
+
-+if test "$with_builtin_oauth" = curl; then
++if test "$with_libcurl" = yes; then
+ ac_fn_c_check_header_mongrel "$LINENO" "curl/curl.h" "ac_cv_header_curl_curl_h" "$ac_includes_default"
+if test "x$ac_cv_header_curl_curl_h" = xyes; then :
+
+else
-+ as_fn_error $? "header file <curl/curl.h> is required for --with-builtin-oauth=curl" "$LINENO" 5
++ as_fn_error $? "header file <curl/curl.h> is required for --with-libcurl" "$LINENO" 5
+fi
+
+
@@ configure: fi
if test "$PORTNAME" = "win32" ; then
## configure.ac ##
-@@ configure.ac: AC_MSG_RESULT([$with_ldap])
- AC_SUBST(with_ldap)
+@@ configure.ac: fi
+ AC_SUBST(with_uuid)
+#
-+# OAuth 2.0
++# libcurl
+#
-+AC_MSG_CHECKING([whether to build with built-in OAuth client support])
-+PGAC_ARG_REQ(with, builtin-oauth, [LIB], [use LIB for built-in OAuth 2.0 client flows (curl)])
-+if test x"$with_builtin_oauth" = x"" ; then
-+ with_builtin_oauth=no
-+fi
++AC_MSG_CHECKING([whether to build with libcurl support for OAuth client flows])
++PGAC_ARG_BOOL(with, libcurl, no, [build with libcurl support for OAuth client flows],
++ [AC_DEFINE([USE_LIBCURL], 1, [Define to 1 to build with libcurl support for OAuth client flows. (--with-libcurl)])])
++AC_MSG_RESULT([$with_libcurl])
++AC_SUBST(with_libcurl)
++
++if test "$with_libcurl" = yes ; then
++ # Check for libcurl 7.61.0 or higher (corresponding to RHEL8 and the ability
++ # to explicitly set TLS 1.3 ciphersuites).
++ PKG_CHECK_MODULES(LIBCURL, [libcurl >= 7.61.0])
+
-+if test x"$with_builtin_oauth" = x"curl"; then
-+ AC_DEFINE([USE_BUILTIN_OAUTH], 1, [Define to 1 to build with OAuth 2.0 client flows. (--with-builtin-oauth)])
-+ AC_DEFINE([USE_OAUTH_CURL], 1, [Define to 1 to use libcurl for OAuth client flows.])
+ # OAuth requires python for testing
+ if test "$with_python" != yes; then
-+ AC_MSG_WARN([*** OAuth support tests requires --with-python to run])
++ AC_MSG_WARN([*** OAuth support tests require --with-python to run])
+ fi
-+elif test x"$with_builtin_oauth" != x"no"; then
-+ AC_MSG_ERROR([--with-builtin-oauth must specify curl])
+fi
+
-+AC_MSG_RESULT([$with_builtin_oauth])
-+AC_SUBST(with_builtin_oauth)
-+
+
#
- # Bonjour
+ # XML
#
@@ configure.ac: failure. It is possible the compiler isn't looking in the proper directory.
Use --without-zlib to disable zlib support.])])
@@ configure.ac: failure. It is possible the compiler isn't looking in the proper
+# XXX libcurl must link after libgssapi_krb5 on FreeBSD to avoid segfaults
+# during gss_acquire_cred(). This is possibly related to Curl's Heimdal
+# dependency on that platform?
-+if test "$with_builtin_oauth" = curl ; then
-+ AC_CHECK_LIB(curl, curl_multi_init, [], [AC_MSG_ERROR([library 'curl' is required for --with-builtin-oauth=curl])])
-+ PGAC_CHECK_LIBCURL
++if test "$with_libcurl" = yes ; then
++ AC_CHECK_LIB(curl, curl_multi_init, [], [AC_MSG_ERROR([library 'curl' is required for --with-libcurl])])
+fi
+
if test "$with_gssapi" = yes ; then
@@ configure.ac: elif test "$with_uuid" = ossp ; then
[AC_MSG_ERROR([header file <ossp/uuid.h> or <uuid.h> is required for OSSP UUID])])])
fi
-+if test "$with_builtin_oauth" = curl; then
-+ AC_CHECK_HEADER(curl/curl.h, [], [AC_MSG_ERROR([header file <curl/curl.h> is required for --with-builtin-oauth=curl])])
++if test "$with_libcurl" = yes; then
++ AC_CHECK_HEADER(curl/curl.h, [], [AC_MSG_ERROR([header file <curl/curl.h> is required for --with-libcurl])])
+fi
+
if test "$PORTNAME" = "win32" ; then
@@ doc/src/sgml/installation.sgml: build-postgresql:
</listitem>
</varlistentry>
-+ <varlistentry id="configure-option-with-builtin-oauth">
-+ <term><option>--with-builtin-oauth=<replaceable>LIBRARY</replaceable></option></term>
++ <varlistentry id="configure-option-with-libcurl">
++ <term><option>--with-libcurl</option></term>
+ <listitem>
+ <para>
-+ Build with support for OAuth 2.0 client flows. The only
-+ <replaceable>LIBRARY</replaceable> supported is <option>curl</option>.
++ Build with libcurl support for OAuth 2.0 client flows.
+ This requires the <productname>curl</productname> package to be
+ installed. Building with this will check for the required header files
+ and libraries to make sure that your <productname>curl</productname>
@@ doc/src/sgml/installation.sgml: build-postgresql:
+ </listitem>
+ </varlistentry>
+
- <varlistentry id="configure-option-with-systemd">
- <term><option>--with-systemd</option></term>
+ <varlistentry id="configure-option-with-libxml">
+ <term><option>--with-libxml</option></term>
<listitem>
@@ doc/src/sgml/installation.sgml: ninja install
</listitem>
</varlistentry>
-+ <varlistentry id="configure-with-builtin-oauth">
-+ <term><option>-Dbuiltin_oauth={ auto | <replaceable>LIBRARY</replaceable> | disabled }</option></term>
++ <varlistentry id="configure-with-libcurl-meson">
++ <term><option>-Dlibcurl={ auto | enabled | disabled }</option></term>
+ <listitem>
+ <para>
-+ Build with support for OAuth 2.0 client flows. The only
-+ <replaceable>LIBRARY</replaceable> supported is <option>curl</option>.
++ Build with libcurl support for OAuth 2.0 client flows.
+ This requires the <productname>curl</productname> package to be
+ installed. Building with this will check for the required header files
+ and libraries to make sure that your <productname>curl</productname>
@@ doc/src/sgml/installation.sgml: ninja install
+ </listitem>
+ </varlistentry>
+
- <varlistentry id="configure-with-systemd-meson">
- <term><option>-Dsystemd={ auto | enabled | disabled }</option></term>
+ <varlistentry id="configure-with-libxml-meson">
+ <term><option>-Dlibxml={ auto | enabled | disabled }</option></term>
<listitem>
## doc/src/sgml/libpq.sgml ##
@@ meson.build: endif
+###############################################################
-+# Library: OAuth (libcurl)
++# Library: libcurl
+###############################################################
+
-+libcurl = not_found_dep
-+oauth_library = 'none'
-+oauthopt = get_option('builtin_oauth')
-+
-+if oauthopt == 'auto' and auto_features.disabled()
-+ oauthopt = 'none'
-+endif
-+
-+if oauthopt in ['auto', 'curl']
++libcurlopt = get_option('libcurl')
++if not libcurlopt.disabled()
+ # Check for libcurl 7.61.0 or higher (corresponding to RHEL8 and the ability
+ # to explicitly set TLS 1.3 ciphersuites).
-+ libcurl = dependency('libcurl', version: '>= 7.61.0',
-+ required: (oauthopt == 'curl'))
++ libcurl = dependency('libcurl', version: '>= 7.61.0', required: libcurlopt)
+ if libcurl.found()
-+ oauth_library = 'curl'
-+ cdata.set('USE_BUILTIN_OAUTH', 1)
-+ cdata.set('USE_OAUTH_CURL', 1)
++ cdata.set('USE_LIBCURL', 1)
+ endif
++else
++ libcurl = not_found_dep
+endif
+
-+if oauthopt == 'auto' and auto_features.enabled() and not libcurl.found()
-+ error('no OAuth implementation library found')
-+endif
+
+
###############################################################
- # Library: Tcl (for pltcl)
- #
+ # Library: libxml
+ ###############################################################
@@ meson.build: libpq_deps += [
gssapi,
@@ meson.build: if meson.version().version_compare('>=0.57')
'llvm': llvm,
## meson_options.txt ##
-@@ meson_options.txt: option('bonjour', type: 'feature', value: 'auto',
- option('bsd_auth', type: 'feature', value: 'auto',
- description: 'BSD Authentication support')
+@@ meson_options.txt: option('icu', type: 'feature', value: 'auto',
+ option('ldap', type: 'feature', value: 'auto',
+ description: 'LDAP support')
-+option('builtin_oauth', type : 'combo', choices : ['auto', 'none', 'curl'],
-+ value: 'auto',
-+ description: 'use LIB for built-in OAuth 2.0 client flows (curl)')
++option('libcurl', type : 'feature', value: 'auto',
++ description: 'libcurl support for OAuth client flows')
+
- option('docs', type: 'feature', value: 'auto',
- description: 'Documentation in HTML and man page format')
+ option('libedit_preferred', type: 'boolean', value: false,
+ description: 'Prefer BSD Libedit over GNU Readline')
## src/Makefile.global.in ##
-@@ src/Makefile.global.in: with_ldap = @with_ldap@
+@@ src/Makefile.global.in: with_systemd = @with_systemd@
+ with_gssapi = @with_gssapi@
+ with_krb_srvnam = @with_krb_srvnam@
+ with_ldap = @with_ldap@
++with_libcurl = @with_libcurl@
with_libxml = @with_libxml@
with_libxslt = @with_libxslt@
with_llvm = @with_llvm@
-+with_builtin_oauth = @with_builtin_oauth@
- with_system_tzdata = @with_system_tzdata@
- with_uuid = @with_uuid@
- with_zlib = @with_zlib@
## src/backend/libpq/Makefile ##
@@ src/backend/libpq/Makefile: include $(top_builddir)/src/Makefile.global
@@ src/backend/libpq/auth-oauth.c (new)
+
+/* Mechanism declaration */
+const pg_be_sasl_mech pg_be_oauth_mech = {
-+ oauth_get_mechanisms,
-+ oauth_init,
-+ oauth_exchange,
++ .get_mechanisms = oauth_get_mechanisms,
++ .init = oauth_init,
++ .exchange = oauth_exchange,
+
-+ PG_MAX_AUTH_TOKEN_LENGTH,
++ .max_message_length = PG_MAX_AUTH_TOKEN_LENGTH,
+};
+
+
@@ src/backend/libpq/auth-oauth.c (new)
+ value = sep + 1;
+ validate_kvpair(key, value);
+
-+ if (!strcmp(key, AUTH_KEY))
++ if (strcmp(key, AUTH_KEY) == 0)
+ {
+ if (auth)
+ ereport(ERROR,
@@ src/backend/libpq/auth-oauth.c (new)
+ if (!(token = validate_token_format(auth)))
+ return false;
+
++ /*
++ * Ensure that we have a validation library loaded, this should always be
++ * the case and an error here is indicative of a bug.
++ */
++ if (!ValidatorCallbacks || !ValidatorCallbacks->validate_cb)
++ ereport(FATAL,
++ errcode(ERRCODE_INTERNAL_ERROR),
++ errmsg("validation of OAuth token requested without a validator loaded"));
++
+ /* Call the validation function from the validator module */
+ ret = ValidatorCallbacks->validate_cb(validator_module_state,
+ token, port->user_name);
@@ src/backend/libpq/auth-oauth.c (new)
+ "OAuth validator", libname, "_PG_oauth_validator_module_init"));
+
+ ValidatorCallbacks = (*validator_init) ();
++ Assert(ValidatorCallbacks);
+
+ /* Allocate memory for validator library private state data */
+ validator_module_state = (ValidatorModuleState *) palloc0(sizeof(ValidatorModuleState));
@@ src/backend/libpq/auth-oauth.c (new)
+ char *file_name = hbaline->sourcefile;
+ char *rawstring;
+ List *elemlist = NIL;
-+ ListCell *l;
+
+ *err_msg = NULL;
+
@@ src/backend/libpq/auth-oauth.c (new)
+ goto done;
+ }
+
-+ foreach(l, elemlist)
++ foreach_ptr(char, allowed, elemlist)
+ {
-+ char *allowed = lfirst(l);
-+
+ if (strcmp(allowed, hbaline->oauth_validator) == 0)
+ goto done;
+ }
@@ src/include/pg_config.h.in
#undef HAVE_LIBLDAP
@@
- /* Define to 1 to build with BSD Authentication support. (--with-bsd-auth) */
- #undef USE_BSD_AUTH
+ /* Define to 1 to build with LDAP support. (--with-ldap) */
+ #undef USE_LDAP
-+/* Define to 1 to build with OAuth 2.0 client flows. (--with-builtin-oauth) */
-+#undef USE_BUILTIN_OAUTH
++/* Define to 1 to build with libcurl support for OAuth client flows.
++ (--with-libcurl) */
++#undef USE_LIBCURL
+
- /* Define to build with ICU support. (--with-icu) */
- #undef USE_ICU
-
-@@
- /* Define to select named POSIX semaphores. */
- #undef USE_NAMED_POSIX_SEMAPHORES
-
-+/* Define to 1 to use libcurl for OAuth client flows. */
-+#undef USE_OAUTH_CURL
-+
- /* Define to 1 to build with OpenSSL support. (--with-ssl=openssl) */
- #undef USE_OPENSSL
+ /* Define to 1 to build with XML support. (--with-libxml) */
+ #undef USE_LIBXML
## src/interfaces/libpq/Makefile ##
@@ src/interfaces/libpq/Makefile: OBJS += \
fe-secure-gssapi.o
endif
-+ifeq ($(with_builtin_oauth),curl)
++ifeq ($(with_libcurl),yes)
+OBJS += fe-auth-oauth-curl.o
+endif
+
@@ src/interfaces/libpq/fe-auth-oauth-curl.c (new)
+ {
+ char **scalar; /* for all scalar types */
+ struct curl_slist **array; /* for type == JSON_TOKEN_ARRAY_START */
-+ };
++ } target;
+
+ bool required; /* REQUIRED field, or just OPTIONAL? */
+};
@@ src/interfaces/libpq/fe-auth-oauth-curl.c (new)
+ }
+ }
+
-+ free(name);
+ return JSON_SUCCESS;
+}
+
@@ src/interfaces/libpq/fe-auth-oauth-curl.c (new)
+ struct oauth_parse *ctx = state;
+
+ --ctx->nested;
++ if (!ctx->nested)
++ Assert(!ctx->active); /* all fields should be fully processed */
++
+ return JSON_SUCCESS;
+}
+
@@ src/interfaces/libpq/fe-auth-oauth-curl.c (new)
+oauth_json_scalar(void *state, char *token, JsonTokenType type)
+{
+ struct oauth_parse *ctx = state;
-+ JsonParseErrorType result = JSON_SUCCESS;
+
+ if (!ctx->nested)
+ {
+ oauth_parse_set_error(ctx, "top-level element must be an object");
-+ result = JSON_SEM_ACTION_FAILED;
-+ goto cleanup;
++ return JSON_SEM_ACTION_FAILED;
+ }
+
+ if (ctx->active)
+ {
-+ JsonTokenType expected;
++ const struct json_field *field = ctx->active;
++ JsonTokenType expected = field->type;
+
-+ /*
-+ * Make sure this matches what the active field expects. Arrays must
-+ * contain only strings with the current implementation.
-+ */
-+ if (ctx->active->type == JSON_TOKEN_ARRAY_START)
++ /* Make sure this matches what the active field expects. */
++ if (expected == JSON_TOKEN_ARRAY_START)
++ {
++ /* Are we actually inside an array? */
++ if (ctx->nested < 2)
++ {
++ report_type_mismatch(ctx);
++ return JSON_SEM_ACTION_FAILED;
++ }
++
++ /* Currently, arrays can only contain strings. */
+ expected = JSON_TOKEN_STRING;
-+ else
-+ expected = ctx->active->type;
++ }
+
+ if (type != expected)
+ {
+ report_type_mismatch(ctx);
-+ result = JSON_SEM_ACTION_FAILED;
-+ goto cleanup;
++ return JSON_SEM_ACTION_FAILED;
+ }
+
+ /*
-+ * FIXME if the JSON field is duplicated, we'll leak the prior value.
-+ * Error out in that case instead.
++ * We don't allow duplicate field names; error out if the target has
++ * already been set.
+ */
-+ if (ctx->active->type != JSON_TOKEN_ARRAY_START)
++ if ((field->type == JSON_TOKEN_ARRAY_START && *field->target.array)
++ || (field->type != JSON_TOKEN_ARRAY_START && *field->target.scalar))
++ {
++ oauth_parse_set_error(ctx, "field \"%s\" is duplicated",
++ field->name);
++ return JSON_SEM_ACTION_FAILED;
++ }
++
++ if (field->type != JSON_TOKEN_ARRAY_START)
+ {
+ Assert(ctx->nested == 1);
+
-+ *ctx->active->scalar = token;
++ *field->target.scalar = strdup(token);
++ if (!*field->target.scalar)
++ return JSON_OUT_OF_MEMORY;
++
+ ctx->active = NULL;
+
-+ return JSON_SUCCESS; /* don't free the token */
++ return JSON_SUCCESS;
+ }
-+ else /* ctx->target_array */
++ else
+ {
+ struct curl_slist *temp;
+
+ Assert(ctx->nested == 2);
+
-+ temp = curl_slist_append(*ctx->active->array, token);
++ /* Note that curl_slist_append() makes a copy of the token. */
++ temp = curl_slist_append(*field->target.array, token);
+ if (!temp)
-+ {
-+ oauth_parse_set_error(ctx, "out of memory");
-+ result = JSON_SEM_ACTION_FAILED;
-+ goto cleanup;
-+ }
++ return JSON_OUT_OF_MEMORY;
+
-+ *ctx->active->array = temp;
-+
-+ /*
-+ * Note that curl_slist_append() makes a copy of the token, so we
-+ * can free it below.
-+ */
++ *field->target.array = temp;
+ }
+ }
+ else
@@ src/interfaces/libpq/fe-auth-oauth-curl.c (new)
+ /* otherwise we just ignore it */
+ }
+
-+cleanup:
-+ free(token);
-+ return result;
++ return JSON_SUCCESS;
+}
+
+/*
@@ src/interfaces/libpq/fe-auth-oauth-curl.c (new)
+ return false;
+ }
+
++ /*
++ * pg_parse_json doesn't validate the incoming UTF-8, so we have to check
++ * that up front.
++ */
++ if (pg_encoding_verifymbstr(PG_UTF8, resp->data, resp->len) != resp->len)
++ {
++ actx_error(actx, "response is not valid UTF-8");
++ return false;
++ }
++
+ makeJsonLexContextCstringLen(&lex, resp->data, resp->len, PG_UTF8, true);
++ setJsonLexContextOwnsTokens(&lex, true); /* must not leak on error */
+
+ ctx.errbuf = &actx->errbuf;
+ ctx.fields = fields;
@@ src/interfaces/libpq/fe-auth-oauth-curl.c (new)
+ /* Check all required fields. */
+ while (fields->name)
+ {
-+ if (fields->required && !*fields->scalar && !*fields->array)
++ if (fields->required
++ && !*fields->target.scalar
++ && !*fields->target.array)
+ {
+ actx_error(actx, "field \"%s\" is missing", fields->name);
+ goto cleanup;
@@ src/interfaces/libpq/fe-auth-oauth-curl.c (new)
+ escaped = curl_easy_escape(NULL, s, 0);
+ if (!escaped)
+ {
-+ markPQExpBufferBroken(buf);
++ termPQExpBuffer(buf); /* mark the buffer broken */
+ return;
+ }
+
@@ src/interfaces/libpq/fe-auth-oauth.c (new)
+ }
+ }
+
-+ free(name);
+ return JSON_SUCCESS;
+}
+
@@ src/interfaces/libpq/fe-auth-oauth.c (new)
+ if (!ctx->nested)
+ {
+ ctx->errmsg = libpq_gettext("top-level element must be an object");
++ return JSON_SEM_ACTION_FAILED;
+ }
-+ else if (ctx->target_field)
++
++ if (ctx->target_field)
+ {
+ Assert(ctx->nested == 1);
+
-+ if (type == JSON_TOKEN_STRING)
++ /*
++ * We don't allow duplicate field names; error out if the target has
++ * already been set.
++ */
++ if (*ctx->target_field)
+ {
-+ *ctx->target_field = token;
-+
-+ ctx->target_field = NULL;
-+ ctx->target_field_name = NULL;
++ oauth_json_set_error(ctx,
++ libpq_gettext("field \"%s\" is duplicated"),
++ ctx->target_field_name);
++ return JSON_SEM_ACTION_FAILED;
++ }
+
-+ return JSON_SUCCESS; /* don't free the token we're using */
++ /* The only fields we support are strings. */
++ if (type != JSON_TOKEN_STRING)
++ {
++ oauth_json_set_error(ctx,
++ libpq_gettext("field \"%s\" must be a string"),
++ ctx->target_field_name);
++ return JSON_SEM_ACTION_FAILED;
+ }
+
-+ oauth_json_set_error(ctx,
-+ libpq_gettext("field \"%s\" must be a string"),
-+ ctx->target_field_name);
++ *ctx->target_field = strdup(token);
++ if (!*ctx->target_field)
++ return JSON_OUT_OF_MEMORY;
++
++ ctx->target_field = NULL;
++ ctx->target_field_name = NULL;
++ }
++ else
++ {
++ /* otherwise we just ignore it */
+ }
+
-+ free(token);
-+ return oauth_json_has_error(ctx) ? JSON_SEM_ACTION_FAILED : JSON_SUCCESS;
++ return JSON_SUCCESS;
+}
+
+#define HTTPS_SCHEME "https://"
@@ src/interfaces/libpq/fe-auth-oauth.c (new)
+}
+
+static bool
-+handle_oauth_sasl_error(PGconn *conn, char *msg, int msglen)
++handle_oauth_sasl_error(PGconn *conn, const char *msg, int msglen)
+{
+ JsonLexContext lex = {0};
+ JsonSemAction sem = {0};
+ JsonParseErrorType err;
+ struct json_ctx ctx = {0};
+ char *errmsg = NULL;
++ bool success = false;
++
++ Assert(conn->oauth_issuer_id); /* ensured by setup_oauth_parameters() */
+
+ /* Sanity check. */
+ if (strlen(msg) != msglen)
@@ src/interfaces/libpq/fe-auth-oauth.c (new)
+ return false;
+ }
+
++ /*
++ * pg_parse_json doesn't validate the incoming UTF-8, so we have to check
++ * that up front.
++ */
++ if (pg_encoding_verifymbstr(PG_UTF8, msg, msglen) != msglen)
++ {
++ libpq_append_conn_error(conn,
++ "server's error response is not valid UTF-8");
++ return false;
++ }
++
+ makeJsonLexContextCstringLen(&lex, msg, msglen, PG_UTF8, true);
++ setJsonLexContextOwnsTokens(&lex, true); /* must not leak on error */
+
+ initPQExpBuffer(&ctx.errbuf);
+ sem.semstate = &ctx;
@@ src/interfaces/libpq/fe-auth-oauth.c (new)
+ freeJsonLexContext(&lex);
+
+ if (errmsg)
-+ return false;
++ goto cleanup;
+
+ /* TODO: what if these override what the user already specified? */
+ /* TODO: what if there's no discovery URI? */
@@ src/interfaces/libpq/fe-auth-oauth.c (new)
+ /* The URI must correspond to our existing issuer, to avoid mix-ups. */
+ discovery_issuer = issuer_from_well_known_uri(conn, ctx.discovery_uri);
+ if (!discovery_issuer)
-+ return false; /* error message already set */
++ goto cleanup; /* error message already set */
+
+ if (strcmp(conn->oauth_issuer_id, discovery_issuer) != 0)
+ {
@@ src/interfaces/libpq/fe-auth-oauth.c (new)
+ conn->oauth_issuer_id);
+
+ free(discovery_issuer);
-+ return false;
++ goto cleanup;
+ }
+
+ free(discovery_issuer);
@@ src/interfaces/libpq/fe-auth-oauth.c (new)
+ free(conn->oauth_discovery_uri);
+
+ conn->oauth_discovery_uri = ctx.discovery_uri;
++ ctx.discovery_uri = NULL;
+ }
+
+ if (ctx.scope)
@@ src/interfaces/libpq/fe-auth-oauth.c (new)
+ free(conn->oauth_scope);
+
+ conn->oauth_scope = ctx.scope;
++ ctx.scope = NULL;
+ }
+ /* TODO: missing error scope should clear any existing connection scope */
+
@@ src/interfaces/libpq/fe-auth-oauth.c (new)
+ {
+ libpq_append_conn_error(conn,
+ "server sent error response without a status");
-+ return false;
++ goto cleanup;
+ }
+
+ if (strcmp(ctx.status, "invalid_token") == 0)
@@ src/interfaces/libpq/fe-auth-oauth.c (new)
+ }
+ /* TODO: include status in hard failure message */
+
-+ return true;
++ success = true;
++
++cleanup:
++ free(ctx.status);
++ free(ctx.scope);
++ free(ctx.discovery_uri);
++
++ return success;
+}
+
+static void
@@ src/interfaces/libpq/fe-auth-oauth.c (new)
+ }
+ else
+ {
-+#if USE_BUILTIN_OAUTH
++#if USE_LIBCURL
+ /*
+ * Hand off to our built-in OAuth flow.
+ *
@@ src/interfaces/libpq/fe-auth-oauth.c (new)
+ conn->oauth_want_retry = PG_BOOL_NO;
+
+#else
-+ libpq_append_conn_error(conn, "no custom OAuth flows are available, and libpq was not built using --with-builtin-oauth");
++ libpq_append_conn_error(conn, "no custom OAuth flows are available, and libpq was not built using --with-libcurl");
+ goto fail;
+
+#endif
@@ src/interfaces/libpq/fe-auth-oauth.c (new)
+ * Respond with the required dummy message (RFC 7628, sec. 3.2.3).
+ */
+ *output = strdup(kvsep);
++ if (unlikely(!*output))
++ {
++ libpq_append_conn_error(conn, "out of memory");
++ return SASL_FAILED;
++ }
+ *outputlen = strlen(*output); /* == 1 */
+
+ state->state = FE_OAUTH_SERVER_ERROR;
@@ src/interfaces/libpq/meson.build: if gssapi.found()
)
endif
-+if oauth_library == 'curl'
++if libcurl.found()
+ libpq_sources += files('fe-auth-oauth-curl.c')
+endif
+
@@ src/interfaces/libpq/meson.build: if gssapi.found()
kwargs: gen_export_kwargs,
)
- ## src/interfaces/libpq/pqexpbuffer.c ##
-@@ src/interfaces/libpq/pqexpbuffer.c: static const char *const oom_buffer_ptr = oom_buffer;
- *
- * Put a PQExpBuffer in "broken" state if it isn't already.
- */
--static void
-+void
- markPQExpBufferBroken(PQExpBuffer str)
- {
- if (str->data != oom_buffer)
-
- ## src/interfaces/libpq/pqexpbuffer.h ##
-@@ src/interfaces/libpq/pqexpbuffer.h: extern void initPQExpBuffer(PQExpBuffer str);
- extern void destroyPQExpBuffer(PQExpBuffer str);
- extern void termPQExpBuffer(PQExpBuffer str);
-
-+/*------------------------
-+ * markPQExpBufferBroken
-+ * Put a PQExpBuffer in "broken" state if it isn't already.
-+ */
-+extern void markPQExpBufferBroken(PQExpBuffer str);
-+
- /*------------------------
- * resetPQExpBuffer
- * Reset a PQExpBuffer to empty
-
## src/makefiles/meson.build ##
-@@ src/makefiles/meson.build: pgxs_kv = {
- 'SUN_STUDIO_CC': 'no', # not supported so far
-
- # want the chosen option, rather than the library
-+ 'with_builtin_oauth' : oauth_library,
- 'with_ssl' : ssl_library,
- 'with_uuid': uuidopt,
-
@@ src/makefiles/meson.build: pgxs_deps = {
'gssapi': gssapi,
'icu': icu,
@@ src/test/modules/oauth_validator/Makefile (new)
+include $(top_srcdir)/contrib/contrib-global.mk
+
+export PYTHON
-+export with_builtin_oauth
++export with_libcurl
+export with_python
+
+endif
@@ src/test/modules/oauth_validator/fail_validator.c (new)
+
+#include "fmgr.h"
+#include "libpq/oauth.h"
-+#include "miscadmin.h"
-+#include "utils/guc.h"
-+#include "utils/memutils.h"
+
+PG_MODULE_MAGIC;
+
@@ src/test/modules/oauth_validator/meson.build (new)
+ ],
+ 'env': {
+ 'PYTHON': python.path(),
-+ 'with_builtin_oauth': oauth_library,
++ 'with_libcurl': libcurl.found() ? 'yes' : 'no',
+ 'with_python': 'yes',
+ },
+ },
@@ src/test/modules/oauth_validator/t/001_server.pl (new)
+ 'Potentially unsafe test oauth not enabled in PG_TEST_EXTRA';
+}
+
-+if ($ENV{with_builtin_oauth} ne 'curl')
++if ($ENV{with_libcurl} ne 'yes')
+{
+ plan skip_all => 'client-side OAuth not supported by this build';
+}
@@ src/test/modules/oauth_validator/t/002_client.pl (new)
+ $log_start,
+ log_like => [ qr/oauth_validator: token="my-token", role="$user"/, ]);
+
-+if ($ENV{with_builtin_oauth} ne 'curl')
++if ($ENV{with_libcurl} ne 'yes')
+{
+ # libpq should help users out if no OAuth support is built in.
+ test(
+ "fails without custom hook installed",
+ flags => ["--no-hook"],
+ expected_stderr =>
-+ qr/no custom OAuth flows are available, and libpq was not built using --with-builtin-oauth/
++ qr/no custom OAuth flows are available, and libpq was not built using --with-libcurl/
+ );
+}
+
@@ src/tools/pgindent/typedefs.list: explain_get_index_name_hook_type
fe_scram_state
fe_scram_state_enum
fetch_range_request
-@@ src/tools/pgindent/typedefs.list: normal_rand_fctx
- nsphash_hash
+@@ src/tools/pgindent/typedefs.list: nsphash_hash
ntile_context
+ nullingrel_info
numeric
+oauth_state
object_access_hook_type
2: 3d169848db ! 2: 28cc3463aa DO NOT MERGE: Add pytest suite for OAuth
@@ src/test/python/client/test_oauth.py (new)
+# The client tests need libpq to have been compiled with OAuth support; skip
+# them otherwise.
+pytestmark = pytest.mark.skipif(
-+ os.getenv("with_builtin_oauth") == "none",
-+ reason="OAuth client tests require --with-builtin-oauth support",
++ os.getenv("with_libcurl") != "yes",
++ reason="OAuth client tests require --with-libcurl support",
+)
+
+if platform.system() == "Darwin":
@@ src/test/python/client/test_oauth.py (new)
+def fail_oauth_handshake(conn, sasl_resp, *, errmsg="doesn't matter"):
+ """
+ Sends a failure response via the OAUTHBEARER mechanism, consumes the
-+ client's dummy reponse, and issues a FATAL error to end the exchange.
++ client's dummy response, and issues a FATAL error to end the exchange.
+
+ sasl_resp is a dictionary which will be serialized as the OAUTHBEARER JSON
+ response. If provided, errmsg is used in the FATAL ErrorResponse.
@@ src/test/python/client/test_oauth.py (new)
+ pass
+
+
++class RawBytes(bytes):
++ """
++ Like RawResponse, but bypasses the UTF-8 encoding step as well, allowing
++ implementations to issue invalid encodings.
++ """
++
++ pass
++
++
+class OpenIDProvider(threading.Thread):
+ """
+ A thread that runs a mock OpenID provider server on an SSL-enabled socket.
@@ src/test/python/client/test_oauth.py (new)
+ self.end_headers()
+
+ if resp is not None:
-+ if not isinstance(resp, RawResponse):
-+ resp = json.dumps(resp)
-+ resp = resp.encode("utf-8")
++ if not isinstance(resp, RawBytes):
++ if not isinstance(resp, RawResponse):
++ resp = json.dumps(resp)
++ resp = resp.encode("utf-8")
+ self.wfile.write(resp)
+
+ self.close_connection = True
@@ src/test/python/client/test_oauth.py (new)
+ id="bad JSON: invalid syntax",
+ ),
+ pytest.param(
++ b"\xFF\xFF\xFF\xFF",
++ "server's error response is not valid UTF-8",
++ id="bad JSON: invalid encoding",
++ ),
++ pytest.param(
+ '"abcde"',
+ "top-level element must be an object",
+ id="bad JSON: top-level element is a string",
@@ src/test/python/client/test_oauth.py (new)
+ id="bad JSON: int openid-configuration member",
+ ),
+ pytest.param(
++ '{ "status": "invalid_token", "openid-configuration": "", "openid-configuration": "" }',
++ 'field "openid-configuration" is duplicated',
++ id="bad JSON: duplicated field",
++ ),
++ pytest.param(
+ '{ "status": "invalid_token", "scope": 1 }',
+ 'field "scope" must be a string',
+ id="bad JSON: int scope member",
@@ src/test/python/client/test_oauth.py (new)
+ with pq3.wrap(sock, debug_stream=sys.stdout) as conn:
+ initial = start_oauth_handshake(conn)
+
++ if isinstance(response, str):
++ response = response.encode("utf-8")
++
+ # Fail the SASL exchange with an invalid JSON response.
+ pq3.send(
+ conn,
+ pq3.types.AuthnRequest,
+ type=pq3.authn.SASLContinue,
-+ body=response.encode("utf-8"),
++ body=response,
+ )
+
+ # The client should disconnect, so the socket is closed here. (If
@@ src/test/python/client/test_oauth.py (new)
+ id="NULL bytes in document",
+ ),
+ pytest.param(
++ (200, RawBytes(b"blah\xFFblah")),
++ r"failed to parse OpenID discovery document: response is not valid UTF-8",
++ id="document is not UTF-8",
++ ),
++ pytest.param(
+ (200, 123),
+ r"failed to parse OpenID discovery document: top-level element must be an object",
+ id="scalar at top level",
@@ src/test/python/client/test_oauth.py (new)
+ pytest.param(
+ (200, {"grant_types_supported": 123}),
+ r'failed to parse OpenID discovery document: field "grant_types_supported" must be an array of strings',
-+ id="scalar grant types field",
++ id="numeric grant types field",
++ ),
++ pytest.param(
++ (
++ 200,
++ {
++ "grant_types_supported": "urn:ietf:params:oauth:grant-type:device_code"
++ },
++ ),
++ r'failed to parse OpenID discovery document: field "grant_types_supported" must be an array of strings',
++ id="string grant types field",
+ ),
+ pytest.param(
+ (200, {"grant_types_supported": {}}),
@@ src/test/python/client/test_oauth.py (new)
+ r"failed to parse OpenID discovery document: the issuer identifier \(https://.*/path\) does not match oauth_issuer \(https://.*\)",
+ id="mismatched issuer identifier",
+ ),
++ pytest.param(
++ (
++ 200,
++ RawResponse(
++ """{
++ "issuer": "https://256.256.256.256/path",
++ "token_endpoint": "https://256.256.256.256/token",
++ "grant_types_supported": [
++ "urn:ietf:params:oauth:grant-type:device_code"
++ ],
++ "device_authorization_endpoint": "https://256.256.256.256/dev",
++ "device_authorization_endpoint": "https://256.256.256.256/dev"
++ }"""
++ ),
++ ),
++ r'failed to parse OpenID discovery document: field "device_authorization_endpoint" is duplicated',
++ id="duplicated field",
++ ),
+ #
+ # Exercise HTTP-level failures by breaking the protocol. Note that the
+ # error messages here are implementation-dependent.
@@ src/test/python/meson.build (new)
+subdir('server')
+
+pytest_env = {
-+ 'with_builtin_oauth': oauth_library,
++ 'with_libcurl': libcurl.found() ? 'yes' : 'no',
+
+ # Point to the default database; the tests will create their own databases as
+ # needed.
@@ src/test/python/server/oauthtest.c (new)
+ }
+ else
+ {
-+ if (*expected_bearer && !strcmp(token, expected_bearer))
++ if (*expected_bearer && strcmp(token, expected_bearer) == 0)
+ res->authorized = true;
+ if (set_authn_id)
+ res->authn_id = authn_id;