Resolve paths from the current working directory instead of the config directory (elastic/x-pack-elasticsearch#637)

This commit changes the resolution of the output and input files so that relative paths will be resolved from the
current working directory instead of the x-pack config directory.

relates elastic/x-pack-elasticsearch#621

Original commit: elastic/x-pack-elasticsearch@bbfd83c2d5
This commit is contained in:
Jay Modi 2017-04-12 10:18:56 -04:00 committed by GitHub
parent 253340a597
commit 666e87c29b
2 changed files with 33 additions and 26 deletions

View File

@ -19,6 +19,8 @@ import org.elasticsearch.cli.EnvironmentAwareCommand;
import org.elasticsearch.cli.Terminal; import org.elasticsearch.cli.Terminal;
import org.elasticsearch.common.ParseField; import org.elasticsearch.common.ParseField;
import org.elasticsearch.common.Strings; import org.elasticsearch.common.Strings;
import org.elasticsearch.common.SuppressForbidden;
import org.elasticsearch.common.io.PathUtils;
import org.elasticsearch.common.network.InetAddresses; import org.elasticsearch.common.network.InetAddresses;
import org.elasticsearch.common.util.set.Sets; import org.elasticsearch.common.util.set.Sets;
import org.elasticsearch.common.xcontent.ConstructingObjectParser; import org.elasticsearch.common.xcontent.ConstructingObjectParser;
@ -27,7 +29,6 @@ import org.elasticsearch.common.xcontent.ObjectParser;
import org.elasticsearch.common.xcontent.XContentParser; import org.elasticsearch.common.xcontent.XContentParser;
import org.elasticsearch.common.xcontent.XContentType; import org.elasticsearch.common.xcontent.XContentType;
import org.elasticsearch.env.Environment; import org.elasticsearch.env.Environment;
import org.elasticsearch.xpack.XPackPlugin;
import javax.security.auth.x500.X500Principal; import javax.security.auth.x500.X500Principal;
import java.io.IOException; import java.io.IOException;
@ -138,11 +139,11 @@ public class CertificateTool extends EnvironmentAwareCommand {
protected void execute(Terminal terminal, OptionSet options, Environment env) throws Exception { protected void execute(Terminal terminal, OptionSet options, Environment env) throws Exception {
final boolean csrOnly = options.has(csrSpec); final boolean csrOnly = options.has(csrSpec);
printIntro(terminal, csrOnly); printIntro(terminal, csrOnly);
final Path outputFile = getOutputFile(terminal, outputPathSpec.value(options), env, csrOnly ? DEFAULT_CSR_FILE : DEFAULT_CERT_FILE); final Path outputFile = getOutputFile(terminal, outputPathSpec.value(options), csrOnly ? DEFAULT_CSR_FILE : DEFAULT_CERT_FILE);
final String inputFile = inputFileSpec.value(options); final String inputFile = inputFileSpec.value(options);
final int keysize = options.has(keysizeSpec) ? keysizeSpec.value(options) : DEFAULT_KEY_SIZE; final int keysize = options.has(keysizeSpec) ? keysizeSpec.value(options) : DEFAULT_KEY_SIZE;
if (csrOnly) { if (csrOnly) {
Collection<CertificateInformation> certificateInformations = getCertificateInformationList(terminal, inputFile, env); Collection<CertificateInformation> certificateInformations = getCertificateInformationList(terminal, inputFile);
generateAndWriteCsrs(outputFile, certificateInformations, keysize); generateAndWriteCsrs(outputFile, certificateInformations, keysize);
} else { } else {
final String dn = options.has(caDnSpec) ? caDnSpec.value(options) : AUTO_GEN_CA_DN; final String dn = options.has(caDnSpec) ? caDnSpec.value(options) : AUTO_GEN_CA_DN;
@ -151,7 +152,7 @@ public class CertificateTool extends EnvironmentAwareCommand {
final int days = options.hasArgument(daysSpec) ? daysSpec.value(options) : DEFAULT_DAYS; final int days = options.hasArgument(daysSpec) ? daysSpec.value(options) : DEFAULT_DAYS;
CAInfo caInfo = getCAInfo(terminal, dn, caCertPathSpec.value(options), caKeyPathSpec.value(options), keyPass, prompt, env, CAInfo caInfo = getCAInfo(terminal, dn, caCertPathSpec.value(options), caKeyPathSpec.value(options), keyPass, prompt, env,
keysize, days); keysize, days);
Collection<CertificateInformation> certificateInformations = getCertificateInformationList(terminal, inputFile, env); Collection<CertificateInformation> certificateInformations = getCertificateInformationList(terminal, inputFile);
generateAndWriteSignedCertificates(outputFile, certificateInformations, caInfo, keysize, days); generateAndWriteSignedCertificates(outputFile, certificateInformations, caInfo, keysize, days);
} }
printConclusion(terminal, csrOnly, outputFile); printConclusion(terminal, csrOnly, outputFile);
@ -171,35 +172,38 @@ public class CertificateTool extends EnvironmentAwareCommand {
* *
* @param terminal terminal to communicate with a user * @param terminal terminal to communicate with a user
* @param outputPath user specified output file, may be {@code null} * @param outputPath user specified output file, may be {@code null}
* @param env the environment for this tool to resolve files with
* @return a {@link Path} to the output file * @return a {@link Path} to the output file
*/ */
static Path getOutputFile(Terminal terminal, String outputPath, Environment env, String defaultFilename) throws IOException { static Path getOutputFile(Terminal terminal, String outputPath, String defaultFilename) throws IOException {
Path file; Path file;
if (outputPath != null) { if (outputPath != null) {
file = XPackPlugin.resolveConfigFile(env, Strings.cleanPath(outputPath)); file = resolvePath(outputPath);
} else { } else {
file = XPackPlugin.resolveConfigFile(env, defaultFilename); file = resolvePath(defaultFilename);
String input = terminal.readText("Please enter the desired output file [" + file + "]: "); String input = terminal.readText("Please enter the desired output file [" + file + "]: ");
if (input.isEmpty() == false) { if (input.isEmpty() == false) {
file = XPackPlugin.resolveConfigFile(env, Strings.cleanPath(input)); file = resolvePath(input);
} }
} }
return file; return file;
} }
@SuppressForbidden(reason = "resolve paths against CWD for a CLI tool")
private static Path resolvePath(String pathStr) {
return PathUtils.get(Strings.cleanPath(pathStr)).toAbsolutePath();
}
/** /**
* This method handles the collection of information about each instance that is necessary to generate a certificate. The user may * This method handles the collection of information about each instance that is necessary to generate a certificate. The user may
* be prompted or the information can be gathered from a file * be prompted or the information can be gathered from a file
* @param terminal the terminal to use for user interaction * @param terminal the terminal to use for user interaction
* @param inputFile an optional file that will be used to load the instance information * @param inputFile an optional file that will be used to load the instance information
* @param env the environment for this tool to resolve files with
* @return a {@link Collection} of {@link CertificateInformation} that represents each instance * @return a {@link Collection} of {@link CertificateInformation} that represents each instance
*/ */
static Collection<CertificateInformation> getCertificateInformationList(Terminal terminal, String inputFile, Environment env) static Collection<CertificateInformation> getCertificateInformationList(Terminal terminal, String inputFile)
throws Exception { throws Exception {
if (inputFile != null) { if (inputFile != null) {
return parseFile(XPackPlugin.resolveConfigFile(env, inputFile)); return parseFile(resolvePath(inputFile));
} }
Map<String, CertificateInformation> map = new HashMap<>(); Map<String, CertificateInformation> map = new HashMap<>();
boolean done = false; boolean done = false;
@ -307,13 +311,14 @@ public class CertificateTool extends EnvironmentAwareCommand {
Environment env, int keysize, int days) throws Exception { Environment env, int keysize, int days) throws Exception {
if (caCertPath != null) { if (caCertPath != null) {
assert caKeyPath != null; assert caKeyPath != null;
Certificate[] certificates = CertUtils.readCertificates(Collections.singletonList(caCertPath), env); final String resolvedCaCertPath = resolvePath(caCertPath).toString();
Certificate[] certificates = CertUtils.readCertificates(Collections.singletonList(resolvedCaCertPath), env);
if (certificates.length != 1) { if (certificates.length != 1) {
throw new IllegalArgumentException("expected a single certificate in file [" + caCertPath + "] but found [" + throw new IllegalArgumentException("expected a single certificate in file [" + caCertPath + "] but found [" +
certificates.length + "]"); certificates.length + "]");
} }
Certificate caCert = certificates[0]; Certificate caCert = certificates[0];
PrivateKey privateKey = readPrivateKey(caKeyPath, keyPass, terminal, env, prompt); PrivateKey privateKey = readPrivateKey(caKeyPath, keyPass, terminal, prompt);
return new CAInfo((X509Certificate) caCert, privateKey); return new CAInfo((X509Certificate) caCert, privateKey);
} }
@ -504,14 +509,13 @@ public class CertificateTool extends EnvironmentAwareCommand {
* @param path the path to the private key * @param path the path to the private key
* @param password the password provided by the user or {@code null} * @param password the password provided by the user or {@code null}
* @param terminal the terminal to use for user interaction * @param terminal the terminal to use for user interaction
* @param env the environment to resolve files from
* @param prompt whether to prompt the user or not * @param prompt whether to prompt the user or not
* @return the {@link PrivateKey} that was read from the file * @return the {@link PrivateKey} that was read from the file
*/ */
private static PrivateKey readPrivateKey(String path, char[] password, Terminal terminal, Environment env, boolean prompt) private static PrivateKey readPrivateKey(String path, char[] password, Terminal terminal, boolean prompt)
throws Exception { throws Exception {
AtomicReference<char[]> passwordReference = new AtomicReference<>(password); AtomicReference<char[]> passwordReference = new AtomicReference<>(password);
try (Reader reader = Files.newBufferedReader(XPackPlugin.resolveConfigFile(env, path), StandardCharsets.UTF_8)) { try (Reader reader = Files.newBufferedReader(resolvePath(path), StandardCharsets.UTF_8)) {
return CertUtils.readPrivateKey(reader, () -> { return CertUtils.readPrivateKey(reader, () -> {
if (password != null || prompt == false) { if (password != null || prompt == false) {
return password; return password;

View File

@ -22,11 +22,12 @@ import org.bouncycastle.openssl.PEMParser;
import org.bouncycastle.pkcs.PKCS10CertificationRequest; import org.bouncycastle.pkcs.PKCS10CertificationRequest;
import org.elasticsearch.cli.MockTerminal; import org.elasticsearch.cli.MockTerminal;
import org.elasticsearch.common.Strings; import org.elasticsearch.common.Strings;
import org.elasticsearch.common.SuppressForbidden;
import org.elasticsearch.common.io.PathUtils;
import org.elasticsearch.common.network.NetworkAddress; import org.elasticsearch.common.network.NetworkAddress;
import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.env.Environment; import org.elasticsearch.env.Environment;
import org.elasticsearch.test.ESTestCase; import org.elasticsearch.test.ESTestCase;
import org.elasticsearch.xpack.XPackPlugin;
import org.elasticsearch.xpack.ssl.CertificateTool.CAInfo; import org.elasticsearch.xpack.ssl.CertificateTool.CAInfo;
import org.elasticsearch.xpack.ssl.CertificateTool.CertificateInformation; import org.elasticsearch.xpack.ssl.CertificateTool.CertificateInformation;
import org.elasticsearch.xpack.ssl.CertificateTool.Name; import org.elasticsearch.xpack.ssl.CertificateTool.Name;
@ -63,7 +64,6 @@ import java.util.function.Function;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.greaterThanOrEqualTo;
import static org.hamcrest.Matchers.instanceOf; import static org.hamcrest.Matchers.instanceOf;
/** /**
@ -89,13 +89,12 @@ public class CertificateToolTests extends ESTestCase {
} }
public void testOutputDirectory() throws Exception { public void testOutputDirectory() throws Exception {
Environment env = new Environment(Settings.builder().put("path.home", createTempDir()).build());
Path outputDir = createTempDir(); Path outputDir = createTempDir();
Path outputFile = outputDir.resolve("certs.zip"); Path outputFile = outputDir.resolve("certs.zip");
MockTerminal terminal = new MockTerminal(); MockTerminal terminal = new MockTerminal();
// test with a user provided dir // test with a user provided dir
Path resolvedOutputFile = CertificateTool.getOutputFile(terminal, outputFile.toString(), env, null); Path resolvedOutputFile = CertificateTool.getOutputFile(terminal, outputFile.toString(), null);
assertEquals(outputFile, resolvedOutputFile); assertEquals(outputFile, resolvedOutputFile);
assertTrue(terminal.getOutput().isEmpty()); assertTrue(terminal.getOutput().isEmpty());
@ -103,15 +102,15 @@ public class CertificateToolTests extends ESTestCase {
Path userPromptedOutputFile = outputDir.resolve("csr"); Path userPromptedOutputFile = outputDir.resolve("csr");
assertFalse(Files.exists(userPromptedOutputFile)); assertFalse(Files.exists(userPromptedOutputFile));
terminal.addTextInput(userPromptedOutputFile.toString()); terminal.addTextInput(userPromptedOutputFile.toString());
resolvedOutputFile = CertificateTool.getOutputFile(terminal, null, env, "out.zip"); resolvedOutputFile = CertificateTool.getOutputFile(terminal, null, "out.zip");
assertEquals(userPromptedOutputFile, resolvedOutputFile); assertEquals(userPromptedOutputFile, resolvedOutputFile);
assertTrue(terminal.getOutput().isEmpty()); assertTrue(terminal.getOutput().isEmpty());
// test with empty user input // test with empty user input
String defaultFilename = randomAlphaOfLengthBetween(1, 10); String defaultFilename = randomAlphaOfLengthBetween(1, 10);
Path expectedDefaultPath = XPackPlugin.resolveConfigFile(env, defaultFilename); Path expectedDefaultPath = resolvePath(defaultFilename);
terminal.addTextInput(""); terminal.addTextInput("");
resolvedOutputFile = CertificateTool.getOutputFile(terminal, null, env, defaultFilename); resolvedOutputFile = CertificateTool.getOutputFile(terminal, null, defaultFilename);
assertEquals(expectedDefaultPath, resolvedOutputFile); assertEquals(expectedDefaultPath, resolvedOutputFile);
assertTrue(terminal.getOutput().isEmpty()); assertTrue(terminal.getOutput().isEmpty());
} }
@ -150,8 +149,7 @@ public class CertificateToolTests extends ESTestCase {
} }
} }
Collection<CertificateInformation> certInfos = CertificateTool.getCertificateInformationList(terminal, null, Collection<CertificateInformation> certInfos = CertificateTool.getCertificateInformationList(terminal, null);
new Environment(Settings.builder().put("path.home", createTempDir()).build()));
logger.info("certificate tool output:\n{}", terminal.getOutput()); logger.info("certificate tool output:\n{}", terminal.getOutput());
assertEquals(numberOfInstances, certInfos.size()); assertEquals(numberOfInstances, certInfos.size());
for (CertificateInformation certInfo : certInfos) { for (CertificateInformation certInfo : certInfos) {
@ -487,4 +485,9 @@ public class CertificateToolTests extends ESTestCase {
return Files.write(path, instances, StandardCharsets.UTF_8); return Files.write(path, instances, StandardCharsets.UTF_8);
} }
@SuppressForbidden(reason = "resolve paths against CWD for a CLI tool")
private static Path resolvePath(String path) {
return PathUtils.get(path).toAbsolutePath();
}
} }