Adds API filter registry, style theme registry, SW bitmask cache clear, KV namespacing, session expiry checks, accessibility improvements, and expanded test coverage. Co-authored-by: Cursor <cursoragent@cursor.com>
121 lines
3.7 KiB
JavaScript
121 lines
3.7 KiB
JavaScript
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<typeof createAPIFilterRegistry>} */
|
|
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');
|
|
});
|
|
});
|