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>
48 lines
1.3 KiB
Go
48 lines
1.3 KiB
Go
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)
|
|
}
|
|
}
|
|
}
|