This commit is contained in:
2026-06-20 19:16:44 +08:00
parent db3d030169
commit dbb87823e8
26 changed files with 676 additions and 115 deletions
+17 -8
View File
@@ -144,14 +144,14 @@ func postToken(deps AdminHandlerDeps) http.HandlerFunc {
}
type tokenRow struct {
ID string `json:"id"`
Name string `json:"name"`
Issuer string `json:"issuer"`
Scope Scope `json:"scope"`
ExpiresAt *string `json:"expires_at,omitempty"`
RevokedAt *string `json:"revoked_at,omitempty"`
ID string `json:"id"`
Name string `json:"name"`
Issuer string `json:"issuer"`
Scope Scope `json:"scope"`
ExpiresAt *string `json:"expires_at,omitempty"`
RevokedAt *string `json:"revoked_at,omitempty"`
LastUsedAt *string `json:"last_used_at,omitempty"`
CreatedAt string `json:"created_at"`
CreatedAt string `json:"created_at"`
}
func listTokens(deps AdminHandlerDeps) http.HandlerFunc {
@@ -200,7 +200,7 @@ func listTokens(deps AdminHandlerDeps) http.HandlerFunc {
func deleteToken(deps AdminHandlerDeps) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
rid := middleware.RequestIDFromContext(r.Context())
caller, _, err := resolveSessionUser(r.Context(), deps.Users, r)
caller, isAdmin, err := resolveSessionUser(r.Context(), deps.Users, r)
if err != nil {
httpx.WriteError(w, rid, err)
return
@@ -211,6 +211,15 @@ func deleteToken(deps AdminHandlerDeps) http.HandlerFunc {
httpx.WriteError(w, rid, errs.Wrap(err, errs.CodeInvalidInput, "id parse"))
return
}
// 授权:仅 admin 或 token 持有者可撤销。非 admin 时按 id 反查 token
// 校验归属;不归属(或不存在)一律返 NotFound,避免暴露 token 是否存在。
if !isAdmin {
tok, err := deps.Tokens.GetByID(r.Context(), id)
if err != nil || tok.UserID != caller {
httpx.WriteError(w, rid, errs.New(errs.CodeMcpTokenNotFound, "mcp token not found"))
return
}
}
if err := deps.Tokens.Revoke(r.Context(), id, caller); err != nil {
httpx.WriteError(w, rid, err)
return
+9 -5
View File
@@ -8,11 +8,15 @@ RETURNING id, token_hash, user_id, name, issuer, scope,
created_by, created_at;
-- name: GetMcpTokenByHash :one
SELECT id, token_hash, user_id, name, issuer, scope,
acp_session_id, expires_at, last_used_at, revoked_at,
created_by, created_at
FROM mcp_tokens
WHERE token_hash = $1;
-- 鉴权用:仅返回属于 enabled 用户的 token,禁用用户的 MCP token 立即失效
-- (与 web session 解析 GetSessionByTokenHash 的 enabled=true 约束一致)。
SELECT t.id, t.token_hash, t.user_id, t.name, t.issuer, t.scope,
t.acp_session_id, t.expires_at, t.last_used_at, t.revoked_at,
t.created_by, t.created_at
FROM mcp_tokens t
JOIN users u ON u.id = t.user_id
WHERE t.token_hash = $1
AND u.enabled = true;
-- name: GetMcpTokenByID :one
SELECT id, token_hash, user_id, name, issuer, scope,
+9 -5
View File
@@ -74,13 +74,17 @@ func (q *Queries) DeleteMcpTokenByID(ctx context.Context, id pgtype.UUID) error
}
const getMcpTokenByHash = `-- name: GetMcpTokenByHash :one
SELECT id, token_hash, user_id, name, issuer, scope,
acp_session_id, expires_at, last_used_at, revoked_at,
created_by, created_at
FROM mcp_tokens
WHERE token_hash = $1
SELECT t.id, t.token_hash, t.user_id, t.name, t.issuer, t.scope,
t.acp_session_id, t.expires_at, t.last_used_at, t.revoked_at,
t.created_by, t.created_at
FROM mcp_tokens t
JOIN users u ON u.id = t.user_id
WHERE t.token_hash = $1
AND u.enabled = true
`
// 鉴权用:仅返回属于 enabled 用户的 token,禁用用户的 MCP token 立即失效
// (与 web session 解析 GetSessionByTokenHash 的 enabled=true 约束一致)。
func (q *Queries) GetMcpTokenByHash(ctx context.Context, tokenHash []byte) (McpToken, error) {
row := q.db.QueryRow(ctx, getMcpTokenByHash, tokenHash)
var i McpToken