diff --git a/server/services/setup/ssl/ssl.go b/server/services/setup/ssl/ssl.go index 3bc9ca0..870a11c 100644 --- a/server/services/setup/ssl/ssl.go +++ b/server/services/setup/ssl/ssl.go @@ -54,6 +54,23 @@ func UserCertificateErrorMessage(err error) string { return "" } +func normalizeUserCertificateImportError(err error) error { + switch { + case stderrors.Is(err, config.ErrSSLCertificateMismatch): + return newUserCertificateError("SSL 证书与私钥不匹配") + case stderrors.Is(err, config.ErrSSLCertificateDomain): + return newUserCertificateError("SSL 证书与已配置域名不匹配,请检查主域名、Web 域名和附加域名") + case stderrors.Is(err, config.ErrSSLCertificateExpired): + return newUserCertificateError("SSL 证书无效、尚未生效或已经过期") + case stderrors.Is(err, config.ErrSSLPrivateKeyPermission): + return newUserCertificateError("SSL 私钥权限过于宽松,请限制为仅 Smail 运行用户可读取") + case stderrors.Is(err, config.ErrInvalidSSLMaterial), stderrors.Is(err, config.ErrUnsafeSSLFile): + return newUserCertificateError("SSL 证书或私钥文件无效") + default: + return err + } +} + func (u *MyUser) GetEmail() string { return u.Email } @@ -109,7 +126,7 @@ func SetSSL(sslType, priKey, crtKey string) error { } reference, err := config.ImportManagedSSL(config.ManagedSSLModeUpload, privatePEM, publicPEM, nil, sslDomains(cfg)) if err != nil { - return err + return normalizeUserCertificateImportError(err) } applyManagedSSLReference(cfg, reference) // 手动设置证书的情况下后台地址默认不启用https diff --git a/server/services/setup/ssl/user_certificate_test.go b/server/services/setup/ssl/user_certificate_test.go index e813db0..8a428d5 100644 --- a/server/services/setup/ssl/user_certificate_test.go +++ b/server/services/setup/ssl/user_certificate_test.go @@ -6,6 +6,7 @@ import ( "crypto/x509" "crypto/x509/pkix" "encoding/pem" + "fmt" "math/big" "os" "path/filepath" @@ -98,6 +99,35 @@ func TestApplyManagedSSLReferenceDoesNotPersistLegacyPaths(t *testing.T) { } } +func TestNormalizeUserCertificateImportError(t *testing.T) { + tests := []struct { + name string + err error + message string + }{ + {name: "certificate mismatch", err: config.ErrSSLCertificateMismatch, message: "SSL 证书与私钥不匹配"}, + {name: "domain mismatch", err: fmt.Errorf("%w: web.xuan.com", config.ErrSSLCertificateDomain), message: "SSL 证书与已配置域名不匹配,请检查主域名、Web 域名和附加域名"}, + {name: "certificate expired", err: config.ErrSSLCertificateExpired, message: "SSL 证书无效、尚未生效或已经过期"}, + {name: "private key permission", err: config.ErrSSLPrivateKeyPermission, message: "SSL 私钥权限过于宽松,请限制为仅 Smail 运行用户可读取"}, + {name: "invalid material", err: config.ErrInvalidSSLMaterial, message: "SSL 证书或私钥文件无效"}, + {name: "unsafe file", err: config.ErrUnsafeSSLFile, message: "SSL 证书或私钥文件无效"}, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + message := UserCertificateErrorMessage(normalizeUserCertificateImportError(test.err)) + if message != test.message { + t.Fatalf("证书错误提示不匹配: got %q, want %q", message, test.message) + } + }) + } + + unknown := fmt.Errorf("unknown certificate import error") + if normalized := normalizeUserCertificateImportError(unknown); normalized != unknown { + t.Fatalf("未知错误不应被替换: %v", normalized) + } +} + func TestValidateUserCertificatePaths(t *testing.T) { oldRoot := config.ROOT_PATH root := t.TempDir()