Files
new-api/middleware/header_nav_test.go
Calcium-Ion 31d70fca39 refactor(auth): replace dashboard sessions with stateless tokens and session control (#6329)
* refactor(auth): replace dashboard sessions with stateless tokens

* feat(auth): harden session issuance and distributed enforcement

* fix(proxy): preserve trusted proxy compatibility defaults

* refactor: address dashboard auth review feedback

* refactor: remove classic frontend and flatten web app
2026-07-20 16:48:43 +08:00

190 lines
5.7 KiB
Go

package middleware
import (
"net/http"
"net/http/httptest"
"testing"
"github.com/QuantumNous/new-api/common"
"github.com/QuantumNous/new-api/model"
"github.com/QuantumNous/new-api/service"
"github.com/gin-gonic/gin"
"github.com/glebarez/sqlite"
"github.com/stretchr/testify/require"
"gorm.io/gorm"
)
func withHeaderNavModules(t *testing.T, raw string) {
t.Helper()
common.OptionMapRWMutex.Lock()
if common.OptionMap == nil {
common.OptionMap = map[string]string{}
}
previous, hadPrevious := common.OptionMap["HeaderNavModules"]
common.OptionMap["HeaderNavModules"] = raw
common.OptionMapRWMutex.Unlock()
t.Cleanup(func() {
common.OptionMapRWMutex.Lock()
defer common.OptionMapRWMutex.Unlock()
if hadPrevious {
common.OptionMap["HeaderNavModules"] = previous
return
}
delete(common.OptionMap, "HeaderNavModules")
})
}
func performHeaderNavRequest(t *testing.T, handler gin.HandlerFunc, authenticated bool) *httptest.ResponseRecorder {
t.Helper()
gin.SetMode(gin.TestMode)
router := gin.New()
router.GET("/api/test", handler, func(c *gin.Context) {
c.JSON(http.StatusOK, gin.H{"success": true})
})
var accessToken string
if authenticated {
previousDB, previousRedis := model.DB, common.RedisEnabled
db, err := gorm.Open(sqlite.Open(":memory:"), &gorm.Config{})
require.NoError(t, err)
require.NoError(t, db.AutoMigrate(&model.User{}))
model.DB = db
common.RedisEnabled = false
t.Cleanup(func() {
model.DB = previousDB
common.RedisEnabled = previousRedis
})
accessToken = "header-nav-pat"
user := model.User{
Username: "tester",
Password: "unused-password-hash",
Role: common.RoleCommonUser,
Status: common.UserStatusEnabled,
Group: "default",
AuthVersion: 1,
}
user.SetAccessToken(accessToken)
require.NoError(t, db.Create(&user).Error)
}
recorder := httptest.NewRecorder()
request := httptest.NewRequest(http.MethodGet, "/api/test", nil)
if authenticated {
request.Header.Set("Authorization", "Bearer "+accessToken)
}
router.ServeHTTP(recorder, request)
return recorder
}
func TestHeaderNavModuleAuthAllowsDefaultPublicAccess(t *testing.T) {
withHeaderNavModules(t, "")
recorder := performHeaderNavRequest(t, HeaderNavModuleAuth("pricing"), false)
require.Equal(t, http.StatusOK, recorder.Code)
}
func TestHeaderNavModuleAuthRejectsDisabledPricing(t *testing.T) {
raw := `{"pricing":{"enabled":false,"requireAuth":false}}`
withHeaderNavModules(t, raw)
recorder := performHeaderNavRequest(t, HeaderNavModuleAuth("pricing"), false)
require.Equal(t, http.StatusForbidden, recorder.Code)
}
func TestHeaderNavModuleAuthRequiresLoginForPricing(t *testing.T) {
raw := `{"pricing":{"enabled":true,"requireAuth":true}}`
withHeaderNavModules(t, raw)
recorder := performHeaderNavRequest(t, HeaderNavModuleAuth("pricing"), false)
require.Equal(t, http.StatusUnauthorized, recorder.Code)
}
func TestHeaderNavModuleAuthRequiresLoginForRankings(t *testing.T) {
raw := `{"rankings":{"enabled":true,"requireAuth":true}}`
withHeaderNavModules(t, raw)
recorder := performHeaderNavRequest(t, HeaderNavModuleAuth("rankings"), false)
require.Equal(t, http.StatusUnauthorized, recorder.Code)
}
func TestHeaderNavModuleAuthRejectsLegacyDisabledModule(t *testing.T) {
raw := `{"rankings":false}`
withHeaderNavModules(t, raw)
recorder := performHeaderNavRequest(t, HeaderNavModuleAuth("rankings"), false)
require.Equal(t, http.StatusForbidden, recorder.Code)
}
func TestHeaderNavModulePublicOrUserAuthAllowsDefaultPublicAccess(t *testing.T) {
withHeaderNavModules(t, "")
recorder := performHeaderNavRequest(t, HeaderNavModulePublicOrUserAuth("pricing"), false)
require.Equal(t, http.StatusOK, recorder.Code)
}
func TestHeaderNavModulePublicOrUserAuthRequiresLoginWhenDisabled(t *testing.T) {
raw := `{"pricing":{"enabled":false,"requireAuth":false}}`
withHeaderNavModules(t, raw)
recorder := performHeaderNavRequest(t, HeaderNavModulePublicOrUserAuth("pricing"), false)
require.Equal(t, http.StatusUnauthorized, recorder.Code)
}
func TestHeaderNavModulePublicOrUserAuthAllowsLoggedInWhenDisabled(t *testing.T) {
raw := `{"pricing":{"enabled":false,"requireAuth":false}}`
withHeaderNavModules(t, raw)
recorder := performHeaderNavRequest(t, HeaderNavModulePublicOrUserAuth("pricing"), true)
require.Equal(t, http.StatusOK, recorder.Code)
}
func TestHeaderNavModulePublicOrUserAuthRequiresLoginWhenRequireAuth(t *testing.T) {
raw := `{"pricing":{"enabled":true,"requireAuth":true}}`
withHeaderNavModules(t, raw)
recorder := performHeaderNavRequest(t, HeaderNavModulePublicOrUserAuth("pricing"), false)
require.Equal(t, http.StatusUnauthorized, recorder.Code)
}
func TestHeaderNavModulePublicOrUserAuthRequiresLoginForLegacyDisabledModule(t *testing.T) {
raw := `{"pricing":false}`
withHeaderNavModules(t, raw)
recorder := performHeaderNavRequest(t, HeaderNavModulePublicOrUserAuth("pricing"), false)
require.Equal(t, http.StatusUnauthorized, recorder.Code)
}
func TestHeaderNavPublicRouteRejectsExpiredInternalAccessToken(t *testing.T) {
setupDashboardAuthMiddlewareTest(t)
withHeaderNavModules(t, "")
gin.SetMode(gin.TestMode)
router := gin.New()
router.GET("/api/test", HeaderNavModuleAuth("pricing"), func(c *gin.Context) {
c.JSON(http.StatusOK, gin.H{"success": true})
})
request := httptest.NewRequest(http.MethodGet, "/api/test", nil)
request.Header.Set("Authorization", "Bearer "+issueExpiredDashboardAccessToken(t, service.AuthIdentity{
UserID: 1, SessionID: "expired-header-nav-session", UserAuthVersion: 1, SessionVersion: 1,
}))
response := httptest.NewRecorder()
router.ServeHTTP(response, request)
require.Equal(t, http.StatusUnauthorized, response.Code)
require.Contains(t, response.Body.String(), "AUTH_TOKEN_EXPIRED")
}