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 :
46 : /*---
47 : * Extension entry point. Sets up GUCs for use by tests:
48 : *
49 : * - oauth_validator.authn_id Sets the user identifier to return during token
50 : * validation. Defaults to the username in the
51 : * startup packet.
52 : *
53 : * - oauth_validator.authorize_tokens
54 : * Sets whether to successfully validate incoming
55 : * tokens. Defaults to true.
56 : *
57 : * - oauth_validator.error_detail
58 : * Sets an error message to be included as a
59 : * DETAIL on failure.
60 : *
61 : * - oauth_validator.internal_error
62 : * Reports an internal error to the server.
63 : */
64 : void
65 0 : _PG_init(void)
66 : {
67 0 : DefineCustomStringVariable("oauth_validator.authn_id",
68 : "Authenticated identity to use for future connections",
69 : NULL,
70 : &authn_id,
71 : NULL,
72 : PGC_SIGHUP,
73 : 0,
74 : NULL, NULL, NULL);
75 0 : DefineCustomBoolVariable("oauth_validator.authorize_tokens",
76 : "Should tokens be marked valid?",
77 : NULL,
78 : &authorize_tokens,
79 : true,
80 : PGC_SIGHUP,
81 : 0,
82 : NULL, NULL, NULL);
83 0 : DefineCustomStringVariable("oauth_validator.error_detail",
84 : "Error message to print during failures",
85 : NULL,
86 : &error_detail,
87 : NULL,
88 : PGC_SIGHUP,
89 : 0,
90 : NULL, NULL, NULL);
91 0 : DefineCustomBoolVariable("oauth_validator.internal_error",
92 : "Should the validator report an internal error?",
93 : NULL,
94 : &internal_error,
95 : false,
96 : PGC_SIGHUP,
97 : 0,
98 : NULL, NULL, NULL);
99 :
100 0 : MarkGUCPrefixReserved("oauth_validator");
101 0 : }
102 :
103 : /*
104 : * Validator module entry point.
105 : */
106 : const OAuthValidatorCallbacks *
107 0 : _PG_oauth_validator_module_init(void)
108 : {
109 0 : return &validator_callbacks;
110 : }
111 :
112 : #define PRIVATE_COOKIE ((void *) 13579)
113 :
114 : /*
115 : * Startup callback, to set up private data for the validator.
116 : */
117 : static void
118 0 : validator_startup(ValidatorModuleState *state)
119 : {
120 : /*
121 : * Make sure the server is correctly setting sversion. (Real modules
122 : * should not do this; it would defeat upgrade compatibility.)
123 : */
124 0 : if (state->sversion != PG_VERSION_NUM)
125 0 : elog(ERROR, "oauth_validator: sversion set to %d", state->sversion);
126 :
127 0 : state->private_data = PRIVATE_COOKIE;
128 0 : }
129 :
130 : /*
131 : * Shutdown callback, to tear down the validator.
132 : */
133 : static void
134 0 : validator_shutdown(ValidatorModuleState *state)
135 : {
136 : /* Check to make sure our private state still exists. */
137 0 : if (state->private_data != PRIVATE_COOKIE)
138 0 : elog(PANIC, "oauth_validator: private state cookie changed to %p in shutdown",
139 : state->private_data);
140 0 : }
141 :
142 : /*
143 : * Validator implementation. Logs the incoming data and authorizes the token by
144 : * default; the behavior can be modified via the module's GUC settings.
145 : */
146 : static bool
147 0 : validate_token(const ValidatorModuleState *state,
148 : const char *token, const char *role,
149 : ValidatorModuleResult *res)
150 : {
151 : /* Check to make sure our private state still exists. */
152 0 : if (state->private_data != PRIVATE_COOKIE)
153 0 : elog(ERROR, "oauth_validator: private state cookie changed to %p in validate",
154 : state->private_data);
155 :
156 0 : elog(LOG, "oauth_validator: token=\"%s\", role=\"%s\"", token, role);
157 0 : elog(LOG, "oauth_validator: issuer=\"%s\", scope=\"%s\"",
158 : MyProcPort->hba->oauth_issuer,
159 : MyProcPort->hba->oauth_scope);
160 :
161 0 : res->error_detail = error_detail; /* only relevant for failures */
162 0 : if (internal_error)
163 0 : return false;
164 :
165 0 : res->authorized = authorize_tokens;
166 0 : if (authn_id)
167 0 : res->authn_id = pstrdup(authn_id);
168 : else
169 0 : res->authn_id = pstrdup(role);
170 :
171 0 : return true;
172 : }
|