PostgreSQL Source Code git master
Loading...
Searching...
No Matches
oauth_hook_client.c
Go to the documentation of this file.
1/*-------------------------------------------------------------------------
2 *
3 * oauth_hook_client.c
4 * Test driver for t/002_client.pl, which verifies OAuth hook
5 * functionality in libpq.
6 *
7 * Portions Copyright (c) 1996-2026, PostgreSQL Global Development Group
8 * Portions Copyright (c) 1994, Regents of the University of California
9 *
10 *
11 * IDENTIFICATION
12 * src/test/modules/oauth_validator/oauth_hook_client.c
13 *
14 *-------------------------------------------------------------------------
15 */
16
17#include "postgres_fe.h"
18
19#include <sys/socket.h>
20
21#include "getopt_long.h"
22#include "libpq-fe.h"
23
24static int handle_auth_data(PGauthData type, PGconn *conn, void *data);
27 pgsocket *altsock);
30 pgsocket *altsock);
31
32static void
33usage(char *argv[])
34{
35 printf("usage: %s [flags] CONNINFO\n\n", argv[0]);
36
37 printf("recognized flags:\n");
38 printf(" -h, --help show this message\n");
39 printf(" -v VERSION select the hook API version (default 2)\n");
40 printf(" --expected-scope SCOPE fail if received scopes do not match SCOPE\n");
41 printf(" --expected-uri URI fail if received configuration link does not match URI\n");
42 printf(" --expected-issuer ISS fail if received issuer does not match ISS (v2 only)\n");
43 printf(" --misbehave=MODE have the hook fail required postconditions\n"
44 " (MODEs: no-hook, fail-async, no-token, no-socket)\n");
45 printf(" --no-hook don't install OAuth hooks\n");
46 printf(" --hang-forever don't ever return a token (combine with connect_timeout)\n");
47 printf(" --token TOKEN use the provided TOKEN value\n");
48 printf(" --error ERRMSG fail instead, with the given ERRMSG (v2 only)\n");
49 printf(" --stress-async busy-loop on PQconnectPoll rather than polling\n");
50}
51
52/* --options */
53static bool no_hook = false;
54static bool hang_forever = false;
55static bool stress_async = false;
56static const char *expected_uri = NULL;
57static const char *expected_issuer = NULL;
58static const char *expected_scope = NULL;
59static const char *misbehave_mode = NULL;
60static char *token = NULL;
61static char *errmsg = NULL;
63
64int
65main(int argc, char *argv[])
66{
67 static const struct option long_options[] = {
68 {"help", no_argument, NULL, 'h'},
69
70 {"expected-scope", required_argument, NULL, 1000},
71 {"expected-uri", required_argument, NULL, 1001},
72 {"no-hook", no_argument, NULL, 1002},
73 {"token", required_argument, NULL, 1003},
74 {"hang-forever", no_argument, NULL, 1004},
75 {"misbehave", required_argument, NULL, 1005},
76 {"stress-async", no_argument, NULL, 1006},
77 {"expected-issuer", required_argument, NULL, 1007},
78 {"error", required_argument, NULL, 1008},
79 {0}
80 };
81
82 const char *conninfo;
83 PGconn *conn;
84 int c;
85
86 while ((c = getopt_long(argc, argv, "hv:", long_options, NULL)) != -1)
87 {
88 switch (c)
89 {
90 case 'h':
91 usage(argv);
92 return 0;
93
94 case 'v':
95 if (strcmp(optarg, "1") == 0)
97 else if (strcmp(optarg, "2") == 0)
99 else
100 {
101 usage(argv);
102 return 1;
103 }
104 break;
105
106 case 1000: /* --expected-scope */
108 break;
109
110 case 1001: /* --expected-uri */
112 break;
113
114 case 1002: /* --no-hook */
115 no_hook = true;
116 break;
117
118 case 1003: /* --token */
119 token = optarg;
120 break;
121
122 case 1004: /* --hang-forever */
123 hang_forever = true;
124 break;
125
126 case 1005: /* --misbehave */
128 break;
129
130 case 1006: /* --stress-async */
131 stress_async = true;
132 break;
133
134 case 1007: /* --expected-issuer */
136 break;
137
138 case 1008: /* --error */
139 errmsg = optarg;
140 break;
141
142 default:
143 usage(argv);
144 return 1;
145 }
146 }
147
148 if (argc != optind + 1)
149 {
150 usage(argv);
151 return 1;
152 }
153
154 conninfo = argv[optind];
155
156 /* Set up our OAuth hooks. */
158
159 /* Connect. (All the actual work is in the hook.) */
160 if (stress_async)
161 {
162 /*
163 * Perform an asynchronous connection, busy-looping on PQconnectPoll()
164 * without actually waiting on socket events. This stresses code paths
165 * that rely on asynchronous work to be done before continuing with
166 * the next step in the flow.
167 */
169
170 conn = PQconnectStart(conninfo);
171
172 do
173 {
174 res = PQconnectPoll(conn);
175 } while (res != PGRES_POLLING_FAILED && res != PGRES_POLLING_OK);
176 }
177 else
178 {
179 /* Perform a standard synchronous connection. */
180 conn = PQconnectdb(conninfo);
181 }
182
184 {
185 fprintf(stderr, "connection to database failed: %s\n",
187 PQfinish(conn);
188 return 1;
189 }
190
191 printf("connection succeeded\n");
192 PQfinish(conn);
193 return 0;
194}
195
196/*
197 * PQauthDataHook implementation. Replaces the default client flow by handling
198 * PQAUTHDATA_OAUTH_BEARER_TOKEN[_V2].
199 */
200static int
202{
205
208
209 if (no_hook || type != hook_version)
210 return 0;
211
212 req = data;
214 req2 = data;
215
216 if (hang_forever)
217 {
218 /* Start asynchronous processing. */
219 req->async = async_cb;
220 return 1;
221 }
222
223 if (misbehave_mode)
224 {
225 if (strcmp(misbehave_mode, "no-hook") != 0)
226 req->async = misbehave_cb;
227 return 1;
228 }
229
230 if (expected_uri)
231 {
232 if (!req->openid_configuration)
233 {
234 fprintf(stderr, "expected URI \"%s\", got NULL\n", expected_uri);
235 return -1;
236 }
237
238 if (strcmp(expected_uri, req->openid_configuration) != 0)
239 {
240 fprintf(stderr, "expected URI \"%s\", got \"%s\"\n", expected_uri, req->openid_configuration);
241 return -1;
242 }
243 }
244
245 if (expected_scope)
246 {
247 if (!req->scope)
248 {
249 fprintf(stderr, "expected scope \"%s\", got NULL\n", expected_scope);
250 return -1;
251 }
252
253 if (strcmp(expected_scope, req->scope) != 0)
254 {
255 fprintf(stderr, "expected scope \"%s\", got \"%s\"\n", expected_scope, req->scope);
256 return -1;
257 }
258 }
259
260 if (expected_issuer)
261 {
262 if (!req2)
263 {
264 fprintf(stderr, "--expected-issuer cannot be combined with -v1\n");
265 return -1;
266 }
267
268 if (!req2->issuer)
269 {
270 fprintf(stderr, "expected issuer \"%s\", got NULL\n", expected_issuer);
271 return -1;
272 }
273
274 if (strcmp(expected_issuer, req2->issuer) != 0)
275 {
276 fprintf(stderr, "expected issuer \"%s\", got \"%s\"\n", expected_issuer, req2->issuer);
277 return -1;
278 }
279 }
280
281 if (errmsg)
282 {
283 if (token)
284 {
285 fprintf(stderr, "--error cannot be combined with --token\n");
286 return -1;
287 }
288 else if (!req2)
289 {
290 fprintf(stderr, "--error cannot be combined with -v1\n");
291 return -1;
292 }
293
294 req2->error = errmsg;
295 return -1;
296 }
297
298 req->token = token;
299 return 1;
300}
301
304{
305 if (hang_forever)
306 {
307 /*
308 * This code tests that nothing is interfering with libpq's handling
309 * of connect_timeout.
310 */
311 static pgsocket sock = PGINVALID_SOCKET;
312
313 if (sock == PGINVALID_SOCKET)
314 {
315 /* First call. Create an unbound socket to wait on. */
316#ifdef WIN32
318 int err;
319
320 err = WSAStartup(MAKEWORD(2, 2), &wsaData);
321 if (err)
322 {
323 perror("WSAStartup failed");
325 }
326#endif
327 sock = socket(AF_INET, SOCK_DGRAM, 0);
328 if (sock == PGINVALID_SOCKET)
329 {
330 perror("failed to create datagram socket");
332 }
333 }
334
335 /* Make libpq wait on the (unreadable) socket. */
336 *altsock = sock;
338 }
339
340 req->token = token;
341 return PGRES_POLLING_OK;
342}
343
346{
347 if (strcmp(misbehave_mode, "fail-async") == 0)
348 {
349 /* Just fail "normally". */
350 if (errmsg)
351 {
353
355 {
356 fprintf(stderr, "--error cannot be combined with -v1\n");
357 exit(1);
358 }
359
361 req2->error = errmsg;
362 }
363
365 }
366 else if (strcmp(misbehave_mode, "no-token") == 0)
367 {
368 /* Callbacks must assign req->token before returning OK. */
369 return PGRES_POLLING_OK;
370 }
371 else if (strcmp(misbehave_mode, "no-socket") == 0)
372 {
373 /* Callbacks must assign *altsock before asking for polling. */
375 }
376 else
377 {
378 fprintf(stderr, "unrecognized --misbehave mode: %s\n", misbehave_mode);
379 exit(1);
380 }
381}
#define Assert(condition)
Definition c.h:945
int main(void)
#define fprintf(file, fmt, msg)
Definition cubescan.l:21
void err(int eval, const char *fmt,...)
Definition err.c:43
void PQsetAuthDataHook(PQauthDataHook_type hook)
Definition fe-auth.c:1595
PGconn * PQconnectdb(const char *conninfo)
Definition fe-connect.c:826
PostgresPollingStatusType PQconnectPoll(PGconn *conn)
ConnStatusType PQstatus(const PGconn *conn)
PGconn * PQconnectStart(const char *conninfo)
Definition fe-connect.c:954
void PQfinish(PGconn *conn)
char * PQerrorMessage(const PGconn *conn)
int getopt_long(int argc, char *const argv[], const char *optstring, const struct option *longopts, int *longindex)
Definition getopt_long.c:60
#define no_argument
Definition getopt_long.h:25
#define required_argument
Definition getopt_long.h:26
@ CONNECTION_OK
Definition libpq-fe.h:90
PostgresPollingStatusType
Definition libpq-fe.h:120
@ PGRES_POLLING_OK
Definition libpq-fe.h:124
@ PGRES_POLLING_READING
Definition libpq-fe.h:122
@ PGRES_POLLING_FAILED
Definition libpq-fe.h:121
PGauthData
Definition libpq-fe.h:199
@ PQAUTHDATA_OAUTH_BEARER_TOKEN
Definition libpq-fe.h:202
@ PQAUTHDATA_OAUTH_BEARER_TOKEN_V2
Definition libpq-fe.h:204
static PostgresPollingStatusType misbehave_cb(PGconn *conn, PGoauthBearerRequest *req, pgsocket *altsock)
static char * token
static char * errmsg
static const char * expected_scope
static bool stress_async
static const char * expected_issuer
static const char * expected_uri
static const char * misbehave_mode
static PostgresPollingStatusType async_cb(PGconn *conn, PGoauthBearerRequest *req, pgsocket *altsock)
static bool no_hook
static int handle_auth_data(PGauthData type, PGconn *conn, void *data)
static bool hang_forever
static int hook_version
static void usage(void)
const void * data
PGDLLIMPORT int optind
Definition getopt.c:51
PGDLLIMPORT char * optarg
Definition getopt.c:53
int pgsocket
Definition port.h:29
#define PGINVALID_SOCKET
Definition port.h:31
#define printf(...)
Definition port.h:266
char * c
static int fb(int x)
PGconn * conn
Definition streamutil.c:52
const char * type
#define socket(af, type, protocol)
Definition win32_port.h:495