import { beforeEach, describe, test } from 'node:test'; import assert from 'node:assert'; import { applyRequestAuthorization, createAPIFilterRegistry, normalizeRequestAuthorization } from '../src/platform/api-filters.js'; import { SECURITY_REQUEST_FILTER, createSecurityRequestFilter } from '../src/security/runtime/api-auth.js'; describe('api-filters', () => { /** @type {ReturnType} */ let registry; beforeEach(() => { registry = createAPIFilterRegistry(); }); test('normalizeRequestAuthorization supports string, scheme/token, and custom headers', () => { assert.deepStrictEqual( normalizeRequestAuthorization('Bearer abc'), { name: 'Authorization', value: 'Bearer abc' } ); assert.deepStrictEqual( normalizeRequestAuthorization({ scheme: 'Basic', token: 'dXNlcjpwYXNz' }), { name: 'Authorization', value: 'Basic dXNlcjpwYXNz' } ); assert.deepStrictEqual( normalizeRequestAuthorization({ name: 'X-Api-Key', value: 'secret' }), { name: 'X-Api-Key', value: 'secret' } ); }); test('applyRequestFilters runs in priority order and supports async filters', async () => { const calls = []; registry.registerRequestFilter('second', async (ctx) => { calls.push('second'); return { ...ctx, headers: applyRequestAuthorization(ctx.headers, { name: 'X-Second', value: '2' }) }; }, { priority: 20 }); registry.registerRequestFilter('first', async (ctx) => { calls.push('first'); return { ...ctx, headers: applyRequestAuthorization(ctx.headers, { name: 'X-First', value: '1' }) }; }, { priority: 10 }); const result = await registry.applyRequestFilters({ url: '/api/items', endpoint: '/items', headers: new Headers() }); assert.deepStrictEqual(calls, ['first', 'second']); assert.strictEqual(result.headers.get('X-First'), '1'); assert.strictEqual(result.headers.get('X-Second'), '2'); }); test('skipRequestFilters can skip named filters', async () => { registry.registerRequestFilter('tenant', (ctx) => ({ ...ctx, headers: applyRequestAuthorization(ctx.headers, { name: 'X-Tenant', value: 'acme' }) }), { priority: 10 }); registry.registerRequestFilter('auth', (ctx) => ({ ...ctx, headers: applyRequestAuthorization(ctx.headers, { scheme: 'Bearer', token: 'token' }) }), { priority: 100 }); const result = await registry.applyRequestFilters({ url: '/api/login', endpoint: '/login', headers: new Headers(), skipRequestFilters: ['auth'] }); assert.strictEqual(result.headers.get('X-Tenant'), 'acme'); assert.strictEqual(result.headers.get('Authorization'), null); }); test('security request filter delegates authorization to the active policy', async () => { const securityService = { state: { enabled: true, provider: 'basic', isAuthenticated: true, session: { jwt_token: 'ignored-if-policy-returns' }, user: { id: 'user-1' }, profile: null, realm: null, config: {}, policy: { async getRequestAuthorization() { return { name: 'X-Api-Key', value: 'policy-key' }; } } } }; const result = await createSecurityRequestFilter(securityService)({ url: '/api/items', endpoint: '/items', headers: new Headers() }); assert.strictEqual(result.headers.get('X-Api-Key'), 'policy-key'); assert.strictEqual(result.headers.get('Authorization'), null); }); test('installable security filter id is stable', () => { assert.strictEqual(SECURITY_REQUEST_FILTER, 'security.auth'); }); });