Skip to content

Commit 5a5315b

Browse files
author
Ganeshwara Hananda
authored
Improve handling password input (#158)
## What is the goal of this PR? We've updated Console such that password input can be supplied interactively and non-interactively. This is done primarily for security purpose, interactive input will be the preferred one since it is not stored in the CLI history. The non-interactive mode is still relevant and therefore supported, particularly for use in automation scripts and test. Additionally, we've refactored the code to improve the terminologies. We used the word "Command" everywhere to refer to several different things which cause confusion. We've now made the terminologies more precise: - "CLI Options" is how we refer to the options specified when starting console (eg., `--username admin`) - "REPL command" is how we refer to the Console commands such as 'database create' - We've renamed the execution mode to be clear. Console has three modes of execution: "REPL mode", "script mode" and "inline command mode". The first mode is the interactive mode that most users will use. The second and the third mode are non-interactive and is suitable for use in automation scripts or tests. ## What are the changes implemented in this PR? - Add support for interactive password input - Refactored various names in the code - Improve the clarity of the different execution mode - Fix the fact that we really like the word "command" which is used everywhere - Make non-final variables final whenever possible
1 parent 9ad46c1 commit 5a5315b

File tree

7 files changed

+188
-155
lines changed

7 files changed

+188
-155
lines changed

TypeDBConsole.java

Lines changed: 78 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -31,8 +31,8 @@
3131
import com.vaticle.typedb.client.common.exception.TypeDBClientException;
3232
import com.vaticle.typedb.common.collection.Either;
3333
import com.vaticle.typedb.common.util.Java;
34-
import com.vaticle.typedb.console.command.ReplCommand;
35-
import com.vaticle.typedb.console.command.TransactionReplCommand;
34+
import com.vaticle.typedb.console.command.REPLCommand;
35+
import com.vaticle.typedb.console.command.TransactionREPLCommand;
3636
import com.vaticle.typedb.console.common.Printer;
3737
import com.vaticle.typedb.console.common.exception.TypeDBConsoleException;
3838
import com.vaticle.typeql.lang.TypeQL;
@@ -83,9 +83,9 @@ public class TypeDBConsole {
8383
"Welcome to TypeDB Console. You are now in TypeDB Wonderland!\n" +
8484
"Copyright (C) 2021 Vaticle\n";
8585
private static final Path COMMAND_HISTORY_FILE =
86-
Paths.get(System.getProperty("user.home"), ".typedb-console-command-history").toAbsolutePath();
86+
Paths.get(System.getProperty("user.home"), ".typedb-console-repl-history").toAbsolutePath();
8787
private static final Path TRANSACTION_HISTORY_FILE =
88-
Paths.get(System.getProperty("user.home"), ".typedb-console-transaction-history").toAbsolutePath();
88+
Paths.get(System.getProperty("user.home"), ".typedb-console-transaction-repl-history").toAbsolutePath();
8989
private static final Logger LOG = LoggerFactory.getLogger(TypeDBConsole.class);
9090

9191
private final Printer printer;
@@ -105,15 +105,15 @@ private TypeDBConsole(Printer printer) {
105105

106106
public static void main(String[] args) {
107107
configureAndVerifyJavaVersion();
108-
CommandLineOptions options = parseCommandLine(args);
108+
CLIOptions options = parseCLIOptions(args);
109109
TypeDBConsole console = new TypeDBConsole(new Printer(System.out, System.err));
110110
if (options.script() == null && options.commands() == null) {
111-
console.runREPL(options);
111+
console.runREPLMode(options);
112112
} else if (options.script() != null) {
113-
boolean success = console.runScript(options, options.script());
113+
boolean success = console.runScriptMode(options, options.script());
114114
if (!success) System.exit(1);
115115
} else if (options.commands() != null) {
116-
boolean success = console.runCommands(options, options.commands());
116+
boolean success = console.runInlineCommandMode(options, options.commands());
117117
if (!success) System.exit(1);
118118
}
119119
}
@@ -127,17 +127,17 @@ private static void configureAndVerifyJavaVersion() {
127127
}
128128
}
129129

130-
private static CommandLineOptions parseCommandLine(String[] args) {
131-
CommandLineOptions options = new CommandLineOptions();
132-
CommandLine command = new CommandLine(options);
130+
private static CLIOptions parseCLIOptions(String[] args) {
131+
CLIOptions options = new CLIOptions();
132+
CommandLine CLI = new CommandLine(options);
133133
try {
134-
int exitCode = command.execute(args);
134+
int exitCode = CLI.execute(args);
135135
if (exitCode == 0) {
136-
if (command.isUsageHelpRequested()) {
137-
command.usage(command.getOut());
136+
if (CLI.isUsageHelpRequested()) {
137+
CLI.usage(CLI.getOut());
138138
System.exit(0);
139-
} else if (command.isVersionHelpRequested()) {
140-
command.printVersionHelp(command.getOut());
139+
} else if (CLI.isVersionHelpRequested()) {
140+
CLI.printVersionHelp(CLI.getOut());
141141
System.exit(0);
142142
} else {
143143
return options;
@@ -146,39 +146,39 @@ private static CommandLineOptions parseCommandLine(String[] args) {
146146
System.exit(1);
147147
}
148148
} catch (CommandLine.ParameterException ex) {
149-
command.getErr().println(ex.getMessage());
150-
if (!CommandLine.UnmatchedArgumentException.printSuggestions(ex, command.getErr())) {
151-
ex.getCommandLine().usage(command.getErr());
149+
CLI.getErr().println(ex.getMessage());
150+
if (!CommandLine.UnmatchedArgumentException.printSuggestions(ex, CLI.getErr())) {
151+
ex.getCommandLine().usage(CLI.getErr());
152152
}
153153
System.exit(1);
154154
}
155155
return null;
156156
}
157157

158-
private void runREPL(CommandLineOptions options) {
158+
private void runREPLMode(CLIOptions options) {
159159
printer.info(COPYRIGHT);
160160
try (TypeDBClient client = createTypeDBClient(options)) {
161161
LineReader reader = LineReaderBuilder.builder()
162162
.terminal(terminal)
163163
.variable(LineReader.HISTORY_FILE, COMMAND_HISTORY_FILE)
164164
.build();
165165
while (true) {
166-
ReplCommand command;
166+
REPLCommand command;
167167
try {
168-
command = ReplCommand.getCommand(reader, printer, "> ", client.isCluster());
168+
command = REPLCommand.readREPLCommand(reader, printer, "> ", client.isCluster());
169169
} catch (InterruptedException e) {
170170
break;
171171
}
172172
if (command.isExit()) {
173173
break;
174174
} else if (command.isHelp()) {
175-
printer.info(ReplCommand.getHelpMenu(client));
175+
printer.info(REPLCommand.createHelpMenu(client));
176176
} else if (command.isClear()) {
177177
reader.getTerminal().puts(InfoCmp.Capability.clear_screen);
178178
} else if (command.isUserList()) {
179179
runUserList(client);
180180
} else if (command.isUserCreate()) {
181-
ReplCommand.User.Create userCommand = command.asUserCreate();
181+
REPLCommand.User.Create userCommand = command.asUserCreate();
182182
runUserCreate(client, userCommand.user(), userCommand.password());
183183
} else if (command.isUserDelete()) {
184184
runUserDelete(client, command.asUserDelete().user());
@@ -201,7 +201,7 @@ private void runREPL(CommandLineOptions options) {
201201
printer.error("The option '--any-replica' is only available in TypeDB Cluster.");
202202
continue;
203203
}
204-
boolean shouldExit = runTransactionREPL(client, database, sessionType, transactionType, typedbOptions);
204+
boolean shouldExit = transactionREPL(client, database, sessionType, transactionType, typedbOptions);
205205
if (shouldExit) break;
206206
}
207207
}
@@ -212,7 +212,7 @@ private void runREPL(CommandLineOptions options) {
212212
}
213213
}
214214

215-
private boolean runTransactionREPL(TypeDBClient client, String database, TypeDBSession.Type sessionType, TypeDBTransaction.Type transactionType, TypeDBOptions options) {
215+
private boolean transactionREPL(TypeDBClient client, String database, TypeDBSession.Type sessionType, TypeDBTransaction.Type transactionType, TypeDBOptions options) {
216216
LineReader reader = LineReaderBuilder.builder()
217217
.terminal(terminal)
218218
.variable(LineReader.HISTORY_FILE, TRANSACTION_HISTORY_FILE)
@@ -224,23 +224,23 @@ private boolean runTransactionREPL(TypeDBClient client, String database, TypeDBS
224224
try (TypeDBSession session = client.session(database, sessionType, options);
225225
TypeDBTransaction tx = session.transaction(transactionType, options)) {
226226
while (true) {
227-
Either<TransactionReplCommand, String> command;
227+
Either<TransactionREPLCommand, String> command;
228228
try {
229-
command = TransactionReplCommand.getCommand(reader, prompt.toString());
229+
command = TransactionREPLCommand.readCommand(reader, prompt.toString());
230230
} catch (InterruptedException e) {
231231
break;
232232
}
233233
if (command.isSecond()) {
234234
printer.error(command.second());
235235
continue;
236236
} else {
237-
TransactionReplCommand replCommand = command.first();
237+
TransactionREPLCommand replCommand = command.first();
238238
if (replCommand.isExit()) {
239239
return true;
240240
} else if (replCommand.isClear()) {
241241
reader.getTerminal().puts(InfoCmp.Capability.clear_screen);
242242
} else if (replCommand.isHelp()) {
243-
printer.info(TransactionReplCommand.getHelpMenu());
243+
printer.info(TransactionREPLCommand.createHelpMenu());
244244
} else if (replCommand.isCommit()) {
245245
runCommit(tx);
246246
break;
@@ -262,27 +262,27 @@ private boolean runTransactionREPL(TypeDBClient client, String database, TypeDBS
262262
return false;
263263
}
264264

265-
private boolean runScript(CommandLineOptions options, String script) {
265+
private boolean runScriptMode(CLIOptions options, String script) {
266266
String scriptLines;
267267
try {
268268
scriptLines = new String(Files.readAllBytes(Paths.get(Objects.requireNonNull(script))), StandardCharsets.UTF_8);
269269
} catch (IOException e) {
270270
printer.error("Failed to open file '" + options.script() + "'");
271271
return false;
272272
}
273-
return runCommands(options, Arrays.stream(scriptLines.split("\n")).collect(toList()));
273+
return runInlineCommandMode(options, Arrays.stream(scriptLines.split("\n")).collect(toList()));
274274
}
275275

276-
private boolean runCommands(CommandLineOptions options, List<String> commandStrings) {
277-
commandStrings = commandStrings.stream().map(x -> x.trim()).filter(x -> !x.isEmpty()).collect(toList());
276+
private boolean runInlineCommandMode(CLIOptions options, List<String> inlineCommands) {
277+
inlineCommands = inlineCommands.stream().map(x -> x.trim()).filter(x -> !x.isEmpty()).collect(toList());
278278
boolean[] cancelled = new boolean[]{false};
279279
terminal.handle(Terminal.Signal.INT, s -> cancelled[0] = true);
280280
try (TypeDBClient client = createTypeDBClient(options)) {
281281
int i = 0;
282-
for (; i < commandStrings.size() && !cancelled[0]; i++) {
283-
String commandString = commandStrings.get(i);
282+
for (; i < inlineCommands.size() && !cancelled[0]; i++) {
283+
String commandString = inlineCommands.get(i);
284284
printer.info("+ " + commandString);
285-
ReplCommand command = ReplCommand.getCommand(commandString, client.isCluster());
285+
REPLCommand command = REPLCommand.readREPLCommand(commandString, null, client.isCluster());
286286
if (command != null) {
287287
if (command.isUserList()) {
288288
boolean success = runUserList(client);
@@ -319,10 +319,10 @@ private boolean runCommands(CommandLineOptions options, List<String> commandStri
319319
}
320320
try (TypeDBSession session = client.session(database, sessionType, sessionOptions);
321321
TypeDBTransaction tx = session.transaction(transactionType)) {
322-
for (i += 1; i < commandStrings.size() && !cancelled[0]; i++) {
323-
String txCommandString = commandStrings.get(i);
322+
for (i += 1; i < inlineCommands.size() && !cancelled[0]; i++) {
323+
String txCommandString = inlineCommands.get(i);
324324
printer.info("++ " + txCommandString);
325-
Either<TransactionReplCommand, String> txCommand = TransactionReplCommand.getCommand(txCommandString);
325+
Either<TransactionREPLCommand, String> txCommand = TransactionREPLCommand.readCommand(txCommandString);
326326
if (txCommand.isSecond()) {
327327
printer.error(txCommand.second());
328328
return false;
@@ -365,7 +365,7 @@ private boolean runCommands(CommandLineOptions options, List<String> commandStri
365365
return true;
366366
}
367367

368-
private TypeDBClient createTypeDBClient(CommandLineOptions options) {
368+
private TypeDBClient createTypeDBClient(CLIOptions options) {
369369
TypeDBClient client = null;
370370
try {
371371
if (options.server() != null) {
@@ -385,7 +385,7 @@ private TypeDBClient createTypeDBClient(CommandLineOptions options) {
385385
return client;
386386
}
387387

388-
private TypeDBCredential createTypeDBCredential(CommandLineOptions options) {
388+
private TypeDBCredential createTypeDBCredential(CLIOptions options) {
389389
TypeDBCredential credential;
390390
if (options.tlsEnabled()) {
391391
String optRootCa = options.tlsRootCA();
@@ -604,50 +604,69 @@ private <T> void printCancellableResult(Stream<T> results, Consumer<T> printFn)
604604
}
605605

606606
@CommandLine.Command(name = "typedb console", mixinStandardHelpOptions = true, version = {com.vaticle.typedb.console.Version.VERSION})
607-
private static class CommandLineOptions implements Runnable {
607+
private static class CLIOptions implements Runnable {
608608

609-
@CommandLine.Option(names = {"--server"},
610-
description = "TypeDB address to which Console will connect to")
609+
@CommandLine.Option(
610+
names = {"--server"},
611+
description = "TypeDB address to which Console will connect to"
612+
)
611613
private @Nullable
612614
String server;
613615

614-
@CommandLine.Option(names = {"--cluster"},
615-
description = "TypeDB Cluster address to which Console will connect to")
616+
@CommandLine.Option(
617+
names = {"--cluster"},
618+
description = "TypeDB Cluster address to which Console will connect to"
619+
)
616620
private @Nullable
617621
String cluster;
618622

619-
@CommandLine.Option(names = {"--username"},
620-
description = "Username")
623+
@CommandLine.Option(names = {"--username"}, description = "Username")
621624
private @Nullable
622625
String username;
623626

624-
@CommandLine.Option(names = {"--password"},
625-
description = "Password")
627+
@CommandLine.Option(
628+
names = {"--password"},
629+
description = "Password",
630+
prompt = "Enter password:",
631+
interactive = true,
632+
arity = "0..1"
633+
)
626634
private @Nullable
627635
String password;
628636

629-
@CommandLine.Option(names = {"--tls-enabled"},
630-
description = "Whether to connect to TypeDB Cluster with TLS encryption")
637+
@CommandLine.Option(
638+
names = {"--tls-enabled"},
639+
description = "Whether to connect to TypeDB Cluster with TLS encryption"
640+
)
631641
private boolean tlsEnabled;
632642

633-
@CommandLine.Option(names = {"--tls-root-ca"},
634-
description = "Path to the TLS root CA file")
643+
@CommandLine.Option(
644+
names = {"--tls-root-ca"},
645+
description = "Path to the TLS root CA file"
646+
)
635647
private @Nullable
636648
String tlsRootCA;
637649

638-
@CommandLine.Option(names = {"--script"},
639-
description = "Script with commands to run in the Console, without interactive mode")
650+
@CommandLine.Option(
651+
names = {"--script"},
652+
description = "Script with commands to run in the Console, without interactive mode"
653+
)
640654
private @Nullable
641655
String script;
642656

643-
@CommandLine.Option(names = {"--command"},
644-
description = "Commands to run in the Console, without interactive mode")
657+
@CommandLine.Option(
658+
names = {"--command"},
659+
description = "Commands to run in the Console, without interactive mode"
660+
)
645661
private @Nullable
646662
List<String> commands;
647663

648664
@CommandLine.Spec
649665
CommandLine.Model.CommandSpec spec;
650666

667+
private CLIOptions() {
668+
}
669+
651670
@Override
652671
public void run() {
653672
validateAddress();

0 commit comments

Comments
 (0)