since-v18.diff.txt
text/plain
Filename: since-v18.diff.txt
Type: text/plain
Part: 0
1: e2a0b48561 = 1: ce06c03e2b common/jsonapi: support FRONTEND clients
2: db625e1d01 = 2: 6989b75153 Refactor SASL exchange to return tri-state status
3: e4ad0260d5 = 3: 783bfe0b95 Explicitly require password for SCRAM exchange
4: 229f602d5c ! 4: 77550a47db libpq: add OAUTHBEARER SASL mechanism
@@ Commit message
are currently implemented (but clients may provide their own flows; see
below).
- The client implementation requires either libcurl or libiddawc and their
- development headers. Pass `curl` or `iddawc` to --with-oauth/-Doauth
- during configuration.
+ The client implementation requires libcurl and its development headers.
+ Pass `curl` to --with-oauth/-Doauth during configuration.
Thomas Munro wrote the kqueue() implementation for oauth-curl; thanks!
@@ configure: Optional Packages:
--with-pam build with PAM support
--with-bsd-auth build with BSD Authentication support
--with-ldap build with LDAP support
-+ --with-oauth=LIB use LIB for OAuth 2.0 support (curl, iddawc)
++ --with-oauth=LIB use LIB for OAuth 2.0 support (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; }
+
+$as_echo "#define USE_OAUTH_CURL 1" >>confdefs.h
+
-+elif test x"$with_oauth" = x"iddawc"; then
-+
-+$as_echo "#define USE_OAUTH 1" >>confdefs.h
-+
-+
-+$as_echo "#define USE_OAUTH_IDDAWC 1" >>confdefs.h
-+
+elif test x"$with_oauth" != x"no"; then
-+ as_fn_error $? "--with-oauth must specify curl or iddawc" "$LINENO" 5
++ as_fn_error $? "--with-oauth must specify curl" "$LINENO" 5
+fi
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $with_oauth" >&5
@@ configure: fi
+ as_fn_error $? "library 'curl' is required for --with-oauth=curl" "$LINENO" 5
+fi
+
-+elif test "$with_oauth" = iddawc ; then
-+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for i_init_session in -liddawc" >&5
-+$as_echo_n "checking for i_init_session in -liddawc... " >&6; }
-+if ${ac_cv_lib_iddawc_i_init_session+:} false; then :
-+ $as_echo_n "(cached) " >&6
-+else
-+ ac_check_lib_save_LIBS=$LIBS
-+LIBS="-liddawc $LIBS"
-+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
-+/* end confdefs.h. */
-+
-+/* Override any GCC internal prototype to avoid an error.
-+ Use char because int might match the return type of a GCC
-+ builtin and then its argument prototype would still apply. */
-+#ifdef __cplusplus
-+extern "C"
-+#endif
-+char i_init_session ();
-+int
-+main ()
-+{
-+return i_init_session ();
-+ ;
-+ return 0;
-+}
-+_ACEOF
-+if ac_fn_c_try_link "$LINENO"; then :
-+ ac_cv_lib_iddawc_i_init_session=yes
-+else
-+ ac_cv_lib_iddawc_i_init_session=no
-+fi
-+rm -f core conftest.err conftest.$ac_objext \
-+ conftest$ac_exeext conftest.$ac_ext
-+LIBS=$ac_check_lib_save_LIBS
-+fi
-+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_iddawc_i_init_session" >&5
-+$as_echo "$ac_cv_lib_iddawc_i_init_session" >&6; }
-+if test "x$ac_cv_lib_iddawc_i_init_session" = xyes; then :
-+ cat >>confdefs.h <<_ACEOF
-+#define HAVE_LIBIDDAWC 1
-+_ACEOF
-+
-+ LIBS="-liddawc $LIBS"
-+
-+else
-+ as_fn_error $? "library 'iddawc' is required for --with-oauth=iddawc" "$LINENO" 5
-+fi
-+
-+ # Check for an older spelling of i_get_openid_config
-+ for ac_func in i_load_openid_config
-+do :
-+ ac_fn_c_check_func "$LINENO" "i_load_openid_config" "ac_cv_func_i_load_openid_config"
-+if test "x$ac_cv_func_i_load_openid_config" = xyes; then :
-+ cat >>confdefs.h <<_ACEOF
-+#define HAVE_I_LOAD_OPENID_CONFIG 1
-+_ACEOF
-+
-+fi
-+done
-+
+fi
+
# for contrib/sepgsql
@@ configure: fi
+ as_fn_error $? "header file <curl/curl.h> is required for OAuth" "$LINENO" 5
+fi
+
-+
-+elif test "$with_oauth" = iddawc; then
-+ ac_fn_c_check_header_mongrel "$LINENO" "iddawc.h" "ac_cv_header_iddawc_h" "$ac_includes_default"
-+if test "x$ac_cv_header_iddawc_h" = xyes; then :
-+
-+else
-+ as_fn_error $? "header file <iddawc.h> is required for OAuth" "$LINENO" 5
-+fi
-+
+
fi
@@ configure.ac: AC_MSG_RESULT([$with_ldap])
+# OAuth 2.0
+#
+AC_MSG_CHECKING([whether to build with OAuth support])
-+PGAC_ARG_REQ(with, oauth, [LIB], [use LIB for OAuth 2.0 support (curl, iddawc)])
++PGAC_ARG_REQ(with, oauth, [LIB], [use LIB for OAuth 2.0 support (curl)])
+if test x"$with_oauth" = x"" ; then
+ with_oauth=no
+fi
@@ configure.ac: AC_MSG_RESULT([$with_ldap])
+if test x"$with_oauth" = x"curl"; then
+ AC_DEFINE([USE_OAUTH], 1, [Define to 1 to build with OAuth 2.0 support. (--with-oauth)])
+ AC_DEFINE([USE_OAUTH_CURL], 1, [Define to 1 to use libcurl for OAuth support.])
-+elif test x"$with_oauth" = x"iddawc"; then
-+ AC_DEFINE([USE_OAUTH], 1, [Define to 1 to build with OAuth 2.0 support. (--with-oauth)])
-+ AC_DEFINE([USE_OAUTH_IDDAWC], 1, [Define to 1 to use libiddawc for OAuth support.])
+elif test x"$with_oauth" != x"no"; then
-+ AC_MSG_ERROR([--with-oauth must specify curl or iddawc])
++ AC_MSG_ERROR([--with-oauth must specify curl])
+fi
+
+AC_MSG_RESULT([$with_oauth])
@@ configure.ac: fi
+if test "$with_oauth" = curl ; then
+ AC_CHECK_LIB(curl, curl_multi_init, [], [AC_MSG_ERROR([library 'curl' is required for --with-oauth=curl])])
-+elif test "$with_oauth" = iddawc ; then
-+ AC_CHECK_LIB(iddawc, i_init_session, [], [AC_MSG_ERROR([library 'iddawc' is required for --with-oauth=iddawc])])
-+ # Check for an older spelling of i_get_openid_config
-+ AC_CHECK_FUNCS([i_load_openid_config])
+fi
+
# for contrib/sepgsql
@@ configure.ac: elif test "$with_uuid" = ossp ; then
+if test "$with_oauth" = curl; then
+ AC_CHECK_HEADER(curl/curl.h, [], [AC_MSG_ERROR([header file <curl/curl.h> is required for OAuth])])
-+elif test "$with_oauth" = iddawc; then
-+ AC_CHECK_HEADER(iddawc.h, [], [AC_MSG_ERROR([header file <iddawc.h> is required for OAuth])])
+fi
+
if test "$PORTNAME" = "win32" ; then
@@ meson.build: endif
+ endif
+endif
+
-+if not oauth.found() and oauthopt in ['auto', 'iddawc']
-+ oauth = dependency('libiddawc', required: (oauthopt == 'iddawc'))
-+
-+ if oauth.found()
-+ oauth_library = 'iddawc'
-+ cdata.set('USE_OAUTH', 1)
-+ cdata.set('USE_OAUTH_IDDAWC', 1)
-+
-+ # Check for an older spelling of i_get_openid_config
-+ if cc.has_function('i_load_openid_config',
-+ dependencies: oauth, args: test_c_args)
-+ cdata.set('HAVE_I_LOAD_OPENID_CONFIG', 1)
-+ endif
-+ endif
-+endif
-+
+if oauthopt == 'auto' and auto_features.enabled() and not oauth.found()
+ error('no OAuth implementation library found')
+endif
@@ meson_options.txt: option('lz4', type: 'feature', value: 'auto',
option('nls', type: 'feature', value: 'auto',
description: 'Native language support')
-+option('oauth', type : 'combo', choices : ['auto', 'none', 'curl', 'iddawc'],
++option('oauth', type : 'combo', choices : ['auto', 'none', 'curl'],
+ value: 'auto',
-+ description: 'use LIB for OAuth 2.0 support (curl, iddawc)')
++ description: 'use LIB for OAuth 2.0 support (curl)')
+
option('pam', type: 'feature', value: 'auto',
description: 'PAM support')
@@ src/include/common/oauth-common.h (new)
+#endif /* OAUTH_COMMON_H */
## src/include/pg_config.h.in ##
-@@
- /* Define to 1 if __builtin_constant_p(x) implies "i"(x) acceptance. */
- #undef HAVE_I_CONSTRAINT__BUILTIN_CONSTANT_P
-
-+/* Define to 1 if you have the `i_load_openid_config' function. */
-+#undef HAVE_I_LOAD_OPENID_CONFIG
-+
- /* Define to 1 if you have the `kqueue' function. */
- #undef HAVE_KQUEUE
-
@@
/* Define to 1 if you have the `crypto' library (-lcrypto). */
#undef HAVE_LIBCRYPTO
+/* Define to 1 if you have the `curl' library (-lcurl). */
+#undef HAVE_LIBCURL
-+
-+/* Define to 1 if you have the `iddawc' library (-liddawc). */
-+#undef HAVE_LIBIDDAWC
+
/* Define to 1 if you have the `ldap' library (-lldap). */
#undef HAVE_LIBLDAP
@@ src/include/pg_config.h.in
+
+/* Define to 1 to use libcurl for OAuth support. */
+#undef USE_OAUTH_CURL
-+
-+/* Define to 1 to use libiddawc for OAuth support. */
-+#undef USE_OAUTH_IDDAWC
+
/* Define to 1 to build with OpenSSL support. (--with-ssl=openssl) */
#undef USE_OPENSSL
@@ src/interfaces/libpq/Makefile: OBJS += \
+ifneq ($(with_oauth),no)
+OBJS += fe-auth-oauth.o
+
-+ifeq ($(with_oauth),iddawc)
-+OBJS += fe-auth-oauth-iddawc.o
-+else
++ifeq ($(with_oauth),curl)
+OBJS += fe-auth-oauth-curl.o
+endif
+endif
@@ src/interfaces/libpq/Makefile: endif
SHLIB_LINK_INTERNAL = -lpgcommon_shlib -lpgport_shlib
ifneq ($(PORTNAME), win32)
-SHLIB_LINK += $(filter -lcrypt -ldes -lcom_err -lcrypto -lk5crypto -lkrb5 -lgssapi_krb5 -lgss -lgssapi -lssl -lsocket -lnsl -lresolv -lintl -lm, $(LIBS)) $(LDAP_LIBS_FE) $(PTHREAD_LIBS)
-+SHLIB_LINK += $(filter -lcrypt -ldes -lcom_err -lcrypto -lk5crypto -lkrb5 -lgssapi_krb5 -lgss -lgssapi -lssl -lcurl -liddawc -lsocket -lnsl -lresolv -lintl -lm, $(LIBS)) $(LDAP_LIBS_FE) $(PTHREAD_LIBS)
++SHLIB_LINK += $(filter -lcrypt -ldes -lcom_err -lcrypto -lk5crypto -lkrb5 -lgssapi_krb5 -lgss -lgssapi -lssl -lcurl -lsocket -lnsl -lresolv -lintl -lm, $(LIBS)) $(LDAP_LIBS_FE) $(PTHREAD_LIBS)
else
SHLIB_LINK += $(filter -lcrypt -ldes -lcom_err -lcrypto -lk5crypto -lkrb5 -lgssapi32 -lssl -lsocket -lnsl -lresolv -lintl -lm $(PTHREAD_LIBS), $(LIBS)) $(LDAP_LIBS_FE)
endif
@@ src/interfaces/libpq/fe-auth-oauth-curl.c (new)
+
+ free_token(&tok);
+ return PGRES_POLLING_FAILED;
-+}
-
- ## src/interfaces/libpq/fe-auth-oauth-iddawc.c (new) ##
-@@
-+/*-------------------------------------------------------------------------
-+ *
-+ * fe-auth-oauth-iddawc.c
-+ * The libiddawc implementation of OAuth/OIDC authentication.
-+ *
-+ * Portions Copyright (c) 1996-2023, PostgreSQL Global Development Group
-+ * Portions Copyright (c) 1994, Regents of the University of California
-+ *
-+ * IDENTIFICATION
-+ * src/interfaces/libpq/fe-auth-oauth-iddawc.c
-+ *
-+ *-------------------------------------------------------------------------
-+ */
-+
-+#include "postgres_fe.h"
-+
-+#include <iddawc.h>
-+
-+#include "fe-auth.h"
-+#include "fe-auth-oauth.h"
-+#include "libpq-int.h"
-+
-+#ifdef HAVE_I_LOAD_OPENID_CONFIG
-+/* Older versions of iddawc used 'load' instead of 'get' for some APIs. */
-+#define i_get_openid_config i_load_openid_config
-+#endif
-+
-+static const char *
-+iddawc_error_string(int errcode)
-+{
-+ switch (errcode)
-+ {
-+ case I_OK:
-+ return "I_OK";
-+
-+ case I_ERROR:
-+ return "I_ERROR";
-+
-+ case I_ERROR_PARAM:
-+ return "I_ERROR_PARAM";
-+
-+ case I_ERROR_MEMORY:
-+ return "I_ERROR_MEMORY";
-+
-+ case I_ERROR_UNAUTHORIZED:
-+ return "I_ERROR_UNAUTHORIZED";
-+
-+ case I_ERROR_SERVER:
-+ return "I_ERROR_SERVER";
-+ }
-+
-+ return "<unknown>";
-+}
-+
-+static void
-+iddawc_error(PGconn *conn, int errcode, const char *msg)
-+{
-+ appendPQExpBufferStr(&conn->errorMessage, libpq_gettext(msg));
-+ appendPQExpBuffer(&conn->errorMessage,
-+ libpq_gettext(" (iddawc error %s)\n"),
-+ iddawc_error_string(errcode));
-+}
-+
-+static void
-+iddawc_request_error(PGconn *conn, struct _i_session *i, int err, const char *msg)
-+{
-+ const char *error_code;
-+ const char *desc;
-+
-+ appendPQExpBuffer(&conn->errorMessage, "%s: ", libpq_gettext(msg));
-+
-+ error_code = i_get_str_parameter(i, I_OPT_ERROR);
-+ if (!error_code)
-+ {
-+ /*
-+ * The server didn't give us any useful information, so just print the
-+ * error code.
-+ */
-+ appendPQExpBuffer(&conn->errorMessage,
-+ libpq_gettext("(iddawc error %s)\n"),
-+ iddawc_error_string(err));
-+ return;
-+ }
-+
-+ /* If the server gave a string description, print that too. */
-+ desc = i_get_str_parameter(i, I_OPT_ERROR_DESCRIPTION);
-+ if (desc)
-+ appendPQExpBuffer(&conn->errorMessage, "%s ", desc);
-+
-+ appendPQExpBuffer(&conn->errorMessage, "(%s)\n", error_code);
-+}
-+
-+/*
-+ * Runs the device authorization flow using libiddawc. If successful, a malloc'd
-+ * token string in "Bearer xxxx..." format, suitable for sending to an
-+ * OAUTHBEARER server, is returned. NULL is returned on error.
-+ */
-+static char *
-+run_iddawc_auth_flow(PGconn *conn, const char *discovery_uri)
-+{
-+ struct _i_session session;
-+ PQExpBuffer token_buf = NULL;
-+ int err;
-+ int auth_method;
-+ bool user_prompted = false;
-+ const char *verification_uri;
-+ const char *user_code;
-+ const char *access_token;
-+ const char *token_type;
-+ char *token = NULL;
-+
-+ i_init_session(&session);
-+
-+ token_buf = createPQExpBuffer();
-+ if (!token_buf)
-+ goto cleanup;
-+
-+ err = i_set_str_parameter(&session, I_OPT_OPENID_CONFIG_ENDPOINT, discovery_uri);
-+ if (err)
-+ {
-+ iddawc_error(conn, err, "failed to set OpenID config endpoint");
-+ goto cleanup;
-+ }
-+
-+ err = i_get_openid_config(&session);
-+ if (err)
-+ {
-+ iddawc_error(conn, err, "failed to fetch OpenID discovery document");
-+ goto cleanup;
-+ }
-+
-+ if (!i_get_str_parameter(&session, I_OPT_TOKEN_ENDPOINT))
-+ {
-+ appendPQExpBufferStr(&conn->errorMessage,
-+ libpq_gettext("issuer has no token endpoint"));
-+ goto cleanup;
-+ }
-+
-+ if (!i_get_str_parameter(&session, I_OPT_DEVICE_AUTHORIZATION_ENDPOINT))
-+ {
-+ appendPQExpBufferStr(&conn->errorMessage,
-+ libpq_gettext("issuer does not support device authorization"));
-+ goto cleanup;
-+ }
-+
-+ err = i_set_response_type(&session, I_RESPONSE_TYPE_DEVICE_CODE);
-+ if (err)
-+ {
-+ iddawc_error(conn, err, "failed to set device code response type");
-+ goto cleanup;
-+ }
-+
-+ auth_method = I_TOKEN_AUTH_METHOD_NONE;
-+ if (conn->oauth_client_secret && *conn->oauth_client_secret)
-+ auth_method = I_TOKEN_AUTH_METHOD_SECRET_BASIC;
-+
-+ err = i_set_parameter_list(&session,
-+ I_OPT_CLIENT_ID, conn->oauth_client_id,
-+ I_OPT_CLIENT_SECRET, conn->oauth_client_secret,
-+ I_OPT_TOKEN_METHOD, auth_method,
-+ I_OPT_SCOPE, conn->oauth_scope,
-+ I_OPT_NONE
-+ );
-+ if (err)
-+ {
-+ iddawc_error(conn, err, "failed to set client identifier");
-+ goto cleanup;
-+ }
-+
-+ err = i_run_device_auth_request(&session);
-+ if (err)
-+ {
-+ iddawc_request_error(conn, &session, err,
-+ "failed to obtain device authorization");
-+ goto cleanup;
-+ }
-+
-+ verification_uri = i_get_str_parameter(&session, I_OPT_DEVICE_AUTH_VERIFICATION_URI);
-+ if (!verification_uri)
-+ {
-+ appendPQExpBufferStr(&conn->errorMessage,
-+ libpq_gettext("issuer did not provide a verification URI"));
-+ goto cleanup;
-+ }
-+
-+ user_code = i_get_str_parameter(&session, I_OPT_DEVICE_AUTH_USER_CODE);
-+ if (!user_code)
-+ {
-+ appendPQExpBufferStr(&conn->errorMessage,
-+ libpq_gettext("issuer did not provide a user code"));
-+ goto cleanup;
-+ }
-+
-+ /*
-+ * Poll the token endpoint until either the user logs in and authorizes
-+ * the use of a token, or a hard failure occurs. We perform one ping
-+ * _before_ prompting the user, so that we don't make them do the work of
-+ * logging in only to find that the token endpoint is completely
-+ * unreachable.
-+ */
-+ err = i_run_token_request(&session);
-+ while (err)
-+ {
-+ const char *error_code;
-+ uint interval;
-+
-+ error_code = i_get_str_parameter(&session, I_OPT_ERROR);
-+
-+ /*
-+ * authorization_pending and slow_down are the only acceptable errors;
-+ * anything else and we bail.
-+ */
-+ if (!error_code || (strcmp(error_code, "authorization_pending")
-+ && strcmp(error_code, "slow_down")))
-+ {
-+ iddawc_request_error(conn, &session, err,
-+ "failed to obtain access token");
-+ goto cleanup;
-+ }
-+
-+ if (!user_prompted)
-+ {
-+ int res;
-+ PQpromptOAuthDevice prompt = {
-+ .verification_uri = verification_uri,
-+ .user_code = user_code,
-+ /* TODO: optional fields */
-+ };
-+
-+ /*
-+ * Now that we know the token endpoint isn't broken, give the user
-+ * the login instructions.
-+ */
-+ res = PQauthDataHook(PQAUTHDATA_PROMPT_OAUTH_DEVICE, conn,
-+ &prompt);
-+
-+ if (!res)
-+ {
-+ fprintf(stderr, "Visit %s and enter the code: %s",
-+ prompt.verification_uri, prompt.user_code);
-+ }
-+ else if (res < 0)
-+ {
-+ appendPQExpBufferStr(&conn->errorMessage,
-+ libpq_gettext("device prompt failed\n"));
-+ goto cleanup;
-+ }
-+
-+ user_prompted = true;
-+ }
-+
-+ /*---
-+ * We are required to wait between polls; the server tells us how
-+ * long.
-+ * TODO: if interval's not set, we need to default to five seconds
-+ * TODO: sanity check the interval
-+ */
-+ interval = i_get_int_parameter(&session, I_OPT_DEVICE_AUTH_INTERVAL);
-+
-+ /*
-+ * A slow_down error requires us to permanently increase our retry
-+ * interval by five seconds. RFC 8628, Sec. 3.5.
-+ */
-+ if (!strcmp(error_code, "slow_down"))
-+ {
-+ interval += 5;
-+ i_set_int_parameter(&session, I_OPT_DEVICE_AUTH_INTERVAL, interval);
-+ }
-+
-+ sleep(interval);
-+
-+ /*
-+ * XXX Reset the error code before every call, because iddawc won't do
-+ * that for us. This matters if the server first sends a "pending"
-+ * error code, then later hard-fails without sending an error code to
-+ * overwrite the first one.
-+ *
-+ * That we have to do this at all seems like a bug in iddawc.
-+ */
-+ i_set_str_parameter(&session, I_OPT_ERROR, NULL);
-+
-+ err = i_run_token_request(&session);
-+ }
-+
-+ access_token = i_get_str_parameter(&session, I_OPT_ACCESS_TOKEN);
-+ token_type = i_get_str_parameter(&session, I_OPT_TOKEN_TYPE);
-+
-+ if (!access_token || !token_type || strcasecmp(token_type, "Bearer"))
-+ {
-+ appendPQExpBufferStr(&conn->errorMessage,
-+ libpq_gettext("issuer did not provide a bearer token"));
-+ goto cleanup;
-+ }
-+
-+ appendPQExpBufferStr(token_buf, "Bearer ");
-+ appendPQExpBufferStr(token_buf, access_token);
-+
-+ if (PQExpBufferBroken(token_buf))
-+ goto cleanup;
-+
-+ token = strdup(token_buf->data);
-+
-+cleanup:
-+ if (token_buf)
-+ destroyPQExpBuffer(token_buf);
-+ i_clean_session(&session);
-+
-+ return token;
-+}
-+
-+PostgresPollingStatusType
-+pg_fe_run_oauth_flow(PGconn *conn, pgsocket *altsock)
-+{
-+ fe_oauth_state *state = conn->sasl_state;
-+
-+ /* TODO: actually make this asynchronous */
-+ state->token = run_iddawc_auth_flow(conn, conn->oauth_discovery_uri);
-+ return state->token ? PGRES_POLLING_OK : PGRES_POLLING_FAILED;
+}
## src/interfaces/libpq/fe-auth-oauth.c (new) ##
@@ src/interfaces/libpq/meson.build: if gssapi.found()
+if oauth.found()
+ libpq_sources += files('fe-auth-oauth.c')
-+ if oauth_library == 'iddawc'
-+ libpq_sources += files('fe-auth-oauth-iddawc.c')
-+ else
++ if oauth_library == 'curl'
+ libpq_sources += files('fe-auth-oauth-curl.c')
+ endif
+endif
5: 5c5a83e44e = 5: 12ae7c4355 backend: add OAUTHBEARER SASL mechanism
6: 7a42365d62 = 6: 707edf9314 Introduce OAuth validator libraries
7: 9c46ea6cf9 ! 7: 47236c5644 Add pytest suite for OAuth
@@ Commit message
option of the same name, which allows an ephemeral server to be spun up
during a test run.
- For iddawc, asynchronous tests still hang, as expected. Bad-interval
- tests fail because iddawc apparently doesn't care that the interval is
- bad.
-
TODOs:
- The --tap-stream option to pytest-tap is slightly broken during test
failures (it suppresses error information), which impedes debugging.
@@ src/test/python/client/test_oauth.py (new)
+ ),
+ pytest.param(
+ (400, {}),
-+ alt_patterns(
-+ r'failed to parse token error response: field "error" is missing',
-+ r"failed to obtain device authorization: \(iddawc error I_ERROR_PARAM\)",
-+ ),
++ r'failed to parse token error response: field "error" is missing',
+ id="broken error response",
+ ),
+ pytest.param(
+ (200, RawResponse(r'{ "interval": 3.5.8 }')),
-+ alt_patterns(
-+ r"failed to parse device authorization: Token .* is invalid",
-+ r"failed to obtain device authorization: \(iddawc error I_ERROR\)",
-+ ),
++ r"failed to parse device authorization: Token .* is invalid",
+ id="non-numeric interval",
+ ),
+ pytest.param(
+ (200, RawResponse(r'{ "interval": 08 }')),
-+ alt_patterns(
-+ r"failed to parse device authorization: Token .* is invalid",
-+ r"failed to obtain device authorization: \(iddawc error I_ERROR\)",
-+ ),
++ r"failed to parse device authorization: Token .* is invalid",
+ id="invalid numeric interval",
+ ),
+ ],
@@ src/test/python/client/test_oauth.py (new)
+ else:
+ assert False, "update error_pattern for new failure mode"
+
-+ # XXX iddawc doesn't really check for problems in the device authorization
-+ # response, leading to this patchwork:
-+ if field_name == "verification_uri":
-+ error_pattern = alt_patterns(
-+ error_pattern,
-+ "issuer did not provide a verification URI",
-+ )
-+ elif field_name == "user_code":
-+ error_pattern = alt_patterns(
-+ error_pattern,
-+ "issuer did not provide a user code",
-+ )
-+ else:
-+ error_pattern = alt_patterns(
-+ error_pattern,
-+ r"failed to obtain access token: \(iddawc error I_ERROR_PARAM\)",
-+ )
-+
+ with pytest.raises(psycopg2.OperationalError, match=error_pattern):
+ client.check_completed()
+
@@ src/test/python/client/test_oauth.py (new)
+ ),
+ pytest.param(
+ (400, {}),
-+ alt_patterns(
-+ r'failed to parse token error response: field "error" is missing',
-+ r"failed to obtain access token: \(iddawc error I_ERROR_PARAM\)",
-+ ),
++ r'failed to parse token error response: field "error" is missing',
+ id="empty error response",
+ ),
+ pytest.param(
+ (200, {}, {}),
-+ alt_patterns(
-+ r"failed to parse access token response: no content type was provided",
-+ r"failed to obtain access token: \(iddawc error I_ERROR\)",
-+ ),
++ r"failed to parse access token response: no content type was provided",
+ id="missing content type",
+ ),
+ pytest.param(
+ (200, {"Content-Type": "text/plain"}, {}),
-+ alt_patterns(
-+ r"failed to parse access token response: unexpected content type",
-+ r"failed to obtain access token: \(iddawc error I_ERROR\)",
-+ ),
++ r"failed to parse access token response: unexpected content type",
+ id="wrong content type",
+ ),
+ ],
@@ src/test/python/client/test_oauth.py (new)
+ else:
+ assert False, "update error_pattern for new failure mode"
+
-+ # XXX iddawc is fairly silent on the topic.
-+ error_pattern = alt_patterns(
-+ error_pattern,
-+ r"failed to obtain access token: \(iddawc error I_ERROR_PARAM\)",
-+ )
-+
+ with pytest.raises(psycopg2.OperationalError, match=error_pattern):
+ client.check_completed()
+
@@ src/test/python/client/test_oauth.py (new)
+
+ expect_disconnected_handshake(sock)
+
-+ # XXX iddawc doesn't differentiate...
-+ expected_error = alt_patterns(
-+ expected_error,
-+ r"failed to fetch OpenID discovery document \(iddawc error I_ERROR(_PARAM)?\)",
-+ )
-+
+ with pytest.raises(psycopg2.OperationalError, match=expected_error):
+ client.check_completed()
+
@@ src/test/python/test_pq3.py (new)
+import contextlib
+import getpass
+import io
++import os
+import platform
+import struct
+import sys
8: 8ad4ce3068 = 8: 116e17eeee XXX temporary patches to build and test
9: 5630465578 = 9: 28756eda1c WIP: Python OAuth provider implementation