Scert/backend/internal/httpapi/http_challenge_test.go

113 lines
4.8 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"
"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)
}
})
}
}