fix(apiserver): abort the request on every middleware failure (bug-1949)
Cause: ReturnError only writes the response and never aborts the gin chain; RequirePermission (token error) and WhitelistIPMiddleware (invalid client IP) returned after ReturnError without ctx.Abort(), so the route handler still ran after the rejection. Fix: ctx.Abort() after ReturnError at both sites (and, for clarity, on the ValidateHandler body-check path, which validateRequestBody already aborted). RequirePermission now answers a bad token with 401 instead of 500. ReturnError stays non-aborting (documented): handlers such as ControlHost call it from helpers and then test ctx.IsAborted(), so a global abort would change handler flows, and it adds nothing in handlers. Tests: TestRequirePermission_AbortsOnInvalidToken, TestWhitelistIPMiddleware_AbortsOnFailure. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
42dd1e7a5e
commit
42a402a5b4
6 changed files with 105 additions and 2 deletions
48
src/frontend/apiserver/controllers/rbac_abort_test.go
Normal file
48
src/frontend/apiserver/controllers/rbac_abort_test.go
Normal file
|
|
@ -0,0 +1,48 @@
|
|||
package controllers
|
||||
|
||||
import (
|
||||
"apiserver/utils"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
// bug-1949: a RequirePermission failure must stop the request before the
|
||||
// handler runs.
|
||||
func TestRequirePermission_AbortsOnInvalidToken(t *testing.T) {
|
||||
resetRBACState()
|
||||
prev := utils.AuthenticationYamlConfig
|
||||
utils.AuthenticationYamlConfig = &utils.Authentication{
|
||||
Token: utils.TokenConfig{Type: "jwt", SecretKey: "rbac-abort-test-secret", TokenExpireTime: 3600},
|
||||
}
|
||||
utils.Log = zap.NewNop()
|
||||
t.Cleanup(func() { utils.AuthenticationYamlConfig = prev })
|
||||
|
||||
gin.SetMode(gin.TestMode)
|
||||
r := gin.New()
|
||||
handlerRan := false
|
||||
r.GET("/protected", RequirePermission("host:view"), func(ctx *gin.Context) {
|
||||
handlerRan = true
|
||||
ReturnSuccess(ctx, http.StatusOK, "success", nil)
|
||||
})
|
||||
|
||||
for _, header := range []string{"", "Bearer not-a-jwt"} {
|
||||
handlerRan = false
|
||||
req := httptest.NewRequest(http.MethodGet, "/protected", nil)
|
||||
if header != "" {
|
||||
req.Header.Set("Authorization", header)
|
||||
}
|
||||
w := httptest.NewRecorder()
|
||||
r.ServeHTTP(w, req)
|
||||
|
||||
if handlerRan {
|
||||
t.Errorf("Authorization %q: handler ran after RequirePermission failed (status %d)", header, w.Code)
|
||||
}
|
||||
if w.Code != http.StatusUnauthorized {
|
||||
t.Errorf("Authorization %q: status = %d, want %d", header, w.Code, http.StatusUnauthorized)
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue