This is an automated email from the ASF dual-hosted git repository.

lizhimins pushed a commit to branch rocketmq-studio
in repository https://gitbox.apache.org/repos/asf/rocketmq-dashboard.git


The following commit(s) were added to refs/heads/rocketmq-studio by this push:
     new 156f8d62b fix(k8s): normalize certificate identities on create like on 
update (#4300)
156f8d62b is described below

commit 156f8d62b3ac464f1da7602e4eb90ed68b224c1e
Author: btlqql <[email protected]>
AuthorDate: Tue Sep 15 21:08:36 2026 +0800

    fix(k8s): normalize certificate identities on create like on update (#4300)
    
    createCert stored k8sId, cluster and issuer verbatim, so a whitespace-only
    
    issuer was accepted and padded identities were persisted, while updateCert
    
    trims them and rejects a blank value.
    
    (cherry picked from commit 891344f37e7445556ea8c9dddaf7da99a1ea09d2)
---
 .../studio/cluster/k8s/K8sCertService.java         | 16 ++++++---
 .../studio/cluster/k8s/K8sCertServiceTest.java     | 39 ++++++++++++++++++++++
 2 files changed, 51 insertions(+), 4 deletions(-)

diff --git 
a/server/src/main/java/org/apache/rocketmq/studio/cluster/k8s/K8sCertService.java
 
b/server/src/main/java/org/apache/rocketmq/studio/cluster/k8s/K8sCertService.java
index dfa541627..ce7109a61 100644
--- 
a/server/src/main/java/org/apache/rocketmq/studio/cluster/k8s/K8sCertService.java
+++ 
b/server/src/main/java/org/apache/rocketmq/studio/cluster/k8s/K8sCertService.java
@@ -78,9 +78,17 @@ public class K8sCertService {
         LocalDateTime now = LocalDateTime.now(clock);
         LocalDateTime notBefore = now;
         LocalDateTime notAfter = now.plusYears(1);
-        String issuer = command.getIssuer();
+        // The identity fields follow the same rules as updateCert: 
surrounding whitespace is trimmed
+        // and a blank value is rejected, so a certificate cannot be created 
in a state that the
+        // update API (and every later lookup by identity) refuses to produce.
+        String k8sId = normalizeOptionalIdentity(command.getK8sId(), "k8sId");
+        String cluster = normalizeOptionalIdentity(command.getCluster(), 
"cluster");
+        boolean certificateSupplied = command.getCertPem() != null && 
!command.getCertPem().isBlank();
+        // A supplied certificate always wins over the submitted issuer, so 
only a create without one
+        // validates the caller-provided value.
+        String issuer = certificateSupplied ? null : 
normalizeOptionalIdentity(command.getIssuer(), "issuer");
         List<String> san = command.getSan();
-        if (command.getCertPem() != null && !command.getCertPem().isBlank()) {
+        if (certificateSupplied) {
             X509Certificate parsed = parseCertificate(command.getCertPem());
             notBefore = 
LocalDateTime.ofInstant(parsed.getNotBefore().toInstant(), 
ZoneId.systemDefault());
             notAfter = 
LocalDateTime.ofInstant(parsed.getNotAfter().toInstant(), 
ZoneId.systemDefault());
@@ -89,8 +97,8 @@ public class K8sCertService {
         }
 
         K8sCertVO cert = K8sCertVO.builder()
-                .k8sId(command.getK8sId())
-                .cluster(command.getCluster())
+                .k8sId(k8sId)
+                .cluster(cluster)
                 .type(type)
                 .issuer(issuer)
                 .notBefore(notBefore)
diff --git 
a/server/src/test/java/org/apache/rocketmq/studio/cluster/k8s/K8sCertServiceTest.java
 
b/server/src/test/java/org/apache/rocketmq/studio/cluster/k8s/K8sCertServiceTest.java
index 39d55cd24..022774bb4 100644
--- 
a/server/src/test/java/org/apache/rocketmq/studio/cluster/k8s/K8sCertServiceTest.java
+++ 
b/server/src/test/java/org/apache/rocketmq/studio/cluster/k8s/K8sCertServiceTest.java
@@ -197,6 +197,45 @@ class K8sCertServiceTest {
         verifyNoInteractions(k8sCertRepository);
     }
 
+    @Test
+    void createCertShouldRejectBlankIdentityFields() {
+        // updateCert refuses a blank identity, so createCert must not accept 
one either: the
+        // certificate would otherwise be stored in a state the update API 
cannot produce.
+        List<Map.Entry<String, Consumer<CreateCertDTO>>> invalidCreates = 
List.of(
+                Map.entry("k8sId", command -> command.setK8sId(" ")),
+                Map.entry("cluster", command -> command.setCluster("\n")),
+                Map.entry("issuer", command -> command.setIssuer("  ")));
+
+        for (Map.Entry<String, Consumer<CreateCertDTO>> invalidCreate : 
invalidCreates) {
+            CreateCertDTO command = 
CreateCertDTO.builder().type("TLS").build();
+            invalidCreate.getValue().accept(command);
+
+            assertThatThrownBy(() -> k8sCertService.createCert(command))
+                    .isInstanceOf(BusinessException.class)
+                    .hasMessage("Certificate " + invalidCreate.getKey() + " 
cannot be blank")
+                    .satisfies(error -> assertThat(((BusinessException) 
error).getCode()).isEqualTo(400));
+        }
+
+        verify(k8sCertRepository, never()).save(any(K8sCertVO.class));
+    }
+
+    @Test
+    void createCertShouldTrimIdentityFieldsLikeUpdateDoes() {
+        CreateCertDTO command = CreateCertDTO.builder()
+                .k8sId(" new-tls-cert ")
+                .cluster(" test-cluster ")
+                .type("TLS")
+                .issuer(" vault ")
+                .build();
+        
when(k8sCertRepository.save(any(K8sCertVO.class))).thenAnswer(invocation -> 
invocation.getArgument(0));
+
+        K8sCertVO result = k8sCertService.createCert(command);
+
+        assertThat(result.getK8sId()).isEqualTo("new-tls-cert");
+        assertThat(result.getCluster()).isEqualTo("test-cluster");
+        assertThat(result.getIssuer()).isEqualTo("vault");
+    }
+
     @Test
     void createCertShouldRejectInvalidTypeBeforeSave() {
         CreateCertDTO command = CreateCertDTO.builder()

Reply via email to