package httpapi import ( "context" "errors" "net/http" "net/http/httptest" "strings" "testing" "git.pcux.cn/xuans/Scert/backend/internal/dnsprovider" ) type fakeAliyunScopeChecker struct { allowedZones []string } func (f *fakeAliyunScopeChecker) Discover(ctx context.Context, credentials map[string]string) (dnsprovider.AuthorizationReport, error) { // 模拟 RAM 账号处于最小资源限制模式,无全局枚举权限 return dnsprovider.AuthorizationReport{}, errors.New("SDKError: Code: Forbidden.NoPermission") } func (f *fakeAliyunScopeChecker) Verify(ctx context.Context, requestedDomain string, credentials map[string]string) (dnsprovider.AuthorizationReport, error) { report := dnsprovider.AuthorizationReport{ Provider: dnsprovider.Aliyun, RequestedDomain: requestedDomain, } for _, zone := range f.allowedZones { if requestedDomain == zone || strings.HasSuffix(requestedDomain, "."+zone) { report.Verified = true report.MatchedDomain = zone return report, nil } } err := errors.New("SDKError: Code: Forbidden.NoPermission, Message: Specified access key is not authorized.") report.Diagnosis = dnsprovider.DiagnoseAliyunError(err, requestedDomain) return report, err } func (f *fakeAliyunScopeChecker) VerifyDomain(ctx context.Context, providerID, requestedDomain string, credentials map[string]string) error { for _, zone := range f.allowedZones { if requestedDomain == zone || strings.HasSuffix(requestedDomain, "."+zone) { return nil } } return errors.New("SDKError: Code: Forbidden.NoPermission, Message: Specified access key is not authorized.") } type fakeSecretReaderStore struct { data map[string]map[string]string } func (s *fakeSecretReaderStore) Put(ctx context.Context, ref string, val map[string]string) error { if s.data == nil { s.data = make(map[string]map[string]string) } s.data[ref] = val return nil } func (s *fakeSecretReaderStore) Delete(ctx context.Context, ref string) error { delete(s.data, ref) return nil } func (s *fakeSecretReaderStore) Get(ctx context.Context, ref string) (map[string]string, error) { if val, ok := s.data[ref]; ok { return val, nil } return nil, errors.New("secret not found") } func TestAliyunScopeAPIsAndDomainValidation(t *testing.T) { cfg := readyTestConfig(t.TempDir()) cfg.SetupReady = true cfg.DNS.Provider = "aliyun" cfg.DNS.CredentialRef = "secret://dns/aliyun/ref1" store := &fakeSecretReaderStore{ data: map[string]map[string]string{ "secret://dns/aliyun/ref1": { "access_key_id": "LTAI_test", "access_key_secret": "test_secret", }, }, } checker := &fakeAliyunScopeChecker{ allowedZones: []string{"solit.cn", "odacm.cn", "apmmk.cn", "ifmj.uno", "vraq.uno"}, } repo := &stubDomainRepository{} server := New(Dependencies{ Config: cfg, Repository: repo, Secrets: store, AliyunChecker: checker, }) // 1. 测试 GET /api/v1/dns/aliyun/scope(受限模式报告) { w := httptest.NewRecorder() req := httptest.NewRequest(http.MethodGet, "/api/v1/dns/aliyun/scope", nil) serveAdminHTTP(server, w, req) if w.Code != http.StatusOK { t.Fatalf("GET /api/v1/dns/aliyun/scope status = %d; want 200, body=%s", w.Code, w.Body.String()) } if !strings.Contains(w.Body.String(), "resource_scoped") { t.Fatalf("expected resource_scoped policy mode, got: %s", w.Body.String()) } } // 2. 测试 POST /api/v1/dns/aliyun/check 验证授权域名(如 solit.cn) { w := httptest.NewRecorder() body := strings.NewReader(`{"domain":"solit.cn"}`) req := httptest.NewRequest(http.MethodPost, "/api/v1/dns/aliyun/check", body) serveAdminHTTP(server, w, req) if w.Code != http.StatusOK { t.Fatalf("POST check solit.cn status = %d; body=%s", w.Code, w.Body.String()) } if !strings.Contains(w.Body.String(), `"verified":true`) { t.Fatalf("solit.cn 应验证通过: %s", w.Body.String()) } } // 3. 测试 POST /api/v1/dns/aliyun/check 验证未授权域名(如 unauth.com) { w := httptest.NewRecorder() body := strings.NewReader(`{"domain":"unauth.com"}`) req := httptest.NewRequest(http.MethodPost, "/api/v1/dns/aliyun/check", body) serveAdminHTTP(server, w, req) if w.Code != http.StatusOK { t.Fatalf("POST check unauth.com status = %d; body=%s", w.Code, w.Body.String()) } if !strings.Contains(w.Body.String(), `"verified":false`) { t.Fatalf("unauth.com 应验证失败: %s", w.Body.String()) } if !strings.Contains(w.Body.String(), "acs:alidns:*:*:domain/unauth.com") { t.Fatalf("应包含明确的 RAM 策略建议: %s", w.Body.String()) } } // 4. 测试 POST /api/v1/domains 添加授权域名(例如 *.solit.cn) { w := httptest.NewRecorder() body := strings.NewReader(`{"name":"test.solit.cn","certificate_type":"single"}`) req := httptest.NewRequest(http.MethodPost, "/api/v1/domains", body) serveAdminHTTP(server, w, req) if w.Code != http.StatusCreated { t.Fatalf("创建授权域名应成功,got status = %d; body=%s", w.Code, w.Body.String()) } } // 5. 测试 POST /api/v1/domains 添加未授权域名(例如 forbidden.cn),应被前置阻断 { w := httptest.NewRecorder() body := strings.NewReader(`{"name":"forbidden.cn","certificate_type":"single"}`) req := httptest.NewRequest(http.MethodPost, "/api/v1/domains", body) serveAdminHTTP(server, w, req) if w.Code != http.StatusBadRequest { t.Fatalf("添加未授权域名应被 400 阻断,got status = %d; body=%s", w.Code, w.Body.String()) } if !strings.Contains(w.Body.String(), "acs:alidns:*:*:domain/forbidden.cn") { t.Fatalf("阻断提示应包含针对该域名的 RAM 授权指导,got: %s", w.Body.String()) } } }