mirror of
https://github.com/whyour/qinglong.git
synced 2026-09-22 10:32:40 +08:00
feat(ql3): establish 3.0 incubation baseline
This commit is contained in:
@@ -0,0 +1,331 @@
|
||||
const assert = require('node:assert/strict');
|
||||
const test = require('node:test');
|
||||
|
||||
const {
|
||||
ModelProviderCredentialAdministrationAuthorizationFenceConflictError,
|
||||
} = require('@qinglong/ai/model-provider-credential-administration');
|
||||
const {
|
||||
createModelProviderCredentialTransition,
|
||||
} = require('@qinglong/ai/model-provider-credential-catalog');
|
||||
const {
|
||||
createModelProviderCredentialTestAllowlist,
|
||||
} = require('@qinglong/ai/model-provider-credential-test-connection');
|
||||
const {
|
||||
ClusterModelProviderCredentialManagementAuthenticationError,
|
||||
ClusterModelProviderCredentialManagementAuthorizationError,
|
||||
ClusterModelProviderCredentialManagementConflictError,
|
||||
ClusterModelProviderCredentialManagementRequestError,
|
||||
createClusterModelProviderCredentialManagementService,
|
||||
} = require('../dist/model-provider-credential/modelProviderCredentialManagement.js');
|
||||
const { createSecretRef } = require('@qinglong/runtime-core/secret-reference');
|
||||
|
||||
const NOW_MS = 1_000_000;
|
||||
const MUTATION_ID = '019f7094-a853-4f3b-82ab-dfa08e6bd1c1';
|
||||
|
||||
function principal(overrides = {}) {
|
||||
return {
|
||||
subject: { type: 'user', id: 'owner-a' },
|
||||
authenticationId: 'authentication-1',
|
||||
authenticatedAtMs: NOW_MS - 1_000,
|
||||
expiresAtMs: NOW_MS + 60_000,
|
||||
assurance: 'multi_factor',
|
||||
...overrides,
|
||||
};
|
||||
}
|
||||
|
||||
function bindRequest(overrides = {}) {
|
||||
return {
|
||||
requestId: 'request-1',
|
||||
mutationId: MUTATION_ID,
|
||||
projectId: 'project-a',
|
||||
provider: 'openai-compatible',
|
||||
expectedGeneration: 0,
|
||||
revision: 'credential-v1',
|
||||
secretRef: createSecretRef({
|
||||
projectId: 'project-a',
|
||||
name: 'openai-token',
|
||||
}),
|
||||
principal: principal(),
|
||||
...overrides,
|
||||
};
|
||||
}
|
||||
|
||||
function fixture(overrides = {}) {
|
||||
const calls = { policy: [], mutations: [], audits: [], testPlans: [] };
|
||||
const policy = {
|
||||
async authorize(...args) {
|
||||
calls.policy.push(args);
|
||||
return (
|
||||
overrides.decision ?? {
|
||||
effect: 'allow',
|
||||
reasons: ['project_owner'],
|
||||
fence: { projectVersion: 3, bindingVersion: 7 },
|
||||
}
|
||||
);
|
||||
},
|
||||
};
|
||||
const credentials = {
|
||||
async findCurrentTransition() {
|
||||
return null;
|
||||
},
|
||||
async commit() {
|
||||
throw new Error('raw commit must not be used');
|
||||
},
|
||||
async commitAuthorized(mutation) {
|
||||
calls.mutations.push(mutation);
|
||||
if (overrides.repositoryError) throw overrides.repositoryError;
|
||||
return {
|
||||
status: 'created',
|
||||
transition: createModelProviderCredentialTransition(
|
||||
mutation.command,
|
||||
null,
|
||||
NOW_MS,
|
||||
),
|
||||
};
|
||||
},
|
||||
};
|
||||
const audit = {
|
||||
async listAuthorized(query) {
|
||||
calls.audits.push(query);
|
||||
if (overrides.auditError) throw overrides.auditError;
|
||||
return {
|
||||
projectId: query.query.projectId,
|
||||
records: [
|
||||
{
|
||||
eventId: MUTATION_ID,
|
||||
requestId: 'request-1',
|
||||
operation: 'provider-credential.bind',
|
||||
actor: { type: 'user', id: 'owner-a' },
|
||||
fence: { projectVersion: 3, bindingVersion: 7 },
|
||||
occurredAtMs: NOW_MS - 1,
|
||||
},
|
||||
],
|
||||
nextCursor: null,
|
||||
};
|
||||
},
|
||||
};
|
||||
const testAllowlist = 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 testPlans = {
|
||||
async createAuthorized(value) {
|
||||
calls.testPlans.push(value);
|
||||
if (overrides.testPlanError) throw overrides.testPlanError;
|
||||
return { status: 'created', plan: value.plan };
|
||||
},
|
||||
};
|
||||
return {
|
||||
calls,
|
||||
service: createClusterModelProviderCredentialManagementService({
|
||||
policy,
|
||||
credentials,
|
||||
audit,
|
||||
testPlans,
|
||||
testAllowlist,
|
||||
testPlanLifetimeMs: 60_000,
|
||||
now: () => NOW_MS,
|
||||
}),
|
||||
};
|
||||
}
|
||||
|
||||
test('test connection plan selects endpoint and hard budgets server-side', async () => {
|
||||
const { service, calls } = fixture();
|
||||
const testId = '319f7094-a853-4f3b-82ab-dfa08e6bd1c4';
|
||||
const result = await service.planTestConnection({
|
||||
requestId: 'test-request-1',
|
||||
testId,
|
||||
projectId: 'project-a',
|
||||
provider: 'openai-compatible',
|
||||
principal: principal(),
|
||||
});
|
||||
assert.equal(result.status, 'created');
|
||||
assert.equal(result.plan.testId, testId);
|
||||
assert.equal(
|
||||
result.plan.endpoint.baseUrl,
|
||||
'https://provider.example.test/v1/',
|
||||
);
|
||||
assert.equal(result.plan.endpoint.maxCostMicrousd, 0);
|
||||
assert.equal(result.plan.endpoint.retryLimit, 0);
|
||||
assert.equal(result.plan.expiresAtMs - result.plan.plannedAtMs, 60_000);
|
||||
assert.equal(calls.policy[0][2], 'secret.manage');
|
||||
assert.equal(calls.testPlans.length, 1);
|
||||
assert.equal(
|
||||
calls.testPlans[0].audit.operationId,
|
||||
'model_provider_credential.test.plan',
|
||||
);
|
||||
assert.doesNotMatch(JSON.stringify(calls.testPlans[0]), /secretRef|token/i);
|
||||
});
|
||||
|
||||
test('test connection request cannot supply URL, budgets or SecretRef', async () => {
|
||||
const { service, calls } = fixture();
|
||||
const base = {
|
||||
requestId: 'test-request-1',
|
||||
testId: '419f7094-a853-4f3b-82ab-dfa08e6bd1c5',
|
||||
projectId: 'project-a',
|
||||
provider: 'openai-compatible',
|
||||
principal: principal(),
|
||||
};
|
||||
for (const widened of [
|
||||
{ baseUrl: 'https://attacker.example/v1/' },
|
||||
{ deadlineMs: 60_000 },
|
||||
{ secretRef: 'qlsecret:project-a/openai-token' },
|
||||
]) {
|
||||
await assert.rejects(
|
||||
service.planTestConnection({ ...base, ...widened }),
|
||||
ClusterModelProviderCredentialManagementRequestError,
|
||||
);
|
||||
}
|
||||
assert.equal(calls.policy.length, 0);
|
||||
assert.equal(calls.testPlans.length, 0);
|
||||
});
|
||||
|
||||
test('bind derives actor and exact secret.manage authority server-side', async () => {
|
||||
const { service, calls } = fixture();
|
||||
const result = await service.bind(bindRequest());
|
||||
|
||||
assert.equal(result.status, 'created');
|
||||
assert.equal(result.transition.activeBindingRevision, 'credential-v1');
|
||||
assert.equal(calls.policy.length, 1);
|
||||
assert.equal(calls.policy[0][1], 'project-a');
|
||||
assert.equal(calls.policy[0][2], 'secret.manage');
|
||||
assert.equal(calls.mutations.length, 1);
|
||||
assert.deepEqual(calls.mutations[0].command.changedBy, {
|
||||
type: 'user',
|
||||
id: 'owner-a',
|
||||
});
|
||||
assert.equal(calls.mutations[0].audit.outcome, 'allowed');
|
||||
assert.equal(
|
||||
calls.mutations[0].audit.operationId,
|
||||
'model_provider_credential.bind',
|
||||
);
|
||||
assert.equal(
|
||||
JSON.stringify(calls.mutations[0]).includes('authorization'),
|
||||
false,
|
||||
);
|
||||
});
|
||||
|
||||
test('revoke is Project-fenced and cannot carry binding material', async () => {
|
||||
const { service, calls } = fixture();
|
||||
const request = bindRequest();
|
||||
const result = await service.revoke({
|
||||
requestId: 'request-2',
|
||||
mutationId: '119f7094-a853-4f3b-82ab-dfa08e6bd1c2',
|
||||
projectId: request.projectId,
|
||||
provider: request.provider,
|
||||
expectedGeneration: 0,
|
||||
principal: request.principal,
|
||||
});
|
||||
assert.equal(result.transition.action, 'revoke');
|
||||
assert.equal(calls.mutations[0].command.binding, null);
|
||||
assert.equal(
|
||||
calls.mutations[0].audit.operationId,
|
||||
'model_provider_credential.revoke',
|
||||
);
|
||||
});
|
||||
|
||||
test('weak, stale and denied principals fail before repository mutation', async () => {
|
||||
for (const scenario of [
|
||||
{
|
||||
request: bindRequest({
|
||||
principal: principal({ assurance: 'single_factor' }),
|
||||
}),
|
||||
error: ClusterModelProviderCredentialManagementAuthenticationError,
|
||||
},
|
||||
{
|
||||
request: bindRequest({
|
||||
principal: principal({ authenticatedAtMs: NOW_MS - 300_001 }),
|
||||
}),
|
||||
error: ClusterModelProviderCredentialManagementAuthenticationError,
|
||||
},
|
||||
{
|
||||
decision: { effect: 'deny', reasons: ['policy_denied'], fence: null },
|
||||
request: bindRequest(),
|
||||
error: ClusterModelProviderCredentialManagementAuthorizationError,
|
||||
},
|
||||
]) {
|
||||
const { service, calls } = fixture({ decision: scenario.decision });
|
||||
await assert.rejects(service.bind(scenario.request), scenario.error);
|
||||
assert.equal(calls.mutations.length, 0);
|
||||
}
|
||||
});
|
||||
|
||||
test('request widening and durable fence drift fail closed', async () => {
|
||||
const { service } = fixture();
|
||||
await assert.rejects(
|
||||
service.bind({
|
||||
...bindRequest(),
|
||||
changedBy: { type: 'user', id: 'owner-b' },
|
||||
}),
|
||||
ClusterModelProviderCredentialManagementRequestError,
|
||||
);
|
||||
await assert.rejects(
|
||||
service.bind({ ...bindRequest(), secretRef: 'not-a-canonical-secret-ref' }),
|
||||
ClusterModelProviderCredentialManagementRequestError,
|
||||
);
|
||||
|
||||
const conflict = fixture({
|
||||
repositoryError:
|
||||
new ModelProviderCredentialAdministrationAuthorizationFenceConflictError(),
|
||||
});
|
||||
await assert.rejects(
|
||||
conflict.service.bind(bindRequest()),
|
||||
ClusterModelProviderCredentialManagementConflictError,
|
||||
);
|
||||
});
|
||||
|
||||
test('audit query reuses secret.manage and returns only content-free events', async () => {
|
||||
const { service, calls } = fixture();
|
||||
const result = await service.listAudit({
|
||||
requestId: 'audit-request-1',
|
||||
queryId: '219f7094-a853-4f3b-82ab-dfa08e6bd1c3',
|
||||
projectId: 'project-a',
|
||||
limit: 8,
|
||||
principal: principal(),
|
||||
});
|
||||
assert.equal(calls.policy.length, 1);
|
||||
assert.equal(calls.policy[0][2], 'secret.manage');
|
||||
assert.equal(calls.audits.length, 1);
|
||||
assert.equal(
|
||||
calls.audits[0].audit.operationId,
|
||||
'model_provider_credential.audit.list',
|
||||
);
|
||||
assert.deepEqual(calls.audits[0].fence, {
|
||||
projectVersion: 3,
|
||||
bindingVersion: 7,
|
||||
});
|
||||
assert.equal(result.records[0].operation, 'provider-credential.bind');
|
||||
assert.doesNotMatch(
|
||||
JSON.stringify(result),
|
||||
/secretRef|provider-token|bindingDigest|authenticationId|openai/i,
|
||||
);
|
||||
});
|
||||
|
||||
test('invalid audit cursors fail before policy or storage', async () => {
|
||||
const { service, calls } = fixture();
|
||||
await assert.rejects(
|
||||
service.listAudit({
|
||||
requestId: 'audit-request-1',
|
||||
queryId: '219f7094-a853-4f3b-82ab-dfa08e6bd1c3',
|
||||
projectId: 'project-a',
|
||||
limit: 8,
|
||||
before: { occurredAtMs: 1, eventId: 'not-a-uuid' },
|
||||
principal: principal(),
|
||||
}),
|
||||
ClusterModelProviderCredentialManagementRequestError,
|
||||
);
|
||||
assert.equal(calls.policy.length, 0);
|
||||
assert.equal(calls.audits.length, 0);
|
||||
});
|
||||
Reference in New Issue
Block a user