- Server no longer mirrors app.Runtime services (gov/svc/pwSvc/...): 574 call sites now use accessors in runtime_accessors.go - backgroundLoop (mutex+sync.Once) stops sweeper/publisher/reminder/wsGC goroutines race-free (baseline44161d2areproduced DATA RACE; now green under -race) - goose 00071 duplicate (dialogue merge1da17a28brought two 00071): context-conflict-adjudications renumbered to 00072; dev DB applied only through 00070 so zero data migration - task_finalize tests build minimal app.Runtime; test injection via serverTestOverrides + setOverride* (explicit-nil markers for fail-closed cases)
70 lines
1.9 KiB
Go
70 lines
1.9 KiB
Go
package server
|
|
|
|
import (
|
|
"context"
|
|
"net/http"
|
|
|
|
"chat-agent-system/internal/auth"
|
|
)
|
|
|
|
type taskRunAction string
|
|
|
|
const (
|
|
taskRunView taskRunAction = "view"
|
|
taskRunCancel taskRunAction = "cancel"
|
|
taskRunResume taskRunAction = "resume"
|
|
)
|
|
|
|
func (s *Server) assertTaskRunAccess(w http.ResponseWriter, ctx context.Context, runID string, user *auth.UserAccount, action taskRunAction) bool {
|
|
if user == nil {
|
|
writeJSONResponse(w, http.StatusUnauthorized, map[string]string{
|
|
"error_code": "ERR_UNAUTHENTICATED",
|
|
"message": "请先登录",
|
|
})
|
|
return false
|
|
}
|
|
rec, err := s.taskRuns().GetRun(runID)
|
|
if err != nil {
|
|
writeSafeError(w, nil, http.StatusNotFound, "run not found")
|
|
return false
|
|
}
|
|
if s.isSystemAdminUser(ctx, user) {
|
|
return true
|
|
}
|
|
isOwner := rec.UserID > 0 && rec.UserID == user.ID
|
|
isProjectOwner := false
|
|
isProjectMember := false
|
|
if rec.ProjectID > 0 && s.authorizer() != nil {
|
|
isProjectOwner, _ = s.authorizer().IsProjectOwner(ctx, rec.ProjectID, user.ID)
|
|
isProjectMember, _ = s.authorizer().IsProjectMember(ctx, rec.ProjectID, user.ID)
|
|
}
|
|
switch action {
|
|
case taskRunView:
|
|
// Align with create-run RequireProjectAccess: any active project member may view.
|
|
if isOwner || isProjectOwner || isProjectMember {
|
|
return true
|
|
}
|
|
case taskRunCancel, taskRunResume:
|
|
if isOwner || isProjectOwner {
|
|
return true
|
|
}
|
|
}
|
|
writeJSONResponse(w, http.StatusForbidden, map[string]string{
|
|
"error_code": "ERR_FORBIDDEN",
|
|
"message": "无权访问该任务",
|
|
})
|
|
return false
|
|
}
|
|
|
|
func (s *Server) assertTaskRunOwnedByUser(w http.ResponseWriter, ctx context.Context, runID string, user *auth.UserAccount) bool {
|
|
return s.assertTaskRunAccess(w, ctx, runID, user, taskRunView)
|
|
}
|
|
|
|
func (s *Server) isSystemAdminUser(ctx context.Context, user *auth.UserAccount) bool {
|
|
if user == nil {
|
|
return false
|
|
}
|
|
admin, err := s.authzSvc().IsSystemAdmin(ctx, user.ID, user.RoleID)
|
|
return err == nil && admin
|
|
}
|