Files
Agent-System-Backend/internal/server/explain.go
T
agent-exec-transport e942a35b73
governance / governance (pull_request) Successful in 14s
ci / vet-and-test (pull_request) Successful in 1m18s
agent-exec transport 8315c523cef26780543b9b2130f4f4a4/5c8333ec4e92
2026-10-19 12:21:32 +00:00

76 lines
2.6 KiB
Go

package server
import (
"database/sql"
"database/sql/driver"
"errors"
"net/http"
"agent-system-backend/internal/store"
"agent-system-backend/pkg/extension"
)
// explainTargetParams would select another actor; the endpoint refuses them
// outright so the response never depends on whether that actor exists.
var explainTargetParams = []string{"owner", "userId", "user", "actor"}
func (s *Server) explainCapability(w http.ResponseWriter, r *http.Request) {
if SessionScope(r) == store.SessionScopeSetPasswordOnly {
http.Error(w, "capability explain denied", http.StatusForbidden)
return
}
query := r.URL.Query()
for _, key := range explainTargetParams {
if query.Has(key) {
http.Error(w, "capability explain is limited to the current actor", http.StatusForbidden)
return
}
}
explanation, err := s.authorizer.ExplainCapabilities(s.owner(r))
if errors.Is(err, store.ErrAccountForbidden) {
http.Error(w, "capability explain denied", http.StatusForbidden)
return
}
if err != nil {
http.Error(w, "capability explain unavailable", http.StatusServiceUnavailable)
return
}
writeJSON(w, http.StatusOK, explanation)
}
// categorizeWorkFailure maps Backend and provider errors for operation into
// one WorkFailure category. Uncategorized errors, notably not-found, keep the
// existing handling so a refusal never reveals another project or account.
func categorizeWorkFailure(operation string, err error) (extension.WorkFailure, bool) {
var failure extension.WorkFailure
switch {
case err == nil:
return extension.WorkFailure{}, false
case errors.As(err, &failure) && failure.StatusCode() != 0:
return extension.CategorizeWorkFailure(operation, err)
case errors.Is(err, store.ErrAccountForbidden):
return extension.WorkFailure{Operation: operation, Reason: extension.WorkFailurePolicyRejected}, true
case operation != extension.WorkOperationConversation && errors.Is(err, store.ErrConflict):
return extension.WorkFailure{Operation: operation, Reason: extension.WorkFailureDuplicateName}, true
case errors.Is(err, sql.ErrConnDone), errors.Is(err, driver.ErrBadConn):
return extension.WorkFailure{Operation: operation, Reason: extension.WorkFailureServiceUnavailable}, true
}
return extension.CategorizeWorkFailure(operation, err)
}
func writeWorkFailure(w http.ResponseWriter, operation string, err error) {
if failure, ok := categorizeWorkFailure(operation, err); ok {
http.Error(w, failure.Error(), failure.StatusCode())
return
}
writeProjectErr(w, err)
}
func writeWorkJSON(w http.ResponseWriter, operation string, v any, err error) {
if err != nil {
writeWorkFailure(w, operation, err)
return
}
writeJSON(w, http.StatusOK, v)
}