-
Notifications
You must be signed in to change notification settings - Fork 3.5k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add support to add and remove multiple keystore keys in a single operation #15612
Merged
edmocosta
merged 3 commits into
elastic:main
from
edmocosta:add-keystore-multi-operation-support
Nov 30, 2023
Merged
Changes from 1 commit
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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; | ||
|
||
/** | ||
|
@@ -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; | ||
|
||
|
@@ -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){ | ||
|
@@ -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){ | ||
|
@@ -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); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Wondering if it's really needed? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I prefer to keep it to avoid jvm dump leak the value |
||
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: { | ||
|
@@ -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("--------"); | ||
|
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's add a note to remind user that the value should only be ASCII.