Skip to content

Commit 2743109

Browse files
committed
add ability to read text in addition to secrets
1 parent fab3106 commit 2743109

File tree

6 files changed

+150
-171
lines changed

6 files changed

+150
-171
lines changed

docs/reference/setup/configuration.asciidoc

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -265,25 +265,27 @@ file which will resolve to an environment setting, for example:
265265
--------------------------------------------------
266266

267267
Additionally, for settings that you do not wish to store in the configuration
268-
file, you can use the value `__prompt__` and start Elasticsearch in the
269-
foreground.
268+
file, you can use the value `__prompt:text__` or `__prompt:secret__` and start
269+
Elasticsearch in the foreground. `__prompt:secret__` has echoing disabled so
270+
that the value entered will not be shown in your terminal; `__prompt:text__`
271+
will allow you to see the value as you type it in. For example:
270272

271273
[source,yaml]
272274
--------------------------------------------------
273275
node:
274-
name: __prompt__
276+
name: __prompt:text__
275277
--------------------------------------------------
276278

277279
On execution of the `elasticsearch` command, you will be prompted to enter
278280
the actual value like so:
279281

280282
[source,sh]
281283
--------------------------------------------------
282-
Enter value for [node.name] :
284+
Enter value for [node.name]:
283285
--------------------------------------------------
284286

285-
NOTE: Elasticsearch will not start if `__prompt__` is used in the settings
286-
and the process is run as a service or in the background.
287+
NOTE: Elasticsearch will not start if `__prompt:text__` or `__prompt:secret__`
288+
is used in the settings and the process is run as a service or in the background.
287289

288290
The location of the configuration file can be set externally using a
289291
system property:

src/main/java/org/elasticsearch/common/property/PromptPlaceholderResolver.java

Lines changed: 0 additions & 64 deletions
This file was deleted.

src/main/java/org/elasticsearch/node/internal/InternalSettingsPreparer.java

Lines changed: 46 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,12 +19,12 @@
1919

2020
package org.elasticsearch.node.internal;
2121

22+
import com.google.common.collect.UnmodifiableIterator;
2223
import org.elasticsearch.cluster.ClusterName;
2324
import org.elasticsearch.common.Names;
2425
import org.elasticsearch.common.Strings;
2526
import org.elasticsearch.common.cli.Terminal;
2627
import org.elasticsearch.common.collect.Tuple;
27-
import org.elasticsearch.common.property.PromptPlaceholderResolver;
2828
import org.elasticsearch.common.settings.ImmutableSettings;
2929
import org.elasticsearch.common.settings.Settings;
3030
import org.elasticsearch.env.Environment;
@@ -40,6 +40,9 @@
4040
*/
4141
public class InternalSettingsPreparer {
4242

43+
public static final String SECRET_PROMPT_VALUE = "__prompt:secret__";
44+
public static final String TEXT_PROMPT_VALUE = "__prompt:text__";
45+
4346
/**
4447
* Prepares the settings by gathering all elasticsearch system properties, optionally loading the configuration settings,
4548
* and then replacing all property placeholders. This method will not work with settings that have <code>__prompt__</code>
@@ -153,7 +156,7 @@ public static Tuple<Settings, Environment> prepareSettings(Settings pSettings, b
153156
settingsBuilder.put(ClusterName.SETTING, ClusterName.DEFAULT.value());
154157
}
155158

156-
Settings v1 = PromptPlaceholderResolver.replacePromptPlaceholders(settingsBuilder.build(), terminal);
159+
Settings v1 = replacePromptPlaceholders(settingsBuilder.build(), terminal);
157160
environment = new Environment(v1);
158161

159162
// put back the env settings
@@ -165,4 +168,45 @@ public static Tuple<Settings, Environment> prepareSettings(Settings pSettings, b
165168

166169
return new Tuple<>(v1, environment);
167170
}
171+
172+
static Settings replacePromptPlaceholders(Settings settings, Terminal terminal) {
173+
UnmodifiableIterator<Map.Entry<String, String>> iter = settings.getAsMap().entrySet().iterator();
174+
ImmutableSettings.Builder builder = ImmutableSettings.builder();
175+
176+
while (iter.hasNext()) {
177+
Map.Entry<String, String> entry = iter.next();
178+
String value = entry.getValue();
179+
String key = entry.getKey();
180+
switch (value) {
181+
case SECRET_PROMPT_VALUE:
182+
String secretValue = promptForValue(key, terminal, true);
183+
if (Strings.hasLength(secretValue)) {
184+
builder.put(key, secretValue);
185+
}
186+
break;
187+
case TEXT_PROMPT_VALUE:
188+
String textValue = promptForValue(key, terminal, false);
189+
if (Strings.hasLength(textValue)) {
190+
builder.put(key, textValue);
191+
}
192+
break;
193+
default:
194+
builder.put(key, value);
195+
break;
196+
}
197+
}
198+
199+
return builder.build();
200+
}
201+
202+
static String promptForValue(String key, Terminal terminal, boolean secret) {
203+
if (terminal == null) {
204+
throw new UnsupportedOperationException("found property [" + key + "] with value [" + (secret ? SECRET_PROMPT_VALUE : TEXT_PROMPT_VALUE) +"]. prompting for property values is only supported when running elasticsearch in the foreground");
205+
}
206+
207+
if (secret) {
208+
return new String(terminal.readSecret("Enter value for [%s]: ", key));
209+
}
210+
return terminal.readText("Enter value for [%s]: ", key);
211+
}
168212
}

src/test/java/org/elasticsearch/common/cli/CliToolTests.java

Lines changed: 18 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -23,10 +23,9 @@
2323
import org.apache.commons.cli.CommandLine;
2424
import org.elasticsearch.ElasticsearchException;
2525
import org.elasticsearch.common.Strings;
26-
import org.elasticsearch.common.property.PromptPlaceholderResolver;
27-
import org.elasticsearch.common.settings.ImmutableSettings;
2826
import org.elasticsearch.common.settings.Settings;
2927
import org.elasticsearch.env.Environment;
28+
import org.elasticsearch.node.internal.InternalSettingsPreparer;
3029
import org.junit.Test;
3130

3231
import java.io.IOException;
@@ -277,32 +276,44 @@ public CliTool.ExitStatus execute(Settings settings, Environment env) {
277276
@Test
278277
public void testPromptForSetting() throws Exception {
279278
final AtomicInteger counter = new AtomicInteger();
280-
final AtomicReference<String> promptedValue = new AtomicReference<>(null);
279+
final AtomicReference<String> promptedSecretValue = new AtomicReference<>(null);
280+
final AtomicReference<String> promptedTextValue = new AtomicReference<>(null);
281281
final Terminal terminal = new MockTerminal() {
282282
@Override
283283
public char[] readSecret(String text, Object... args) {
284284
counter.incrementAndGet();
285285
assertThat(args, arrayContaining((Object) "foo.password"));
286286
return "changeit".toCharArray();
287287
}
288+
289+
@Override
290+
public String readText(String text, Object... args) {
291+
counter.incrementAndGet();
292+
assertThat(args, arrayContaining((Object) "replace"));
293+
return "replaced";
294+
}
288295
};
289296
final NamedCommand cmd = new NamedCommand("noop", terminal) {
290297
@Override
291298
public CliTool.ExitStatus execute(Settings settings, Environment env) {
292-
promptedValue.set(settings.get("foo.password"));
299+
promptedSecretValue.set(settings.get("foo.password"));
300+
promptedTextValue.set(settings.get("replace"));
293301
return CliTool.ExitStatus.OK;
294302
}
295303
};
296304

297-
System.setProperty("es.foo.password", PromptPlaceholderResolver.PROMPT_VALUE);
305+
System.setProperty("es.foo.password", InternalSettingsPreparer.SECRET_PROMPT_VALUE);
306+
System.setProperty("es.replace", InternalSettingsPreparer.TEXT_PROMPT_VALUE);
298307
try {
299308
new SingleCmdTool("tool", terminal, cmd).execute();
300309
} finally {
301310
System.clearProperty("es.foo.password");
311+
System.clearProperty("es.replace");
302312
}
303313

304-
assertThat(counter.intValue(), is(1));
305-
assertThat(promptedValue.get(), is("changeit"));
314+
assertThat(counter.intValue(), is(2));
315+
assertThat(promptedSecretValue.get(), is("changeit"));
316+
assertThat(promptedTextValue.get(), is("replaced"));
306317
}
307318

308319
private void assertStatus(int status, CliTool.ExitStatus expectedStatus) {

src/test/java/org/elasticsearch/common/property/PromptPlaceholderResolverTests.java

Lines changed: 0 additions & 92 deletions
This file was deleted.

0 commit comments

Comments
 (0)