Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ services:

data-service:
platform: ${MYSTICAT_DATA_SERVICE_PLATFORM:-linux/amd64}
image: ${MYSTICAT_DATA_SERVICE_REPOSITORY:-682033462621.dkr.ecr.us-east-1.amazonaws.com/mysticat-data-service}:${MYSTICAT_DATA_SERVICE_TAG:-v1.11.0}
image: ${MYSTICAT_DATA_SERVICE_REPOSITORY:-682033462621.dkr.ecr.us-east-1.amazonaws.com/mysticat-data-service}:${MYSTICAT_DATA_SERVICE_TAG:-v1.13.0}
depends_on:
db:
condition: service_healthy
Expand All @@ -27,6 +27,7 @@ services:
PGRST_DB_SCHEMAS: public
PGRST_DB_ANON_ROLE: postgrest_anon
PGRST_DB_EXTRA_SEARCH_PATH: ""
PGRST_JWT_SECRET: local-dev-jwt-secret-for-postgrest-only
PGRST_OPENAPI_SERVER_PROXY_URI: http://localhost:${IT_POSTGREST_PORT:-3300}
ports:
- "${IT_POSTGREST_PORT:-3300}:3000"
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
*/

import { createDataAccess } from '../../../src/service/index.js';
import { POSTGREST_WRITER_JWT } from '../util/postgrest-jwt.js';

export const TEST_IDS = {
organizationId: '04f63783-3f76-4076-bbda-71a11145303c',
Expand All @@ -36,5 +37,6 @@ export const createLogger = () => ({

export const createITDataAccess = () => {
const postgrestUrl = process.env.POSTGREST_URL || 'http://127.0.0.1:3300';
return createDataAccess({ postgrestUrl }, createLogger());
const postgrestApiKey = process.env.POSTGREST_API_KEY || POSTGREST_WRITER_JWT;
return createDataAccess({ postgrestUrl, postgrestApiKey }, createLogger());
};
5 changes: 4 additions & 1 deletion packages/spacecat-shared-data-access/test/it/util/db.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import { fileURLToPath } from 'node:url';
import { spawn } from 'node:child_process';

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

const filePath = fileURLToPath(import.meta.url);
const directoryPath = path.dirname(filePath);
Expand Down Expand Up @@ -184,7 +185,9 @@ export const getDataAccess = (config = {}, logger = console) => {
return createDataAccess({
postgrestUrl,
postgrestSchema,
postgrestApiKey: config.postgrestApiKey || process.env.POSTGREST_API_KEY,
postgrestApiKey: config.postgrestApiKey
|| process.env.POSTGREST_API_KEY
|| POSTGREST_WRITER_JWT,
s3Bucket: config.s3Bucket || process.env.S3_CONFIG_BUCKET,
region: config.region || process.env.AWS_REGION,
s2sAllowedImsOrgIds,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
/*
* Copyright 2025 Adobe. All rights reserved.
* This file is licensed to you under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. You may obtain a copy
* of the License at http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under
* the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
* OF ANY KIND, either express or implied. See the License for the specific language
* governing permissions and limitations under the License.
*/

/**
* PostgREST JWT utilities for IT tests.
*
* Creates minimal HS256 JWTs for PostgREST authentication.
* Mirrors the _make_jwt() function from mysticat-data-service/tests/conftest.py
*
* After mysticat-data-service PR #92 (postgrest_writer role), DELETE and UPDATE
* operations require JWT authentication. The data access layer uses postgrestApiKey
* to authenticate to PostgREST.
*/

import crypto from 'node:crypto';

// Must match PGRST_JWT_SECRET in docker-compose.yml
export const POSTGREST_JWT_SECRET = 'local-dev-jwt-secret-for-postgrest-only';

/**
* Base64url encode (RFC 4648 §5).
* @param {string|Buffer} data - Data to encode
* @returns {string} Base64url-encoded string
*/
function base64url(data) {
const buf = Buffer.isBuffer(data) ? data : Buffer.from(data);
return buf
.toString('base64')
.replace(/\+/g, '-')
.replace(/\//g, '_')
.replace(/=+$/, '');
}

/**
* Creates a minimal HS256 JWT for PostgREST authentication.
*
* @param {string} secret - JWT secret (must match PGRST_JWT_SECRET)
* @param {string} role - PostgreSQL role to assume (e.g., 'postgrest_writer')
* @returns {string} Signed JWT token
*/
export function makePostgrestJwt(secret, role) {
const header = base64url(JSON.stringify({ alg: 'HS256', typ: 'JWT' }));
const payload = base64url(JSON.stringify({ role }));
const signature = base64url(
crypto.createHmac('sha256', secret)
.update(`${header}.${payload}`)
.digest(),
);
return `${header}.${payload}.${signature}`;
}

/**
* Pre-generated JWT for postgrest_writer role.
* Used by the data access layer to authenticate DELETE/UPDATE operations to PostgREST.
*/
export const POSTGREST_WRITER_JWT = makePostgrestJwt(
POSTGREST_JWT_SECRET,
'postgrest_writer',
);