Scert/backend/internal/httpapi/aliyun_scope_api_test.go

168 lines
5.6 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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())
}
}
}