Quellcode durchsuchen

feat(certificate): add certificate type detection and update logic in AddCert and ModifyCert functions

Jacky vor 3 Wochen
Ursprung
Commit
771859d3b8

+ 53 - 6
api/certificate/certificate.go

@@ -131,6 +131,31 @@ func AddCert(c *gin.Context) {
 		return
 	}
 
+	// Detect and set certificate type
+	if len(json.SSLCertificate) > 0 {
+		keyType, err := cert.GetKeyType(json.SSLCertificate)
+		if err == nil && keyType != "" {
+			// Set KeyType based on certificate type
+			switch keyType {
+			case "2048":
+				certModel.KeyType = certcrypto.RSA2048
+			case "3072":
+				certModel.KeyType = certcrypto.RSA3072
+			case "4096":
+				certModel.KeyType = certcrypto.RSA4096
+			case "P256":
+				certModel.KeyType = certcrypto.EC256
+			case "P384":
+				certModel.KeyType = certcrypto.EC384
+			}
+			// Update certificate model
+			err = certModel.Updates(&model.Cert{KeyType: certModel.KeyType})
+			if err != nil {
+				notification.Error("Update Certificate Type Error", err.Error(), nil)
+			}
+		}
+	}
+
 	err = cert.SyncToRemoteServer(certModel)
 	if err != nil {
 		notification.Error("Sync Certificate Error", err.Error(), nil)
@@ -157,7 +182,8 @@ func ModifyCert(c *gin.Context) {
 		return
 	}
 
-	err = certModel.Updates(&model.Cert{
+	// Create update data object
+	updateData := &model.Cert{
 		Name:                  json.Name,
 		SSLCertificatePath:    json.SSLCertificatePath,
 		SSLCertificateKeyPath: json.SSLCertificateKeyPath,
@@ -166,11 +192,6 @@ func ModifyCert(c *gin.Context) {
 		DnsCredentialID:       json.DnsCredentialID,
 		ACMEUserID:            json.ACMEUserID,
 		SyncNodeIds:           json.SyncNodeIds,
-	})
-
-	if err != nil {
-		cosy.ErrHandler(c, err)
-		return
 	}
 
 	content := &cert.Content{
@@ -186,6 +207,32 @@ func ModifyCert(c *gin.Context) {
 		return
 	}
 
+	// Detect and set certificate type
+	if len(json.SSLCertificate) > 0 {
+		keyType, err := cert.GetKeyType(json.SSLCertificate)
+		if err == nil && keyType != "" {
+			// Set KeyType based on certificate type
+			switch keyType {
+			case "2048":
+				updateData.KeyType = certcrypto.RSA2048
+			case "3072":
+				updateData.KeyType = certcrypto.RSA3072
+			case "4096":
+				updateData.KeyType = certcrypto.RSA4096
+			case "P256":
+				updateData.KeyType = certcrypto.EC256
+			case "P384":
+				updateData.KeyType = certcrypto.EC384
+			}
+		}
+	}
+
+	err = certModel.Updates(updateData)
+	if err != nil {
+		cosy.ErrHandler(c, err)
+		return
+	}
+
 	err = cert.SyncToRemoteServer(certModel)
 	if err != nil {
 		notification.Error("Sync Certificate Error", err.Error(), nil)

+ 3 - 6
app/src/views/certificate/CertificateList/certColumns.tsx

@@ -33,27 +33,24 @@ const columns: Column[] = [{
     if (text === true || text === 1) {
       template.push(
         <Tag bordered={false} color="processing">
-          { managed }
+          {managed}
         </Tag>,
       )
     }
     else if (text === 2) {
       template.push(
         <Tag bordered={false} color="success">
-          { sync }
+          {sync}
         </Tag>,
       )
     }
     else {
       template.push(
         <Tag bordered={false} color="purple">
-          {
-            general
-          }
+          {general}
         </Tag>,
       )
     }
-
     return h('div', template)
   },
   sorter: true,

+ 1 - 1
app/src/views/site/site_list/SiteList.vue

@@ -164,7 +164,7 @@ function handleBatchUpdated() {
       :get-params="{
         env_group_id: envGroupId,
       }"
-      :scroll-x="1200"
+      :scroll-x="1600"
       @click-edit="(r: string) => router.push({
         path: `/sites/${r}`,
       })"

+ 1 - 0
app/src/views/stream/StreamList.vue

@@ -24,6 +24,7 @@ const columns: Column[] = [{
     type: input,
   },
   search: true,
+  width: 150,
 }, {
   title: () => $gettext('Node Group'),
   dataIndex: 'env_group_id',

+ 1 - 0
internal/cert/errors.go

@@ -10,4 +10,5 @@ var (
 	ErrCertParse                         = e.New(50004, "certificate parse error")
 	ErrPayloadResourceIsNil              = e.New(50005, "payload resource is nil")
 	ErrPathIsNotUnderTheNginxConfDir     = e.New(50006, "path: {0} is not under the nginx conf dir: {1}")
+	ErrCertPathIsEmpty                   = e.New(50007, "certificate path is empty")
 )

+ 66 - 0
internal/cert/helper.go

@@ -1,6 +1,8 @@
 package cert
 
 import (
+	"crypto/ecdsa"
+	"crypto/rsa"
 	"crypto/x509"
 	"encoding/pem"
 	"os"
@@ -79,3 +81,67 @@ func IsPrivateKeyPath(path string) bool {
 
 	return IsPrivateKey(string(bytes))
 }
+
+// GetKeyType determines the key type from a PEM certificate string.
+// Returns "2048", "3072", "4096", "P256", "P384" or empty string.
+func GetKeyType(pemStr string) (string, error) {
+	block, _ := pem.Decode([]byte(pemStr))
+	if block == nil {
+		return "", ErrCertDecode
+	}
+
+	cert, err := x509.ParseCertificate(block.Bytes)
+	if err != nil {
+		return "", ErrCertParse
+	}
+
+	switch cert.PublicKeyAlgorithm {
+	case x509.RSA:
+		rsaKey, ok := cert.PublicKey.(*rsa.PublicKey)
+		if !ok {
+			return "", nil
+		}
+		keySize := rsaKey.Size() * 8 // Size returns size in bytes, convert to bits
+		switch keySize {
+		case 2048:
+			return "2048", nil
+		case 3072:
+			return "3072", nil
+		case 4096:
+			return "4096", nil
+		default:
+			return "", nil
+		}
+	case x509.ECDSA:
+		ecKey, ok := cert.PublicKey.(*ecdsa.PublicKey)
+		if !ok {
+			return "", nil
+		}
+		curve := ecKey.Curve.Params().Name
+		switch curve {
+		case "P-256":
+			return "P256", nil
+		case "P-384":
+			return "P384", nil
+		default:
+			return "", nil
+		}
+	default:
+		return "", nil
+	}
+}
+
+// GetKeyTypeFromPath determines the key type from a certificate file.
+// Returns "2048", "3072", "4096", "P256", "P384" or empty string.
+func GetKeyTypeFromPath(path string) (string, error) {
+	if path == "" {
+		return "", ErrCertPathIsEmpty
+	}
+
+	bytes, err := os.ReadFile(path)
+	if err != nil {
+		return "", err
+	}
+
+	return GetKeyType(string(bytes))
+}