Skip to content

Commit

Permalink
Added support to add and remove multiple keys in a single operation
Browse files Browse the repository at this point in the history
Fixed/Added empty and ASCII value validation
  • Loading branch information
edmocosta committed Nov 24, 2023
1 parent 57dc14c commit f36a46d
Show file tree
Hide file tree
Showing 4 changed files with 201 additions and 86 deletions.
8 changes: 4 additions & 4 deletions docs/static/keystore.asciidoc
Original file line number Diff line number Diff line change
Expand Up @@ -154,10 +154,10 @@ use the `add` command:

["source","sh",subs="attributes"]
----------------------------------------------------------------
bin/logstash-keystore add ES_PWD
bin/logstash-keystore add ES_USER ES_PWD
----------------------------------------------------------------

When prompted, enter a value for the key.
When prompted, enter a value for each key.

[discrete]
[[list-settings]]
Expand All @@ -174,9 +174,9 @@ bin/logstash-keystore list
[[remove-settings]]
=== Remove keys

To remove a key from the keystore, use:
To remove keys from the keystore, use:

["source","sh",subs="attributes"]
----------------------------------------------------------------
bin/logstash-keystore remove ES_PWD
bin/logstash-keystore remove ES_USER ES_PWD
----------------------------------------------------------------
2 changes: 1 addition & 1 deletion lib/secretstore/cli.rb
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ class LogStash::SecretStoreCli
LogStash::Util::SettingsHelper.post_process
secure_config = SecretStoreExt.getConfig(LogStash::SETTINGS.get_setting("keystore.file").value, LogStash::SETTINGS.get_setting("keystore.classname").value)
cli = SecretStoreCli.new(Terminal.new)
cli.command(ARGV[0], secure_config, ARGV[1])
cli.command(ARGV[0], secure_config, *ARGV[1, ARGV.length])
exit 0
rescue => e
logger.error(e.message, :cause => e.cause, :backtrace => e.backtrace)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,13 @@
import org.logstash.secret.SecretIdentifier;
import org.logstash.secret.store.*;

import java.nio.CharBuffer;
import java.nio.charset.CharsetEncoder;
import java.nio.charset.StandardCharsets;
import java.util.*;
import java.util.stream.Collectors;

import static java.util.Objects.nonNull;
import static org.logstash.secret.store.SecretStoreFactory.LOGSTASH_MARKER;

/**
Expand All @@ -34,6 +38,7 @@
*/
public class SecretStoreCli {

private static final CharsetEncoder ASCII_ENCODER = StandardCharsets.US_ASCII.newEncoder();
private final Terminal terminal;
private final SecretStoreFactory secretStoreFactory;

Expand All @@ -50,6 +55,10 @@ static Optional<Command> fromString(final String input) {
Optional<Command> command = EnumSet.allOf(Command.class).stream().filter(c -> c.option.equals(input)).findFirst();
return command;
}

String getOption() {
return option;
}
}

public SecretStoreCli(Terminal terminal){
Expand All @@ -65,13 +74,13 @@ public SecretStoreCli(Terminal terminal){
* Entry point to issue a command line command.
* @param primaryCommand The string representation of a {@link SecretStoreCli.Command}, if the String does not map to a {@link SecretStoreCli.Command}, then it will show the help menu.
* @param config The configuration needed to work a secret store. May be null for help.
* @param argument This can be either the identifier for a secret, or a sub command like --help. May be null.
* @param arguments This can be either identifiers for a secret, or a sub command like --help. May be null.
*/
public void command(String primaryCommand, SecureConfig config, String argument) {
public void command(String primaryCommand, SecureConfig config, String... arguments) {
terminal.writeLine("");
final Command command = Command.fromString(primaryCommand).orElse(Command.HELP);
final Optional<Command> sub = Command.fromString(argument);
boolean help = Command.HELP.equals(sub.orElse(null));
boolean help = nonNull(arguments) && Arrays.asList(arguments).contains(Command.HELP.getOption());

switch (command) {
case CREATE: {
if (help){
Expand Down Expand Up @@ -102,59 +111,75 @@ public void command(String primaryCommand, SecureConfig config, String argument)
}
case ADD: {
if (help){
terminal.writeLine("Adds a new secret to the keystore. For example: " +
terminal.writeLine("Add secrets to the keystore. For example: " +
"`bin/logstash-keystore add my-secret`, at the prompt enter your secret. You will use the identifier ${my-secret} in your Logstash configuration.");
return;
}
if (argument == null || argument.isEmpty()) {
terminal.writeLine("ERROR: You must supply a identifier to add. (e.g. bin/logstash-keystore add my-secret)");
if (arguments == null || arguments.length == 0) {
terminal.writeLine("ERROR: You must supply an identifier to add. (e.g. bin/logstash-keystore add my-secret)");
return;
}
if (secretStoreFactory.exists(config.clone())) {
SecretIdentifier id = new SecretIdentifier(argument);
SecretStore secretStore = secretStoreFactory.load(config);
byte[] s = secretStore.retrieveSecret(id);
if (s == null) {
terminal.write(String.format("Enter value for %s: ", argument));
char[] secret = terminal.readSecret();
if(secret == null || secret.length == 0){
terminal.writeLine("ERROR: You must supply a identifier to add. (e.g. bin/logstash-keystore add my-secret)");
return;
final SecretStore secretStore = secretStoreFactory.load(config);
for (String argument : arguments) {
final SecretIdentifier id = new SecretIdentifier(argument);
final byte[] existingValue = secretStore.retrieveSecret(id);
if (existingValue != null) {
SecretStoreUtil.clearBytes(existingValue);
terminal.write(String.format("%s already exists. Overwrite ? [y/N] ", argument));
if (!isYes(terminal.readLine())) {
continue;
}
}
add(secretStore, id, SecretStoreUtil.asciiCharToBytes(secret));
} else {
SecretStoreUtil.clearBytes(s);
terminal.write(String.format("%s already exists. Overwrite ? [y/N] ", argument));
if (isYes(terminal.readLine())) {
terminal.write(String.format("Enter value for %s: ", argument));
char[] secret = terminal.readSecret();
add(secretStore, id, SecretStoreUtil.asciiCharToBytes(secret));

final String enterValueMessage = String.format("Enter value for %s: ", argument);
char[] secret = null;
while(secret == null) {
terminal.write(enterValueMessage);
final char[] readSecret = terminal.readSecret();

if (readSecret == null || readSecret.length == 0) {
terminal.writeLine("ERROR: Value cannot be empty");
continue;
}

if (!ASCII_ENCODER.canEncode(CharBuffer.wrap(readSecret))) {
terminal.writeLine("ERROR: Value must contain only ASCII characters");
continue;
}

secret = readSecret;
}

add(secretStore, id, SecretStoreUtil.asciiCharToBytes(secret));
}
} else {
terminal.writeLine(String.format("ERROR: Logstash keystore not found. Use 'create' command to create one."));
terminal.writeLine("ERROR: Logstash keystore not found. Use 'create' command to create one.");
}
break;
}
case REMOVE: {
if (help){
terminal.writeLine("Removes a secret from the keystore. For example: " +
terminal.writeLine("Remove secrets from the keystore. For example: " +
"`bin/logstash-keystore remove my-secret`");
return;
}
if (argument == null || argument.isEmpty()) {
if (arguments == null || arguments.length == 0) {
terminal.writeLine("ERROR: You must supply a value to remove. (e.g. bin/logstash-keystore remove my-secret)");
return;
}
SecretIdentifier id = new SecretIdentifier(argument);

SecretStore secretStore = secretStoreFactory.load(config);
if (secretStore.containsSecret(id)) {
secretStore.purgeSecret(id);
terminal.writeLine(String.format("Removed '%s' from the Logstash keystore.", id.getKey()));
} else {
terminal.writeLine(String.format("ERROR: '%s' does not exist in the Logstash keystore.", argument));
final SecretStore secretStore = secretStoreFactory.load(config);
for (String argument : arguments) {
SecretIdentifier id = new SecretIdentifier(argument);
if (secretStore.containsSecret(id)) {
secretStore.purgeSecret(id);
terminal.writeLine(String.format("Removed '%s' from the Logstash keystore.", id.getKey()));
} else {
terminal.writeLine(String.format("ERROR: '%s' does not exist in the Logstash keystore.", argument));
}
}

break;
}
case HELP: {
Expand All @@ -166,8 +191,8 @@ public void command(String primaryCommand, SecureConfig config, String argument)
terminal.writeLine("--------");
terminal.writeLine("create - Creates a new Logstash keystore (e.g. bin/logstash-keystore create)");
terminal.writeLine("list - List entries in the keystore (e.g. bin/logstash-keystore list)");
terminal.writeLine("add - Add a value to the keystore (e.g. bin/logstash-keystore add my-secret)");
terminal.writeLine("remove - Remove a value from the keystore (e.g. bin/logstash-keystore remove my-secret)");
terminal.writeLine("add - Add values to the keystore (e.g. bin/logstash-keystore add secret-one secret-two)");
terminal.writeLine("remove - Remove values from the keystore (e.g. bin/logstash-keystore remove secret-one secret-two)");
terminal.writeLine("");
terminal.writeLine("Argument:");
terminal.writeLine("--------");
Expand Down
Loading

0 comments on commit f36a46d

Please sign in to comment.