mirror of
https://github.com/whyour/qinglong.git
synced 2026-09-21 01:32:44 +08:00
364 lines
10 KiB
JavaScript
364 lines
10 KiB
JavaScript
const assert = require('node:assert/strict');
|
|
const { spawnSync } = require('node:child_process');
|
|
const {
|
|
chmodSync,
|
|
mkdtempSync,
|
|
readFileSync,
|
|
realpathSync,
|
|
rmSync,
|
|
writeFileSync,
|
|
} = require('node:fs');
|
|
const { createServer } = require('node:https');
|
|
const { tmpdir } = require('node:os');
|
|
const { join, resolve } = require('node:path');
|
|
const { afterEach, test } = require('node:test');
|
|
|
|
const {
|
|
executeClusterModelProviderCredentialManagementClient,
|
|
validateClusterModelProviderCredentialManagementClientResult,
|
|
} = require('@qinglong/cluster-admin/model-provider-credential-management-client');
|
|
const {
|
|
createModelProviderCredentialTestAllowlist,
|
|
createModelProviderCredentialTestPlan,
|
|
} = require('@qinglong/ai/model-provider-credential-test-connection');
|
|
|
|
const FIXTURES = resolve(
|
|
__dirname,
|
|
'../../ql3-cluster-control/test/fixtures/mtls',
|
|
);
|
|
const CLI = resolve(
|
|
__dirname,
|
|
'../dist/model-provider-credential/modelProviderCredentialManagementClientCli.js',
|
|
);
|
|
const temporaryDirectories = [];
|
|
|
|
const bindCommand = Object.freeze({
|
|
schemaVersion: 1,
|
|
operation: 'provider-credential.bind',
|
|
request: Object.freeze({
|
|
requestId: 'request-bind-1',
|
|
mutationId: '123e4567-e89b-42d3-a456-426614174000',
|
|
projectId: 'project-a',
|
|
provider: 'openai-compatible',
|
|
expectedGeneration: 0,
|
|
revision: 'credential-v1',
|
|
secretRef: 'project/project-a/provider-token',
|
|
}),
|
|
});
|
|
|
|
const auditCommand = Object.freeze({
|
|
schemaVersion: 1,
|
|
operation: 'provider-credential.audit.list',
|
|
request: Object.freeze({
|
|
requestId: 'audit-request-1',
|
|
queryId: '219f7094-a853-4f3b-82ab-dfa08e6bd1c3',
|
|
projectId: 'project-a',
|
|
limit: 2,
|
|
}),
|
|
});
|
|
|
|
const testPlanCommand = Object.freeze({
|
|
schemaVersion: 1,
|
|
operation: 'provider-credential.test.plan',
|
|
request: Object.freeze({
|
|
requestId: 'test-request-1',
|
|
testId: '319f7094-a853-4f3b-82ab-dfa08e6bd1c4',
|
|
projectId: 'project-a',
|
|
provider: 'openai-compatible',
|
|
}),
|
|
});
|
|
|
|
function testPlanResult(overrides = {}) {
|
|
const allowlist = createModelProviderCredentialTestAllowlist({
|
|
revision: 'catalog-v1',
|
|
providers: [
|
|
{
|
|
provider: 'openai-compatible',
|
|
adapter: 'openai-compatible',
|
|
baseUrl: 'https://provider.example.test/v1/',
|
|
revision: 'endpoint-v1',
|
|
deadlineMs: 5_000,
|
|
maxResponseBytes: 64 * 1_024,
|
|
maxModels: 64,
|
|
maxCostMicrousd: 0,
|
|
retryLimit: 0,
|
|
},
|
|
],
|
|
});
|
|
const plan = createModelProviderCredentialTestPlan({
|
|
testId: testPlanCommand.request.testId,
|
|
requestId: testPlanCommand.request.requestId,
|
|
projectId: testPlanCommand.request.projectId,
|
|
provider: testPlanCommand.request.provider,
|
|
endpoint: allowlist.providers[0],
|
|
requestedBy: { type: 'user', id: 'owner-a' },
|
|
fence: { projectVersion: 3, bindingVersion: 7 },
|
|
plannedAtMs: 1_000,
|
|
expiresAtMs: 61_000,
|
|
...overrides,
|
|
});
|
|
return {
|
|
schemaVersion: 1,
|
|
operation: 'provider-credential.test.plan',
|
|
status: 'created',
|
|
plan,
|
|
};
|
|
}
|
|
|
|
function bindResult(overrides = {}) {
|
|
return {
|
|
schemaVersion: 1,
|
|
operation: 'provider-credential.bind',
|
|
status: 'created',
|
|
credential: {
|
|
projectId: 'project-a',
|
|
provider: 'openai-compatible',
|
|
generation: 1,
|
|
action: 'bind',
|
|
activeBindingRevision: 'credential-v1',
|
|
activeBindingDigest: 'a'.repeat(64),
|
|
transitionDigest: 'b'.repeat(64),
|
|
changedAtMs: 1_001,
|
|
...overrides,
|
|
},
|
|
};
|
|
}
|
|
|
|
function auditResult(overrides = {}) {
|
|
return {
|
|
schemaVersion: 1,
|
|
operation: 'provider-credential.audit.list',
|
|
audit: {
|
|
projectId: 'project-a',
|
|
records: [
|
|
{
|
|
eventId: '019f7094-a853-4f3b-82ab-dfa08e6bd1c1',
|
|
requestId: 'request-bind-1',
|
|
operation: 'provider-credential.bind',
|
|
actor: { type: 'user', id: 'owner-a' },
|
|
fence: { projectVersion: 3, bindingVersion: 7 },
|
|
occurredAtMs: 1_001,
|
|
},
|
|
],
|
|
nextCursor: null,
|
|
...overrides,
|
|
},
|
|
};
|
|
}
|
|
|
|
function privateWrite(filePath, value) {
|
|
writeFileSync(filePath, value, { mode: 0o600 });
|
|
chmodSync(filePath, 0o600);
|
|
}
|
|
|
|
function clientFiles(port, command) {
|
|
const directory = realpathSync(
|
|
mkdtempSync(join(tmpdir(), 'ql3-provider-credential-client-')),
|
|
);
|
|
temporaryDirectories.push(directory);
|
|
const paths = {
|
|
configFile: join(directory, 'client.json'),
|
|
commandFile: join(directory, 'command.json'),
|
|
assertionFile: join(directory, 'assertion.jwt'),
|
|
};
|
|
const caFile = join(directory, 'ca.crt');
|
|
const clientCertificateFile = join(directory, 'client.crt');
|
|
const clientPrivateKeyFile = join(directory, 'client.key');
|
|
privateWrite(caFile, readFileSync(join(FIXTURES, 'ca-cert.pem')));
|
|
privateWrite(
|
|
clientCertificateFile,
|
|
readFileSync(join(FIXTURES, 'client-cert.pem')),
|
|
);
|
|
privateWrite(
|
|
clientPrivateKeyFile,
|
|
readFileSync(join(FIXTURES, 'client-key.pem')),
|
|
);
|
|
privateWrite(
|
|
paths.configFile,
|
|
`${JSON.stringify({
|
|
schemaVersion: 1,
|
|
endpoint: `https://localhost:${port}/api/v3/provider-credentials/management`,
|
|
servername: 'localhost',
|
|
caFile,
|
|
clientCertificateFile,
|
|
clientPrivateKeyFile,
|
|
requestTimeoutMs: 2_000,
|
|
})}\n`,
|
|
);
|
|
privateWrite(paths.commandFile, `${JSON.stringify(command)}\n`);
|
|
privateWrite(
|
|
paths.assertionFile,
|
|
'eyJhbGciOiJFZERTQSJ9.eyJzdWIiOiJ1In0.c2lnbmF0dXJl',
|
|
);
|
|
return paths;
|
|
}
|
|
|
|
function listen(server) {
|
|
return new Promise((resolvePromise, reject) => {
|
|
server.once('error', reject);
|
|
server.listen(0, 'localhost', () => resolvePromise(server.address()));
|
|
});
|
|
}
|
|
|
|
function close(server) {
|
|
return new Promise((resolvePromise, reject) => {
|
|
server.close((error) => (error ? reject(error) : resolvePromise()));
|
|
});
|
|
}
|
|
|
|
afterEach(() => {
|
|
for (const directory of temporaryDirectories.splice(0)) {
|
|
rmSync(directory, { recursive: true, force: true });
|
|
}
|
|
});
|
|
|
|
test('validates an exact content-free result against the request fence', () => {
|
|
const result = validateClusterModelProviderCredentialManagementClientResult(
|
|
bindResult(),
|
|
bindCommand,
|
|
);
|
|
assert.equal(result.credential.generation, 1);
|
|
assert.doesNotMatch(
|
|
JSON.stringify(result),
|
|
/secretRef|provider-token|mutationId|authenticationId/,
|
|
);
|
|
assert.throws(() =>
|
|
validateClusterModelProviderCredentialManagementClientResult(
|
|
bindResult({ projectId: 'project-b' }),
|
|
bindCommand,
|
|
),
|
|
);
|
|
assert.throws(() =>
|
|
validateClusterModelProviderCredentialManagementClientResult(
|
|
{ ...bindResult(), secretRef: 'must-not-leak' },
|
|
bindCommand,
|
|
),
|
|
);
|
|
});
|
|
|
|
test('sends one TLS 1.3 mTLS provider credential command', async () => {
|
|
let observed;
|
|
const server = createServer(
|
|
{
|
|
key: readFileSync(join(FIXTURES, 'server-key.pem')),
|
|
cert: readFileSync(join(FIXTURES, 'server-cert.pem')),
|
|
ca: readFileSync(join(FIXTURES, 'ca-cert.pem')),
|
|
requestCert: true,
|
|
rejectUnauthorized: true,
|
|
minVersion: 'TLSv1.3',
|
|
maxVersion: 'TLSv1.3',
|
|
},
|
|
(request, response) => {
|
|
const chunks = [];
|
|
request.on('data', (chunk) => chunks.push(chunk));
|
|
request.once('end', () => {
|
|
observed = {
|
|
method: request.method,
|
|
path: request.url,
|
|
authorization: request.headers.authorization,
|
|
authorized: request.socket.authorized,
|
|
protocol: request.socket.getProtocol(),
|
|
command: JSON.parse(Buffer.concat(chunks).toString('utf8')),
|
|
};
|
|
const body = Buffer.from(
|
|
JSON.stringify({
|
|
schemaVersion: 1,
|
|
requestId: 'http-request-1',
|
|
result: bindResult(),
|
|
}),
|
|
);
|
|
response.writeHead(200, {
|
|
'content-type': 'application/json; charset=utf-8',
|
|
'content-length': String(body.length),
|
|
});
|
|
response.end(body);
|
|
});
|
|
},
|
|
);
|
|
const address = await listen(server);
|
|
try {
|
|
const result = await executeClusterModelProviderCredentialManagementClient(
|
|
clientFiles(address.port, bindCommand),
|
|
);
|
|
assert.equal(result.requestId, 'http-request-1');
|
|
assert.equal(result.result.credential.generation, 1);
|
|
assert.deepEqual(observed, {
|
|
method: 'POST',
|
|
path: '/api/v3/provider-credentials/management',
|
|
authorization: 'Bearer eyJhbGciOiJFZERTQSJ9.eyJzdWIiOiJ1In0.c2lnbmF0dXJl',
|
|
authorized: true,
|
|
protocol: 'TLSv1.3',
|
|
command: bindCommand,
|
|
});
|
|
} finally {
|
|
await close(server);
|
|
}
|
|
});
|
|
|
|
test('validates a bounded content-free audit page and rejects widening', () => {
|
|
const result = validateClusterModelProviderCredentialManagementClientResult(
|
|
auditResult(),
|
|
auditCommand,
|
|
);
|
|
assert.equal(result.audit.records.length, 1);
|
|
assert.doesNotMatch(
|
|
JSON.stringify(result),
|
|
/secretRef|provider-token|bindingDigest|transitionDigest|authenticationId|openai/i,
|
|
);
|
|
assert.throws(() =>
|
|
validateClusterModelProviderCredentialManagementClientResult(
|
|
auditResult({ provider: 'openai-compatible' }),
|
|
auditCommand,
|
|
),
|
|
);
|
|
assert.throws(() =>
|
|
validateClusterModelProviderCredentialManagementClientResult(
|
|
auditResult({
|
|
nextCursor: {
|
|
occurredAtMs: 1_001,
|
|
eventId: '019f7094-a853-4f3b-82ab-dfa08e6bd1c1',
|
|
},
|
|
}),
|
|
auditCommand,
|
|
),
|
|
);
|
|
});
|
|
|
|
test('validates the exact server-selected test plan identity', () => {
|
|
const result = validateClusterModelProviderCredentialManagementClientResult(
|
|
testPlanResult(),
|
|
testPlanCommand,
|
|
);
|
|
assert.equal(result.plan.endpoint.maxCostMicrousd, 0);
|
|
assert.equal(result.plan.endpoint.retryLimit, 0);
|
|
assert.doesNotMatch(JSON.stringify(result), /secretRef|token/i);
|
|
assert.throws(() =>
|
|
validateClusterModelProviderCredentialManagementClientResult(
|
|
testPlanResult({ projectId: 'project-b' }),
|
|
testPlanCommand,
|
|
),
|
|
);
|
|
assert.throws(() =>
|
|
validateClusterModelProviderCredentialManagementClientResult(
|
|
{ ...testPlanResult(), secretRef: 'must-not-leak' },
|
|
testPlanCommand,
|
|
),
|
|
);
|
|
});
|
|
|
|
test('client CLI exposes only private file paths and stable errors', () => {
|
|
const help = spawnSync(process.execPath, [CLI, '--help'], {
|
|
encoding: 'utf8',
|
|
});
|
|
assert.equal(help.status, 0);
|
|
assert.match(help.stdout, /^Usage: ql3-provider-credential-client /);
|
|
assert.doesNotMatch(help.stdout, /token value|secret value/i);
|
|
|
|
const invalid = spawnSync(process.execPath, [CLI, '--assertion=value'], {
|
|
encoding: 'utf8',
|
|
});
|
|
assert.equal(invalid.status, 64);
|
|
assert.match(invalid.stderr, /USAGE_INVALID/);
|
|
assert.doesNotMatch(invalid.stderr, /assertion=value/);
|
|
});
|