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

jimin pushed a commit to branch 2.x
in repository https://gitbox.apache.org/repos/asf/incubator-seata.git


The following commit(s) were added to refs/heads/2.x by this push:
     new 50d7003694 feature: enforce account initialization and disable default 
credentials (#7261)
50d7003694 is described below

commit 50d7003694d79dc61dc1f15945cbd153596f47e3
Author: Yongjun Hong <dev.yongj...@gmail.com>
AuthorDate: Sun May 11 23:37:00 2025 +0900

    feature: enforce account initialization and disable default credentials 
(#7261)
---
 changes/en-us/2.x.md                               |  2 +-
 changes/zh-cn/2.x.md                               |  2 +-
 .../security/CustomUserDetailsServiceImpl.java     | 31 ++++++--
 .../org/apache/seata/console/security/User.java    |  4 +
 namingserver/src/main/resources/application.yml    |  4 -
 .../AuthControllerWithCustomPropertiesTest.java    | 78 ++++++++++++++++++
 .../AuthControllerWithRandomPasswordTest.java      | 92 ++++++++++++++++++++++
 .../NamingControllerLoggerPrintSmokeTest.java      | 41 ++++++++++
 .../smoke/NamingControllerPropertiesSmokeTest.java | 41 ++++++++++
 namingserver/src/test/resources/application.yml    |  4 -
 10 files changed, 282 insertions(+), 17 deletions(-)

diff --git a/changes/en-us/2.x.md b/changes/en-us/2.x.md
index 45822fec2f..3f35d9ff2a 100644
--- a/changes/en-us/2.x.md
+++ b/changes/en-us/2.x.md
@@ -20,7 +20,7 @@ Add changes here for all PR submitted to the 2.x branch.
 
 ### feature:
 
-- [[#PR_NO](https://github.com/seata/seata/pull/PR_NO)] support XXX
+- [[#7261](https://github.com/apache/incubator-seata/pull/7261)] enforce 
account initialization and disable default credentials
 
 
 ### bugfix:
diff --git a/changes/zh-cn/2.x.md b/changes/zh-cn/2.x.md
index c796d2f816..2df96f51a2 100644
--- a/changes/zh-cn/2.x.md
+++ b/changes/zh-cn/2.x.md
@@ -20,7 +20,7 @@
 
 ### feature:
 
-- [[#PR_NO](https://github.com/seata/seata/pull/PR_NO)] support XXX
+- [[#7261](https://github.com/apache/incubator-seata/pull/7261)] 
强制进行账户初始化并禁用默认凭据
 
 
 ### bugfix:
diff --git 
a/console/src/main/java/org/apache/seata/console/security/CustomUserDetailsServiceImpl.java
 
b/console/src/main/java/org/apache/seata/console/security/CustomUserDetailsServiceImpl.java
index a001b4e116..d58798ce19 100644
--- 
a/console/src/main/java/org/apache/seata/console/security/CustomUserDetailsServiceImpl.java
+++ 
b/console/src/main/java/org/apache/seata/console/security/CustomUserDetailsServiceImpl.java
@@ -16,8 +16,10 @@
  */
 package org.apache.seata.console.security;
 
+import java.util.UUID;
 import javax.annotation.PostConstruct;
-
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.annotation.Value;
 import org.springframework.security.core.userdetails.UserDetails;
 import org.springframework.security.core.userdetails.UserDetailsService;
@@ -32,10 +34,12 @@ import org.springframework.stereotype.Service;
 @Service
 public class CustomUserDetailsServiceImpl implements UserDetailsService {
 
-    @Value("${console.user.username}")
+    private static final Logger LOGGER = 
LoggerFactory.getLogger(CustomUserDetailsServiceImpl.class);
+
+    @Value("${console.user.username:seata}")
     private String username;
 
-    @Value("${console.user.password}")
+    @Value("${console.user.password:}")
     private String password;
 
     private User user;
@@ -45,10 +49,23 @@ public class CustomUserDetailsServiceImpl implements 
UserDetailsService {
      */
     @PostConstruct
     public void init() {
-        // TODO: get userInfo by db
-        user = new User();
-        user.setUsername(username);
-        user.setPassword(new BCryptPasswordEncoder().encode(password));
+        if (!password.isEmpty()) {
+            user = new User(username, new 
BCryptPasswordEncoder().encode(password));
+            return;
+        }
+
+        password = generateRandomPassword();
+        LOGGER.info(
+                "No password was configured. A random password has been 
generated for security purposes. You may either:\n"
+                        + "1. Use the auto-generated password: [{}]\n"
+                        + "2. Set a custom password in the configuration.",
+                password);
+
+        user = new User(username, new 
BCryptPasswordEncoder().encode(password));
+    }
+
+    private String generateRandomPassword() {
+        return UUID.randomUUID().toString().replace("-", "").substring(0, 8);
     }
 
     @Override
diff --git a/console/src/main/java/org/apache/seata/console/security/User.java 
b/console/src/main/java/org/apache/seata/console/security/User.java
index 44067dc51e..797430628b 100644
--- a/console/src/main/java/org/apache/seata/console/security/User.java
+++ b/console/src/main/java/org/apache/seata/console/security/User.java
@@ -30,6 +30,10 @@ public class User {
      */
     String password;
 
+    public User(String username, String password) {
+        this.username = username;
+        this.password = password;
+    }
 
     //region Getter && Setter
 
diff --git a/namingserver/src/main/resources/application.yml 
b/namingserver/src/main/resources/application.yml
index 4764227f0e..0ea1c8b5d2 100644
--- a/namingserver/src/main/resources/application.yml
+++ b/namingserver/src/main/resources/application.yml
@@ -25,10 +25,6 @@ logging:
   config: classpath:logback-spring.xml
   file:
     path: ${log.home:${user.home}/logs/seata}
-console:
-  user:
-    username: seata
-    password: seata
 heartbeat:
   threshold: 90000
   period: 60000
diff --git 
a/namingserver/src/test/java/org/apache/seata/namingserver/AuthControllerWithCustomPropertiesTest.java
 
b/namingserver/src/test/java/org/apache/seata/namingserver/AuthControllerWithCustomPropertiesTest.java
new file mode 100644
index 0000000000..2b0b79a6c3
--- /dev/null
+++ 
b/namingserver/src/test/java/org/apache/seata/namingserver/AuthControllerWithCustomPropertiesTest.java
@@ -0,0 +1,78 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.seata.namingserver;
+
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static 
org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
+import static 
org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+import org.apache.seata.common.result.Code;
+import org.apache.seata.console.config.WebSecurityConfig;
+import org.apache.seata.console.security.User;
+import org.junit.jupiter.api.Test;
+import org.springframework.beans.factory.annotation.Autowired;
+import 
org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.http.MediaType;
+import org.springframework.test.context.TestPropertySource;
+import org.springframework.test.web.servlet.MockMvc;
+import org.springframework.test.web.servlet.MvcResult;
+
+@SpringBootTest
+@TestPropertySource(properties = {"console.user.username=seata", 
"console.user.password=foo"})
+@AutoConfigureMockMvc
+public class AuthControllerWithCustomPropertiesTest {
+
+    @Autowired
+    private MockMvc mockMvc;
+
+    @Autowired
+    private ObjectMapper objectMapper;
+
+    @Test
+    public void loginSuccess_shouldReturnTokenAndAddToHeader() throws 
Exception {
+        User user = new User("seata", "foo");
+        String userJson = objectMapper.writeValueAsString(user);
+
+        MvcResult result = mockMvc.perform(post("/api/v1/auth/login")
+                        .contentType(MediaType.APPLICATION_JSON)
+                        .content(userJson))
+                .andExpect(status().isOk())
+                .andExpect(jsonPath("$.success").value(true))
+                .andExpect(jsonPath("$.data").isNotEmpty())
+                
.andExpect(header().exists(WebSecurityConfig.AUTHORIZATION_HEADER))
+                .andReturn();
+
+        String authHeader = 
result.getResponse().getHeader(WebSecurityConfig.AUTHORIZATION_HEADER);
+        assertNotNull(authHeader);
+        assert (authHeader.startsWith(WebSecurityConfig.TOKEN_PREFIX));
+    }
+
+    @Test
+    public void loginFailure_shouldReturnErrorCode() throws Exception {
+        User user = new User("wrong_user", "wrong_password");
+        String userJson = objectMapper.writeValueAsString(user);
+
+        mockMvc.perform(post("/api/v1/auth/login")
+                        .contentType(MediaType.APPLICATION_JSON)
+                        .content(userJson))
+                .andExpect(status().isOk())
+                .andExpect(jsonPath("$.success").value(false))
+                
.andExpect(jsonPath("$.code").value(Code.LOGIN_FAILED.getCode()));
+    }
+}
diff --git 
a/namingserver/src/test/java/org/apache/seata/namingserver/AuthControllerWithRandomPasswordTest.java
 
b/namingserver/src/test/java/org/apache/seata/namingserver/AuthControllerWithRandomPasswordTest.java
new file mode 100644
index 0000000000..af886b0baa
--- /dev/null
+++ 
b/namingserver/src/test/java/org/apache/seata/namingserver/AuthControllerWithRandomPasswordTest.java
@@ -0,0 +1,92 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.seata.namingserver;
+
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static 
org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
+import static 
org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+import org.apache.seata.common.result.Code;
+import org.apache.seata.console.config.WebSecurityConfig;
+import org.apache.seata.console.security.User;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.springframework.beans.factory.annotation.Autowired;
+import 
org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.boot.test.system.CapturedOutput;
+import org.springframework.boot.test.system.OutputCaptureExtension;
+import org.springframework.http.MediaType;
+import org.springframework.test.web.servlet.MockMvc;
+import org.springframework.test.web.servlet.MvcResult;
+
+@SpringBootTest
+@ExtendWith(OutputCaptureExtension.class)
+@AutoConfigureMockMvc
+public class AuthControllerWithRandomPasswordTest {
+
+    @Autowired
+    private MockMvc mockMvc;
+
+    @Autowired
+    private ObjectMapper objectMapper;
+
+    @Test
+    public void loginSuccess_shouldReturnTokenAndAddToHeader(CapturedOutput 
output) throws Exception {
+        String logs = output.getOut();
+
+        Pattern pattern = Pattern.compile("Use the auto-generated password: 
\\[(.+?)\\]");
+        Matcher matcher = pattern.matcher(logs);
+
+        assertTrue(matcher.find(), "captured password not found in logs");
+
+        String extractedPassword = matcher.group(1);
+        User user = new User("seata", extractedPassword);
+
+        String userJson = objectMapper.writeValueAsString(user);
+
+        MvcResult result = mockMvc.perform(post("/api/v1/auth/login")
+                        .contentType(MediaType.APPLICATION_JSON)
+                        .content(userJson))
+                .andExpect(status().isOk())
+                .andExpect(jsonPath("$.success").value(true))
+                .andExpect(jsonPath("$.data").isNotEmpty())
+                
.andExpect(header().exists(WebSecurityConfig.AUTHORIZATION_HEADER))
+                .andReturn();
+
+        String authHeader = 
result.getResponse().getHeader(WebSecurityConfig.AUTHORIZATION_HEADER);
+        assertNotNull(authHeader);
+        assert (authHeader.startsWith(WebSecurityConfig.TOKEN_PREFIX));
+    }
+
+    @Test
+    public void loginFailure_shouldReturnErrorCode() throws Exception {
+        User user = new User("wrong_user", "wrong_password");
+        String userJson = objectMapper.writeValueAsString(user);
+
+        mockMvc.perform(post("/api/v1/auth/login")
+                        .contentType(MediaType.APPLICATION_JSON)
+                        .content(userJson))
+                .andExpect(status().isOk())
+                .andExpect(jsonPath("$.success").value(false))
+                
.andExpect(jsonPath("$.code").value(Code.LOGIN_FAILED.getCode()));
+    }
+}
diff --git 
a/namingserver/src/test/java/org/apache/seata/namingserver/smoke/NamingControllerLoggerPrintSmokeTest.java
 
b/namingserver/src/test/java/org/apache/seata/namingserver/smoke/NamingControllerLoggerPrintSmokeTest.java
new file mode 100644
index 0000000000..9fe83142cb
--- /dev/null
+++ 
b/namingserver/src/test/java/org/apache/seata/namingserver/smoke/NamingControllerLoggerPrintSmokeTest.java
@@ -0,0 +1,41 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.seata.namingserver.smoke;
+
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import org.apache.seata.namingserver.NamingserverApplication;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.boot.test.system.CapturedOutput;
+import org.springframework.boot.test.system.OutputCaptureExtension;
+
+@SpringBootTest(
+        classes = NamingserverApplication.class,
+        webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT,
+        properties = "console.user.password=")
+@ExtendWith(OutputCaptureExtension.class)
+class NamingControllerLoggerPrintSmokeTest {
+
+    @Test
+    void 
processShouldPrintLogAndGeneratePasswordWhenDefaultPasswordIsNotDefined(CapturedOutput
 output) {
+        String logs = output.getOut();
+        assertTrue(logs.contains("No password was configured."));
+    }
+}
diff --git 
a/namingserver/src/test/java/org/apache/seata/namingserver/smoke/NamingControllerPropertiesSmokeTest.java
 
b/namingserver/src/test/java/org/apache/seata/namingserver/smoke/NamingControllerPropertiesSmokeTest.java
new file mode 100644
index 0000000000..f2641ae7ea
--- /dev/null
+++ 
b/namingserver/src/test/java/org/apache/seata/namingserver/smoke/NamingControllerPropertiesSmokeTest.java
@@ -0,0 +1,41 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.seata.namingserver.smoke;
+
+import static org.junit.jupiter.api.Assertions.assertFalse;
+
+import org.apache.seata.namingserver.NamingserverApplication;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.boot.test.system.CapturedOutput;
+import org.springframework.boot.test.system.OutputCaptureExtension;
+
+@SpringBootTest(
+        classes = NamingserverApplication.class,
+        webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT,
+        properties = {"console.user.username=seata", 
"console.user.password=foo"})
+@ExtendWith(OutputCaptureExtension.class)
+class NamingControllerPropertiesSmokeTest {
+
+    @Test
+    void 
processShouldNotPrintLogsAndGeneratePasswordWhenPasswordIsDefined(CapturedOutput
 output) {
+        String logs = output.getOut();
+        assertFalse(logs.contains("No password was configured."));
+    }
+}
diff --git a/namingserver/src/test/resources/application.yml 
b/namingserver/src/test/resources/application.yml
index 573dde0b26..8ffcfe613b 100644
--- a/namingserver/src/test/resources/application.yml
+++ b/namingserver/src/test/resources/application.yml
@@ -25,10 +25,6 @@ logging:
   config: classpath:logback-spring.xml
   file:
     path: ${log.home:${user.home}/logs/seata}
-console:
-  user:
-    username: seata
-    password: seata
 heartbeat:
   threshold: 5000
   period: 5000


---------------------------------------------------------------------
To unsubscribe, e-mail: notifications-unsubscr...@seata.apache.org
For additional commands, e-mail: notifications-h...@seata.apache.org

Reply via email to