Skip to content

Commit cb45ae4

Browse files
ekremneyclaude
andcommitted
test(data-access): add JWT auth for PostgREST mutation access control
Adapts IT tests for mysticat-data-service PR #92 which introduces the postgrest_writer role for JWT-based mutation access control. Changes: - Add postgrest-jwt.js utility for HS256 JWT generation - Configure PGRST_JWT_SECRET in docker-compose.yml - Pass postgrestApiKey to createDataAccess() with generated JWT - Bump mysticat-data-service image to v1.13.0 After this change, DELETE and UPDATE operations through PostgREST require JWT authentication, while SELECT and INSERT remain anonymous. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
1 parent bf62ba2 commit cb45ae4

File tree

3 files changed

+74
-2
lines changed

3 files changed

+74
-2
lines changed

packages/spacecat-shared-data-access/test/it/postgrest/docker-compose.yml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ services:
1717

1818
data-service:
1919
platform: ${MYSTICAT_DATA_SERVICE_PLATFORM:-linux/amd64}
20-
image: ${MYSTICAT_DATA_SERVICE_REPOSITORY:-682033462621.dkr.ecr.us-east-1.amazonaws.com/mysticat-data-service}:${MYSTICAT_DATA_SERVICE_TAG:-v1.11.0}
20+
image: ${MYSTICAT_DATA_SERVICE_REPOSITORY:-682033462621.dkr.ecr.us-east-1.amazonaws.com/mysticat-data-service}:${MYSTICAT_DATA_SERVICE_TAG:-v1.13.0}
2121
depends_on:
2222
db:
2323
condition: service_healthy
@@ -27,6 +27,7 @@ services:
2727
PGRST_DB_SCHEMAS: public
2828
PGRST_DB_ANON_ROLE: postgrest_anon
2929
PGRST_DB_EXTRA_SEARCH_PATH: ""
30+
PGRST_JWT_SECRET: local-dev-jwt-secret-for-postgrest-only
3031
PGRST_OPENAPI_SERVER_PROXY_URI: http://localhost:${IT_POSTGREST_PORT:-3300}
3132
ports:
3233
- "${IT_POSTGREST_PORT:-3300}:3000"

packages/spacecat-shared-data-access/test/it/util/db.js

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import { fileURLToPath } from 'node:url';
1515
import { spawn } from 'node:child_process';
1616

1717
import { createDataAccess } from '../../../src/service/index.js';
18+
import { POSTGREST_WRITER_JWT } from './postgrest-jwt.js';
1819

1920
const filePath = fileURLToPath(import.meta.url);
2021
const directoryPath = path.dirname(filePath);
@@ -184,7 +185,9 @@ export const getDataAccess = (config = {}, logger = console) => {
184185
return createDataAccess({
185186
postgrestUrl,
186187
postgrestSchema,
187-
postgrestApiKey: config.postgrestApiKey || process.env.POSTGREST_API_KEY,
188+
postgrestApiKey: config.postgrestApiKey
189+
|| process.env.POSTGREST_API_KEY
190+
|| POSTGREST_WRITER_JWT,
188191
s3Bucket: config.s3Bucket || process.env.S3_CONFIG_BUCKET,
189192
region: config.region || process.env.AWS_REGION,
190193
s2sAllowedImsOrgIds,
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
/*
2+
* Copyright 2025 Adobe. All rights reserved.
3+
* This file is licensed to you under the Apache License, Version 2.0 (the "License");
4+
* you may not use this file except in compliance with the License. You may obtain a copy
5+
* of the License at http://www.apache.org/licenses/LICENSE-2.0
6+
*
7+
* Unless required by applicable law or agreed to in writing, software distributed under
8+
* the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
9+
* OF ANY KIND, either express or implied. See the License for the specific language
10+
* governing permissions and limitations under the License.
11+
*/
12+
13+
/**
14+
* PostgREST JWT utilities for IT tests.
15+
*
16+
* Creates minimal HS256 JWTs for PostgREST authentication.
17+
* Mirrors the _make_jwt() function from mysticat-data-service/tests/conftest.py
18+
*
19+
* After mysticat-data-service PR #92 (postgrest_writer role), DELETE and UPDATE
20+
* operations require JWT authentication. The data access layer uses postgrestApiKey
21+
* to authenticate to PostgREST.
22+
*/
23+
24+
import crypto from 'node:crypto';
25+
26+
// Must match PGRST_JWT_SECRET in docker-compose.yml
27+
export const POSTGREST_JWT_SECRET = 'local-dev-jwt-secret-for-postgrest-only';
28+
29+
/**
30+
* Base64url encode (RFC 4648 §5).
31+
* @param {string|Buffer} data - Data to encode
32+
* @returns {string} Base64url-encoded string
33+
*/
34+
function base64url(data) {
35+
const buf = Buffer.isBuffer(data) ? data : Buffer.from(data);
36+
return buf
37+
.toString('base64')
38+
.replace(/\+/g, '-')
39+
.replace(/\//g, '_')
40+
.replace(/=+$/, '');
41+
}
42+
43+
/**
44+
* Creates a minimal HS256 JWT for PostgREST authentication.
45+
*
46+
* @param {string} secret - JWT secret (must match PGRST_JWT_SECRET)
47+
* @param {string} role - PostgreSQL role to assume (e.g., 'postgrest_writer')
48+
* @returns {string} Signed JWT token
49+
*/
50+
export function makePostgrestJwt(secret, role) {
51+
const header = base64url(JSON.stringify({ alg: 'HS256', typ: 'JWT' }));
52+
const payload = base64url(JSON.stringify({ role }));
53+
const signature = base64url(
54+
crypto.createHmac('sha256', secret)
55+
.update(`${header}.${payload}`)
56+
.digest(),
57+
);
58+
return `${header}.${payload}.${signature}`;
59+
}
60+
61+
/**
62+
* Pre-generated JWT for postgrest_writer role.
63+
* Used by the data access layer to authenticate DELETE/UPDATE operations to PostgREST.
64+
*/
65+
export const POSTGREST_WRITER_JWT = makePostgrestJwt(
66+
POSTGREST_JWT_SECRET,
67+
'postgrest_writer',
68+
);

0 commit comments

Comments
 (0)