Line data Source code
1 : /*-------------------------------------------------------------------------
2 : *
3 : * validator.c
4 : * Test module for serverside OAuth token validation callbacks
5 : *
6 : * Portions Copyright (c) 1996-2026, PostgreSQL Global Development Group
7 : * Portions Copyright (c) 1994, Regents of the University of California
8 : *
9 : * src/test/modules/oauth_validator/validator.c
10 : *
11 : *-------------------------------------------------------------------------
12 : */
13 :
14 : #include "postgres.h"
15 :
16 : #include "fmgr.h"
17 : #include "libpq/oauth.h"
18 : #include "miscadmin.h"
19 : #include "utils/guc.h"
20 : #include "utils/memutils.h"
21 :
22 0 : PG_MODULE_MAGIC;
23 :
24 : static void validator_startup(ValidatorModuleState *state);
25 : static void validator_shutdown(ValidatorModuleState *state);
26 : static bool validate_token(const ValidatorModuleState *state,
27 : const char *token,
28 : const char *role,
29 : ValidatorModuleResult *res);
30 :
31 : /* Callback implementations (exercise all three) */
32 : static const OAuthValidatorCallbacks validator_callbacks = {
33 : PG_OAUTH_VALIDATOR_MAGIC,
34 :
35 : .startup_cb = validator_startup,
36 : .shutdown_cb = validator_shutdown,
37 : .validate_cb = validate_token
38 : };
39 :
40 : /* GUCs */
41 : static char *authn_id = NULL;
42 : static bool authorize_tokens = true;
43 : static char *error_detail = NULL;
44 : static bool internal_error = false;
45 : static bool invalid_hba = false;
46 :
47 : /* HBA options */
48 : static const char *hba_opts[] = {
49 : "authn_id", /* overrides the default authn_id */
50 : "log", /* logs an arbitrary string */
51 : };
52 :
53 : /*---
54 : * Extension entry point. Sets up GUCs for use by tests:
55 : *
56 : * - oauth_validator.authn_id Sets the user identifier to return during token
57 : * validation. Defaults to the username in the
58 : * startup packet, or the validator.authn_id HBA
59 : * option if it is set.
60 : *
61 : * - oauth_validator.authorize_tokens
62 : * Sets whether to successfully validate incoming
63 : * tokens. Defaults to true.
64 : *
65 : * - oauth_validator.error_detail
66 : * Sets an error message to be included as a
67 : * DETAIL on failure.
68 : *
69 : * - oauth_validator.internal_error
70 : * Reports an internal error to the server.
71 : */
72 : void
73 0 : _PG_init(void)
74 : {
75 0 : DefineCustomStringVariable("oauth_validator.authn_id",
76 : "Authenticated identity to use for future connections",
77 : NULL,
78 : &authn_id,
79 : NULL,
80 : PGC_SIGHUP,
81 : 0,
82 : NULL, NULL, NULL);
83 0 : DefineCustomBoolVariable("oauth_validator.authorize_tokens",
84 : "Should tokens be marked valid?",
85 : NULL,
86 : &authorize_tokens,
87 : true,
88 : PGC_SIGHUP,
89 : 0,
90 : NULL, NULL, NULL);
91 0 : DefineCustomStringVariable("oauth_validator.error_detail",
92 : "Error message to print during failures",
93 : NULL,
94 : &error_detail,
95 : NULL,
96 : PGC_SIGHUP,
97 : 0,
98 : NULL, NULL, NULL);
99 0 : DefineCustomBoolVariable("oauth_validator.internal_error",
100 : "Should the validator report an internal error?",
101 : NULL,
102 : &internal_error,
103 : false,
104 : PGC_SIGHUP,
105 : 0,
106 : NULL, NULL, NULL);
107 0 : DefineCustomBoolVariable("oauth_validator.invalid_hba",
108 : "Should the validator register an invalid option?",
109 : NULL,
110 : &invalid_hba,
111 : false,
112 : PGC_SIGHUP,
113 : 0,
114 : NULL, NULL, NULL);
115 :
116 0 : MarkGUCPrefixReserved("oauth_validator");
117 0 : }
118 :
119 : /*
120 : * Validator module entry point.
121 : */
122 : const OAuthValidatorCallbacks *
123 0 : _PG_oauth_validator_module_init(void)
124 : {
125 0 : return &validator_callbacks;
126 : }
127 :
128 : #define PRIVATE_COOKIE ((void *) 13579)
129 :
130 : /*
131 : * Startup callback, to set up private data for the validator.
132 : */
133 : static void
134 0 : validator_startup(ValidatorModuleState *state)
135 : {
136 : /*
137 : * Make sure the server is correctly setting sversion. (Real modules
138 : * should not do this; it would defeat upgrade compatibility.)
139 : */
140 0 : if (state->sversion != PG_VERSION_NUM)
141 0 : elog(ERROR, "oauth_validator: sversion set to %d", state->sversion);
142 :
143 : /*
144 : * Test the behavior of custom HBA options. Registered options should not
145 : * be retrievable during startup (we want to discourage modules from
146 : * relying on the relative order of client connections and the
147 : * startup_cb).
148 : */
149 0 : RegisterOAuthHBAOptions(state, lengthof(hba_opts), hba_opts);
150 0 : for (int i = 0; i < lengthof(hba_opts); i++)
151 : {
152 0 : if (GetOAuthHBAOption(state, hba_opts[i]))
153 0 : elog(ERROR,
154 : "oauth_validator: GetOAuthValidatorOption(\"%s\") was non-NULL during startup_cb",
155 : hba_opts[i]);
156 : }
157 :
158 0 : if (invalid_hba)
159 : {
160 : /* Register a bad option, which should print a WARNING to the logs. */
161 0 : const char *invalid = "bad option name";
162 :
163 0 : RegisterOAuthHBAOptions(state, 1, &invalid);
164 : }
165 :
166 0 : state->private_data = PRIVATE_COOKIE;
167 0 : }
168 :
169 : /*
170 : * Shutdown callback, to tear down the validator.
171 : */
172 : static void
173 0 : validator_shutdown(ValidatorModuleState *state)
174 : {
175 : /* Check to make sure our private state still exists. */
176 0 : if (state->private_data != PRIVATE_COOKIE)
177 0 : elog(PANIC, "oauth_validator: private state cookie changed to %p in shutdown",
178 : state->private_data);
179 0 : }
180 :
181 : /*
182 : * Validator implementation. Logs the incoming data and authorizes the token by
183 : * default; the behavior can be modified via the module's GUC and HBA settings.
184 : */
185 : static bool
186 0 : validate_token(const ValidatorModuleState *state,
187 : const char *token, const char *role,
188 : ValidatorModuleResult *res)
189 : {
190 : /* Check to make sure our private state still exists. */
191 0 : if (state->private_data != PRIVATE_COOKIE)
192 0 : elog(ERROR, "oauth_validator: private state cookie changed to %p in validate",
193 : state->private_data);
194 :
195 0 : if (GetOAuthHBAOption(state, "log"))
196 0 : elog(LOG, "%s", GetOAuthHBAOption(state, "log"));
197 :
198 0 : elog(LOG, "oauth_validator: token=\"%s\", role=\"%s\"", token, role);
199 0 : elog(LOG, "oauth_validator: issuer=\"%s\", scope=\"%s\"",
200 : MyProcPort->hba->oauth_issuer,
201 : MyProcPort->hba->oauth_scope);
202 :
203 0 : res->error_detail = error_detail; /* only relevant for failures */
204 0 : if (internal_error)
205 0 : return false;
206 :
207 0 : res->authorized = authorize_tokens;
208 0 : if (authn_id)
209 0 : res->authn_id = pstrdup(authn_id);
210 0 : else if (GetOAuthHBAOption(state, "authn_id"))
211 0 : res->authn_id = pstrdup(GetOAuthHBAOption(state, "authn_id"));
212 : else
213 0 : res->authn_id = pstrdup(role);
214 :
215 0 : return true;
216 : }
|