From 49a6b2e4e72caa943a96f0d844ff08f4da0ed291 Mon Sep 17 00:00:00 2001 From: "Peter.Morton" Date: Wed, 22 Apr 2026 19:57:03 -0500 Subject: [PATCH] [Spec Kit] Implementation progress --- CHANGELOG.md | 33 +++++++++++++++ README.md | 112 +++++++++++++++++++++++++++++++++++++++++++++++++++ package.json | 2 +- 3 files changed, 146 insertions(+), 1 deletion(-) create mode 100644 CHANGELOG.md diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..d695156 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,33 @@ +# Changelog + +All notable changes to this project will be documented in this file. + +The format follows [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). +This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). + +--- + +## [Unreleased] + +--- + +## [0.1.0] - 2026-04-23 + +### Added + +- `src/proxyScripts/kmeContentSourceAdapter.js` — OIDC authentication proxy script running in a Node.js VM sandbox (zero imports/exports) +- `src/globalVariables/kme_CSA_settings.json` — OIDC credentials and token endpoint configuration (gitignored) +- `src/globalVariables/kme_CSA_settings.json.example` — placeholder settings file for version control +- Redis-backed token cache (`authorization` hash, fields `token` and `expiry`) — token persists across adapter restarts +- Token stampede guard via in-process `_pendingFetch` promise — only one token fetch in-flight at a time +- Absolute Unix epoch expiry check (`Date.now() / 1000 < expiry`) +- `200 OK / Authorized` response on successful authentication +- `401 Unauthorized` response with descriptive message on auth failure (bad credentials, timeout, unreachable service) +- 5-second timeout on OIDC token POST requests +- Structured logging throughout proxy script using `console.debug`, `console.info`, and `console.error` +- `redis` dependency wired into VM context via `createClient().connect()` in `server.js` +- Unit tests (`tests/unit/proxy.test.js`) — 12 tests covering US1, US2, US3, and stampede guard +- Contract tests (`tests/contract/proxy-http.test.js`) — 2 tests covering HTTP 200/401 response shape + +[Unreleased]: https://github.com/your-org/kme-content-adapter/compare/v0.1.0...HEAD +[0.1.0]: https://github.com/your-org/kme-content-adapter/releases/tag/v0.1.0 diff --git a/README.md b/README.md index e69de29..bd791ea 100644 --- a/README.md +++ b/README.md @@ -0,0 +1,112 @@ +# kme-content-adapter + +An HTTP proxy adapter that authenticates against KME and proxies content requests through an isolated VM sandbox, mirroring the IVA Studio proxy script execution environment. + +## Requirements + +- Node.js ≥ 18 +- Redis (used for token caching) +- `jq` (optional — used by `npm start` for log pretty-printing) + +## Setup + +```bash +npm install +cp src/globalVariables/kme_CSA_settings.json.example src/globalVariables/kme_CSA_settings.json +# Edit kme_CSA_settings.json with real credentials +``` + +## Configuration + +### `src/globalVariables/kme_CSA_settings.json` + +Credentials and OIDC settings — **never commit this file**. + +```json +{ + "tokenUrl": "https:///oidc-token-service//token", + "username": "", + "password": "", + "clientId": "default", + "scope": "openid tags content_entitlements" +} +``` + +### `config/default.json` + +Infrastructure settings (port, host, log level). Override with environment variables: + +| Variable | Default | Description | +|---|---|---| +| `PORT` | `3000` | HTTP server port | +| `HOST` | `0.0.0.0` | Bind address | +| `LOG_LEVEL` | `debug` | Log level: `DEBUG`, `INFO`, `WARN`, `ERROR` | + +## Running + +```bash +npm run dev # Development — auto-restart on file changes +npm start # Production — logs piped through jq +``` + +## Testing + +```bash +npm test # All tests +npm run test:unit # Unit tests only +npm run test:integration # Integration tests only +npm run test:contract # Contract tests only + +# Single test file +node --test tests/unit/proxy.test.js +``` + +Tests use the Node.js built-in `node:test` runner. No external test framework. + +## Architecture + +The server loads `src/proxyScripts/kmeContentSourceAdapter.js` once at startup via `vm.Script`, then executes it in a **fresh isolated VM context per request** via `vm.createContext`. + +``` +src/ +├── proxyScripts/ +│ └── kmeContentSourceAdapter.js # All business logic (zero imports/exports) +├── globalVariables/ +│ ├── kme_CSA_settings.json # OIDC credentials (gitignored) +│ └── adapterHelper.js # Pure utilities (optional) +├── logger.js # Structured JSON logger +└── server.js # HTTP server bootstrap only +config/ +└── default.json # Infrastructure settings +``` + +### VM Context Globals + +All dependencies are injected into each request's sandbox: + +| Variable | Source | +|---|---| +| `console` | Structured logger | +| `crypto` | Node.js Web Crypto API | +| `axios` | HTTP client | +| `jwt` | `jsonwebtoken` | +| `uuidv4` | UUID v4 generator | +| `xmlBuilder` | `xmlbuilder2` `create` | +| `redis` | Connected Redis client | +| `URLSearchParams`, `URL` | Node.js globals | +| `kme_CSA_settings` | Loaded from `src/globalVariables/kme_CSA_settings.json` | +| `req`, `res` | Node.js HTTP request/response | + +### Key Constraints for `kmeContentSourceAdapter.js` + +- **Zero `import`/`export`** — runs in a VM with no module system +- **No `config`, `global.config`, or `process.env`** — use injected globals only +- Routing metadata is available via `req.params` (set by `server.js`) + +## Token Caching + +OIDC tokens are cached in Redis under the hash key `authorization` (fields `token` and `expiry`). The cache survives adapter restarts. Token expiry is stored as an absolute Unix epoch timestamp. + +## Changelog + +See [CHANGELOG.md](CHANGELOG.md). diff --git a/package.json b/package.json index 90b2757..68ffb06 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "kme-content-adapter", - "version": "1.0.0", + "version": "0.1.0", "description": "HTTP proxy adapter to search and export documents from KME", "type": "module", "main": "src/server.js",