prole/pg-knoe-auth/test/unit/test_alg_validation.c
chrisfu 57886f9268 feat(pg-knoe-auth): import upstream PostgreSQL JWT auth extension; compile in knoe-db image
- Copy pg-knoe-auth/ wholesale from upstream/knoe-db/20260523 (Task 1 of
  docs/plans/junie/upstream-knoe-db-20260523-integration.md).
- Extension: PG18 OAUTHBEARER JWT validator using libcurl + OpenSSL RS256.
- knoe-db/Dockerfile: add libcurl4-openssl-dev to dev deps; COPY src/ and
  build with make USE_PGXS=1 install after tds_fdw.
- NOT enabled in the default database build (absent from 20_create_extensions.sh).
  To enable: CREATE EXTENSION pg_knoe_auth; (requires pg_hba.conf oauth_issuer).

Closes Task 1 of upstream-knoe-db-20260523-integration.md.
2026-05-23 21:52:06 -07:00

67 lines
2.0 KiB
C

/*
* test_alg_validation.c — unit tests for H1: JWT alg header validation.
*
* Tests that only RS256 is accepted; alg=none, alg=HS256, missing alg all fail.
* See docs/plans/junie/pg_knoe_auth-rename-harden-modularize.md §2.2.1
*/
#include "test_helpers.h"
/*
* Inline the alg-check logic from pg_knoe_auth.c so we can test it without
* linking against PostgreSQL. The logic is: extract "alg" from a JSON
* header string and reject anything that isn't "RS256".
*/
/* Minimal json_get_string (same logic as production code) */
static char *
json_get_string(const char *json, const char *key)
{
char search[256];
const char *p, *start, *end;
size_t len;
snprintf(search, sizeof(search), "\"%s\"", key);
p = strstr(json, search);
if (!p) return NULL;
p += strlen(search);
while (*p == ' ' || *p == '\t') p++;
if (*p != ':') return NULL;
p++;
while (*p == ' ' || *p == '\t') p++;
if (*p != '"') return NULL;
p++;
start = p;
while (*p && *p != '"') { if (*p == '\\') p++; if (*p) p++; }
end = p;
len = (size_t)(end - start);
return pnstrdup(start, len);
}
/* Returns true if alg is valid (RS256), false otherwise — mirrors H1 logic */
static bool
alg_is_valid(const char *header_json)
{
char *alg = json_get_string(header_json, "alg");
bool ok = alg && strcmp(alg, "RS256") == 0;
if (alg) pfree(alg);
return ok;
}
int main(void)
{
ASSERT(!alg_is_valid("{\"alg\":\"none\",\"typ\":\"JWT\"}"),
"alg=none is rejected");
ASSERT(!alg_is_valid("{\"alg\":\"HS256\",\"typ\":\"JWT\"}"),
"alg=HS256 is rejected");
ASSERT(!alg_is_valid("{\"alg\":\"RS512\",\"typ\":\"JWT\"}"),
"alg=RS512 is rejected");
ASSERT(!alg_is_valid("{\"typ\":\"JWT\"}"),
"missing alg is rejected");
ASSERT( alg_is_valid("{\"alg\":\"RS256\",\"typ\":\"JWT\"}"),
"alg=RS256 is accepted");
ASSERT( alg_is_valid("{\"kid\":\"k1\",\"alg\":\"RS256\",\"typ\":\"JWT\"}"),
"alg=RS256 with kid is accepted");
TEST_SUMMARY();
}