113 lines
4.8 KiB
Go
113 lines
4.8 KiB
Go
package httpapi
|
||
|
||
import (
|
||
"context"
|
||
"net/http"
|
||
"net/http/httptest"
|
||
"strings"
|
||
"testing"
|
||
|
||
"git.pcux.cn/xuans/Scert/backend/internal/certificate"
|
||
"git.pcux.cn/xuans/Scert/backend/internal/deployment"
|
||
"git.pcux.cn/xuans/Scert/backend/internal/domain"
|
||
"git.pcux.cn/xuans/Scert/backend/internal/httpchallenge"
|
||
"git.pcux.cn/xuans/Scert/backend/internal/repository"
|
||
)
|
||
|
||
func TestHTTPChallengeProductionHandler(t *testing.T) {
|
||
store := httpchallenge.NewStore()
|
||
lease, err := store.NewLease(t.Context(), []string{"issued.example.com"})
|
||
if err != nil {
|
||
t.Fatal(err)
|
||
}
|
||
defer lease.Close()
|
||
const token = "abcdefghijklmnopqrstuvwxyz0123456789ABCDEFG"
|
||
if err := lease.Present(t.Context(), "issued.example.com", token, token+"."+token); err != nil {
|
||
t.Fatal(err)
|
||
}
|
||
server := New(Dependencies{Config: readyTestConfig(t.TempDir()), HTTPChallenges: store})
|
||
for _, tc := range []struct {
|
||
host, path string
|
||
status int
|
||
}{
|
||
{"issued.example.com", token, 200},
|
||
{"other.example.com", token, 404},
|
||
{"issued.example.com", "unknown", 404},
|
||
{"issued.example.com", token + "/", 404},
|
||
} {
|
||
w := httptest.NewRecorder()
|
||
server.Handler().ServeHTTP(w, httptest.NewRequest("GET", "http://"+tc.host+httpchallenge.PathPrefix+tc.path, nil))
|
||
if w.Code != tc.status || w.Header().Get("Location") != "" || w.Header().Get("Cache-Control") != "no-store" {
|
||
t.Fatalf("正式挑战路由错误:%d %s", w.Code, w.Body.String())
|
||
}
|
||
}
|
||
server.httpChallenges = nil
|
||
w := httptest.NewRecorder()
|
||
server.Handler().ServeHTTP(w, httptest.NewRequest("GET", "http://issued.example.com"+httpchallenge.PathPrefix+token, nil))
|
||
if w.Code != 404 {
|
||
t.Fatal("缺失挑战存储时未拒绝")
|
||
}
|
||
}
|
||
|
||
func TestHTTPDomainCreateAndUpdateBypassDNSOnly(t *testing.T) {
|
||
cfg := readyTestConfig(t.TempDir())
|
||
cfg.DNS.Provider = "aliyun"
|
||
repo := &stubDomainRepository{loaded: domain.Domain{ID: "domain-1", Name: "example.com", ChallengeType: domain.ChallengeHTTP01}}
|
||
server := New(Dependencies{
|
||
Config: cfg, Repository: repo,
|
||
ProjectDomains: projectDomainRepositoryStub{DomainRepository: repo},
|
||
ProjectResources: projectResourceReaderStub{domains: repo},
|
||
})
|
||
for _, tc := range []struct {
|
||
method, path, body string
|
||
status int
|
||
}{
|
||
{"POST", "/api/v1/domains", `{"name":"example.com","certificate_type":"single","challenge_type":"http-01"}`, 201},
|
||
{"POST", "/api/v1/domains", `{"name":"example.com","certificate_type":"san","sans":["*.example.com"],"challenge_type":"http-01"}`, 400},
|
||
{"POST", "/api/v1/domains", `{"name":"example.com","certificate_type":"single","challenge_type":"unknown"}`, 400},
|
||
{"PUT", "/api/v1/domains/domain-1", `{"name":"example.com","certificate_type":"single"}`, 200},
|
||
} {
|
||
w := httptest.NewRecorder()
|
||
serveAdminHTTP(server, w, httptest.NewRequest(tc.method, tc.path, strings.NewReader(tc.body)))
|
||
if w.Code != tc.status {
|
||
t.Fatalf("域名接口状态 %d,预期 %d:%s", w.Code, tc.status, w.Body.String())
|
||
}
|
||
}
|
||
if repo.created.ChallengeType != domain.ChallengeHTTP01 || repo.updated.ChallengeType != domain.ChallengeHTTP01 {
|
||
t.Fatal("HTTP 验证方式未保存或旧客户端覆盖了现有方式")
|
||
}
|
||
}
|
||
|
||
func TestHTTPTaskAuthorizationRetainsProjectPermissions(t *testing.T) {
|
||
for _, denied := range []domain.ProjectPermission{domain.PermissionDNSCredentialUse, domain.PermissionCertificateManage, domain.PermissionDeploymentExecute} {
|
||
t.Run(string(denied), func(t *testing.T) {
|
||
repo := &stubDomainRepository{loaded: domain.Domain{ID: "domain-1", Name: "example.com", ChallengeType: domain.ChallengeHTTP01, ResourceScope: domain.ResourceScope{ProjectID: "project-a"}}}
|
||
calls := 0
|
||
server := New(Dependencies{
|
||
Config: readyTestConfig(t.TempDir()), Repository: repo,
|
||
ProjectResources: projectResourceReaderStub{domains: repo},
|
||
ProjectMembers: permissionProjectMemberRepository{allow: func(permission domain.ProjectPermission) error {
|
||
if permission == denied {
|
||
return repository.ErrProjectAccessDenied
|
||
}
|
||
return nil
|
||
}},
|
||
IssueCertificate: func(context.Context, string) (certificate.IssueResult, deployment.Report, error) {
|
||
calls++
|
||
return certificate.IssueResult{}, deployment.Report{}, nil
|
||
},
|
||
})
|
||
w := httptest.NewRecorder()
|
||
serveAdminHTTP(server, w, httptest.NewRequest(http.MethodPost, "/api/v1/domains/domain-1/issue", nil))
|
||
want := denied == domain.PermissionDNSCredentialUse
|
||
if (calls == 1) != want {
|
||
t.Fatalf("HTTP 签发项目授权错误:%d %s", w.Code, w.Body.String())
|
||
}
|
||
err := server.authorizeCertificateTaskExecution(t.Context(), repository.Task{DomainID: "domain-1", HistoryScope: repository.HistoryScope{ProjectID: "project-a", ScopeType: repository.ScopeProject}, OperatorID: "actor", TaskType: "certificate_issue"}, repository.TaskAttempt{})
|
||
if (err == nil) != want {
|
||
t.Fatal("执行前授权与验证方式不匹配:", err)
|
||
}
|
||
})
|
||
}
|
||
}
|