From ee42650f9c832cb548ce4667a1bcf6eb9636ec54 Mon Sep 17 00:00:00 2001 From: zh Date: Tue, 29 Sep 2026 11:01:50 +0800 Subject: [PATCH] =?UTF-8?q?fix(core):=20=E6=89=A7=E8=A1=8C=E8=AE=B0?= =?UTF-8?q?=E5=BD=95=E6=9F=A5=E8=AF=A2=E8=A1=A5=E8=B6=8A=E6=9D=83=E6=A0=A1?= =?UTF-8?q?=E9=AA=8C=EF=BC=8C=E5=BE=85=E7=A1=AE=E8=AE=A4=E4=B8=AD=E7=9A=84?= =?UTF-8?q?=E6=89=A7=E8=A1=8C=E4=B9=9F=E8=83=BD=E6=8C=89=20executionId=20?= =?UTF-8?q?=E6=9F=A5=E5=88=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- scripts/control-plane.test.mjs | 6 ++++++ scripts/core-invoke.test.mjs | 4 ++-- server/app/service.ts | 14 +++++++++++--- server/entry/http.ts | 2 +- 4 files changed, 20 insertions(+), 6 deletions(-) diff --git a/scripts/control-plane.test.mjs b/scripts/control-plane.test.mjs index ab0a362..607b2bb 100644 --- a/scripts/control-plane.test.mjs +++ b/scripts/control-plane.test.mjs @@ -257,6 +257,12 @@ test('A4.4 确认卡:写操作等确认、确认后执行、事件齐全、重 assert.ok(await stream.waitFor((event) => event.type === 'approval.requested'), '要推确认卡给界面'); assert.equal(aps.stats.calls['aps.write'], undefined, '没确认前不能动业务系统'); + // 待确认中的执行也要能按 executionId 查到,且只有本人能查 + const pendingView = await fetch(`${server.url}/v1/executions/${receipt.executionId}`, { headers: headersFor(subject) }); + assert.equal((await pendingView.json()).status, 'pending-confirmation'); + const stolenView = await fetch(`${server.url}/v1/executions/${receipt.executionId}`, { headers: headersFor(otherSubject) }); + assert.equal(stolenView.status, 403, '执行记录只有本人能查'); + const decision = await fetch(`${server.url}/v1/approvals/${receipt.approvalId}`, { method: 'POST', headers: headersFor(subject), diff --git a/scripts/core-invoke.test.mjs b/scripts/core-invoke.test.mjs index 52cf322..70764ac 100644 --- a/scripts/core-invoke.test.mjs +++ b/scripts/core-invoke.test.mjs @@ -227,13 +227,13 @@ test('HTTP:注册 Provider 后能力清单非空,执行记录端点可查回 params: { text: '通过 HTTP 查回执' }, }); - const response = await fetch(`${server.url}/v1/executions/${receipt.executionId}`); + const response = await fetch(`${server.url}/v1/executions/${receipt.executionId}`, { headers: { 'x-aps-user': subject.userId, 'x-aps-tenant': subject.tenantId } }); assert.equal(response.status, 200, '执行记录端点应返回 200'); const body = await response.json(); assert.equal(body.executionId, receipt.executionId); assert.equal(body.status, 'succeeded'); - const missing = await fetch(`${server.url}/v1/executions/exec-none`); + const missing = await fetch(`${server.url}/v1/executions/exec-none`, { headers: { 'x-aps-user': subject.userId } }); assert.equal(missing.status, 404, '未知执行记录应 404'); const missingBody = await missing.json(); assert.equal(missingBody.error.code, 'not_found'); diff --git a/server/app/service.ts b/server/app/service.ts index b99a3fe..e5ab446 100644 --- a/server/app/service.ts +++ b/server/app/service.ts @@ -114,9 +114,17 @@ export class ApplicationService { return this.contextRepository?.get(sessionId); } - /** 执行记录查询:GET /v1/executions/{id} */ - execution(executionId: string) { - return this.core.executions.get(executionId)?.receipt; + /** 执行记录查询:GET /v1/executions/{id};待确认中的执行也能查到(回执 status = pending-confirmation) */ + execution(subject: Subject, executionId: string) { + const record = this.core.executions.get(executionId); + if (record) { + if (record.subjectUserId !== subject.userId) throw new AppError('forbidden', '这是别人的执行记录', { status: 403 }); + return record.receipt; + } + const action = this.core.approvals?.byExecution(executionId); + if (!action) return undefined; + if (action.ownerUserId !== subject.userId) throw new AppError('forbidden', '这是别人的执行记录', { status: 403 }); + return action.receipt; } /** 执行追溯(A4.5):按 executionId 追到人、参数摘要、审批与回执;只有本人能看 */ diff --git a/server/entry/http.ts b/server/entry/http.ts index 0cf8c7b..2221554 100644 --- a/server/entry/http.ts +++ b/server/entry/http.ts @@ -100,7 +100,7 @@ async function handleRequest(req: IncomingMessage, res: ServerResponse, service: return sendJson(res, 200, service.capabilities(subject, sessionFilter)); case 'GET /v1/executions/{executionId}': { const executionId = params.get('executionId') ?? ''; - const receipt = service.execution(executionId); + const receipt = service.execution(subject, executionId); if (!receipt) throw new AppError('not_found', `执行记录 不存在`, { status: 404 }); return sendJson(res, 200, receipt); }