diff --git a/go-backend/internal/http/handler/config_access_test.go b/go-backend/internal/http/handler/config_access_test.go index 738d818..fd0792c 100644 --- a/go-backend/internal/http/handler/config_access_test.go +++ b/go-backend/internal/http/handler/config_access_test.go @@ -7,6 +7,7 @@ import ( "net/http/httptest" "testing" + "go-backend/internal/auth" "go-backend/internal/http/middleware" "go-backend/internal/http/response" "go-backend/internal/store/repo" @@ -53,6 +54,34 @@ func TestConfigGetNowRequiresAuth(t *testing.T) { assertHandlerCodeMsg(t, resp, 401, "未登录或token已过期") } +func TestConfigUpdateRejectsSensitiveKeys(t *testing.T) { + router, _ := setupConfigAccessTestRouter(t) + adminToken := mustGenerateConfigAccessToken(t, 1, "admin_user", 0) + + req := httptest.NewRequest(http.MethodPost, "/api/v1/config/update", bytes.NewBufferString(`{"jwt_secret":"rotated-secret"}`)) + req.Header.Set("Content-Type", "application/json") + req.Header.Set("Authorization", adminToken) + resp := httptest.NewRecorder() + + router.ServeHTTP(resp, req) + + assertHandlerCodeMsg(t, resp, 403, "禁止访问敏感配置") +} + +func TestConfigUpdateSingleRejectsSensitiveKeys(t *testing.T) { + router, _ := setupConfigAccessTestRouter(t) + adminToken := mustGenerateConfigAccessToken(t, 1, "admin_user", 0) + + req := httptest.NewRequest(http.MethodPost, "/api/v1/config/update-single", bytes.NewBufferString(`{"name":"jwt_secret","value":"rotated-secret"}`)) + req.Header.Set("Content-Type", "application/json") + req.Header.Set("Authorization", adminToken) + resp := httptest.NewRecorder() + + router.ServeHTTP(resp, req) + + assertHandlerCodeMsg(t, resp, 403, "禁止访问敏感配置") +} + func setupConfigAccessTestRouter(t *testing.T) (http.Handler, *repo.Repository) { t.Helper() r, err := repo.Open(t.TempDir() + "/config-access.db") @@ -80,6 +109,15 @@ func seedConfigValue(t *testing.T, r *repo.Repository, name, value string) { } } +func mustGenerateConfigAccessToken(t *testing.T, userID int64, username string, roleID int) string { + t.Helper() + token, err := auth.GenerateToken(userID, username, roleID, "unit-test-secret") + if err != nil { + t.Fatalf("generate token: %v", err) + } + return token +} + func assertHandlerCode(t *testing.T, rec *httptest.ResponseRecorder, expected int) { t.Helper() var out response.R diff --git a/go-backend/internal/http/handler/handler.go b/go-backend/internal/http/handler/handler.go index 51c0766..efdf721 100644 --- a/go-backend/internal/http/handler/handler.go +++ b/go-backend/internal/http/handler/handler.go @@ -984,6 +984,10 @@ func (h *Handler) updateConfigs(w http.ResponseWriter, r *http.Request) { if key == "" { continue } + if repo.IsSensitiveConfigKey(key) { + response.WriteJSON(w, response.Err(403, "禁止访问敏感配置")) + return + } if protectedKeys[key] && isCommercial != "true" { response.WriteJSON(w, response.ErrDefault("需要商业版授权")) @@ -1021,6 +1025,10 @@ func (h *Handler) updateSingleConfig(w http.ResponseWriter, r *http.Request) { response.WriteJSON(w, response.ErrDefault("配置名称不能为空")) return } + if repo.IsSensitiveConfigKey(name) { + response.WriteJSON(w, response.Err(403, "禁止访问敏感配置")) + return + } isCommercial, _ := h.repo.GetViteConfigValue("is_commercial") if (name == "app_name" || name == "app_logo" || name == "app_favicon" || name == "hide_footer_brand") && isCommercial != "true" {