diff --git a/docs/Reference/Pantheon-CLI-Syntax.md b/docs/Reference/Pantheon-CLI-Syntax.md index 7899aa55b2..b26e198b6b 100644 --- a/docs/Reference/Pantheon-CLI-Syntax.md +++ b/docs/Reference/Pantheon-CLI-Syntax.md @@ -926,14 +926,18 @@ $ pantheon public-key export --to=/home/me/me_project/not_precious_pub_key Exports node public key to the specified file. -### password-hash +### password + +This command provides password related actions. + +#### hash This command generates the hash of a given password. ```bash tab="Syntax" -$ pantheon password-hash +$ pantheon password hash --password= ``` ```bash tab="Example" -$ pantheon password-hash "password123" +$ pantheon password hash --password=myPassword123 ``` \ No newline at end of file diff --git a/pantheon/src/main/java/tech/pegasys/pantheon/cli/PasswordSubCommand.java b/pantheon/src/main/java/tech/pegasys/pantheon/cli/PasswordSubCommand.java index 7bc95ae877..5bc8e93566 100644 --- a/pantheon/src/main/java/tech/pegasys/pantheon/cli/PasswordSubCommand.java +++ b/pantheon/src/main/java/tech/pegasys/pantheon/cli/PasswordSubCommand.java @@ -12,45 +12,71 @@ */ package tech.pegasys.pantheon.cli; +import static com.google.common.base.Preconditions.checkNotNull; import static tech.pegasys.pantheon.cli.PasswordSubCommand.COMMAND_NAME; +import tech.pegasys.pantheon.cli.PasswordSubCommand.HashSubCommand; + import java.io.PrintStream; import org.springframework.security.crypto.bcrypt.BCrypt; import picocli.CommandLine.Command; import picocli.CommandLine.Model.CommandSpec; -import picocli.CommandLine.Parameters; +import picocli.CommandLine.Option; import picocli.CommandLine.ParentCommand; import picocli.CommandLine.Spec; @Command( name = COMMAND_NAME, - description = "This command generates the hash of a given password.", - mixinStandardHelpOptions = true) + description = "This command provides password related actions.", + mixinStandardHelpOptions = true, + subcommands = {HashSubCommand.class}) class PasswordSubCommand implements Runnable { - static final String COMMAND_NAME = "password-hash"; + static final String COMMAND_NAME = "password"; @SuppressWarnings("unused") @ParentCommand - private PantheonCommand parentCommand; // Picocli injects reference to parent command + private PantheonCommand parentCommand; @SuppressWarnings("unused") @Spec - private CommandSpec spec; // Picocli injects reference to command spec + private CommandSpec spec; final PrintStream out; - @SuppressWarnings("FieldMustBeFinal") - @Parameters(arity = "1..1", description = "The password input") - private String password = null; - PasswordSubCommand(final PrintStream out) { this.out = out; } @Override public void run() { - out.print(BCrypt.hashpw(password, BCrypt.gensalt())); + spec.commandLine().usage(out); + } + + @Command( + name = "hash", + description = "This command generates the hash of a given password.", + mixinStandardHelpOptions = true) + static class HashSubCommand implements Runnable { + + @SuppressWarnings("FieldMustBeFinal") + @Option( + names = "--password", + arity = "1..1", + required = true, + description = "The password input") + private String password = null; + + @SuppressWarnings("unused") + @ParentCommand + private PasswordSubCommand parentCommand; + + @Override + public void run() { + checkNotNull(parentCommand); + + parentCommand.out.print(BCrypt.hashpw(password, BCrypt.gensalt())); + } } } diff --git a/pantheon/src/test/java/tech/pegasys/pantheon/cli/PasswordSubCommandTest.java b/pantheon/src/test/java/tech/pegasys/pantheon/cli/PasswordSubCommandTest.java index 1bcaa3d916..e389e3527f 100644 --- a/pantheon/src/test/java/tech/pegasys/pantheon/cli/PasswordSubCommandTest.java +++ b/pantheon/src/test/java/tech/pegasys/pantheon/cli/PasswordSubCommandTest.java @@ -20,37 +20,39 @@ public class PasswordSubCommandTest extends CommandTestAbstract { @Test - public void passwordHashSubCommandExists() { + public void passwordSubCommandExistAnbHaveSubCommands() { CommandSpec spec = parseCommand(); - assertThat(spec.subcommands()).containsKeys("password-hash"); + assertThat(spec.subcommands()).containsKeys("password"); + assertThat(spec.subcommands().get("password").getSubcommands()).containsKeys("hash"); assertThat(commandOutput.toString()).isEmpty(); assertThat(commandErrorOutput.toString()).isEmpty(); } @Test - public void callingPasswordHashWithoutPasswordParameterMustDisplayUsage() { - final String expectedUsage = - "Missing required parameter: " - + System.lineSeparator() - + "Usage: pantheon password-hash [-hV] " - + System.lineSeparator() - + "This command generates the hash of a given password." - + System.lineSeparator() - + " The password input" - + System.lineSeparator() - + " -h, --help Show this help message and exit." - + System.lineSeparator() - + " -V, --version Print version information and exit." - + System.lineSeparator(); - - parseCommand("password-hash"); + public void passwordSubCommandExists() { + CommandSpec spec = parseCommand(); + parseCommand("password"); + + assertThat(commandOutput.toString()).contains("This command provides password related actions"); + assertThat(commandErrorOutput.toString()).isEmpty(); + } + + @Test + public void passwordHashSubCommandExist() { + parseCommand("password", "hash"); + assertThat(commandOutput.toString()).isEmpty(); - assertThat(commandErrorOutput.toString()).startsWith(expectedUsage); + assertThat(commandErrorOutput.toString()) + .contains("Missing required option '--password='"); + assertThat(commandErrorOutput.toString()) + .contains("Usage: pantheon password hash [-hV] --password="); + assertThat(commandErrorOutput.toString()) + .contains("This command generates the hash of a given password"); } @Test - public void publicKeySubCommandExistAnbHaveSubCommands() { - parseCommand("password-hash", "foo"); + public void passwordHashSubCommandHashesPassword() { + parseCommand("password", "hash", "--password", "foo"); // we can't predict the final value so we are only checking if it starts with the hash marker assertThat(commandOutput.toString()).startsWith("$2");