From 2ecd7bb25108dc284ac6094653d54fb75bf3ffd6 Mon Sep 17 00:00:00 2001 From: xuans Date: Tue, 29 Sep 2026 21:52:36 +0800 Subject: [PATCH] =?UTF-8?q?=E6=94=B6=E7=B4=A7=E6=89=8B=E5=8A=A8=E7=BB=AD?= =?UTF-8?q?=E6=9C=9F=E6=9D=83=E9=99=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 手动批量续期入口仅允许超级管理员访问,并新增非超级管理员不得触发续期工作流的回归测试。 --- backend/internal/httpapi/server.go | 2 +- backend/internal/httpapi/settings_api_test.go | 23 +++++++++++++++++++ 2 files changed, 24 insertions(+), 1 deletion(-) diff --git a/backend/internal/httpapi/server.go b/backend/internal/httpapi/server.go index 198eb8a..cca78c6 100644 --- a/backend/internal/httpapi/server.go +++ b/backend/internal/httpapi/server.go @@ -226,7 +226,7 @@ func (s *Server) registerRoutes() { adminAPI.GET("/dns/accounts", s.listDNSAccounts) adminAPI.POST("/dns/test", s.dnsOperation, s.testAdminDNS) adminAPI.POST("/dns/aliyun/check", s.dnsOperation, s.checkAliyunDNSDomain) - adminAPI.POST("/renewal/run", s.longOperation(certificateTaskTimeout), s.runRenewalNow) + adminAPI.POST("/renewal/run", s.requireSuperAdmin, s.longOperation(certificateTaskTimeout), s.runRenewalNow) domainAPI.POST("/domains/:id/certificate/import", s.longOperation(certificateTaskTimeout), s.importCertificateFiles) domainAPI.POST("/domains/:id/certificate/validate", s.longOperation(certificateTaskTimeout), s.validateCertificate) domainAPI.POST("/domains/:id/deploy", s.longOperation(certificateTaskTimeout), s.deployCertificateCurrent) diff --git a/backend/internal/httpapi/settings_api_test.go b/backend/internal/httpapi/settings_api_test.go index cfe289f..264275d 100644 --- a/backend/internal/httpapi/settings_api_test.go +++ b/backend/internal/httpapi/settings_api_test.go @@ -397,6 +397,29 @@ func TestRenewalAPIHidesWorkflowErrors(t *testing.T) { t.Fatalf("renewal API did not include the safe error message: %s", body) } } +func TestRenewalAPIRequiresSuperAdmin(t *testing.T) { + cfg := config.Default(t.TempDir()) + cfg.SetupReady = true + runCalls := 0 + server := New(Dependencies{ + Config: cfg, + RunRenewal: func(context.Context) (certificate.RenewalReport, error) { + runCalls++ + return certificate.RenewalReport{}, nil + }, + }) + cookie := httpUserSessionCookie(server, "renewal-admin", repository.UserRoleAdmin) + request := httptest.NewRequest(http.MethodPost, "/api/v1/renewal/run", nil) + request.AddCookie(cookie) + response := httptest.NewRecorder() + server.Handler().ServeHTTP(response, request) + if response.Code != http.StatusForbidden { + t.Fatalf("non-super-admin renewal request returned %d: %s", response.Code, response.Body.String()) + } + if runCalls != 0 { + t.Fatalf("renewal runner was called for a non-super-admin: %d", runCalls) + } +} func loginForSettingsTest(t *testing.T, server *Server) *http.Cookie { t.Helper() request := httptest.NewRequest(http.MethodPost, "/api/v1/auth/login", strings.NewReader(`{"identifier":"admin","password":"Password123"}`))