mirror of
https://github.com/whyour/qinglong.git
synced 2026-09-21 00:36:13 +08:00
290 lines
8.9 KiB
JavaScript
290 lines
8.9 KiB
JavaScript
'use strict';
|
|
|
|
const assert = require('node:assert/strict');
|
|
const { test } = require('node:test');
|
|
|
|
const {
|
|
consumeApprovalRequest,
|
|
createApprovalRequest,
|
|
decideApprovalRequest,
|
|
} = require('@qinglong/runtime-core/approved-action');
|
|
const {
|
|
claimApprovedActionExecution,
|
|
createApprovedActionExecution,
|
|
startApprovedActionExecution,
|
|
} = require('@qinglong/runtime-core/approved-action-execution');
|
|
const {
|
|
createPluginPackageResourceGeneration,
|
|
} = require('@qinglong/runtime-core/plugin-package-resource-generation');
|
|
const {
|
|
createPluginPackageSecretBinding,
|
|
} = require('@qinglong/runtime-core/plugin-package-secret-binding');
|
|
const {
|
|
createPluginPackageSecretBindingTransitionApprovalPlan,
|
|
pluginPackageSecretBindingTransitionApprovedAction,
|
|
} = require('@qinglong/runtime-core/plugin-package-secret-binding-transition-approval-plan');
|
|
const {
|
|
createPluginPackageSecretBindingTransitionPlan,
|
|
} = require('@qinglong/runtime-core/plugin-package-secret-binding-transition-plan');
|
|
const { createSecretRef } = require('@qinglong/runtime-core/secret-reference');
|
|
const {
|
|
ClusterPluginPackageSecretBindingTransitionApprovedActionHandler,
|
|
} = require('@qinglong/cluster-admin/plugin-package-secret-binding-transition-approved-action');
|
|
|
|
const REQUESTER = Object.freeze({ type: 'user', id: 'cluster-owner' });
|
|
const REVIEWER = Object.freeze({ type: 'user', id: 'security-reviewer' });
|
|
const CONSUMER = Object.freeze({
|
|
type: 'system',
|
|
id: 'cluster_package_executor',
|
|
});
|
|
const FENCE = Object.freeze({ projectVersion: 3, bindingVersion: 4 });
|
|
|
|
function manifest(version) {
|
|
return {
|
|
apiVersion: 'qinglong.io/v1alpha1',
|
|
kind: 'Package',
|
|
metadata: {
|
|
name: 'example-monitor',
|
|
displayName: 'Example Monitor',
|
|
version,
|
|
description: 'Secret transition handler fixture',
|
|
license: 'Apache-2.0',
|
|
},
|
|
spec: {
|
|
compatibility: {
|
|
qinglong: '>=3.0.0-0 <4.0.0',
|
|
architectures: ['arm64'],
|
|
deploymentProfiles: ['cluster-control'],
|
|
},
|
|
runtimes: [],
|
|
resources: {
|
|
memory: { recommended: '32Mi' },
|
|
disk: { install: '4Mi', working: '8Mi' },
|
|
},
|
|
permissions: {
|
|
network: { allowedHosts: [] },
|
|
secrets: [{ name: 'TOKEN', required: true }],
|
|
tools: ['secret.use'],
|
|
},
|
|
contents: { tasks: [], workflows: [], prompts: [], tools: [] },
|
|
},
|
|
};
|
|
}
|
|
|
|
function approvalPlan() {
|
|
const previousManifest = manifest('1.0.0');
|
|
const previousGeneration = createPluginPackageResourceGeneration({
|
|
installationId: 'install-secret-transition-v1',
|
|
projectId: 'project-1',
|
|
packageName: 'example-monitor',
|
|
lockDigest: 'a'.repeat(64),
|
|
generation: 1,
|
|
previousActiveLockDigest: null,
|
|
contentDigest: 'b'.repeat(64),
|
|
contents: previousManifest.spec.contents,
|
|
});
|
|
const previousBinding = createPluginPackageSecretBinding({
|
|
generation: previousGeneration,
|
|
manifest: previousManifest,
|
|
assignments: [{
|
|
name: 'TOKEN',
|
|
secretRef: createSecretRef({
|
|
projectId: 'project-1',
|
|
name: 'runtime-token',
|
|
version: 1,
|
|
}),
|
|
}],
|
|
authority: {
|
|
kind: 'approved-action-execution',
|
|
evidenceDigest: 'c'.repeat(64),
|
|
},
|
|
boundAtMs: 80,
|
|
});
|
|
const nextManifest = manifest('2.0.0');
|
|
const transitionPlan = createPluginPackageSecretBindingTransitionPlan({
|
|
previousTarget: previousBinding.target,
|
|
previousBinding,
|
|
previousAttemptGeneration: 1,
|
|
nextGeneration: createPluginPackageResourceGeneration({
|
|
installationId: 'install-secret-transition-v2',
|
|
projectId: 'project-1',
|
|
packageName: 'example-monitor',
|
|
lockDigest: 'd'.repeat(64),
|
|
generation: 2,
|
|
previousActiveLockDigest: 'a'.repeat(64),
|
|
contentDigest: 'e'.repeat(64),
|
|
contents: nextManifest.spec.contents,
|
|
}),
|
|
nextManifest,
|
|
assignments: [{
|
|
name: 'TOKEN',
|
|
secretRef: createSecretRef({
|
|
projectId: 'project-1',
|
|
name: 'runtime-token',
|
|
version: 2,
|
|
}),
|
|
}],
|
|
plannedAtMs: 100,
|
|
});
|
|
return createPluginPackageSecretBindingTransitionApprovalPlan({
|
|
actionRef: 'secret-transition:example-monitor-v2',
|
|
transitionPlan,
|
|
requestedBy: REQUESTER,
|
|
plannedAtMs: 100,
|
|
expiresAtMs: 1_000,
|
|
});
|
|
}
|
|
|
|
function dispatch(plan) {
|
|
const action = pluginPackageSecretBindingTransitionApprovedAction(plan);
|
|
const pending = createApprovalRequest({
|
|
id: 'approval-secret-transition-1',
|
|
projectId: 'project-1',
|
|
action,
|
|
risk: 'high',
|
|
decisionMode: 'separation_of_duty',
|
|
requestedBy: REQUESTER,
|
|
requestedAtMs: 110,
|
|
expiresAtMs: 900,
|
|
requestFence: FENCE,
|
|
});
|
|
const approved = decideApprovalRequest(pending, {
|
|
expectedVersion: 1,
|
|
decisionId: 'decision-secret-transition-1',
|
|
decision: 'approved',
|
|
reasonCode: 'reviewed',
|
|
principal: {
|
|
subject: REVIEWER,
|
|
authenticationId: 'auth-reviewer',
|
|
authenticatedAtMs: 100,
|
|
expiresAtMs: 800,
|
|
assurance: 'multi_factor',
|
|
},
|
|
decidedAtMs: 120,
|
|
authorizationFence: FENCE,
|
|
});
|
|
return consumeApprovalRequest(approved, {
|
|
expectedVersion: 2,
|
|
consumptionId: 'consume-secret-transition-1',
|
|
dispatchId: 'dispatch-secret-transition-1',
|
|
action,
|
|
requestedBy: REQUESTER,
|
|
consumedBy: CONSUMER,
|
|
consumedAtMs: 130,
|
|
authorizationFence: FENCE,
|
|
}).dispatch;
|
|
}
|
|
|
|
function execution(approvedDispatch, startedAtMs = 140) {
|
|
const claimed = claimApprovedActionExecution(
|
|
createApprovedActionExecution(approvedDispatch, 5),
|
|
{
|
|
owner: 'secret-transition-executor',
|
|
leaseToken: 'lease-secret-transition-1',
|
|
nowMs: 131,
|
|
leaseDurationMs: 2_000,
|
|
},
|
|
);
|
|
return startApprovedActionExecution(
|
|
{ dispatch: approvedDispatch, execution: claimed },
|
|
{
|
|
dispatchId: approvedDispatch.id,
|
|
approvalRequestId: approvedDispatch.approvalRequestId,
|
|
actionDigest: approvedDispatch.action.actionDigest,
|
|
owner: claimed.leaseOwner,
|
|
leaseToken: claimed.leaseToken,
|
|
expectedVersion: claimed.version,
|
|
startedAtMs,
|
|
},
|
|
);
|
|
}
|
|
|
|
test('commits exactly one approved transition and verifies projected references', async () => {
|
|
const plan = approvalPlan();
|
|
const approvedDispatch = dispatch(plan);
|
|
const started = execution(approvedDispatch);
|
|
const applied = [];
|
|
const inspected = [];
|
|
const subject = new ClusterPluginPackageSecretBindingTransitionApprovedActionHandler(
|
|
{ async findByActionRef() { return plan; } },
|
|
{
|
|
async apply(input) {
|
|
applied.push(input);
|
|
return {
|
|
status: 'created',
|
|
receipt: { receiptDigest: 'f'.repeat(64) },
|
|
};
|
|
},
|
|
},
|
|
{ async assertExists(refs) { inspected.push(...refs); } },
|
|
);
|
|
assert.deepEqual(await subject.inspect(approvedDispatch), {
|
|
status: 'ready',
|
|
actionDigest: plan.approvalPlanDigest,
|
|
});
|
|
const result = await subject.execute({
|
|
dispatch: approvedDispatch,
|
|
execution: started,
|
|
idempotencyKey: approvedDispatch.id,
|
|
fence: {
|
|
owner: started.leaseOwner,
|
|
leaseToken: started.leaseToken,
|
|
version: started.version,
|
|
},
|
|
});
|
|
assert.deepEqual(result, {
|
|
outcome: 'succeeded',
|
|
resultCode: 'package_secret_transition_committed',
|
|
resultDigest: 'f'.repeat(64),
|
|
});
|
|
assert.equal(applied.length, 1);
|
|
assert.deepEqual(applied[0], {
|
|
transitionPlan: plan.transitionPlan,
|
|
evidenceDigest: plan.approvalPlanDigest,
|
|
committedAtMs: 140,
|
|
});
|
|
assert.deepEqual(inspected, [
|
|
plan.transitionPlan.nextBindingPlan.entries[0].secretRef,
|
|
plan.transitionPlan.nextBindingPlan.entries[0].secretRef,
|
|
]);
|
|
});
|
|
|
|
test('blocks plan drift and execution that starts after plan expiry', async () => {
|
|
const plan = approvalPlan();
|
|
const approvedDispatch = dispatch(plan);
|
|
const applied = [];
|
|
const subject = new ClusterPluginPackageSecretBindingTransitionApprovedActionHandler(
|
|
{ async findByActionRef() { return plan; } },
|
|
{ async apply(input) { applied.push(input); throw new Error('must not apply'); } },
|
|
{ async assertExists() {} },
|
|
);
|
|
const late = execution(approvedDispatch, 1_001);
|
|
assert.deepEqual(
|
|
await subject.execute({
|
|
dispatch: approvedDispatch,
|
|
execution: late,
|
|
idempotencyKey: approvedDispatch.id,
|
|
fence: {
|
|
owner: late.leaseOwner,
|
|
leaseToken: late.leaseToken,
|
|
version: late.version,
|
|
},
|
|
}),
|
|
{
|
|
outcome: 'failed',
|
|
resultCode: 'package_secret_transition_plan_rejected',
|
|
},
|
|
);
|
|
assert.equal(applied.length, 0);
|
|
const drifted = { ...plan, approvalPlanDigest: '9'.repeat(64) };
|
|
const driftedSubject = new ClusterPluginPackageSecretBindingTransitionApprovedActionHandler(
|
|
{ async findByActionRef() { return drifted; } },
|
|
{ async apply() { throw new Error('must not apply'); } },
|
|
{ async assertExists() {} },
|
|
);
|
|
assert.deepEqual(await driftedSubject.inspect(approvedDispatch), {
|
|
status: 'blocked',
|
|
resultCode: 'package_secret_transition_plan_rejected',
|
|
});
|
|
});
|