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 32f70c1c89a..1dc72c3b193 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.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 @@ private String getClusterUser() { 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 String getPassword() { 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 602d81cdf30..c320be6f22b 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 Object execute(ActionContext context) throws Exception { 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 20a88683589..ddd1466ec49 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.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 @@ private boolean fileContains(File file, String search) { } 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 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