Skip to content
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

IGNITE-23459 Add console input password if --user argument presen… #11604

Merged
merged 16 commits into from
Feb 6, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
16 commits
Select commit Hold shift + click to select a range
411f817
IGNITE-23459 Add console input if password argument presented without…
Oct 17, 2024
a8d5ee0
IGNITE-23749 Removed redundant permission check to cancel services fr…
EAFomin Jan 29, 2025
dbf1d5c
IGNITE-24093 Remove PARTIAL_COUNTERS_MAP_SINCE (#11820)
maksaska Jan 29, 2025
a5a3785
IGNITE-24101 Remove SEPARATE_CACHE_PER_NON_COLLOCATED_SET_SINCE (#11810)
vladnovoren Jan 29, 2025
ab74d03
IGNITE-24323 SQL Calcite: Add query blocking tasks executor (allows t…
alex-plekhanov Jan 30, 2025
9147c1c
IGNITE-24336 SQL Calcite: Add support of user-defined table functions…
Vladsz83 Jan 31, 2025
45095bf
IGNITE-23414 SQL Calcite: Fix cast of dynamic parameters - Fixes #11787.
Vladsz83 Jan 31, 2025
509e955
IGNITE-24362 Fix CacheExchangeMessageDuplicatedStateTest - Fixes #11842.
Jan 31, 2025
1a0534f
IGNITE-24348 Provide custom FrameworkConfig to SQL views (#11839)
timoninmaxim Jan 31, 2025
9617b1c
IGNITE-24102 Remove VERSION_SINCE_CLIENT_COULD_WAIT_TO_CONNECT (#11821)
maksaska Feb 3, 2025
d9409b7
IGNITE-24130 Move binaryMetadata, marshaller directories logic to Nod…
nizhikov Feb 3, 2025
e762d30
IGNITE-23459 Add console input if password argument presented without…
Feb 4, 2025
17d0396
Merge remote-tracking branch 'origin/master' into ignite-23459
Feb 4, 2025
a44606a
IGNITE-23459 Add console input if password argument presented without…
Feb 5, 2025
2af604b
Merge remote-tracking branch 'origin/master' into ignite-23459
Feb 6, 2025
ba4b59d
IGNITE-23459 Add console input if password argument presented without…
Feb 6, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -15,16 +15,15 @@
* limitations under the License.
*/


package org.apache.ignite.internal.commandline;

import java.lang.reflect.Field;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Deque;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;
import java.util.Set;
import java.util.function.BiConsumer;
import java.util.function.BiFunction;
Expand All @@ -42,15 +41,17 @@
import org.apache.ignite.internal.management.api.CommandUtils;
import org.apache.ignite.internal.management.api.CommandsRegistry;
import org.apache.ignite.internal.management.api.Positional;
import org.apache.ignite.internal.util.typedef.internal.SB;
import org.apache.ignite.internal.util.typedef.internal.U;
import org.apache.ignite.lang.IgniteExperimental;
import org.apache.ignite.ssl.SslContextFactory;

import static org.apache.ignite.IgniteSystemProperties.IGNITE_ENABLE_EXPERIMENTAL_COMMAND;
import static org.apache.ignite.internal.commandline.CommandHandler.DFLT_HOST;
import static org.apache.ignite.internal.commandline.CommandHandler.DFLT_PORT;
import static org.apache.ignite.internal.commandline.CommandHandler.UTILITY_NAME;
import static org.apache.ignite.internal.commandline.argument.parser.CLIArgument.optionalArg;
import static org.apache.ignite.internal.commandline.argument.parser.CLIArgument.CLIArgumentBuilder.argument;
import static org.apache.ignite.internal.commandline.argument.parser.CLIArgument.CLIArgumentBuilder.optionalArgument;
import static org.apache.ignite.internal.commandline.argument.parser.CLIArgumentParser.readNextValueToken;
import static org.apache.ignite.internal.management.api.CommandUtils.CMD_WORDS_DELIM;
import static org.apache.ignite.internal.management.api.CommandUtils.NAME_PREFIX;
import static org.apache.ignite.internal.management.api.CommandUtils.PARAM_WORDS_DELIM;
Expand All @@ -62,7 +63,9 @@
import static org.apache.ignite.internal.management.api.CommandUtils.toFormattedCommandName;
import static org.apache.ignite.internal.management.api.CommandUtils.toFormattedFieldName;
import static org.apache.ignite.internal.management.api.CommandUtils.visitCommandParams;
import static org.apache.ignite.ssl.SslContextFactory.DFLT_KEY_ALGORITHM;
import static org.apache.ignite.ssl.SslContextFactory.DFLT_SSL_PROTOCOL;
import static org.apache.ignite.ssl.SslContextFactory.DFLT_STORE_TYPE;

/**
* Argument parser.
Expand Down Expand Up @@ -131,9 +134,6 @@ public class ArgumentParser {
/** */
static final String CMD_SSL_FACTORY = "--ssl-factory";

/** Set of sensitive arguments */
private static final Set<String> SENSITIVE_ARGUMENTS = new HashSet<>();

/** */
private static final BiConsumer<String, Integer> PORT_VALIDATOR = (name, val) -> {
if (val <= 0 || val > 65535)
Expand All @@ -143,64 +143,46 @@ public class ArgumentParser {
/** */
private final List<CLIArgument<?>> common = new ArrayList<>();

static {
SENSITIVE_ARGUMENTS.add(CMD_PASSWORD);
SENSITIVE_ARGUMENTS.add(CMD_KEYSTORE_PASSWORD);
SENSITIVE_ARGUMENTS.add(CMD_TRUSTSTORE_PASSWORD);
}

/**
* @param arg To check.
* @return True if provided argument is among sensitive one and not should be displayed.
*/
public static boolean isSensitiveArgument(String arg) {
return SENSITIVE_ARGUMENTS.contains(arg);
}
/** Console instance. */
protected final GridConsole console;

/**
* @param log Logger.
* @param registry Supported commands.
* @param console Console.
*/
public ArgumentParser(IgniteLogger log, IgniteCommandRegistry registry) {
public ArgumentParser(IgniteLogger log, IgniteCommandRegistry registry, GridConsole console) {
this.log = log;
this.registry = registry;

BiConsumer<String, ?> securityWarn = (name, val) -> log.info(String.format("Warning: %s is insecure. " +
"Whenever possible, use interactive prompt for password (just discard %s option).", name, name));

arg(CMD_HOST, "HOST_OR_IP", String.class, DFLT_HOST);
arg(CMD_PORT, "PORT", Integer.class, DFLT_PORT, PORT_VALIDATOR);
arg(CMD_USER, "USER", String.class, null);
arg(CMD_PASSWORD, "PASSWORD", String.class, null, (BiConsumer<String, String>)securityWarn);
arg(CMD_VERBOSE, CMD_VERBOSE, boolean.class, false);
arg(CMD_SSL_PROTOCOL, "SSL_PROTOCOL[, SSL_PROTOCOL_2, ..., SSL_PROTOCOL_N]", String[].class, new String[] {DFLT_SSL_PROTOCOL});
arg(CMD_SSL_CIPHER_SUITES, "SSL_CIPHER_1[, SSL_CIPHER_2, ..., SSL_CIPHER_N]", String[].class, null);
arg(CMD_SSL_KEY_ALGORITHM, "SSL_KEY_ALGORITHM", String.class, SslContextFactory.DFLT_KEY_ALGORITHM);
arg(CMD_SSL_FACTORY, "SSL_FACTORY_PATH", String.class, null);
arg(CMD_KEYSTORE_TYPE, "KEYSTORE_TYPE", String.class, SslContextFactory.DFLT_STORE_TYPE);
arg(CMD_KEYSTORE, "KEYSTORE_PATH", String.class, null);
arg(CMD_KEYSTORE_PASSWORD, "KEYSTORE_PASSWORD", char[].class, null, (BiConsumer<String, char[]>)securityWarn);
arg(CMD_TRUSTSTORE_TYPE, "TRUSTSTORE_TYPE", String.class, SslContextFactory.DFLT_STORE_TYPE);
arg(CMD_TRUSTSTORE, "TRUSTSTORE_PATH", String.class, null);
arg(CMD_TRUSTSTORE_PASSWORD, "TRUSTSTORE_PASSWORD", char[].class, null, (BiConsumer<String, char[]>)securityWarn);
arg(CMD_AUTO_CONFIRMATION, CMD_AUTO_CONFIRMATION, boolean.class, false);
arg(
CMD_ENABLE_EXPERIMENTAL,
CMD_ENABLE_EXPERIMENTAL, Boolean.class,
IgniteSystemProperties.getBoolean(IGNITE_ENABLE_EXPERIMENTAL_COMMAND)
this.console = console;

common.addAll(List.of(
optionalArgument(CMD_HOST, String.class).withUsage("HOST_OR_IP").withDefault(DFLT_HOST).build(),
optionalArgument(CMD_PORT, Integer.class).withUsage("PORT").withDefault(DFLT_PORT).withValidator(PORT_VALIDATOR).build(),
optionalArgument(CMD_USER, String.class).withUsage("USER").build(),
optionalArgument(CMD_PASSWORD, String.class).withUsage("PASSWORD").markSensitive().build(),
optionalArgument(CMD_VERBOSE, boolean.class).withUsage(CMD_VERBOSE).withDefault(false).build(),
optionalArgument(CMD_SSL_PROTOCOL, String[].class)
.withUsage("SSL_PROTOCOL[, SSL_PROTOCOL_2, ..., SSL_PROTOCOL_N]")
.withDefault(t -> new String[] {DFLT_SSL_PROTOCOL})
.build(),
optionalArgument(CMD_SSL_CIPHER_SUITES, String[].class).withUsage("SSL_CIPHER_1[, SSL_CIPHER_2, ..., SSL_CIPHER_N]").build(),
optionalArgument(CMD_SSL_KEY_ALGORITHM, String.class).withUsage("SSL_KEY_ALGORITHM").withDefault(DFLT_KEY_ALGORITHM).build(),
optionalArgument(CMD_SSL_FACTORY, String.class).withUsage("SSL_FACTORY_PATH").build(),
optionalArgument(CMD_KEYSTORE_TYPE, String.class).withUsage("KEYSTORE_TYPE").withDefault(DFLT_STORE_TYPE).build(),
optionalArgument(CMD_KEYSTORE, String.class).withUsage("KEYSTORE_PATH").build(),
optionalArgument(CMD_KEYSTORE_PASSWORD, char[].class).withUsage("KEYSTORE_PASSWORD").markSensitive().build(),
optionalArgument(CMD_TRUSTSTORE_TYPE, String.class).withUsage("TRUSTSTORE_TYPE").withDefault(DFLT_STORE_TYPE).build(),
optionalArgument(CMD_TRUSTSTORE, String.class).withUsage("TRUSTSTORE_PATH").build(),
optionalArgument(CMD_TRUSTSTORE_PASSWORD, char[].class).withUsage("TRUSTSTORE_PASSWORD").markSensitive().build(),
optionalArgument(CMD_AUTO_CONFIRMATION, boolean.class).withUsage(CMD_AUTO_CONFIRMATION).withDefault(false).build(),
optionalArgument(CMD_ENABLE_EXPERIMENTAL, Boolean.class)
.withUsage(CMD_ENABLE_EXPERIMENTAL)
.withDefault(t -> IgniteSystemProperties.getBoolean(IGNITE_ENABLE_EXPERIMENTAL_COMMAND))
.build())
);
}

/** */
private <T> void arg(String name, String usage, Class<T> type, T dflt, BiConsumer<String, T> validator) {
common.add(optionalArg(name, usage, type, t -> dflt, validator));
}

/** */
private <T> void arg(String name, String usage, Class<T> type, T dflt) {
common.add(optionalArg(name, usage, type, () -> dflt));
}

/**
* Creates list of common utility options.
*
Expand Down Expand Up @@ -236,7 +218,9 @@ public <A extends IgniteDataTransferObject> ConnectionAndSslParameters<A> parseA

CLIArgumentParser parser = createArgumentParser();

parser.parse(args.iterator());
parser.parse(args.listIterator());

String safeCmd = buildSafeCommandString(raw.listIterator(), parser);

A arg = (A)argument(
cmdPath.peek().argClass(),
Expand All @@ -253,7 +237,7 @@ public <A extends IgniteDataTransferObject> ConnectionAndSslParameters<A> parseA
throw new IllegalArgumentException("Experimental commands disabled");
}

return new ConnectionAndSslParameters<>(cmdPath, arg, parser);
return new ConnectionAndSslParameters<>(cmdPath, arg, parser, safeCmd);
}

/**
Expand Down Expand Up @@ -323,14 +307,11 @@ private CLIArgumentParser createArgumentParser() {
List<CLIArgument<?>> positionalArgs = new ArrayList<>();
List<CLIArgument<?>> namedArgs = new ArrayList<>();

BiFunction<Field, Boolean, CLIArgument<?>> toArg = (fld, optional) -> new CLIArgument<>(
toFormattedFieldName(fld).toLowerCase(),
null,
optional,
fld.getType(),
null,
(name, val) -> {}
);
BiFunction<Field, Boolean, CLIArgument<?>> toArg =
(fld, optional) -> argument(toFormattedFieldName(fld).toLowerCase(), fld.getType())
.withOptional(optional)
.withSensitive(fld.getAnnotation(Argument.class).sensitive())
.build();

List<Set<String>> grpdFlds = CommandUtils.argumentGroupsValues(cmdPath.peek().argClass());

Expand All @@ -339,14 +320,10 @@ private CLIArgumentParser createArgumentParser() {
|| fld.getAnnotation(Argument.class).optional())
);

Consumer<Field> positionalArgCb = fld -> positionalArgs.add(new CLIArgument<>(
fld.getName().toLowerCase(),
null,
fld.getAnnotation(Argument.class).optional(),
fld.getType(),
null,
(name, val) -> {}
));
Consumer<Field> positionalArgCb = fld -> positionalArgs.add(argument(fld.getName().toLowerCase(), fld.getType())
.withOptional(fld.getAnnotation(Argument.class).optional())
.build()
);

BiConsumer<ArgumentGroup, List<Field>> argGrpCb = (argGrp0, flds) -> flds.forEach(fld -> {
if (fld.isAnnotationPresent(Positional.class))
Expand All @@ -359,6 +336,37 @@ private CLIArgumentParser createArgumentParser() {

namedArgs.addAll(common);

return new CLIArgumentParser(positionalArgs, namedArgs);
return new CLIArgumentParser(positionalArgs, namedArgs, console);
}

/** @return String representation of command with hidden values of sensitive arguments. */
private String buildSafeCommandString(ListIterator<String> rawIter, CLIArgumentParser parser) {
SB res = new SB();

while (rawIter.hasNext()) {
String arg = rawIter.next();

CLIArgument<?> argDesc = parser.getArgumentDescriptor(arg.toLowerCase());

res.a(arg).a(' ');

if (argDesc == null || argDesc.isFlag())
continue;

String argVal = readNextValueToken(rawIter);

if (argVal != null) {
if (!argDesc.isSensitive())
res.a(argVal).a(' ');
else {
res.a("***** ");

log.info(String.format("Warning: %s is insecure. Whenever possible, use interactive prompt for " +
"password (just omit the argument value).", argDesc.name()));
}
}
}

return res.toString();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,6 @@
import org.apache.ignite.internal.util.spring.IgniteSpringHelperImpl;
import org.apache.ignite.internal.util.typedef.F;
import org.apache.ignite.internal.util.typedef.X;
import org.apache.ignite.internal.util.typedef.internal.SB;
import org.apache.ignite.internal.util.typedef.internal.U;
import org.apache.ignite.lang.IgniteExperimental;
import org.apache.ignite.ssl.SslContextFactory;
Expand Down Expand Up @@ -247,7 +246,7 @@ public <A extends IgniteDataTransferObject> int execute(List<String> rawArgs) {

verbose = F.exist(rawArgs, CMD_VERBOSE::equalsIgnoreCase);

ConnectionAndSslParameters<A> args = new ArgumentParser(logger, registry).parseAndValidate(rawArgs);
ConnectionAndSslParameters<A> args = new ArgumentParser(logger, registry, console).parseAndValidate(rawArgs);

cmdName = toFormattedCommandName(args.cmdPath().peekLast().getClass()).toUpperCase();

Expand All @@ -272,7 +271,7 @@ public <A extends IgniteDataTransferObject> int execute(List<String> rawArgs) {
}

logger.info("Command [" + cmdName + "] started");
logger.info("Arguments: " + argumentsToString(rawArgs));
logger.info("Arguments: " + args.safeCommandString());
logger.info(U.DELIM);

String deprecationMsg = args.command().deprecationMessage(args.commandArg());
Expand Down Expand Up @@ -445,36 +444,6 @@ private boolean isConnectionClosedSilentlyException(Throwable e) {
return e instanceof ClientConnectionException && e.getMessage().startsWith("Channel is closed");
}

/**
* Joins user's arguments and hides sensitive information.
*
* @param rawArgs Arguments which user has provided.
* @return String which could be shown in console and pritned to log.
*/
private String argumentsToString(List<String> rawArgs) {
boolean hide = false;

SB sb = new SB();

for (int i = 0; i < rawArgs.size(); i++) {
if (hide) {
sb.a("***** ");

hide = false;

continue;
}

String arg = rawArgs.get(i);

sb.a(arg).a(' ');

hide = ArgumentParser.isSensitiveArgument(arg);
}

return sb.toString();
}

/**
* @param args Common arguments.
* @return Thin client configuration to connect to cluster.
Expand Down Expand Up @@ -651,7 +620,8 @@ private void printHelp(List<String> rawArgs) {
"The command has the following syntax:");
logger.info("");

logger.info(INDENT + join(" ", join(" ", UTILITY_NAME, join(" ", new ArgumentParser(logger, registry).getCommonOptions())),
logger.info(INDENT + join(" ",
join(" ", UTILITY_NAME, join(" ", new ArgumentParser(logger, registry, null).getCommonOptions())),
asOptional("command", true), "<command_parameters>"));
logger.info("");
logger.info("");
Expand Down Expand Up @@ -717,8 +687,8 @@ private void printCacheHelpHeader(IgniteLogger logger) {
logger.info(INDENT + "The '--cache subcommand' is used to get information about and perform actions" +
" with caches. The command has the following syntax:");
logger.info("");
logger.info(INDENT + join(" ", UTILITY_NAME, join(" ", new ArgumentParser(logger, null).getCommonOptions())) + " " +
"--cache [subcommand] <subcommand_parameters>");
logger.info(INDENT + join(" ", UTILITY_NAME, join(" ", new ArgumentParser(logger, null, null).getCommonOptions())) +
" --cache [subcommand] <subcommand_parameters>");
logger.info("");
logger.info(INDENT + "The subcommands that take [nodeId] as an argument ('list', 'find_garbage', " +
"'contention' and 'validate_indexes') will be executed on the given node or on all server nodes" +
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,19 +65,25 @@ public class ConnectionAndSslParameters<A extends IgniteDataTransferObject> {
/** */
private final CLIArgumentParser parser;

/** String representation of command with hidden values of sensitive arguments. */
private final String safeCmd;

/**
* @param cmdPath Path to the command in {@link CommandsRegistry} hierarchy.
* @param arg Command argument.
* @param parser CLI arguments parser.
* @param safeCmd String safe representation of command.
*/
public ConnectionAndSslParameters(
Deque<Command<?, ?>> cmdPath,
A arg,
CLIArgumentParser parser
CLIArgumentParser parser,
String safeCmd
) {
this.cmdPath = cmdPath;
this.arg = arg;
this.parser = parser;
this.safeCmd = safeCmd;

this.user = parser.get(CMD_USER);
this.pwd = parser.get(CMD_PASSWORD);
Expand Down Expand Up @@ -249,4 +255,9 @@ public String sslFactoryConfigPath() {
public boolean verbose() {
return parser.get(CMD_VERBOSE);
}

/** @return String representation of command with hidden values of sensitive arguments. */
public String safeCommandString() {
return safeCmd;
}
}
Loading
Loading