mirror of
https://github.com/Sagit-chu/flvx.git
synced 2026-10-07 18:26:37 +08:00
fix: allow admin access to sensitive configs (#511)
This commit is contained in:
@@ -67,7 +67,22 @@ func TestConfigGetRejectsSensitiveKeysWithoutAuth(t *testing.T) {
|
|||||||
assertHandlerCodeMsg(t, resp, 403, "禁止访问敏感配置")
|
assertHandlerCodeMsg(t, resp, 403, "禁止访问敏感配置")
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestConfigUpdateRejectsSensitiveKeys(t *testing.T) {
|
func TestConfigGetAllowsSensitiveKeysForAdmin(t *testing.T) {
|
||||||
|
router, r := setupConfigAccessTestRouter(t)
|
||||||
|
adminToken := mustGenerateConfigAccessToken(t, 1, "admin_user", 0)
|
||||||
|
seedConfigValue(t, r, "jwt_secret", "jwt-secret")
|
||||||
|
|
||||||
|
req := httptest.NewRequest(http.MethodPost, "/api/v1/config/get", bytes.NewBufferString(`{"name":"jwt_secret"}`))
|
||||||
|
req.Header.Set("Content-Type", "application/json")
|
||||||
|
req.Header.Set("Authorization", adminToken)
|
||||||
|
resp := httptest.NewRecorder()
|
||||||
|
|
||||||
|
router.ServeHTTP(resp, req)
|
||||||
|
|
||||||
|
assertHandlerConfigValue(t, resp, "jwt_secret", "jwt-secret")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestConfigUpdateAllowsSensitiveKeysForAdmin(t *testing.T) {
|
||||||
router, _ := setupConfigAccessTestRouter(t)
|
router, _ := setupConfigAccessTestRouter(t)
|
||||||
adminToken := mustGenerateConfigAccessToken(t, 1, "admin_user", 0)
|
adminToken := mustGenerateConfigAccessToken(t, 1, "admin_user", 0)
|
||||||
|
|
||||||
@@ -78,10 +93,10 @@ func TestConfigUpdateRejectsSensitiveKeys(t *testing.T) {
|
|||||||
|
|
||||||
router.ServeHTTP(resp, req)
|
router.ServeHTTP(resp, req)
|
||||||
|
|
||||||
assertHandlerCodeMsg(t, resp, 403, "禁止访问敏感配置")
|
assertHandlerCode(t, resp, 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestConfigUpdateSingleRejectsSensitiveKeys(t *testing.T) {
|
func TestConfigUpdateSingleAllowsSensitiveKeysForAdmin(t *testing.T) {
|
||||||
router, _ := setupConfigAccessTestRouter(t)
|
router, _ := setupConfigAccessTestRouter(t)
|
||||||
adminToken := mustGenerateConfigAccessToken(t, 1, "admin_user", 0)
|
adminToken := mustGenerateConfigAccessToken(t, 1, "admin_user", 0)
|
||||||
|
|
||||||
@@ -92,7 +107,95 @@ func TestConfigUpdateSingleRejectsSensitiveKeys(t *testing.T) {
|
|||||||
|
|
||||||
router.ServeHTTP(resp, req)
|
router.ServeHTTP(resp, req)
|
||||||
|
|
||||||
assertHandlerCodeMsg(t, resp, 403, "禁止访问敏感配置")
|
assertHandlerCode(t, resp, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestConfigUpdateAllowsCloudflareSecretKeyWrite(t *testing.T) {
|
||||||
|
router, r := setupConfigAccessTestRouter(t)
|
||||||
|
adminToken := mustGenerateConfigAccessToken(t, 1, "admin_user", 0)
|
||||||
|
|
||||||
|
req := httptest.NewRequest(http.MethodPost, "/api/v1/config/update", bytes.NewBufferString(`{"cloudflare_secret_key":"turnstile-secret"}`))
|
||||||
|
req.Header.Set("Content-Type", "application/json")
|
||||||
|
req.Header.Set("Authorization", adminToken)
|
||||||
|
resp := httptest.NewRecorder()
|
||||||
|
|
||||||
|
router.ServeHTTP(resp, req)
|
||||||
|
|
||||||
|
assertHandlerCode(t, resp, 0)
|
||||||
|
|
||||||
|
cfg, err := r.GetConfigByName("cloudflare_secret_key")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("get config: %v", err)
|
||||||
|
}
|
||||||
|
if cfg == nil || cfg.Value != "turnstile-secret" {
|
||||||
|
t.Fatalf("expected cloudflare_secret_key to be updated, got %#v", cfg)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestConfigUpdateSingleAllowsCloudflareSecretKeyWrite(t *testing.T) {
|
||||||
|
router, r := setupConfigAccessTestRouter(t)
|
||||||
|
adminToken := mustGenerateConfigAccessToken(t, 1, "admin_user", 0)
|
||||||
|
|
||||||
|
req := httptest.NewRequest(http.MethodPost, "/api/v1/config/update-single", bytes.NewBufferString(`{"name":"cloudflare_secret_key","value":"turnstile-secret"}`))
|
||||||
|
req.Header.Set("Content-Type", "application/json")
|
||||||
|
req.Header.Set("Authorization", adminToken)
|
||||||
|
resp := httptest.NewRecorder()
|
||||||
|
|
||||||
|
router.ServeHTTP(resp, req)
|
||||||
|
|
||||||
|
assertHandlerCode(t, resp, 0)
|
||||||
|
|
||||||
|
cfg, err := r.GetConfigByName("cloudflare_secret_key")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("get config: %v", err)
|
||||||
|
}
|
||||||
|
if cfg == nil || cfg.Value != "turnstile-secret" {
|
||||||
|
t.Fatalf("expected cloudflare_secret_key to be updated, got %#v", cfg)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestConfigUpdateAllowsLicenseKeyWrite(t *testing.T) {
|
||||||
|
router, r := setupConfigAccessTestRouter(t)
|
||||||
|
adminToken := mustGenerateConfigAccessToken(t, 1, "admin_user", 0)
|
||||||
|
|
||||||
|
req := httptest.NewRequest(http.MethodPost, "/api/v1/config/update", bytes.NewBufferString(`{"license_key":"license-secret"}`))
|
||||||
|
req.Header.Set("Content-Type", "application/json")
|
||||||
|
req.Header.Set("Authorization", adminToken)
|
||||||
|
resp := httptest.NewRecorder()
|
||||||
|
|
||||||
|
router.ServeHTTP(resp, req)
|
||||||
|
|
||||||
|
assertHandlerCode(t, resp, 0)
|
||||||
|
|
||||||
|
cfg, err := r.GetConfigByName("license_key")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("get config: %v", err)
|
||||||
|
}
|
||||||
|
if cfg == nil || cfg.Value != "license-secret" {
|
||||||
|
t.Fatalf("expected license_key to be updated, got %#v", cfg)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestConfigUpdateSingleAllowsLicenseKeyWrite(t *testing.T) {
|
||||||
|
router, r := setupConfigAccessTestRouter(t)
|
||||||
|
adminToken := mustGenerateConfigAccessToken(t, 1, "admin_user", 0)
|
||||||
|
|
||||||
|
req := httptest.NewRequest(http.MethodPost, "/api/v1/config/update-single", bytes.NewBufferString(`{"name":"license_key","value":"license-secret"}`))
|
||||||
|
req.Header.Set("Content-Type", "application/json")
|
||||||
|
req.Header.Set("Authorization", adminToken)
|
||||||
|
resp := httptest.NewRecorder()
|
||||||
|
|
||||||
|
router.ServeHTTP(resp, req)
|
||||||
|
|
||||||
|
assertHandlerCode(t, resp, 0)
|
||||||
|
|
||||||
|
cfg, err := r.GetConfigByName("license_key")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("get config: %v", err)
|
||||||
|
}
|
||||||
|
if cfg == nil || cfg.Value != "license-secret" {
|
||||||
|
t.Fatalf("expected license_key to be updated, got %#v", cfg)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func setupConfigAccessTestRouter(t *testing.T) (http.Handler, *repo.Repository) {
|
func setupConfigAccessTestRouter(t *testing.T) (http.Handler, *repo.Repository) {
|
||||||
|
|||||||
@@ -393,8 +393,7 @@ func (h *Handler) getConfigByName(w http.ResponseWriter, r *http.Request) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
configName := strings.ToLower(strings.TrimSpace(req.Name))
|
configName := strings.ToLower(strings.TrimSpace(req.Name))
|
||||||
switch configName {
|
if repo.IsSensitiveConfigKey(configName) && !isAdminRequest(r) {
|
||||||
case "license_key", "cloudflare_secret_key", "jwt_secret":
|
|
||||||
response.WriteJSON(w, response.Err(403, "禁止访问敏感配置"))
|
response.WriteJSON(w, response.Err(403, "禁止访问敏感配置"))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -989,7 +988,7 @@ func (h *Handler) updateConfigs(w http.ResponseWriter, r *http.Request) {
|
|||||||
if key == "" {
|
if key == "" {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if repo.IsSensitiveConfigKey(key) {
|
if repo.IsSensitiveConfigKey(key) && !isAdminRequest(r) {
|
||||||
response.WriteJSON(w, response.Err(403, "禁止访问敏感配置"))
|
response.WriteJSON(w, response.Err(403, "禁止访问敏感配置"))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -1030,7 +1029,7 @@ func (h *Handler) updateSingleConfig(w http.ResponseWriter, r *http.Request) {
|
|||||||
response.WriteJSON(w, response.ErrDefault("配置名称不能为空"))
|
response.WriteJSON(w, response.ErrDefault("配置名称不能为空"))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if repo.IsSensitiveConfigKey(name) {
|
if repo.IsSensitiveConfigKey(name) && !isAdminRequest(r) {
|
||||||
response.WriteJSON(w, response.Err(403, "禁止访问敏感配置"))
|
response.WriteJSON(w, response.Err(403, "禁止访问敏感配置"))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -1060,6 +1059,14 @@ func (h *Handler) updateSingleConfig(w http.ResponseWriter, r *http.Request) {
|
|||||||
response.WriteJSON(w, response.OKEmpty())
|
response.WriteJSON(w, response.OKEmpty())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func isAdminRequest(r *http.Request) bool {
|
||||||
|
if r == nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
claims, ok := r.Context().Value(middleware.ClaimsContextKey).(auth.Claims)
|
||||||
|
return ok && claims.RoleID == 0
|
||||||
|
}
|
||||||
|
|
||||||
func normalizeAndValidateConfigValue(key, value string) (string, error) {
|
func normalizeAndValidateConfigValue(key, value string) (string, error) {
|
||||||
switch strings.TrimSpace(key) {
|
switch strings.TrimSpace(key) {
|
||||||
case "app_logo", "app_favicon":
|
case "app_logo", "app_favicon":
|
||||||
|
|||||||
Reference in New Issue
Block a user