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

clebertsuconic pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/artemis.git


The following commit(s) were added to refs/heads/main by this push:
     new bae1c1ada8 ARTEMIS-6049 mask cluster-password input from CLI
bae1c1ada8 is described below

commit bae1c1ada82ccd67e709dec51d04774ccd608a47
Author: Justin Bertram <[email protected]>
AuthorDate: Tue May 5 15:50:22 2026 -0500

    ARTEMIS-6049 mask cluster-password input from CLI
---
 .../activemq/artemis/cli/commands/Create.java      |  21 ++++-
 .../artemis/cli/commands/InputAbstract.java        |  12 ++-
 .../cli/commands/CreateTestIndividualSettings.java | 101 +++++++++++++++++++++
 3 files changed, 130 insertions(+), 4 deletions(-)

diff --git 
a/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/Create.java
 
b/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/Create.java
index 32f70c1c89..1dc72c3b19 100644
--- 
a/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/Create.java
+++ 
b/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/Create.java
@@ -35,6 +35,7 @@ import 
org.apache.activemq.artemis.core.server.cluster.impl.MessageLoadBalancing
 import org.apache.activemq.artemis.nativo.jlibaio.LibaioContext;
 import org.apache.activemq.artemis.nativo.jlibaio.LibaioFile;
 import org.apache.activemq.artemis.utils.FileUtil;
+import org.apache.activemq.artemis.utils.PasswordMaskingUtil;
 import picocli.CommandLine.Command;
 import picocli.CommandLine.Option;
 
@@ -444,10 +445,24 @@ public class Create extends InstallAbstract {
       return clusterUser;
    }
 
-   private String getClusterPassword() {
+   protected void setClusterPassword(String clusterPassword) {
+      this.clusterPassword = clusterPassword;
+   }
+
+   protected String getClusterPassword() {
       if (clusterPassword == null) {
          clusterPassword = inputPassword("--cluster-password", "What is the 
cluster password?", "password-admin");
       }
+      if (!PasswordMaskingUtil.isEncMasked(clusterPassword)) {
+         try {
+            clusterPassword = 
PasswordMaskingUtil.wrap(PasswordMaskingUtil.getDefaultCodec().encode(clusterPassword));
+            getActionContext().out.println("Using masked cluster-password: " + 
clusterPassword);
+         } catch (Exception e) {
+            getActionContext().err.println("Warning: Failed to mask 
password.");
+            getActionContext().err.println("Reason: " + e.getMessage());
+            e.printStackTrace();
+         }
+      }
       return clusterPassword;
    }
 
@@ -489,7 +504,9 @@ public class Create extends InstallAbstract {
          password = inputPassword("--password", "What is the default 
password?", "admin");
       }
 
-      password = HashUtil.tryHash(getActionContext(), password);
+      if (!PasswordMaskingUtil.isEncMasked(password)) {
+         password = HashUtil.tryHash(getActionContext(), password);
+      }
 
       return password;
    }
diff --git 
a/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/InputAbstract.java
 
b/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/InputAbstract.java
index 602d81cdf3..c320be6f22 100644
--- 
a/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/InputAbstract.java
+++ 
b/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/InputAbstract.java
@@ -31,13 +31,17 @@ public class InputAbstract extends ActionAbstract {
    private static boolean inputEnabled = false;
 
    /**
-    * Test cases validating or using the CLI cannot deal with inputs, so they 
are generally disabled, however the main
-    * method from the CLI will enable it back.
+    * Test cases validating or using the CLI usually don't deal with inputs, 
so they are generally disabled, however
+    * the main method from the CLI will enable it back.
     */
    public static void enableInput() {
       inputEnabled = true;
    }
 
+   public static void disableInput() {
+      inputEnabled = false;
+   }
+
    @Option(names = "--silent", description = "Disable all the inputs, and make 
a best guess for any required input.")
    private boolean silentInput = false;
 
@@ -145,4 +149,8 @@ public class InputAbstract extends ActionAbstract {
 
       return null;
    }
+
+   public void setLineReader(InputReader lineReader) {
+      this.lineReader = lineReader;
+   }
 }
diff --git 
a/artemis-cli/src/test/java/org/apache/activemq/artemis/cli/commands/CreateTestIndividualSettings.java
 
b/artemis-cli/src/test/java/org/apache/activemq/artemis/cli/commands/CreateTestIndividualSettings.java
index 20a8868358..ddd1466ec4 100644
--- 
a/artemis-cli/src/test/java/org/apache/activemq/artemis/cli/commands/CreateTestIndividualSettings.java
+++ 
b/artemis-cli/src/test/java/org/apache/activemq/artemis/cli/commands/CreateTestIndividualSettings.java
@@ -20,14 +20,21 @@ import java.io.BufferedReader;
 import java.io.File;
 import java.io.FileReader;
 import java.io.IOException;
+import java.util.Map;
 
+import org.apache.activemq.artemis.cli.commands.util.input.InputReader;
 import org.apache.activemq.artemis.tests.extensions.TargetTempDirFactory;
+import org.apache.activemq.artemis.utils.DefaultSensitiveStringCodec;
+import org.apache.activemq.artemis.utils.PasswordMaskingUtil;
 import org.apache.activemq.artemis.utils.RandomUtil;
+import org.apache.activemq.artemis.utils.SensitiveDataCodec;
 import org.apache.activemq.cli.test.TestActionContext;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
 import org.junit.jupiter.api.io.TempDir;
+import org.mockito.Mockito;
 
+import static org.junit.jupiter.api.Assertions.assertEquals;
 import static org.junit.jupiter.api.Assertions.assertFalse;
 import static org.junit.jupiter.api.Assertions.assertTrue;
 
@@ -84,4 +91,98 @@ public class CreateTestIndividualSettings {
       }
       return false;
    }
+
+   @Test
+   public void testMaskClusterPasswordUserInput() throws Exception {
+      final String password = RandomUtil.randomUUIDString();
+
+      Create c = new Create();
+      Create.enableInput();
+      try {
+         InputReader inputReader = Mockito.mock(InputReader.class);
+         
Mockito.when(inputReader.readPassword(Mockito.anyString())).thenReturn(password);
+         c.setLineReader(inputReader);
+
+         
assertEquals(PasswordMaskingUtil.wrap(PasswordMaskingUtil.getDefaultCodec().encode(password)),
 c.getClusterPassword());
+      } finally {
+         Create.disableInput();
+      }
+   }
+
+   @Test
+   public void testMaskClusterPasswordSwitch() throws Exception {
+      final String password = RandomUtil.randomUUIDString();
+
+      Create c = new Create();
+      c.setClusterPassword(password);
+      
assertEquals(PasswordMaskingUtil.wrap(PasswordMaskingUtil.getDefaultCodec().encode(password)),
 c.getClusterPassword());
+   }
+
+   @Test
+   public void testMaskClusterPasswordAlreadyEncoded() throws Exception {
+      final String password = RandomUtil.randomUUIDString();
+      final String encodedPassword = 
PasswordMaskingUtil.wrap(PasswordMaskingUtil.getDefaultCodec().encode(password));
+
+      Create c = new Create();
+      c.setClusterPassword(encodedPassword);
+      assertEquals(encodedPassword, c.getClusterPassword());
+   }
+
+   @Test
+   public void testMaskClusterPasswordMultipleGets() throws Exception {
+      final String password = RandomUtil.randomUUIDString();
+      final String encodedPassword = 
PasswordMaskingUtil.wrap(PasswordMaskingUtil.getDefaultCodec().encode(password));
+
+      Create c = new Create();
+      c.setClusterPassword(password);
+      assertEquals(encodedPassword, c.getClusterPassword());
+      assertEquals(encodedPassword, c.getClusterPassword());
+   }
+
+   @Test
+   public void testMaskAdminPasswordUserInput() throws Exception {
+      final String password = RandomUtil.randomUUIDString();
+
+      Create c = new Create();
+      Create.enableInput();
+      try {
+         InputReader inputReader = Mockito.mock(InputReader.class);
+         
Mockito.when(inputReader.readPassword(Mockito.anyString())).thenReturn(password);
+         c.setLineReader(inputReader);
+
+         
assertTrue(PasswordMaskingUtil.getDefaultCodec(Map.of(DefaultSensitiveStringCodec.ALGORITHM,
 DefaultSensitiveStringCodec.ONE_WAY)).verify(password.toCharArray(), 
PasswordMaskingUtil.unwrap(c.getPassword())));
+      } finally {
+         Create.disableInput();
+      }
+   }
+
+   @Test
+   public void testMaskAdminPasswordSwitch() throws Exception {
+      final String password = RandomUtil.randomUUIDString();
+
+      Create c = new Create();
+      c.setPassword(password);
+      
assertTrue(PasswordMaskingUtil.getDefaultCodec(Map.of(DefaultSensitiveStringCodec.ALGORITHM,
 DefaultSensitiveStringCodec.ONE_WAY)).verify(password.toCharArray(), 
PasswordMaskingUtil.unwrap(c.getPassword())));
+   }
+
+   @Test
+   public void testMaskAdminPasswordAlreadyHashed() throws Exception {
+      final String password = RandomUtil.randomUUIDString();
+      final String hashedPassword = 
PasswordMaskingUtil.getHashProcessor().hash(password);
+
+      Create c = new Create();
+      c.setPassword(hashedPassword);
+      assertEquals(hashedPassword, c.getPassword());
+   }
+
+   @Test
+   public void testMaskAdminPasswordMultipleGets() throws Exception {
+      final String password = RandomUtil.randomUUIDString();
+      final SensitiveDataCodec<String> codec = 
PasswordMaskingUtil.getDefaultCodec(Map.of(DefaultSensitiveStringCodec.ALGORITHM,
 DefaultSensitiveStringCodec.ONE_WAY));
+
+      Create c = new Create();
+      c.setPassword(password);
+      assertTrue(codec.verify(password.toCharArray(), 
PasswordMaskingUtil.unwrap(c.getPassword())));
+      assertTrue(codec.verify(password.toCharArray(), 
PasswordMaskingUtil.unwrap(c.getPassword())));
+   }
 }
\ No newline at end of file


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to