92 lines
3.4 KiB
Go
92 lines
3.4 KiB
Go
package certificate
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"net/http/httptest"
|
|
"testing"
|
|
|
|
"github.com/go-acme/lego/v5/challenge"
|
|
|
|
"git.pcux.cn/xuans/Scert/backend/internal/config"
|
|
"git.pcux.cn/xuans/Scert/backend/internal/domain"
|
|
"git.pcux.cn/xuans/Scert/backend/internal/httpchallenge"
|
|
)
|
|
|
|
type httpTestIssuer func(context.Context, ObtainRequest) (Resource, error)
|
|
|
|
func (f httpTestIssuer) Obtain(ctx context.Context, request ObtainRequest) (Resource, error) {
|
|
return f(ctx, request)
|
|
}
|
|
|
|
func TestServiceHTTPChallengeDoesNotNeedDNSAndCleansAfterIssue(t *testing.T) {
|
|
for _, failure := range []bool{false, true} {
|
|
t.Run(map[bool]string{false: "success", true: "failure"}[failure], func(t *testing.T) {
|
|
cfg := certificateTestConfig(t.TempDir())
|
|
cfg.DNS = config.DNSConfig{}
|
|
item, err := domain.NewDomain("example.com", domain.CertificateSingle, nil)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
item.ChallengeType = domain.ChallengeHTTP01
|
|
repo := &serviceRepository{item: item}
|
|
service := NewService(cfg, repo, nil, nil, serviceAccounts{})
|
|
store := httpchallenge.NewStore()
|
|
service.SetHTTPChallenges(store)
|
|
resource := testResource(t, "example.com")
|
|
service.acmeRoots = testResourceRoots(t, resource)
|
|
const token = "abcdefghijklmnopqrstuvwxyz0123456789ABCDEFG"
|
|
const auth = token + "." + token
|
|
checker := &serviceAuthorizationChecker{}
|
|
service.SetDNSAuthorizationChecker(checker)
|
|
service.newIssuer = func(_ *AccountUser, provider challenge.Provider, _ string) Issuer {
|
|
return httpTestIssuer(func(ctx context.Context, request ObtainRequest) (Resource, error) {
|
|
if request.ChallengeType != domain.ChallengeHTTP01 {
|
|
t.Fatal("未传递 HTTP 验证方式")
|
|
}
|
|
if err := provider.Present(ctx, item.Name, token, auth); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
w := httptest.NewRecorder()
|
|
store.ServeHTTP(w, httptest.NewRequest("GET", "http://example.com"+httpchallenge.PathPrefix+token, nil))
|
|
if w.Code != 200 || w.Body.String() != auth {
|
|
t.Fatal("签发期间挑战不可读")
|
|
}
|
|
// 模拟签发器遗漏清理,服务自身仍须在任意返回路径回收订单。
|
|
if failure {
|
|
return Resource{}, errors.New("模拟签发失败")
|
|
}
|
|
return resource, nil
|
|
})
|
|
}
|
|
_, err = service.Issue(context.Background(), item.ID)
|
|
if (err != nil) != failure {
|
|
t.Fatal("签发结果不符合预期:", err)
|
|
}
|
|
if checker.verifyCalls != 0 || checker.invalidateCalls != 0 {
|
|
t.Fatal("HTTP 验证触碰 DNS 授权")
|
|
}
|
|
w := httptest.NewRecorder()
|
|
store.ServeHTTP(w, httptest.NewRequest("GET", "http://example.com"+httpchallenge.PathPrefix+token, nil))
|
|
if w.Code != 404 {
|
|
t.Fatal("签发结束后仍残留挑战")
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestServiceHTTPRejectsWildcardSANBeforeDependencies(t *testing.T) {
|
|
item := domain.Domain{ID: "wild", Name: "example.com", CertificateType: domain.CertificateSAN, SANs: []string{"example.com", "*.example.com"}, ChallengeType: domain.ChallengeHTTP01}
|
|
repo := &serviceRepository{item: item}
|
|
service := NewService(certificateTestConfig(t.TempDir()), repo, nil, nil, serviceAccounts{})
|
|
service.SetHTTPChallenges(httpchallenge.NewStore())
|
|
called := false
|
|
service.newIssuer = func(*AccountUser, challenge.Provider, string) Issuer { called = true; return nil }
|
|
if _, err := service.Issue(context.Background(), item.ID); err == nil {
|
|
t.Fatal("未拒绝泛域名 SAN")
|
|
}
|
|
if called || len(repo.states) != 0 {
|
|
t.Fatal("拒绝前产生签发副作用")
|
|
}
|
|
}
|