You've already forked agentic-coding-workflow
feat(mcp): IssueSystemTokenWithTx for cross-module tx (spec §6.1)
- Extract issueSystemToken helper; pool-bound + tx-bound entries share impl - IssueSystemTokenWithTx accepts pgx.Tx, uses repo.WithTx(tx) for DB writes - Audit Record stays outside tx (audit failure doesn't roll back business path) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -6,6 +6,7 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"github.com/jackc/pgx/v5"
|
||||
|
||||
"github.com/yan1h/agent-coding-workflow/internal/audit"
|
||||
"github.com/yan1h/agent-coding-workflow/internal/infra/errs"
|
||||
@@ -20,6 +21,7 @@ import (
|
||||
type TokenService interface {
|
||||
IssueAdmin(ctx context.Context, in IssueAdminInput) (IssueResult, error)
|
||||
IssueSystemToken(ctx context.Context, in IssueSystemTokenInput) (IssueResult, error)
|
||||
IssueSystemTokenWithTx(ctx context.Context, tx pgx.Tx, in IssueSystemTokenInput) (IssueResult, error)
|
||||
Authenticate(ctx context.Context, plaintext string) (*Token, error)
|
||||
Revoke(ctx context.Context, id, byUserID uuid.UUID) error
|
||||
RevokeBySession(ctx context.Context, sessionID uuid.UUID) error
|
||||
@@ -137,15 +139,21 @@ func (s *tokenService) IssueAdmin(ctx context.Context, in IssueAdminInput) (Issu
|
||||
// FK 校验失败。spec §6.1 明确建议同事务提交。本 service 不直接管事务边界(由
|
||||
// ACP SessionService 在 Create 流程内编排),但调用顺序由调用方保证。
|
||||
//
|
||||
// TODO(Part 3): 当前 pgRepo 持有 *pgxpool.Pool 并忽略 ctx 中可能存在的 pgx tx,
|
||||
// 因此严格意义上 IssueSystemToken 与 acp_sessions row 的写入并不在同一事务里 ——
|
||||
// 调用顺序虽然能避开 FK 错误,但不是真正的原子提交。要兑现 spec §6.1 同事务
|
||||
// 保证,Part 3 需要为 Repository 增加 `WithTx(tx pgx.Tx) Repository` 构造,
|
||||
// 并在 acp.SessionService.Create 中以同一事务执行 acp_sessions 插入与本次签发。
|
||||
//
|
||||
// 默认 ExpiresIn = 24h;默认 Tools 为内置 PM 工具白名单(spec §6.2)。
|
||||
// 写一条 audit `mcp.token.create_system`。
|
||||
func (s *tokenService) IssueSystemToken(ctx context.Context, in IssueSystemTokenInput) (IssueResult, error) {
|
||||
return s.issueSystemToken(ctx, s.repo, in)
|
||||
}
|
||||
|
||||
// IssueSystemTokenWithTx 与 IssueSystemToken 相同,但所有 DB 写操作走传入事务。
|
||||
// 调用方(如 acp.SessionService.Create)负责在同一个 tx 里先写入 acp_sessions row,
|
||||
// 再调本方法签发 token,实现 spec §6.1 同事务原子提交。
|
||||
func (s *tokenService) IssueSystemTokenWithTx(ctx context.Context, tx pgx.Tx, in IssueSystemTokenInput) (IssueResult, error) {
|
||||
return s.issueSystemToken(ctx, s.repo.WithTx(tx), in)
|
||||
}
|
||||
|
||||
// issueSystemToken 是共享实现。repo 参数决定 DB 写入走 pool 还是 tx。
|
||||
func (s *tokenService) issueSystemToken(ctx context.Context, repo Repository, in IssueSystemTokenInput) (IssueResult, error) {
|
||||
if in.ACPSessionID == uuid.Nil {
|
||||
return IssueResult{}, errs.New(errs.CodeInvalidInput, "acp_session_id is required for system token")
|
||||
}
|
||||
@@ -168,7 +176,7 @@ func (s *tokenService) IssueSystemToken(ctx context.Context, in IssueSystemToken
|
||||
}
|
||||
|
||||
id := uuid.New()
|
||||
created, err := s.repo.Create(ctx, &Token{
|
||||
created, err := repo.Create(ctx, &Token{
|
||||
ID: id,
|
||||
TokenHash: hash,
|
||||
UserID: in.UserID,
|
||||
|
||||
@@ -385,3 +385,24 @@ func TestIssueSystemToken_NilUserID(t *testing.T) {
|
||||
|
||||
// 编译期保证:类型实现接口
|
||||
var _ = errors.New
|
||||
|
||||
func TestIssueSystemTokenWithTx_DelegatesToRepoWithTx(t *testing.T) {
|
||||
t.Parallel()
|
||||
repo := newFakeRepo()
|
||||
au := &fakeAudit{}
|
||||
svc := mcp.NewTokenService(repo, au, nil)
|
||||
|
||||
uid := uuid.New()
|
||||
sid := uuid.New()
|
||||
pid := uuid.New()
|
||||
res, err := svc.IssueSystemTokenWithTx(context.Background(), nil, mcp.IssueSystemTokenInput{
|
||||
UserID: uid, ACPSessionID: sid, ProjectIDs: []uuid.UUID{pid},
|
||||
})
|
||||
require.NoError(t, err)
|
||||
assert.NotEmpty(t, res.Plaintext)
|
||||
assert.Equal(t, []string{"mcp.token.create_system"}, au.actions())
|
||||
|
||||
got, err := repo.GetByID(context.Background(), res.ID)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, mcp.IssuerSystem, got.Issuer)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user