Merge branch 'master' into deguice8
Original commit: elastic/x-pack-elasticsearch@8b273d3f8a
This commit is contained in:
commit
e2303f2584
|
@ -14,7 +14,7 @@ import org.elasticsearch.xpack.watcher.condition.script.ScriptCondition;
|
|||
import org.elasticsearch.xpack.watcher.execution.ManualExecutionContext;
|
||||
import org.elasticsearch.xpack.watcher.execution.ManualExecutionTests.ExecutionRunner;
|
||||
import org.elasticsearch.xpack.watcher.history.WatchRecord;
|
||||
import org.elasticsearch.xpack.watcher.support.Script;
|
||||
import org.elasticsearch.xpack.watcher.support.WatcherScript;
|
||||
import org.elasticsearch.xpack.watcher.test.AbstractWatcherIntegrationTestCase;
|
||||
import org.elasticsearch.xpack.watcher.transport.actions.delete.DeleteWatchResponse;
|
||||
import org.elasticsearch.xpack.watcher.transport.actions.get.GetWatchRequest;
|
||||
|
@ -65,7 +65,7 @@ public class GroovyManualExecutionIT extends AbstractWatcherIntegrationTestCase
|
|||
WatchSourceBuilder watchBuilder = watchBuilder()
|
||||
.trigger(schedule(cron("0 0 0 1 * ? 2099")))
|
||||
.input(simpleInput("foo", "bar"))
|
||||
.condition(new ScriptCondition((new Script.Builder.Inline("sleep 100; return true")).build()))
|
||||
.condition(new ScriptCondition((new WatcherScript.Builder.Inline("sleep 100; return true")).build()))
|
||||
.addAction("log", loggingAction("foobar"));
|
||||
|
||||
Watch watch = watchParser().parse("_id", false, watchBuilder.buildAsBytes(XContentType.JSON));
|
||||
|
@ -80,7 +80,7 @@ public class GroovyManualExecutionIT extends AbstractWatcherIntegrationTestCase
|
|||
WatchSourceBuilder watchBuilder = watchBuilder()
|
||||
.trigger(schedule(cron("0 0 0 1 * ? 2099")))
|
||||
.input(simpleInput("foo", "bar"))
|
||||
.condition(new ScriptCondition((new Script.Builder.Inline("sleep 10000; return true")).build()))
|
||||
.condition(new ScriptCondition((new WatcherScript.Builder.Inline("sleep 10000; return true")).build()))
|
||||
.defaultThrottlePeriod(new TimeValue(1, TimeUnit.HOURS))
|
||||
.addAction("log", loggingAction("foobar"));
|
||||
|
||||
|
|
|
@ -8,6 +8,7 @@ package org.elasticsearch.messy.tests;
|
|||
import org.elasticsearch.action.search.SearchRequestBuilder;
|
||||
import org.elasticsearch.action.search.SearchResponse;
|
||||
import org.elasticsearch.plugins.Plugin;
|
||||
import org.elasticsearch.script.ScriptService;
|
||||
import org.elasticsearch.script.groovy.GroovyPlugin;
|
||||
import org.elasticsearch.search.aggregations.AggregationBuilders;
|
||||
import org.elasticsearch.search.aggregations.bucket.histogram.Histogram;
|
||||
|
@ -16,8 +17,7 @@ import org.elasticsearch.threadpool.ThreadPool;
|
|||
import org.elasticsearch.xpack.watcher.condition.script.ExecutableScriptCondition;
|
||||
import org.elasticsearch.xpack.watcher.condition.script.ScriptCondition;
|
||||
import org.elasticsearch.xpack.watcher.execution.WatchExecutionContext;
|
||||
import org.elasticsearch.xpack.watcher.support.Script;
|
||||
import org.elasticsearch.xpack.common.ScriptServiceProxy;
|
||||
import org.elasticsearch.xpack.watcher.support.WatcherScript;
|
||||
import org.elasticsearch.xpack.watcher.test.AbstractWatcherIntegrationTestCase;
|
||||
import org.elasticsearch.xpack.watcher.watch.Payload;
|
||||
import org.junit.AfterClass;
|
||||
|
@ -28,7 +28,7 @@ import java.util.List;
|
|||
import java.util.Locale;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import static org.elasticsearch.messy.tests.MessyTestUtils.getScriptServiceProxy;
|
||||
import static org.elasticsearch.messy.tests.MessyTestUtils.createScriptService;
|
||||
import static org.elasticsearch.xpack.watcher.test.WatcherTestUtils.mockExecutionContext;
|
||||
|
||||
public class GroovyScriptConditionIT extends AbstractWatcherIntegrationTestCase {
|
||||
|
@ -46,7 +46,7 @@ public class GroovyScriptConditionIT extends AbstractWatcherIntegrationTestCase
|
|||
}
|
||||
|
||||
private static ThreadPool THREAD_POOL;
|
||||
private ScriptServiceProxy scriptService;
|
||||
private ScriptService scriptService;
|
||||
|
||||
@BeforeClass
|
||||
public static void startThreadPool() {
|
||||
|
@ -55,7 +55,7 @@ public class GroovyScriptConditionIT extends AbstractWatcherIntegrationTestCase
|
|||
|
||||
@Before
|
||||
public void init() throws Exception {
|
||||
scriptService = getScriptServiceProxy(THREAD_POOL);
|
||||
scriptService = createScriptService(THREAD_POOL);
|
||||
}
|
||||
|
||||
@AfterClass
|
||||
|
@ -83,7 +83,7 @@ public class GroovyScriptConditionIT extends AbstractWatcherIntegrationTestCase
|
|||
SearchResponse unmetResponse = builder.get();
|
||||
|
||||
ExecutableScriptCondition condition =
|
||||
new ExecutableScriptCondition(new ScriptCondition(Script.inline(
|
||||
new ExecutableScriptCondition(new ScriptCondition(WatcherScript.inline(
|
||||
String.join(
|
||||
" ",
|
||||
"if (ctx.payload.hits.total < 1) return false;",
|
||||
|
|
|
@ -6,9 +6,6 @@
|
|||
package org.elasticsearch.messy.tests;
|
||||
|
||||
import org.apache.lucene.util.LuceneTestCase;
|
||||
import org.elasticsearch.cluster.ClusterName;
|
||||
import org.elasticsearch.cluster.ClusterState;
|
||||
import org.elasticsearch.cluster.service.ClusterService;
|
||||
import org.elasticsearch.common.SuppressForbidden;
|
||||
import org.elasticsearch.common.settings.Settings;
|
||||
import org.elasticsearch.env.Environment;
|
||||
|
@ -19,9 +16,8 @@ import org.elasticsearch.script.ScriptSettings;
|
|||
import org.elasticsearch.script.groovy.GroovyScriptEngineService;
|
||||
import org.elasticsearch.threadpool.ThreadPool;
|
||||
import org.elasticsearch.watcher.ResourceWatcherService;
|
||||
import org.elasticsearch.xpack.common.ScriptServiceProxy;
|
||||
import org.elasticsearch.xpack.watcher.support.WatcherScript;
|
||||
import org.junit.Ignore;
|
||||
import org.mockito.Mockito;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
|
@ -29,7 +25,7 @@ import java.util.Collections;
|
|||
@Ignore // not a test.
|
||||
@SuppressForbidden(reason = "gradle is broken and tries to run me as a test")
|
||||
public final class MessyTestUtils {
|
||||
public static ScriptServiceProxy getScriptServiceProxy(ThreadPool tp) throws Exception {
|
||||
public static ScriptService createScriptService(ThreadPool tp) throws Exception {
|
||||
Settings settings = Settings.builder()
|
||||
.put("script.inline", "true")
|
||||
.put("script.indexed", "true")
|
||||
|
@ -37,10 +33,10 @@ public final class MessyTestUtils {
|
|||
.build();
|
||||
GroovyScriptEngineService groovyScriptEngineService = new GroovyScriptEngineService(settings);
|
||||
ScriptEngineRegistry scriptEngineRegistry = new ScriptEngineRegistry(Collections.singleton(groovyScriptEngineService));
|
||||
ScriptContextRegistry scriptContextRegistry = new ScriptContextRegistry(Arrays.asList(ScriptServiceProxy.INSTANCE));
|
||||
ScriptContextRegistry scriptContextRegistry = new ScriptContextRegistry(Arrays.asList(WatcherScript.CTX_PLUGIN));
|
||||
|
||||
ScriptSettings scriptSettings = new ScriptSettings(scriptEngineRegistry, scriptContextRegistry);
|
||||
return ScriptServiceProxy.of(new ScriptService(settings, new Environment(settings),
|
||||
new ResourceWatcherService(settings, tp), scriptEngineRegistry, scriptContextRegistry, scriptSettings));
|
||||
return new ScriptService(settings, new Environment(settings), new ResourceWatcherService(settings, tp),
|
||||
scriptEngineRegistry, scriptContextRegistry, scriptSettings);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,6 +10,7 @@ import org.elasticsearch.action.search.ShardSearchFailure;
|
|||
import org.elasticsearch.common.text.Text;
|
||||
import org.elasticsearch.index.Index;
|
||||
import org.elasticsearch.plugins.Plugin;
|
||||
import org.elasticsearch.script.ScriptService;
|
||||
import org.elasticsearch.script.groovy.GroovyPlugin;
|
||||
import org.elasticsearch.search.SearchShardTarget;
|
||||
import org.elasticsearch.search.aggregations.AggregationBuilders;
|
||||
|
@ -20,11 +21,10 @@ import org.elasticsearch.search.internal.InternalSearchHits;
|
|||
import org.elasticsearch.search.internal.InternalSearchResponse;
|
||||
import org.elasticsearch.threadpool.TestThreadPool;
|
||||
import org.elasticsearch.threadpool.ThreadPool;
|
||||
import org.elasticsearch.xpack.common.ScriptServiceProxy;
|
||||
import org.elasticsearch.xpack.watcher.condition.script.ExecutableScriptCondition;
|
||||
import org.elasticsearch.xpack.watcher.condition.script.ScriptCondition;
|
||||
import org.elasticsearch.xpack.watcher.execution.WatchExecutionContext;
|
||||
import org.elasticsearch.xpack.watcher.support.Script;
|
||||
import org.elasticsearch.xpack.watcher.support.WatcherScript;
|
||||
import org.elasticsearch.xpack.watcher.test.AbstractWatcherIntegrationTestCase;
|
||||
import org.elasticsearch.xpack.watcher.watch.Payload;
|
||||
import org.junit.After;
|
||||
|
@ -40,7 +40,7 @@ import static org.mockito.Mockito.when;
|
|||
*/
|
||||
public class ScriptConditionSearchIT extends AbstractWatcherIntegrationTestCase {
|
||||
private ThreadPool tp = null;
|
||||
private ScriptServiceProxy scriptService;
|
||||
private ScriptService scriptService;
|
||||
|
||||
@Override
|
||||
protected List<Class<? extends Plugin>> pluginTypes() {
|
||||
|
@ -52,7 +52,7 @@ public class ScriptConditionSearchIT extends AbstractWatcherIntegrationTestCase
|
|||
@Before
|
||||
public void init() throws Exception {
|
||||
tp = new TestThreadPool(ThreadPool.Names.SAME);
|
||||
scriptService = MessyTestUtils.getScriptServiceProxy(tp);
|
||||
scriptService = MessyTestUtils.createScriptService(tp);
|
||||
}
|
||||
|
||||
@After
|
||||
|
@ -73,7 +73,7 @@ public class ScriptConditionSearchIT extends AbstractWatcherIntegrationTestCase
|
|||
.get();
|
||||
|
||||
ExecutableScriptCondition condition = new ExecutableScriptCondition(
|
||||
new ScriptCondition(Script.inline("ctx.payload.aggregations.rate.buckets[0]?.doc_count >= 5").build()),
|
||||
new ScriptCondition(WatcherScript.inline("ctx.payload.aggregations.rate.buckets[0]?.doc_count >= 5").build()),
|
||||
logger, scriptService);
|
||||
|
||||
WatchExecutionContext ctx = mockExecutionContext("_name", new Payload.XContent(response));
|
||||
|
@ -92,7 +92,7 @@ public class ScriptConditionSearchIT extends AbstractWatcherIntegrationTestCase
|
|||
|
||||
public void testExecuteAccessHits() throws Exception {
|
||||
ExecutableScriptCondition condition = new ExecutableScriptCondition(new ScriptCondition(
|
||||
Script.inline("ctx.payload.hits?.hits[0]?._score == 1.0").build()), logger, scriptService);
|
||||
WatcherScript.inline("ctx.payload.hits?.hits[0]?._score == 1.0").build()), logger, scriptService);
|
||||
InternalSearchHit hit = new InternalSearchHit(0, "1", new Text("type"), null);
|
||||
hit.score(1f);
|
||||
hit.shard(new SearchShardTarget("a", new Index("a", "testUUID"), 0));
|
||||
|
|
|
@ -15,17 +15,17 @@ import org.elasticsearch.common.xcontent.XContentFactory;
|
|||
import org.elasticsearch.common.xcontent.XContentParser;
|
||||
import org.elasticsearch.script.GeneralScriptException;
|
||||
import org.elasticsearch.script.ScriptException;
|
||||
import org.elasticsearch.script.ScriptService;
|
||||
import org.elasticsearch.script.ScriptService.ScriptType;
|
||||
import org.elasticsearch.search.internal.InternalSearchResponse;
|
||||
import org.elasticsearch.test.ESTestCase;
|
||||
import org.elasticsearch.threadpool.TestThreadPool;
|
||||
import org.elasticsearch.threadpool.ThreadPool;
|
||||
import org.elasticsearch.xpack.common.ScriptServiceProxy;
|
||||
import org.elasticsearch.xpack.watcher.condition.script.ExecutableScriptCondition;
|
||||
import org.elasticsearch.xpack.watcher.condition.script.ScriptCondition;
|
||||
import org.elasticsearch.xpack.watcher.condition.script.ScriptConditionFactory;
|
||||
import org.elasticsearch.xpack.watcher.execution.WatchExecutionContext;
|
||||
import org.elasticsearch.xpack.watcher.support.Script;
|
||||
import org.elasticsearch.xpack.watcher.support.WatcherScript;
|
||||
import org.elasticsearch.xpack.watcher.watch.Payload;
|
||||
import org.joda.time.DateTime;
|
||||
import org.joda.time.DateTimeZone;
|
||||
|
@ -36,7 +36,7 @@ import java.io.IOException;
|
|||
|
||||
import static java.util.Collections.singletonMap;
|
||||
import static org.elasticsearch.common.xcontent.XContentFactory.jsonBuilder;
|
||||
import static org.elasticsearch.messy.tests.MessyTestUtils.getScriptServiceProxy;
|
||||
import static org.elasticsearch.messy.tests.MessyTestUtils.createScriptService;
|
||||
import static org.elasticsearch.xpack.watcher.support.Exceptions.illegalArgument;
|
||||
import static org.elasticsearch.xpack.watcher.test.WatcherTestUtils.mockExecutionContext;
|
||||
import static org.hamcrest.Matchers.containsString;
|
||||
|
@ -57,18 +57,18 @@ public class ScriptConditionTests extends ESTestCase {
|
|||
}
|
||||
|
||||
public void testExecute() throws Exception {
|
||||
ScriptServiceProxy scriptService = getScriptServiceProxy(tp);
|
||||
ScriptService scriptService = createScriptService(tp);
|
||||
ExecutableScriptCondition condition = new ExecutableScriptCondition(
|
||||
new ScriptCondition(Script.inline("ctx.payload.hits.total > 1").build()), logger, scriptService);
|
||||
new ScriptCondition(WatcherScript.inline("ctx.payload.hits.total > 1").build()), logger, scriptService);
|
||||
SearchResponse response = new SearchResponse(InternalSearchResponse.empty(), "", 3, 3, 500L, new ShardSearchFailure[0]);
|
||||
WatchExecutionContext ctx = mockExecutionContext("_name", new Payload.XContent(response));
|
||||
assertFalse(condition.execute(ctx).met());
|
||||
}
|
||||
|
||||
public void testExecuteMergedParams() throws Exception {
|
||||
ScriptServiceProxy scriptService = getScriptServiceProxy(tp);
|
||||
Script script = Script.inline("ctx.payload.hits.total > threshold")
|
||||
.lang(Script.DEFAULT_LANG).params(singletonMap("threshold", 1)).build();
|
||||
ScriptService scriptService = createScriptService(tp);
|
||||
WatcherScript script = WatcherScript.inline("ctx.payload.hits.total > threshold")
|
||||
.lang(WatcherScript.DEFAULT_LANG).params(singletonMap("threshold", 1)).build();
|
||||
ExecutableScriptCondition executable = new ExecutableScriptCondition(new ScriptCondition(script), logger, scriptService);
|
||||
SearchResponse response = new SearchResponse(InternalSearchResponse.empty(), "", 3, 3, 500L, new ShardSearchFailure[0]);
|
||||
WatchExecutionContext ctx = mockExecutionContext("_name", new Payload.XContent(response));
|
||||
|
@ -76,7 +76,7 @@ public class ScriptConditionTests extends ESTestCase {
|
|||
}
|
||||
|
||||
public void testParserValid() throws Exception {
|
||||
ScriptConditionFactory factory = new ScriptConditionFactory(Settings.builder().build(), getScriptServiceProxy(tp));
|
||||
ScriptConditionFactory factory = new ScriptConditionFactory(Settings.builder().build(), createScriptService(tp));
|
||||
|
||||
XContentBuilder builder = createConditionContent("ctx.payload.hits.total > 1", null, ScriptType.INLINE);
|
||||
|
||||
|
@ -103,7 +103,7 @@ public class ScriptConditionTests extends ESTestCase {
|
|||
}
|
||||
|
||||
public void testParserInvalid() throws Exception {
|
||||
ScriptConditionFactory factory = new ScriptConditionFactory(Settings.builder().build(), getScriptServiceProxy(tp));
|
||||
ScriptConditionFactory factory = new ScriptConditionFactory(Settings.builder().build(), createScriptService(tp));
|
||||
XContentBuilder builder = XContentFactory.jsonBuilder();
|
||||
builder.startObject().endObject();
|
||||
XContentParser parser = XContentFactory.xContent(builder.bytes()).createParser(builder.bytes());
|
||||
|
@ -118,7 +118,7 @@ public class ScriptConditionTests extends ESTestCase {
|
|||
}
|
||||
|
||||
public void testScriptConditionParserBadScript() throws Exception {
|
||||
ScriptConditionFactory conditionParser = new ScriptConditionFactory(Settings.builder().build(), getScriptServiceProxy(tp));
|
||||
ScriptConditionFactory conditionParser = new ScriptConditionFactory(Settings.builder().build(), createScriptService(tp));
|
||||
ScriptType scriptType = randomFrom(ScriptType.values());
|
||||
String script;
|
||||
switch (scriptType) {
|
||||
|
@ -139,7 +139,7 @@ public class ScriptConditionTests extends ESTestCase {
|
|||
}
|
||||
|
||||
public void testScriptConditionParser_badLang() throws Exception {
|
||||
ScriptConditionFactory conditionParser = new ScriptConditionFactory(Settings.builder().build(), getScriptServiceProxy(tp));
|
||||
ScriptConditionFactory conditionParser = new ScriptConditionFactory(Settings.builder().build(), createScriptService(tp));
|
||||
ScriptType scriptType = ScriptType.INLINE;
|
||||
String script = "return true";
|
||||
XContentBuilder builder = createConditionContent(script, "not_a_valid_lang", scriptType);
|
||||
|
@ -152,9 +152,9 @@ public class ScriptConditionTests extends ESTestCase {
|
|||
}
|
||||
|
||||
public void testScriptConditionThrowException() throws Exception {
|
||||
ScriptServiceProxy scriptService = getScriptServiceProxy(tp);
|
||||
ScriptService scriptService = createScriptService(tp);
|
||||
ExecutableScriptCondition condition = new ExecutableScriptCondition(
|
||||
new ScriptCondition(Script.inline("null.foo").build()), logger, scriptService);
|
||||
new ScriptCondition(WatcherScript.inline("null.foo").build()), logger, scriptService);
|
||||
SearchResponse response = new SearchResponse(InternalSearchResponse.empty(), "", 3, 3, 500L, new ShardSearchFailure[0]);
|
||||
WatchExecutionContext ctx = mockExecutionContext("_name", new Payload.XContent(response));
|
||||
ScriptException exception = expectThrows(ScriptException.class, () -> condition.execute(ctx));
|
||||
|
@ -162,9 +162,9 @@ public class ScriptConditionTests extends ESTestCase {
|
|||
}
|
||||
|
||||
public void testScriptConditionReturnObjectThrowsException() throws Exception {
|
||||
ScriptServiceProxy scriptService = getScriptServiceProxy(tp);
|
||||
ScriptService scriptService = createScriptService(tp);
|
||||
ExecutableScriptCondition condition = new ExecutableScriptCondition(
|
||||
new ScriptCondition(Script.inline("return new Object()").build()), logger, scriptService);
|
||||
new ScriptCondition(WatcherScript.inline("return new Object()").build()), logger, scriptService);
|
||||
SearchResponse response = new SearchResponse(InternalSearchResponse.empty(), "", 3, 3, 500L, new ShardSearchFailure[0]);
|
||||
WatchExecutionContext ctx = mockExecutionContext("_name", new Payload.XContent(response));
|
||||
Exception exception = expectThrows(GeneralScriptException.class, () -> condition.execute(ctx));
|
||||
|
@ -173,9 +173,9 @@ public class ScriptConditionTests extends ESTestCase {
|
|||
}
|
||||
|
||||
public void testScriptConditionAccessCtx() throws Exception {
|
||||
ScriptServiceProxy scriptService = getScriptServiceProxy(tp);
|
||||
ScriptService scriptService = createScriptService(tp);
|
||||
ExecutableScriptCondition condition = new ExecutableScriptCondition(
|
||||
new ScriptCondition(Script.inline("ctx.trigger.scheduled_time.getMillis() < new Date().time ").build()),
|
||||
new ScriptCondition(WatcherScript.inline("ctx.trigger.scheduled_time.getMillis() < new Date().time ").build()),
|
||||
logger, scriptService);
|
||||
SearchResponse response = new SearchResponse(InternalSearchResponse.empty(), "", 3, 3, 500L, new ShardSearchFailure[0]);
|
||||
WatchExecutionContext ctx = mockExecutionContext("_name", new DateTime(DateTimeZone.UTC), new Payload.XContent(response));
|
||||
|
|
|
@ -13,7 +13,7 @@ import org.elasticsearch.common.io.Streams;
|
|||
import org.elasticsearch.common.settings.Settings;
|
||||
import org.elasticsearch.plugins.Plugin;
|
||||
import org.elasticsearch.script.groovy.GroovyPlugin;
|
||||
import org.elasticsearch.xpack.watcher.support.Script;
|
||||
import org.elasticsearch.xpack.watcher.support.WatcherScript;
|
||||
import org.elasticsearch.xpack.watcher.test.AbstractWatcherIntegrationTestCase;
|
||||
import org.elasticsearch.xpack.watcher.test.WatcherTestUtils;
|
||||
import org.elasticsearch.xpack.watcher.transport.actions.put.PutWatchResponse;
|
||||
|
@ -72,10 +72,10 @@ public class TransformIT extends AbstractWatcherIntegrationTestCase {
|
|||
}
|
||||
|
||||
public void testScriptTransform() throws Exception {
|
||||
final Script script;
|
||||
final WatcherScript script;
|
||||
if (randomBoolean()) {
|
||||
logger.info("testing script transform with an inline script");
|
||||
script = Script.inline("return [key3 : ctx.payload.key1 + ctx.payload.key2]").lang("groovy").build();
|
||||
script = WatcherScript.inline("return [key3 : ctx.payload.key1 + ctx.payload.key2]").lang("groovy").build();
|
||||
} else if (randomBoolean()) {
|
||||
logger.info("testing script transform with an indexed script");
|
||||
client().admin().cluster().preparePutStoredScript()
|
||||
|
@ -83,10 +83,10 @@ public class TransformIT extends AbstractWatcherIntegrationTestCase {
|
|||
.setScriptLang("groovy")
|
||||
.setSource(new BytesArray("{\"script\" : \"return [key3 : ctx.payload.key1 + ctx.payload.key2]\"}"))
|
||||
.get();
|
||||
script = Script.indexed("_id").lang("groovy").build();
|
||||
script = WatcherScript.indexed("_id").lang("groovy").build();
|
||||
} else {
|
||||
logger.info("testing script transform with a file script");
|
||||
script = Script.file("my-script").lang("groovy").build();
|
||||
script = WatcherScript.file("my-script").lang("groovy").build();
|
||||
}
|
||||
|
||||
// put a watch that has watch level transform:
|
||||
|
@ -182,8 +182,8 @@ public class TransformIT extends AbstractWatcherIntegrationTestCase {
|
|||
}
|
||||
|
||||
public void testChainTransform() throws Exception {
|
||||
final Script script1 = Script.inline("return [key3 : ctx.payload.key1 + ctx.payload.key2]").lang("groovy").build();
|
||||
final Script script2 = Script.inline("return [key4 : ctx.payload.key3 + 10]").lang("groovy").build();
|
||||
final WatcherScript script1 = WatcherScript.inline("return [key3 : ctx.payload.key1 + ctx.payload.key2]").lang("groovy").build();
|
||||
final WatcherScript script2 = WatcherScript.inline("return [key4 : ctx.payload.key3 + 10]").lang("groovy").build();
|
||||
// put a watch that has watch level transform:
|
||||
PutWatchResponse putWatchResponse = watcherClient().preparePutWatch("_id1")
|
||||
.setSource(watchBuilder()
|
||||
|
|
|
@ -28,7 +28,6 @@ import org.elasticsearch.search.builder.SearchSourceBuilder;
|
|||
import org.elasticsearch.search.suggest.Suggesters;
|
||||
import org.elasticsearch.test.ESIntegTestCase;
|
||||
import org.elasticsearch.test.ESIntegTestCase.ClusterScope;
|
||||
import org.elasticsearch.xpack.common.ScriptServiceProxy;
|
||||
import org.elasticsearch.xpack.common.text.TextTemplate;
|
||||
import org.elasticsearch.xpack.watcher.actions.ActionWrapper;
|
||||
import org.elasticsearch.xpack.watcher.actions.ExecutableActions;
|
||||
|
@ -41,7 +40,7 @@ import org.elasticsearch.xpack.watcher.input.search.SearchInput;
|
|||
import org.elasticsearch.xpack.watcher.input.search.SearchInputFactory;
|
||||
import org.elasticsearch.xpack.watcher.input.simple.ExecutableSimpleInput;
|
||||
import org.elasticsearch.xpack.watcher.input.simple.SimpleInput;
|
||||
import org.elasticsearch.xpack.watcher.support.Script;
|
||||
import org.elasticsearch.xpack.watcher.support.WatcherScript;
|
||||
import org.elasticsearch.xpack.watcher.support.init.proxy.WatcherClientProxy;
|
||||
import org.elasticsearch.xpack.watcher.support.search.WatcherSearchTemplateRequest;
|
||||
import org.elasticsearch.xpack.watcher.support.search.WatcherSearchTemplateService;
|
||||
|
@ -190,7 +189,7 @@ public class SearchInputIT extends ESIntegTestCase {
|
|||
Map<String, Object> params = new HashMap<>();
|
||||
params.put("seconds_param", "30s");
|
||||
|
||||
Script template = Script.inline(TEMPLATE_QUERY).lang("mustache").params(params).build();
|
||||
WatcherScript template = WatcherScript.inline(TEMPLATE_QUERY).lang("mustache").params(params).build();
|
||||
|
||||
SearchRequest request = client().prepareSearch()
|
||||
.setSearchType(ExecutableSearchInput.DEFAULT_SEARCH_TYPE)
|
||||
|
@ -224,7 +223,7 @@ public class SearchInputIT extends ESIntegTestCase {
|
|||
Map<String, Object> params = new HashMap<>();
|
||||
params.put("seconds_param", "30s");
|
||||
|
||||
Script template = Script.indexed("test-template").lang("mustache").params(params).build();
|
||||
WatcherScript template = WatcherScript.indexed("test-template").lang("mustache").params(params).build();
|
||||
|
||||
jsonBuilder().value(TextTemplate.indexed("test-template").params(params).build()).bytes();
|
||||
SearchRequest request = client().prepareSearch().setSearchType(ExecutableSearchInput.DEFAULT_SEARCH_TYPE)
|
||||
|
@ -252,7 +251,7 @@ public class SearchInputIT extends ESIntegTestCase {
|
|||
Map<String, Object> params = new HashMap<>();
|
||||
params.put("seconds_param", "30s");
|
||||
|
||||
Script template = Script.file("test_disk_template").lang("mustache").params(params).build();
|
||||
WatcherScript template = WatcherScript.file("test_disk_template").lang("mustache").params(params).build();
|
||||
SearchRequest request = client().prepareSearch().setSearchType(ExecutableSearchInput.DEFAULT_SEARCH_TYPE)
|
||||
.setIndices("test-search-index").request();
|
||||
|
||||
|
@ -347,7 +346,8 @@ public class SearchInputIT extends ESIntegTestCase {
|
|||
timeValueSeconds(5));
|
||||
}
|
||||
|
||||
private SearchInput.Result executeSearchInput(SearchRequest request, Script template, WatchExecutionContext ctx) throws IOException {
|
||||
private SearchInput.Result executeSearchInput(SearchRequest request, WatcherScript template,
|
||||
WatchExecutionContext ctx) throws IOException {
|
||||
createIndex("test-search-index");
|
||||
ensureGreen("test-search-index");
|
||||
SearchInput.Builder siBuilder = SearchInput.builder(new WatcherSearchTemplateRequest(request, template));
|
||||
|
@ -362,15 +362,15 @@ public class SearchInputIT extends ESIntegTestCase {
|
|||
protected WatcherSearchTemplateService watcherSearchTemplateService() {
|
||||
String master = internalCluster().getMasterName();
|
||||
return new WatcherSearchTemplateService(internalCluster().clusterService(master).getSettings(),
|
||||
ScriptServiceProxy.of(internalCluster().getInstance(ScriptService.class, master)),
|
||||
internalCluster().getInstance(ScriptService.class, master),
|
||||
internalCluster().getInstance(IndicesQueriesRegistry.class, master),
|
||||
internalCluster().getInstance(AggregatorParsers.class, master),
|
||||
internalCluster().getInstance(Suggesters.class, master)
|
||||
);
|
||||
}
|
||||
|
||||
protected ScriptServiceProxy scriptService() {
|
||||
return ScriptServiceProxy.of(internalCluster().getInstance(ScriptService.class));
|
||||
protected ScriptService scriptService() {
|
||||
return internalCluster().getInstance(ScriptService.class);
|
||||
}
|
||||
|
||||
private XContentSource toXContentSource(SearchInput.Result result) throws IOException {
|
||||
|
@ -387,7 +387,7 @@ public class SearchInputIT extends ESIntegTestCase {
|
|||
|
||||
@Override
|
||||
public ScriptContext.Plugin getCustomScriptContexts() {
|
||||
return ScriptServiceProxy.INSTANCE;
|
||||
return WatcherScript.CTX_PLUGIN;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -32,7 +32,6 @@ import org.elasticsearch.search.builder.SearchSourceBuilder;
|
|||
import org.elasticsearch.search.suggest.Suggesters;
|
||||
import org.elasticsearch.test.ESIntegTestCase;
|
||||
import org.elasticsearch.test.ESIntegTestCase.ClusterScope;
|
||||
import org.elasticsearch.xpack.common.ScriptServiceProxy;
|
||||
import org.elasticsearch.xpack.common.text.TextTemplate;
|
||||
import org.elasticsearch.xpack.watcher.actions.ExecutableActions;
|
||||
import org.elasticsearch.xpack.watcher.condition.always.ExecutableAlwaysCondition;
|
||||
|
@ -40,7 +39,7 @@ import org.elasticsearch.xpack.watcher.execution.TriggeredExecutionContext;
|
|||
import org.elasticsearch.xpack.watcher.execution.WatchExecutionContext;
|
||||
import org.elasticsearch.xpack.watcher.input.simple.ExecutableSimpleInput;
|
||||
import org.elasticsearch.xpack.watcher.input.simple.SimpleInput;
|
||||
import org.elasticsearch.xpack.watcher.support.Script;
|
||||
import org.elasticsearch.xpack.watcher.support.WatcherScript;
|
||||
import org.elasticsearch.xpack.watcher.support.init.proxy.WatcherClientProxy;
|
||||
import org.elasticsearch.xpack.watcher.support.search.WatcherSearchTemplateRequest;
|
||||
import org.elasticsearch.xpack.watcher.support.search.WatcherSearchTemplateService;
|
||||
|
@ -348,7 +347,7 @@ public class SearchTransformIT extends ESIntegTestCase {
|
|||
}
|
||||
if (templateName != null) {
|
||||
assertThat(executable.transform().getRequest().getTemplate(),
|
||||
equalTo(Script.file("template1").build()));
|
||||
equalTo(WatcherScript.file("template1").build()));
|
||||
}
|
||||
SearchSourceBuilder source = new SearchSourceBuilder().query(QueryBuilders.matchAllQuery());
|
||||
assertThat(executable.transform().getRequest().getRequest().source(), equalTo(source));
|
||||
|
@ -381,7 +380,7 @@ public class SearchTransformIT extends ESIntegTestCase {
|
|||
Map<String, Object> params = new HashMap<>();
|
||||
params.put("seconds_param", "30s");
|
||||
|
||||
Script template = Script.inline(templateQuery).lang("mustache").params(params).build();
|
||||
WatcherScript template = WatcherScript.inline(templateQuery).lang("mustache").params(params).build();
|
||||
SearchRequest request = client().prepareSearch().setSearchType(ExecutableSearchTransform.DEFAULT_SEARCH_TYPE)
|
||||
.setIndices("test-search-index").request();
|
||||
|
||||
|
@ -415,7 +414,7 @@ public class SearchTransformIT extends ESIntegTestCase {
|
|||
Map<String, Object> params = new HashMap<>();
|
||||
params.put("seconds_param", "30s");
|
||||
|
||||
Script template = Script.indexed("test-script").lang("mustache").params(params).build();
|
||||
WatcherScript template = WatcherScript.indexed("test-script").lang("mustache").params(params).build();
|
||||
|
||||
SearchRequest request = client()
|
||||
.prepareSearch()
|
||||
|
@ -441,7 +440,7 @@ public class SearchTransformIT extends ESIntegTestCase {
|
|||
Map<String, Object> params = new HashMap<>();
|
||||
params.put("seconds_param", "30s");
|
||||
|
||||
Script template = Script.file("test_disk_template").lang("mustache").params(params).build();
|
||||
WatcherScript template = WatcherScript.file("test_disk_template").lang("mustache").params(params).build();
|
||||
SearchRequest request = client().prepareSearch().setSearchType(ExecutableSearchTransform.DEFAULT_SEARCH_TYPE)
|
||||
.setIndices("test-search-index").request();
|
||||
|
||||
|
@ -504,7 +503,7 @@ public class SearchTransformIT extends ESIntegTestCase {
|
|||
timeValueSeconds(5));
|
||||
}
|
||||
|
||||
private SearchTransform.Result executeSearchTransform(SearchRequest request, Script template, WatchExecutionContext ctx)
|
||||
private SearchTransform.Result executeSearchTransform(SearchRequest request, WatcherScript template, WatchExecutionContext ctx)
|
||||
throws IOException {
|
||||
createIndex("test-search-index");
|
||||
ensureGreen("test-search-index");
|
||||
|
@ -519,15 +518,15 @@ public class SearchTransformIT extends ESIntegTestCase {
|
|||
protected WatcherSearchTemplateService watcherSearchTemplateService() {
|
||||
String master = internalCluster().getMasterName();
|
||||
return new WatcherSearchTemplateService(internalCluster().clusterService(master).getSettings(),
|
||||
ScriptServiceProxy.of(internalCluster().getInstance(ScriptService.class, master)),
|
||||
internalCluster().getInstance(ScriptService.class, master),
|
||||
internalCluster().getInstance(IndicesQueriesRegistry.class, master),
|
||||
internalCluster().getInstance(AggregatorParsers.class, master),
|
||||
internalCluster().getInstance(Suggesters.class, master)
|
||||
);
|
||||
}
|
||||
|
||||
protected ScriptServiceProxy scriptService() {
|
||||
return ScriptServiceProxy.of(internalCluster().getInstance(ScriptService.class));
|
||||
protected ScriptService scriptService() {
|
||||
return internalCluster().getInstance(ScriptService.class);
|
||||
}
|
||||
|
||||
private static Map<String, Object> doc(String date, String value) {
|
||||
|
@ -551,7 +550,7 @@ public class SearchTransformIT extends ESIntegTestCase {
|
|||
|
||||
@Override
|
||||
public ScriptContext.Plugin getCustomScriptContexts() {
|
||||
return ScriptServiceProxy.INSTANCE;
|
||||
return WatcherScript.CTX_PLUGIN;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,14 +7,13 @@ package org.elasticsearch.messy.tests;
|
|||
|
||||
import org.elasticsearch.ElasticsearchSecurityException;
|
||||
import org.elasticsearch.Version;
|
||||
import org.elasticsearch.action.search.SearchPhaseExecutionException;
|
||||
import org.elasticsearch.action.search.SearchResponse;
|
||||
import org.elasticsearch.index.query.QueryBuilders;
|
||||
import org.elasticsearch.indices.TermsLookup;
|
||||
import org.elasticsearch.plugins.Plugin;
|
||||
import org.elasticsearch.script.Template;
|
||||
import org.elasticsearch.script.ScriptService;
|
||||
import org.elasticsearch.script.mustache.MustachePlugin;
|
||||
import org.elasticsearch.script.mustache.MustacheScriptEngineService;
|
||||
import org.elasticsearch.script.mustache.TemplateQueryBuilder;
|
||||
import org.elasticsearch.test.SecurityIntegTestCase;
|
||||
import org.elasticsearch.test.SecuritySettingsSource;
|
||||
import org.elasticsearch.xpack.security.authc.support.SecuredString;
|
||||
|
@ -25,7 +24,6 @@ import java.util.ArrayList;
|
|||
import java.util.Collection;
|
||||
|
||||
import static java.util.Collections.singletonMap;
|
||||
import static org.elasticsearch.script.ScriptService.ScriptType.INLINE;
|
||||
import static org.elasticsearch.xpack.security.authc.support.UsernamePasswordToken.basicAuthHeaderValue;
|
||||
import static org.hamcrest.Matchers.containsString;
|
||||
import static org.hamcrest.Matchers.is;
|
||||
|
@ -109,7 +107,7 @@ public class SecurityCachePermissionIT extends SecurityIntegTestCase {
|
|||
|
||||
//Template template = new Template(source, INLINE, MustacheScriptEngineService.NAME, null, singletonMap("name", "token"));
|
||||
SearchResponse response = client().prepareSearch("data").setTypes("a")
|
||||
.setQuery(QueryBuilders.templateQuery(source, singletonMap("name", "token")))
|
||||
.setQuery(new TemplateQueryBuilder(source, ScriptService.ScriptType.INLINE, singletonMap("name", "token")))
|
||||
.execute().actionGet();
|
||||
assertThat(response.isTimedOut(), is(false));
|
||||
assertThat(response.getHits().hits().length, is(1));
|
||||
|
@ -119,7 +117,7 @@ public class SecurityCachePermissionIT extends SecurityIntegTestCase {
|
|||
.filterWithHeader(singletonMap("Authorization", basicAuthHeaderValue(READ_ONE_IDX_USER,
|
||||
new SecuredString("changeme".toCharArray()))))
|
||||
.prepareSearch("data").setTypes("a")
|
||||
.setQuery(QueryBuilders.templateQuery(source, singletonMap("name", "token")))
|
||||
.setQuery(new TemplateQueryBuilder(source, ScriptService.ScriptType.INLINE, singletonMap("name", "token")))
|
||||
.execute().actionGet());
|
||||
assertThat(e.toString(), containsString("ElasticsearchSecurityException[action"));
|
||||
assertThat(e.toString(), containsString("unauthorized"));
|
||||
|
|
|
@ -13,6 +13,7 @@ import org.elasticsearch.client.transport.TransportClient;
|
|||
import org.elasticsearch.common.settings.Settings;
|
||||
import org.elasticsearch.common.transport.TransportAddress;
|
||||
import org.elasticsearch.plugins.Plugin;
|
||||
import org.elasticsearch.transport.MockTransportClient;
|
||||
import org.elasticsearch.xpack.security.Security;
|
||||
import org.elasticsearch.xpack.security.authc.support.SecuredString;
|
||||
import org.elasticsearch.test.ESIntegTestCase;
|
||||
|
@ -115,6 +116,6 @@ public class SecurityTransportClientIT extends ESIntegTestCase {
|
|||
.put("cluster.name", clusterName)
|
||||
.build();
|
||||
|
||||
return TransportClient.builder().settings(settings).addPlugin(XPackPlugin.class).build().addTransportAddress(publishAddress);
|
||||
return new MockTransportClient(settings, XPackPlugin.class).addTransportAddress(publishAddress);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -20,7 +20,9 @@ import org.elasticsearch.env.Environment;
|
|||
import org.elasticsearch.plugins.Plugin;
|
||||
import org.elasticsearch.test.ESIntegTestCase;
|
||||
import org.elasticsearch.xpack.XPackPlugin;
|
||||
import org.elasticsearch.xpack.XPackTransportClient;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
@ -78,7 +80,7 @@ public class CustomRealmIT extends ESIntegTestCase {
|
|||
.put(ThreadContext.PREFIX + "." + CustomRealm.USER_HEADER, CustomRealm.KNOWN_USER)
|
||||
.put(ThreadContext.PREFIX + "." + CustomRealm.PW_HEADER, CustomRealm.KNOWN_PW)
|
||||
.build();
|
||||
try (TransportClient client = TransportClient.builder().settings(settings).addPlugin(XPackPlugin.class).build()) {
|
||||
try (TransportClient client = new XPackTransportClient(settings)) {
|
||||
client.addTransportAddress(publishAddress);
|
||||
ClusterHealthResponse response = client.admin().cluster().prepareHealth().execute().actionGet();
|
||||
assertThat(response.isTimedOut(), is(false));
|
||||
|
@ -98,7 +100,7 @@ public class CustomRealmIT extends ESIntegTestCase {
|
|||
.put(ThreadContext.PREFIX + "." + CustomRealm.USER_HEADER, CustomRealm.KNOWN_USER + randomAsciiOfLength(1))
|
||||
.put(ThreadContext.PREFIX + "." + CustomRealm.PW_HEADER, CustomRealm.KNOWN_PW)
|
||||
.build();
|
||||
try (TransportClient client = TransportClient.builder().addPlugin(XPackPlugin.class).settings(settings).build()) {
|
||||
try (TransportClient client = new XPackTransportClient(settings)) {
|
||||
client.addTransportAddress(publishAddress);
|
||||
client.admin().cluster().prepareHealth().execute().actionGet();
|
||||
fail("authentication failure should have resulted in a NoNodesAvailableException");
|
||||
|
|
|
@ -14,10 +14,8 @@ import org.elasticsearch.common.logging.ESLoggerFactory;
|
|||
import org.elasticsearch.common.settings.Settings;
|
||||
import org.elasticsearch.common.transport.InetSocketTransportAddress;
|
||||
import org.elasticsearch.common.transport.TransportAddress;
|
||||
import org.elasticsearch.node.internal.InternalSettingsPreparer;
|
||||
import org.elasticsearch.test.ESIntegTestCase;
|
||||
import org.elasticsearch.xpack.security.Security;
|
||||
import org.elasticsearch.xpack.XPackPlugin;
|
||||
import org.elasticsearch.xpack.XPackTransportClient;
|
||||
import org.junit.After;
|
||||
import org.junit.AfterClass;
|
||||
import org.junit.Before;
|
||||
|
@ -26,10 +24,8 @@ import org.junit.BeforeClass;
|
|||
import java.net.InetAddress;
|
||||
import java.net.UnknownHostException;
|
||||
import java.nio.file.Path;
|
||||
import java.util.Locale;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
import static com.carrotsearch.randomizedtesting.RandomizedTest.randomAsciiOfLength;
|
||||
import static org.hamcrest.Matchers.notNullValue;
|
||||
|
||||
/**
|
||||
|
@ -81,10 +77,7 @@ public abstract class MigrateToolTestCase extends LuceneTestCase {
|
|||
.put(Security.USER_SETTING.getKey(), "transport_user:changeme")
|
||||
.build();
|
||||
|
||||
TransportClient.Builder transportClientBuilder = TransportClient.builder()
|
||||
.addPlugin(XPackPlugin.class)
|
||||
.settings(clientSettings);
|
||||
TransportClient client = transportClientBuilder.build().addTransportAddresses(transportAddresses);
|
||||
TransportClient client = new XPackTransportClient(clientSettings).addTransportAddresses(transportAddresses);
|
||||
|
||||
logger.info("--> Elasticsearch Java TransportClient started");
|
||||
|
||||
|
|
|
@ -6,37 +6,31 @@
|
|||
package org.elasticsearch.smoketest;
|
||||
|
||||
import com.fasterxml.jackson.core.io.JsonStringEncoder;
|
||||
import org.elasticsearch.cluster.ClusterName;
|
||||
import org.elasticsearch.cluster.ClusterState;
|
||||
import org.elasticsearch.cluster.service.ClusterService;
|
||||
import org.elasticsearch.common.Nullable;
|
||||
import org.elasticsearch.common.settings.Settings;
|
||||
import org.elasticsearch.common.xcontent.XContentType;
|
||||
import org.elasticsearch.env.Environment;
|
||||
import org.elasticsearch.script.ScriptContextRegistry;
|
||||
import org.elasticsearch.script.ScriptEngineRegistry;
|
||||
import org.elasticsearch.script.ScriptEngineService;
|
||||
import org.elasticsearch.script.ScriptService;
|
||||
import org.elasticsearch.script.ScriptSettings;
|
||||
import org.elasticsearch.script.mustache.MustacheScriptEngineService;
|
||||
import org.elasticsearch.test.ESTestCase;
|
||||
import org.elasticsearch.watcher.ResourceWatcherService;
|
||||
import org.elasticsearch.xpack.common.ScriptServiceProxy;
|
||||
import org.elasticsearch.xpack.common.text.DefaultTextTemplateEngine;
|
||||
import org.elasticsearch.xpack.common.text.TextTemplate;
|
||||
import org.elasticsearch.xpack.common.text.TextTemplateEngine;
|
||||
import org.elasticsearch.xpack.watcher.support.WatcherScript;
|
||||
import org.junit.Before;
|
||||
import org.mockito.Mockito;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.StringWriter;
|
||||
import java.io.Writer;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import static org.hamcrest.Matchers.equalTo;
|
||||
import static org.hamcrest.Matchers.notNullValue;
|
||||
|
@ -50,7 +44,7 @@ public class WatcherTemplateTests extends ESTestCase {
|
|||
Settings setting = Settings.builder().put(ScriptService.SCRIPT_AUTO_RELOAD_ENABLED_SETTING, true).build();
|
||||
Environment environment = Mockito.mock(Environment.class);
|
||||
ResourceWatcherService resourceWatcherService = Mockito.mock(ResourceWatcherService.class);
|
||||
ScriptContextRegistry registry = new ScriptContextRegistry(Collections.singletonList(ScriptServiceProxy.INSTANCE));
|
||||
ScriptContextRegistry registry = new ScriptContextRegistry(Collections.singletonList(WatcherScript.CTX_PLUGIN));
|
||||
|
||||
ScriptEngineRegistry scriptEngineRegistry = new ScriptEngineRegistry(
|
||||
Collections.singleton(new MustacheScriptEngineService(setting))
|
||||
|
@ -58,7 +52,7 @@ public class WatcherTemplateTests extends ESTestCase {
|
|||
ScriptSettings scriptSettings = new ScriptSettings(scriptEngineRegistry, registry);
|
||||
ScriptService scriptService = new ScriptService(setting, environment, resourceWatcherService, scriptEngineRegistry,
|
||||
registry, scriptSettings);
|
||||
engine = new DefaultTextTemplateEngine(Settings.EMPTY, ScriptServiceProxy.of(scriptService));
|
||||
engine = new DefaultTextTemplateEngine(Settings.EMPTY, scriptService);
|
||||
}
|
||||
|
||||
public void testEscaping() throws Exception {
|
||||
|
|
|
@ -29,7 +29,7 @@ dependencies {
|
|||
// security deps
|
||||
compile project(path: ':modules:transport-netty3', configuration: 'runtime')
|
||||
compile 'dk.brics.automaton:automaton:1.11-8'
|
||||
compile 'com.unboundid:unboundid-ldapsdk:2.3.8'
|
||||
compile 'com.unboundid:unboundid-ldapsdk:3.1.1'
|
||||
compile 'org.bouncycastle:bcprov-jdk15on:1.54'
|
||||
compile 'org.bouncycastle:bcpkix-jdk15on:1.54'
|
||||
testCompile 'com.google.jimfs:jimfs:1.1'
|
||||
|
|
|
@ -0,0 +1,104 @@
|
|||
#!/bin/bash
|
||||
|
||||
# Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
# or more contributor license agreements. Licensed under the Elastic License;
|
||||
# you may not use this file except in compliance with the Elastic License.
|
||||
|
||||
SCRIPT="$0"
|
||||
|
||||
# SCRIPT may be an arbitrarily deep series of symlinks. Loop until we have the concrete path.
|
||||
while [ -h "$SCRIPT" ] ; do
|
||||
ls=`ls -ld "$SCRIPT"`
|
||||
# Drop everything prior to ->
|
||||
link=`expr "$ls" : '.*-> \(.*\)$'`
|
||||
if expr "$link" : '/.*' > /dev/null; then
|
||||
SCRIPT="$link"
|
||||
else
|
||||
SCRIPT=`dirname "$SCRIPT"`/"$link"
|
||||
fi
|
||||
done
|
||||
|
||||
# determine elasticsearch home
|
||||
ES_HOME=`dirname "$SCRIPT"`/../..
|
||||
|
||||
# make ELASTICSEARCH_HOME absolute
|
||||
ES_HOME=`cd "$ES_HOME"; pwd`
|
||||
|
||||
# If an include wasn't specified in the environment, then search for one...
|
||||
if [ "x$ES_INCLUDE" = "x" ]; then
|
||||
# Locations (in order) to use when searching for an include file.
|
||||
for include in /usr/share/elasticsearch/elasticsearch.in.sh \
|
||||
/usr/local/share/elasticsearch/elasticsearch.in.sh \
|
||||
/opt/elasticsearch/elasticsearch.in.sh \
|
||||
~/.elasticsearch.in.sh \
|
||||
"`dirname "$0"`"/../elasticsearch.in.sh \
|
||||
"$ES_HOME/bin/elasticsearch.in.sh"; do
|
||||
if [ -r "$include" ]; then
|
||||
. "$include"
|
||||
break
|
||||
fi
|
||||
done
|
||||
# ...otherwise, source the specified include.
|
||||
elif [ -r "$ES_INCLUDE" ]; then
|
||||
. "$ES_INCLUDE"
|
||||
fi
|
||||
|
||||
if [ -x "$JAVA_HOME/bin/java" ]; then
|
||||
JAVA="$JAVA_HOME/bin/java"
|
||||
else
|
||||
JAVA=`which java`
|
||||
fi
|
||||
|
||||
if [ ! -x "$JAVA" ]; then
|
||||
echo "Could not find any executable java binary. Please install java in your PATH or set JAVA_HOME"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ -z "$ES_CLASSPATH" ]; then
|
||||
echo "You must set the ES_CLASSPATH var" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ -z "$CONF_DIR" ]; then
|
||||
# Try to read package config files
|
||||
if [ -f "/etc/sysconfig/elasticsearch" ]; then
|
||||
CONF_DIR=/etc/elasticsearch
|
||||
|
||||
. "/etc/sysconfig/elasticsearch"
|
||||
elif [ -f "/etc/default/elasticsearch" ]; then
|
||||
CONF_DIR=/etc/elasticsearch
|
||||
|
||||
. "/etc/default/elasticsearch"
|
||||
fi
|
||||
fi
|
||||
|
||||
export HOSTNAME=`hostname -s`
|
||||
|
||||
# include x-pack jars in classpath
|
||||
ES_CLASSPATH="$ES_CLASSPATH:$ES_HOME/plugins/x-pack/*"
|
||||
|
||||
# don't let JAVA_TOOL_OPTIONS slip in (e.g. crazy agents in ubuntu)
|
||||
# works around https://bugs.launchpad.net/ubuntu/+source/jayatana/+bug/1441487
|
||||
if [ "x$JAVA_TOOL_OPTIONS" != "x" ]; then
|
||||
echo "Warning: Ignoring JAVA_TOOL_OPTIONS=$JAVA_TOOL_OPTIONS"
|
||||
echo "Please pass JVM parameters via ES_JAVA_OPTS instead"
|
||||
unset JAVA_TOOL_OPTIONS
|
||||
fi
|
||||
|
||||
# CONF_FILE setting was removed
|
||||
if [ ! -z "$CONF_FILE" ]; then
|
||||
echo "CONF_FILE setting is no longer supported. elasticsearch.yml must be placed in the config directory and cannot be renamed."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
declare -a args=("$@")
|
||||
|
||||
if [ -e "$CONF_DIR" ]; then
|
||||
args=("${args[@]}" -Edefault.path.conf="$CONF_DIR")
|
||||
fi
|
||||
|
||||
cd "$ES_HOME" > /dev/null
|
||||
"$JAVA" $ES_JAVA_OPTS -cp "$ES_CLASSPATH" -Des.path.home="$ES_HOME" org.elasticsearch.xpack.security.ssl.CertificateTool "${args[@]}"
|
||||
status=$?
|
||||
cd - > /dev/null
|
||||
exit $status
|
|
@ -0,0 +1,9 @@
|
|||
@echo off
|
||||
|
||||
rem Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
rem or more contributor license agreements. Licensed under the Elastic License;
|
||||
rem you may not use this file except in compliance with the Elastic License.
|
||||
|
||||
PUSHD "%~dp0"
|
||||
CALL "%~dp0.in.bat" org.elasticsearch.xpack.security.ssl.CertificateTool %*
|
||||
POPD
|
|
@ -59,15 +59,17 @@ if [ -z "$ES_CLASSPATH" ]; then
|
|||
exit 1
|
||||
fi
|
||||
|
||||
# Try to read package config files
|
||||
if [ -f "/etc/sysconfig/elasticsearch" ]; then
|
||||
CONF_DIR=/etc/elasticsearch
|
||||
if [ -z "$CONF_DIR" ]; then
|
||||
# Try to read package config files
|
||||
if [ -f "/etc/sysconfig/elasticsearch" ]; then
|
||||
CONF_DIR=/etc/elasticsearch
|
||||
|
||||
. "/etc/sysconfig/elasticsearch"
|
||||
elif [ -f "/etc/default/elasticsearch" ]; then
|
||||
CONF_DIR=/etc/elasticsearch
|
||||
. "/etc/sysconfig/elasticsearch"
|
||||
elif [ -f "/etc/default/elasticsearch" ]; then
|
||||
CONF_DIR=/etc/elasticsearch
|
||||
|
||||
. "/etc/default/elasticsearch"
|
||||
. "/etc/default/elasticsearch"
|
||||
fi
|
||||
fi
|
||||
|
||||
export HOSTNAME=`hostname -s`
|
||||
|
|
|
@ -59,15 +59,17 @@ if [ -z "$ES_CLASSPATH" ]; then
|
|||
exit 1
|
||||
fi
|
||||
|
||||
# Try to read package config files
|
||||
if [ -f "/etc/sysconfig/elasticsearch" ]; then
|
||||
CONF_DIR=/etc/elasticsearch
|
||||
if [ -z "$CONF_DIR" ]; then
|
||||
# Try to read package config files
|
||||
if [ -f "/etc/sysconfig/elasticsearch" ]; then
|
||||
CONF_DIR=/etc/elasticsearch
|
||||
|
||||
. "/etc/sysconfig/elasticsearch"
|
||||
elif [ -f "/etc/default/elasticsearch" ]; then
|
||||
CONF_DIR=/etc/elasticsearch
|
||||
. "/etc/sysconfig/elasticsearch"
|
||||
elif [ -f "/etc/default/elasticsearch" ]; then
|
||||
CONF_DIR=/etc/elasticsearch
|
||||
|
||||
. "/etc/default/elasticsearch"
|
||||
. "/etc/default/elasticsearch"
|
||||
fi
|
||||
fi
|
||||
|
||||
export HOSTNAME=`hostname -s`
|
||||
|
|
|
@ -59,15 +59,17 @@ if [ -z "$ES_CLASSPATH" ]; then
|
|||
exit 1
|
||||
fi
|
||||
|
||||
# Try to read package config files
|
||||
if [ -f "/etc/sysconfig/elasticsearch" ]; then
|
||||
CONF_DIR=/etc/elasticsearch
|
||||
if [ -z "$CONF_DIR" ]; then
|
||||
# Try to read package config files
|
||||
if [ -f "/etc/sysconfig/elasticsearch" ]; then
|
||||
CONF_DIR=/etc/elasticsearch
|
||||
|
||||
. "/etc/sysconfig/elasticsearch"
|
||||
elif [ -f "/etc/default/elasticsearch" ]; then
|
||||
CONF_DIR=/etc/elasticsearch
|
||||
. "/etc/sysconfig/elasticsearch"
|
||||
elif [ -f "/etc/default/elasticsearch" ]; then
|
||||
CONF_DIR=/etc/elasticsearch
|
||||
|
||||
. "/etc/default/elasticsearch"
|
||||
. "/etc/default/elasticsearch"
|
||||
fi
|
||||
fi
|
||||
|
||||
export HOSTNAME=`hostname -s`
|
||||
|
|
|
@ -82,7 +82,8 @@ public class SecurityTemplateService extends AbstractComponent implements Cluste
|
|||
if (securityIndexRouting == null) {
|
||||
if (event.localNodeMaster()) {
|
||||
ClusterState state = event.state();
|
||||
// TODO for the future need to add some checking in the event the template needs to be updated...
|
||||
// norelease we need to add some checking in the event the template needs to be updated and also the mappings need to be
|
||||
// updated on index too!
|
||||
IndexTemplateMetaData templateMeta = state.metaData().templates().get(SECURITY_TEMPLATE_NAME);
|
||||
final boolean createTemplate = (templateMeta == null);
|
||||
|
||||
|
|
|
@ -8,18 +8,19 @@ package org.elasticsearch.xpack.security.action.role;
|
|||
import org.elasticsearch.action.ActionRequest;
|
||||
import org.elasticsearch.action.ActionRequestValidationException;
|
||||
import org.elasticsearch.action.support.WriteRequest;
|
||||
import org.elasticsearch.action.support.WriteRequest.RefreshPolicy;
|
||||
import org.elasticsearch.common.Nullable;
|
||||
import org.elasticsearch.common.Strings;
|
||||
import org.elasticsearch.common.bytes.BytesReference;
|
||||
import org.elasticsearch.common.io.stream.StreamInput;
|
||||
import org.elasticsearch.common.io.stream.StreamOutput;
|
||||
import org.elasticsearch.xpack.security.authz.RoleDescriptor;
|
||||
import org.elasticsearch.xpack.security.support.MetadataUtils;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import static org.elasticsearch.action.ValidateActions.addValidationError;
|
||||
|
||||
|
@ -33,6 +34,7 @@ public class PutRoleRequest extends ActionRequest<PutRoleRequest> implements Wri
|
|||
private List<RoleDescriptor.IndicesPrivileges> indicesPrivileges = new ArrayList<>();
|
||||
private String[] runAs = Strings.EMPTY_ARRAY;
|
||||
private RefreshPolicy refreshPolicy = RefreshPolicy.IMMEDIATE;
|
||||
private Map<String, Object> metadata;
|
||||
|
||||
public PutRoleRequest() {
|
||||
}
|
||||
|
@ -43,6 +45,10 @@ public class PutRoleRequest extends ActionRequest<PutRoleRequest> implements Wri
|
|||
if (name == null) {
|
||||
validationException = addValidationError("role name is missing", validationException);
|
||||
}
|
||||
if (metadata != null && MetadataUtils.containsReservedMetadata(metadata)) {
|
||||
validationException =
|
||||
addValidationError("metadata keys may not start with [" + MetadataUtils.RESERVED_PREFIX + "]", validationException);
|
||||
}
|
||||
return validationException;
|
||||
}
|
||||
|
||||
|
@ -86,6 +92,10 @@ public class PutRoleRequest extends ActionRequest<PutRoleRequest> implements Wri
|
|||
return refreshPolicy;
|
||||
}
|
||||
|
||||
public void metadata(Map<String, Object> metadata) {
|
||||
this.metadata = metadata;
|
||||
}
|
||||
|
||||
public String name() {
|
||||
return name;
|
||||
}
|
||||
|
@ -102,6 +112,10 @@ public class PutRoleRequest extends ActionRequest<PutRoleRequest> implements Wri
|
|||
return runAs;
|
||||
}
|
||||
|
||||
public Map<String, Object> metadata() {
|
||||
return metadata;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void readFrom(StreamInput in) throws IOException {
|
||||
super.readFrom(in);
|
||||
|
@ -114,6 +128,7 @@ public class PutRoleRequest extends ActionRequest<PutRoleRequest> implements Wri
|
|||
}
|
||||
runAs = in.readStringArray();
|
||||
refreshPolicy = RefreshPolicy.readFrom(in);
|
||||
metadata = in.readMap();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -127,12 +142,14 @@ public class PutRoleRequest extends ActionRequest<PutRoleRequest> implements Wri
|
|||
}
|
||||
out.writeStringArray(runAs);
|
||||
refreshPolicy.writeTo(out);
|
||||
out.writeMap(metadata);
|
||||
}
|
||||
|
||||
RoleDescriptor roleDescriptor() {
|
||||
return new RoleDescriptor(name,
|
||||
clusterPrivileges,
|
||||
indicesPrivileges.toArray(new RoleDescriptor.IndicesPrivileges[indicesPrivileges.size()]),
|
||||
runAs);
|
||||
runAs,
|
||||
metadata);
|
||||
}
|
||||
}
|
|
@ -12,6 +12,8 @@ import org.elasticsearch.common.Nullable;
|
|||
import org.elasticsearch.common.bytes.BytesReference;
|
||||
import org.elasticsearch.xpack.security.authz.RoleDescriptor;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Builder for requests to add a role to the administrative index
|
||||
*/
|
||||
|
@ -33,6 +35,7 @@ public class PutRoleRequestBuilder extends ActionRequestBuilder<PutRoleRequest,
|
|||
request.cluster(descriptor.getClusterPrivileges());
|
||||
request.addIndex(descriptor.getIndicesPrivileges());
|
||||
request.runAs(descriptor.getRunAs());
|
||||
request.metadata(descriptor.getMetadata());
|
||||
return this;
|
||||
}
|
||||
|
||||
|
@ -56,4 +59,9 @@ public class PutRoleRequestBuilder extends ActionRequestBuilder<PutRoleRequest,
|
|||
request.addIndex(indices, privileges, fields, query);
|
||||
return this;
|
||||
}
|
||||
|
||||
public PutRoleRequestBuilder metadata(Map<String, Object> metadata) {
|
||||
request.metadata(metadata);
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -28,7 +28,6 @@ import org.elasticsearch.common.Nullable;
|
|||
import org.elasticsearch.common.collect.Tuple;
|
||||
import org.elasticsearch.common.component.AbstractComponent;
|
||||
import org.elasticsearch.common.inject.Inject;
|
||||
import org.elasticsearch.common.inject.Provider;
|
||||
import org.elasticsearch.common.io.Streams;
|
||||
import org.elasticsearch.common.logging.ESLogger;
|
||||
import org.elasticsearch.common.network.NetworkAddress;
|
||||
|
@ -56,9 +55,8 @@ import org.elasticsearch.xpack.security.authz.privilege.SystemPrivilege;
|
|||
import org.elasticsearch.xpack.security.rest.RemoteHostHeader;
|
||||
import org.elasticsearch.xpack.security.transport.filter.SecurityIpFilterRule;
|
||||
import org.elasticsearch.threadpool.ThreadPool;
|
||||
import org.elasticsearch.transport.Transport;
|
||||
import org.elasticsearch.transport.TransportMessage;
|
||||
import org.elasticsearch.xpack.XPackPlugin;
|
||||
import org.elasticsearch.xpack.XPackTransportClient;
|
||||
import org.joda.time.DateTime;
|
||||
import org.joda.time.DateTimeZone;
|
||||
|
||||
|
@ -737,12 +735,9 @@ public class IndexAuditTrail extends AbstractComponent implements AuditTrail, Cl
|
|||
+ REMOTE_CLIENT_SETTINGS.getKey() + ".hosts]");
|
||||
}
|
||||
final Settings theClientSetting = clientSettings.filter((s) -> s.startsWith("hosts") == false); // hosts is not a valid setting
|
||||
final TransportClient transportClient = TransportClient.builder()
|
||||
.settings(Settings.builder()
|
||||
final TransportClient transportClient = new XPackTransportClient(Settings.builder()
|
||||
.put("node.name", DEFAULT_CLIENT_NAME + "-" + Node.NODE_NAME_SETTING.get(settings))
|
||||
.put(theClientSetting))
|
||||
.addPlugin(XPackPlugin.class)
|
||||
.build();
|
||||
.put(theClientSetting).build());
|
||||
for (Tuple<String, Integer> pair : hostPortPairs) {
|
||||
try {
|
||||
transportClient.addTransportAddress(new InetSocketTransportAddress(InetAddress.getByName(pair.v1()), pair.v2()));
|
||||
|
|
|
@ -13,15 +13,15 @@ import com.unboundid.ldap.sdk.SearchRequest;
|
|||
import com.unboundid.ldap.sdk.SearchResult;
|
||||
import com.unboundid.ldap.sdk.SearchResultEntry;
|
||||
import com.unboundid.ldap.sdk.SearchScope;
|
||||
import org.elasticsearch.common.Strings;
|
||||
import org.elasticsearch.common.logging.ESLogger;
|
||||
import org.elasticsearch.common.settings.Settings;
|
||||
import org.elasticsearch.common.unit.TimeValue;
|
||||
import org.elasticsearch.xpack.security.authc.ldap.support.LdapSearchScope;
|
||||
import org.elasticsearch.xpack.security.authc.ldap.support.LdapSession.GroupsResolver;
|
||||
import org.elasticsearch.xpack.security.support.Exceptions;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import static org.elasticsearch.xpack.security.authc.ldap.support.LdapUtils.OBJECT_CLASS_PRESENCE_FILTER;
|
||||
|
@ -41,9 +41,14 @@ public class ActiveDirectoryGroupsResolver implements GroupsResolver {
|
|||
this.scope = LdapSearchScope.resolve(settings.get("scope"), LdapSearchScope.SUB_TREE);
|
||||
}
|
||||
|
||||
public List<String> resolve(LDAPInterface connection, String userDn, TimeValue timeout, ESLogger logger) {
|
||||
@Override
|
||||
public List<String> resolve(LDAPInterface connection, String userDn, TimeValue timeout, ESLogger logger,
|
||||
Collection<Attribute> attributes) {
|
||||
Filter groupSearchFilter = buildGroupQuery(connection, userDn, timeout, logger);
|
||||
logger.debug("group SID to DN search filter: [{}]", groupSearchFilter);
|
||||
if (groupSearchFilter == null) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
SearchRequest searchRequest = new SearchRequest(baseDn, scope.scope(), groupSearchFilter, SearchRequest.NO_ATTRIBUTES);
|
||||
searchRequest.setTimeLimitSeconds(Math.toIntExact(timeout.seconds()));
|
||||
|
@ -51,7 +56,8 @@ public class ActiveDirectoryGroupsResolver implements GroupsResolver {
|
|||
try {
|
||||
results = search(connection, searchRequest, logger);
|
||||
} catch (LDAPException e) {
|
||||
throw Exceptions.authenticationError("failed to fetch AD groups for DN [{}]", e, userDn);
|
||||
logger.error("failed to fetch AD groups for DN [{}]", e, userDn);
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
List<String> groupList = new ArrayList<>();
|
||||
|
@ -64,11 +70,20 @@ public class ActiveDirectoryGroupsResolver implements GroupsResolver {
|
|||
return groupList;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] attributes() {
|
||||
// we have to return null since the tokenGroups attribute is computed and can only be retrieved using a BASE level search
|
||||
return null;
|
||||
}
|
||||
|
||||
static Filter buildGroupQuery(LDAPInterface connection, String userDn, TimeValue timeout, ESLogger logger) {
|
||||
try {
|
||||
SearchRequest request = new SearchRequest(userDn, SearchScope.BASE, OBJECT_CLASS_PRESENCE_FILTER, "tokenGroups");
|
||||
request.setTimeLimitSeconds(Math.toIntExact(timeout.seconds()));
|
||||
SearchResultEntry entry = searchForEntry(connection, request, logger);
|
||||
if (entry == null) {
|
||||
return null;
|
||||
}
|
||||
Attribute attribute = entry.getAttribute("tokenGroups");
|
||||
byte[][] tokenGroupSIDBytes = attribute.getValueByteArrays();
|
||||
List<Filter> orFilters = new ArrayList<>(tokenGroupSIDBytes.length);
|
||||
|
@ -77,7 +92,8 @@ public class ActiveDirectoryGroupsResolver implements GroupsResolver {
|
|||
}
|
||||
return Filter.createORFilter(orFilters);
|
||||
} catch (LDAPException e) {
|
||||
throw Exceptions.authenticationError("failed to fetch AD groups for DN [{}]", e, userDn);
|
||||
logger.error("failed to fetch AD groups for DN [{}]", e, userDn);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -20,7 +20,7 @@ public class ActiveDirectoryRealm extends AbstractLdapRealm {
|
|||
public static final String TYPE = "active_directory";
|
||||
|
||||
public ActiveDirectoryRealm(RealmConfig config, ResourceWatcherService watcherService, ClientSSLService clientSSLService) {
|
||||
this(config, new ActiveDirectorySessionFactory(config, clientSSLService).init(),
|
||||
this(config, new ActiveDirectorySessionFactory(config, clientSSLService),
|
||||
new DnRoleMapper(TYPE, config, watcherService, null));
|
||||
}
|
||||
|
||||
|
|
|
@ -5,12 +5,18 @@
|
|||
*/
|
||||
package org.elasticsearch.xpack.security.authc.activedirectory;
|
||||
|
||||
import com.unboundid.ldap.sdk.Attribute;
|
||||
import com.unboundid.ldap.sdk.LDAPConnection;
|
||||
import com.unboundid.ldap.sdk.LDAPConnectionOptions;
|
||||
import com.unboundid.ldap.sdk.LDAPException;
|
||||
import com.unboundid.ldap.sdk.SearchRequest;
|
||||
import com.unboundid.ldap.sdk.SearchResult;
|
||||
import org.elasticsearch.common.Strings;
|
||||
import org.elasticsearch.ElasticsearchException;
|
||||
import org.elasticsearch.common.cache.Cache;
|
||||
import org.elasticsearch.common.cache.CacheBuilder;
|
||||
import org.elasticsearch.common.logging.ESLogger;
|
||||
import org.elasticsearch.common.settings.Settings;
|
||||
import org.elasticsearch.common.unit.TimeValue;
|
||||
import org.elasticsearch.xpack.security.authc.RealmConfig;
|
||||
import org.elasticsearch.xpack.security.authc.ldap.support.LdapSearchScope;
|
||||
import org.elasticsearch.xpack.security.authc.ldap.support.LdapSession;
|
||||
|
@ -19,11 +25,11 @@ import org.elasticsearch.xpack.security.authc.ldap.support.SessionFactory;
|
|||
import org.elasticsearch.xpack.security.authc.support.SecuredString;
|
||||
import org.elasticsearch.xpack.security.ssl.ClientSSLService;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
|
||||
import static org.elasticsearch.xpack.security.authc.ldap.support.LdapUtils.attributesToSearchFor;
|
||||
import static org.elasticsearch.xpack.security.authc.ldap.support.LdapUtils.createFilter;
|
||||
import static org.elasticsearch.xpack.security.authc.ldap.support.LdapUtils.search;
|
||||
import static org.elasticsearch.xpack.security.support.Exceptions.authenticationError;
|
||||
|
||||
/**
|
||||
* This Class creates LdapSessions authenticating via the custom Active Directory protocol. (that being
|
||||
|
@ -40,12 +46,13 @@ public class ActiveDirectorySessionFactory extends SessionFactory {
|
|||
public static final String AD_USER_SEARCH_BASEDN_SETTING = "user_search.base_dn";
|
||||
public static final String AD_USER_SEARCH_FILTER_SETTING = "user_search.filter";
|
||||
public static final String AD_USER_SEARCH_SCOPE_SETTING = "user_search.scope";
|
||||
private static final String NETBIOS_NAME_FILTER_TEMPLATE = "(netbiosname={0})";
|
||||
|
||||
private final String userSearchDN;
|
||||
private final String domainName;
|
||||
private final String userSearchFilter;
|
||||
private final LdapSearchScope userSearchScope;
|
||||
private final GroupsResolver groupResolver;
|
||||
private final DefaultADAuthenticator defaultADAuthenticator;
|
||||
private final DownLevelADAuthenticator downLevelADAuthenticator;
|
||||
private final UpnADAuthenticator upnADAuthenticator;
|
||||
|
||||
public ActiveDirectorySessionFactory(RealmConfig config, ClientSSLService sslService) {
|
||||
super(config, sslService);
|
||||
|
@ -55,63 +62,227 @@ public class ActiveDirectorySessionFactory extends SessionFactory {
|
|||
throw new IllegalArgumentException("missing [" + AD_DOMAIN_NAME_SETTING + "] setting for active directory");
|
||||
}
|
||||
String domainDN = buildDnFromDomain(domainName);
|
||||
userSearchDN = settings.get(AD_USER_SEARCH_BASEDN_SETTING, domainDN);
|
||||
userSearchScope = LdapSearchScope.resolve(settings.get(AD_USER_SEARCH_SCOPE_SETTING), LdapSearchScope.SUB_TREE);
|
||||
userSearchFilter = settings.get(AD_USER_SEARCH_FILTER_SETTING, "(&(objectClass=user)(|(sAMAccountName={0})" +
|
||||
"(userPrincipalName={0}@" + domainName + ")))");
|
||||
groupResolver = new ActiveDirectoryGroupsResolver(settings.getAsSettings("group_search"), domainDN);
|
||||
defaultADAuthenticator = new DefaultADAuthenticator(settings, timeout, logger, groupResolver, domainDN);
|
||||
downLevelADAuthenticator = new DownLevelADAuthenticator(settings, timeout, logger, groupResolver, domainDN);
|
||||
upnADAuthenticator = new UpnADAuthenticator(settings, timeout, logger, groupResolver, domainDN);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected LDAPServers ldapServers(Settings settings) {
|
||||
String[] ldapUrls = settings.getAsArray(URLS_SETTING, new String[]{"ldap://" + domainName + ":389"});
|
||||
return new LDAPServers(ldapUrls);
|
||||
protected String[] getDefaultLdapUrls(Settings settings) {
|
||||
return new String[] {"ldap://" + settings.get(AD_DOMAIN_NAME_SETTING) + ":389"};
|
||||
}
|
||||
|
||||
/**
|
||||
* This is an active directory bind that looks up the user DN after binding with a windows principal.
|
||||
*
|
||||
* @param userName name of the windows user without the domain
|
||||
* @return An authenticated
|
||||
* @param username name of the windows user without the domain
|
||||
* @return An authenticated LdapSession
|
||||
*/
|
||||
@Override
|
||||
protected LdapSession getSession(String userName, SecuredString password) throws Exception {
|
||||
LDAPConnection connection;
|
||||
|
||||
try {
|
||||
connection = serverSet.getConnection();
|
||||
} catch (LDAPException e) {
|
||||
throw new IOException("failed to connect to any active directory servers", e);
|
||||
}
|
||||
|
||||
String userPrincipal = userName + "@" + domainName;
|
||||
try {
|
||||
connection.bind(userPrincipal, new String(password.internalChars()));
|
||||
SearchRequest searchRequest = new SearchRequest(userSearchDN, userSearchScope.scope(),
|
||||
createFilter(userSearchFilter, userName), SearchRequest.NO_ATTRIBUTES);
|
||||
searchRequest.setTimeLimitSeconds(Math.toIntExact(timeout.seconds()));
|
||||
SearchResult results = search(connection, searchRequest, logger);
|
||||
int numResults = results.getEntryCount();
|
||||
if (numResults > 1) {
|
||||
throw new IllegalStateException("search for user [" + userName + "] by principle name yielded multiple results");
|
||||
} else if (numResults < 1) {
|
||||
throw new IllegalStateException("search for user [" + userName + "] by principle name yielded no results");
|
||||
}
|
||||
String dn = results.getSearchEntries().get(0).getDN();
|
||||
return new LdapSession(connectionLogger, connection, dn, groupResolver, timeout);
|
||||
} catch (LDAPException e) {
|
||||
connection.close();
|
||||
throw authenticationError("unable to authenticate user [{}] to active directory domain [{}]", e, userName, domainName);
|
||||
}
|
||||
protected LdapSession getSession(String username, SecuredString password) throws Exception {
|
||||
LDAPConnection connection = serverSet.getConnection();
|
||||
ADAuthenticator authenticator = getADAuthenticator(username);
|
||||
return authenticator.authenticate(connection, username, password);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param domain active directory domain name
|
||||
* @return LDAP DN, distinguished name, of the root of the domain
|
||||
*/
|
||||
String buildDnFromDomain(String domain) {
|
||||
static String buildDnFromDomain(String domain) {
|
||||
return "DC=" + domain.replace(".", ",DC=");
|
||||
}
|
||||
|
||||
ADAuthenticator getADAuthenticator(String username) {
|
||||
if (username.indexOf('\\') > 0) {
|
||||
return downLevelADAuthenticator;
|
||||
} else if (username.indexOf("@") > 0) {
|
||||
return upnADAuthenticator;
|
||||
}
|
||||
return defaultADAuthenticator;
|
||||
}
|
||||
|
||||
abstract static class ADAuthenticator {
|
||||
|
||||
final TimeValue timeout;
|
||||
final ESLogger logger;
|
||||
final GroupsResolver groupsResolver;
|
||||
final String userSearchDN;
|
||||
final LdapSearchScope userSearchScope;
|
||||
|
||||
ADAuthenticator(Settings settings, TimeValue timeout, ESLogger logger, GroupsResolver groupsResolver, String domainDN) {
|
||||
this.timeout = timeout;
|
||||
this.logger = logger;
|
||||
this.groupsResolver = groupsResolver;
|
||||
userSearchDN = settings.get(AD_USER_SEARCH_BASEDN_SETTING, domainDN);
|
||||
userSearchScope = LdapSearchScope.resolve(settings.get(AD_USER_SEARCH_SCOPE_SETTING), LdapSearchScope.SUB_TREE);
|
||||
}
|
||||
|
||||
LdapSession authenticate(LDAPConnection connection, String username, SecuredString password) throws LDAPException {
|
||||
boolean success = false;
|
||||
try {
|
||||
connection.bind(bindUsername(username), new String(password.internalChars()));
|
||||
SearchRequest searchRequest = getSearchRequest(connection, username, password);
|
||||
searchRequest.setTimeLimitSeconds(Math.toIntExact(timeout.seconds()));
|
||||
SearchResult results = search(connection, searchRequest, logger);
|
||||
int numResults = results.getEntryCount();
|
||||
if (numResults > 1) {
|
||||
throw new IllegalStateException("search for user [" + username + "] by principle name yielded multiple results");
|
||||
} else if (numResults < 1) {
|
||||
throw new IllegalStateException("search for user [" + username + "] by principle name yielded no results");
|
||||
}
|
||||
|
||||
String dn = results.getSearchEntries().get(0).getDN();
|
||||
LdapSession session = new LdapSession(logger, connection, dn, groupsResolver, timeout, null);
|
||||
success = true;
|
||||
return session;
|
||||
} finally {
|
||||
if (success == false) {
|
||||
connection.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
String bindUsername(String username) {
|
||||
return username;
|
||||
}
|
||||
|
||||
abstract SearchRequest getSearchRequest(LDAPConnection connection, String username, SecuredString password) throws LDAPException;
|
||||
}
|
||||
|
||||
/**
|
||||
* This authenticator is used for usernames that do not contain an `@` or `/`. It attempts a bind with the provided username combined
|
||||
* with the domain name specified in settings. On AD DS this will work for both upn@domain and samaccountname@domain; AD LDS will only
|
||||
* support the upn format
|
||||
*/
|
||||
static class DefaultADAuthenticator extends ADAuthenticator {
|
||||
|
||||
final String userSearchFilter;
|
||||
|
||||
final String domainName;
|
||||
DefaultADAuthenticator(Settings settings, TimeValue timeout, ESLogger logger, GroupsResolver groupsResolver, String domainDN) {
|
||||
super(settings, timeout, logger, groupsResolver, domainDN);
|
||||
domainName = settings.get(AD_DOMAIN_NAME_SETTING);
|
||||
userSearchFilter = settings.get(AD_USER_SEARCH_FILTER_SETTING, "(&(objectClass=user)(|(sAMAccountName={0})" +
|
||||
"(userPrincipalName={0}@" + domainName + ")))");
|
||||
}
|
||||
|
||||
@Override
|
||||
SearchRequest getSearchRequest(LDAPConnection connection, String username, SecuredString password) throws LDAPException {
|
||||
return new SearchRequest(userSearchDN, userSearchScope.scope(),
|
||||
createFilter(userSearchFilter, username), attributesToSearchFor(groupsResolver.attributes()));
|
||||
}
|
||||
|
||||
@Override
|
||||
String bindUsername(String username) {
|
||||
return username + "@" + domainName;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Active Directory calls the format <code>DOMAIN\\username</code> down-level credentials and this class contains the logic necessary
|
||||
* to authenticate this form of a username
|
||||
*/
|
||||
static class DownLevelADAuthenticator extends ADAuthenticator {
|
||||
Cache<String, String> domainNameCache = CacheBuilder.<String, String>builder().setMaximumWeight(100).build();
|
||||
|
||||
final String domainDN;
|
||||
final Settings settings;
|
||||
|
||||
DownLevelADAuthenticator(Settings settings, TimeValue timeout, ESLogger logger, GroupsResolver groupsResolver, String domainDN) {
|
||||
super(settings, timeout, logger, groupsResolver, domainDN);
|
||||
this.domainDN = domainDN;
|
||||
this.settings = settings;
|
||||
}
|
||||
|
||||
SearchRequest getSearchRequest(LDAPConnection connection, String username, SecuredString password) throws LDAPException {
|
||||
String[] parts = username.split("\\\\");
|
||||
assert parts.length == 2;
|
||||
final String netBiosDomainName = parts[0];
|
||||
final String accountName = parts[1];
|
||||
|
||||
final String domainDn = netBiosDomainNameToDn(connection, netBiosDomainName, username, password);
|
||||
|
||||
return new SearchRequest(domainDn, LdapSearchScope.SUB_TREE.scope(),
|
||||
createFilter("(&(objectClass=user)(sAMAccountName={0}))", accountName),
|
||||
attributesToSearchFor(groupsResolver.attributes()));
|
||||
}
|
||||
|
||||
String netBiosDomainNameToDn(LDAPConnection connection, String netBiosDomainName, String username, SecuredString password)
|
||||
throws LDAPException {
|
||||
try {
|
||||
return domainNameCache.computeIfAbsent(netBiosDomainName, (key) -> {
|
||||
LDAPConnection searchConnection = connection;
|
||||
boolean openedConnection = false;
|
||||
try {
|
||||
// global catalog does not replicate the necessary information by default
|
||||
// TODO add settings for ports and maybe cache connectionOptions
|
||||
if (usingGlobalCatalog(settings, connection)) {
|
||||
LDAPConnectionOptions options = connectionOptions(settings);
|
||||
if (connection.getSSLSession() != null) {
|
||||
searchConnection = new LDAPConnection(connection.getSocketFactory(), options,
|
||||
connection.getConnectedAddress(), 636);
|
||||
} else {
|
||||
searchConnection = new LDAPConnection(options, connection.getConnectedAddress(), 389);
|
||||
}
|
||||
openedConnection = true;
|
||||
searchConnection.bind(username, new String(password.internalChars()));
|
||||
}
|
||||
|
||||
SearchRequest searchRequest = new SearchRequest(domainDN, LdapSearchScope.SUB_TREE.scope(),
|
||||
createFilter(NETBIOS_NAME_FILTER_TEMPLATE, netBiosDomainName), "ncname");
|
||||
SearchResult results = search(searchConnection, searchRequest, logger);
|
||||
if (results.getEntryCount() > 0) {
|
||||
Attribute attribute = results.getSearchEntries().get(0).getAttribute("ncname");
|
||||
if (attribute != null) {
|
||||
return attribute.getValue();
|
||||
}
|
||||
}
|
||||
logger.debug("failed to find domain name DN from netbios name [{}]", netBiosDomainName);
|
||||
return null;
|
||||
} finally {
|
||||
if (openedConnection) {
|
||||
searchConnection.close();
|
||||
}
|
||||
}
|
||||
});
|
||||
} catch (ExecutionException e) {
|
||||
Throwable cause = e.getCause();
|
||||
if (cause instanceof LDAPException) {
|
||||
throw (LDAPException) cause;
|
||||
} else {
|
||||
connection.close();
|
||||
throw new ElasticsearchException("error occurred while mapping [{}] to domain DN", cause, netBiosDomainName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static boolean usingGlobalCatalog(Settings settings, LDAPConnection ldapConnection) {
|
||||
Boolean usingGlobalCatalog = settings.getAsBoolean("global_catalog", null);
|
||||
if (usingGlobalCatalog != null) {
|
||||
return usingGlobalCatalog;
|
||||
}
|
||||
return ldapConnection.getConnectedPort() == 3268 || ldapConnection.getConnectedPort() == 3269;
|
||||
}
|
||||
}
|
||||
|
||||
static class UpnADAuthenticator extends ADAuthenticator {
|
||||
|
||||
private static final String UPN_USER_FILTER = "(&(objectClass=user)(|(sAMAccountName={0})(userPrincipalName={1})))";
|
||||
|
||||
UpnADAuthenticator(Settings settings, TimeValue timeout, ESLogger logger, GroupsResolver groupsResolver, String domainDN) {
|
||||
super(settings, timeout, logger, groupsResolver, domainDN);
|
||||
}
|
||||
|
||||
SearchRequest getSearchRequest(LDAPConnection connection, String username, SecuredString password) throws LDAPException {
|
||||
String[] parts = username.split("@");
|
||||
assert parts.length == 2;
|
||||
final String accountName = parts[0];
|
||||
final String domainName = parts[1];
|
||||
final String domainDN = buildDnFromDomain(domainName);
|
||||
return new SearchRequest(domainDN, LdapSearchScope.SUB_TREE.scope(),
|
||||
createFilter(UPN_USER_FILTER, accountName, username), attributesToSearchFor(groupsResolver.attributes()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,6 +7,8 @@ package org.elasticsearch.xpack.security.authc.ldap;
|
|||
|
||||
import java.util.Map;
|
||||
|
||||
import com.unboundid.ldap.sdk.LDAPException;
|
||||
import org.elasticsearch.ElasticsearchException;
|
||||
import org.elasticsearch.common.settings.Settings;
|
||||
import org.elasticsearch.watcher.ResourceWatcherService;
|
||||
import org.elasticsearch.xpack.security.authc.RealmConfig;
|
||||
|
@ -15,6 +17,7 @@ import org.elasticsearch.xpack.security.authc.ldap.support.SessionFactory;
|
|||
import org.elasticsearch.xpack.security.authc.support.DnRoleMapper;
|
||||
import org.elasticsearch.xpack.security.ssl.ClientSSLService;
|
||||
|
||||
|
||||
/**
|
||||
* Authenticates username/password tokens against ldap, locates groups and maps them to roles.
|
||||
*/
|
||||
|
@ -33,15 +36,19 @@ public class LdapRealm extends AbstractLdapRealm {
|
|||
|
||||
static SessionFactory sessionFactory(RealmConfig config, ClientSSLService clientSSLService) {
|
||||
Settings searchSettings = userSearchSettings(config);
|
||||
if (!searchSettings.names().isEmpty()) {
|
||||
if (config.settings().getAsArray(LdapSessionFactory.USER_DN_TEMPLATES_SETTING).length > 0) {
|
||||
throw new IllegalArgumentException("settings were found for both user search and user template modes of operation. " +
|
||||
"Please remove the settings for the mode you do not wish to use. For more details refer to the ldap " +
|
||||
"authentication section of the X-Pack guide.");
|
||||
try {
|
||||
if (!searchSettings.names().isEmpty()) {
|
||||
if (config.settings().getAsArray(LdapSessionFactory.USER_DN_TEMPLATES_SETTING).length > 0) {
|
||||
throw new IllegalArgumentException("settings were found for both user search and user template modes of operation. " +
|
||||
"Please remove the settings for the mode you do not wish to use. For more details refer to the ldap " +
|
||||
"authentication section of the X-Pack guide.");
|
||||
}
|
||||
return new LdapUserSearchSessionFactory(config, clientSSLService);
|
||||
}
|
||||
return new LdapUserSearchSessionFactory(config, clientSSLService).init();
|
||||
return new LdapSessionFactory(config, clientSSLService);
|
||||
} catch (LDAPException e) {
|
||||
throw new ElasticsearchException("failed to create realm [{}/{}]", e, LdapRealm.TYPE, config.name());
|
||||
}
|
||||
return new LdapSessionFactory(config, clientSSLService).init();
|
||||
}
|
||||
|
||||
static Settings userSearchSettings(RealmConfig config) {
|
||||
|
|
|
@ -14,9 +14,7 @@ import org.elasticsearch.xpack.security.authc.ldap.support.LdapSession.GroupsRes
|
|||
import org.elasticsearch.xpack.security.authc.ldap.support.SessionFactory;
|
||||
import org.elasticsearch.xpack.security.authc.support.SecuredString;
|
||||
import org.elasticsearch.xpack.security.ssl.ClientSSLService;
|
||||
import org.elasticsearch.xpack.security.support.Exceptions;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.text.MessageFormat;
|
||||
import java.util.Locale;
|
||||
|
||||
|
@ -54,13 +52,7 @@ public class LdapSessionFactory extends SessionFactory {
|
|||
*/
|
||||
@Override
|
||||
protected LdapSession getSession(String username, SecuredString password) throws Exception {
|
||||
LDAPConnection connection;
|
||||
|
||||
try {
|
||||
connection = serverSet.getConnection();
|
||||
} catch (LDAPException e) {
|
||||
throw new IOException("failed to connect to any LDAP servers", e);
|
||||
}
|
||||
LDAPConnection connection = serverSet.getConnection();
|
||||
|
||||
LDAPException lastException = null;
|
||||
String passwordString = new String(password.internalChars());
|
||||
|
@ -68,20 +60,22 @@ public class LdapSessionFactory extends SessionFactory {
|
|||
String dn = buildDnFromTemplate(username, template);
|
||||
try {
|
||||
connection.bind(dn, passwordString);
|
||||
return new LdapSession(connectionLogger, connection, dn, groupResolver, timeout);
|
||||
return new LdapSession(logger, connection, dn, groupResolver, timeout, null);
|
||||
} catch (LDAPException e) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("failed LDAP authentication with user template [{}] and DN [{}]", e, template, dn);
|
||||
// we catch the ldapException here since we expect it can happen and we shouldn't be logging this all the time otherwise
|
||||
// it is just noise
|
||||
logger.debug("failed LDAP authentication with user template [{}] and DN [{}]", e, template, dn);
|
||||
if (lastException == null) {
|
||||
lastException = e;
|
||||
} else {
|
||||
logger.warn("failed LDAP authentication with user template [{}] and DN [{}]: {}", template, dn, e.getMessage());
|
||||
lastException.addSuppressed(e);
|
||||
}
|
||||
|
||||
lastException = e;
|
||||
}
|
||||
}
|
||||
|
||||
connection.close();
|
||||
throw Exceptions.authenticationError("failed LDAP authentication", lastException);
|
||||
assert lastException != null;
|
||||
throw lastException;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -8,12 +8,13 @@ package org.elasticsearch.xpack.security.authc.ldap;
|
|||
import com.unboundid.ldap.sdk.GetEntryLDAPConnectionPoolHealthCheck;
|
||||
import com.unboundid.ldap.sdk.LDAPConnection;
|
||||
import com.unboundid.ldap.sdk.LDAPConnectionPool;
|
||||
import com.unboundid.ldap.sdk.LDAPConnectionPoolHealthCheck;
|
||||
import com.unboundid.ldap.sdk.LDAPException;
|
||||
import com.unboundid.ldap.sdk.LDAPInterface;
|
||||
import com.unboundid.ldap.sdk.SearchRequest;
|
||||
import com.unboundid.ldap.sdk.SearchResultEntry;
|
||||
import com.unboundid.ldap.sdk.ServerSet;
|
||||
import com.unboundid.ldap.sdk.SimpleBindRequest;
|
||||
import org.elasticsearch.common.Strings;
|
||||
import org.elasticsearch.common.logging.ESLogger;
|
||||
import org.elasticsearch.common.settings.Settings;
|
||||
import org.elasticsearch.common.unit.TimeValue;
|
||||
|
@ -26,28 +27,29 @@ import org.elasticsearch.xpack.security.authc.support.SecuredString;
|
|||
import org.elasticsearch.xpack.security.ssl.ClientSSLService;
|
||||
import org.elasticsearch.xpack.security.support.Exceptions;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Locale;
|
||||
|
||||
import static com.unboundid.ldap.sdk.Filter.createEqualityFilter;
|
||||
import static com.unboundid.ldap.sdk.Filter.encodeValue;
|
||||
import static org.elasticsearch.xpack.security.authc.ldap.support.LdapUtils.attributesToSearchFor;
|
||||
import static org.elasticsearch.xpack.security.authc.ldap.support.LdapUtils.searchForEntry;
|
||||
|
||||
public class LdapUserSearchSessionFactory extends SessionFactory {
|
||||
class LdapUserSearchSessionFactory extends SessionFactory {
|
||||
|
||||
static final int DEFAULT_CONNECTION_POOL_SIZE = 20;
|
||||
static final int DEFAULT_CONNECTION_POOL_INITIAL_SIZE = 5;
|
||||
static final int DEFAULT_CONNECTION_POOL_INITIAL_SIZE = 0;
|
||||
static final String DEFAULT_USERNAME_ATTRIBUTE = "uid";
|
||||
static final TimeValue DEFAULT_HEALTH_CHECK_INTERVAL = TimeValue.timeValueSeconds(60L);
|
||||
|
||||
private final String userSearchBaseDn;
|
||||
private final LdapSearchScope scope;
|
||||
private final String userAttribute;
|
||||
private final GroupsResolver groupResolver;
|
||||
private final boolean useConnectionPool;
|
||||
|
||||
private LDAPConnectionPool connectionPool;
|
||||
private GroupsResolver groupResolver;
|
||||
private final LDAPConnectionPool connectionPool;
|
||||
|
||||
public LdapUserSearchSessionFactory(RealmConfig config, ClientSSLService sslService) {
|
||||
LdapUserSearchSessionFactory(RealmConfig config, ClientSSLService sslService) throws LDAPException {
|
||||
super(config, sslService);
|
||||
Settings settings = config.settings();
|
||||
userSearchBaseDn = settings.get("user_search.base_dn");
|
||||
|
@ -56,63 +58,52 @@ public class LdapUserSearchSessionFactory extends SessionFactory {
|
|||
}
|
||||
scope = LdapSearchScope.resolve(settings.get("user_search.scope"), LdapSearchScope.SUB_TREE);
|
||||
userAttribute = settings.get("user_search.attribute", DEFAULT_USERNAME_ATTRIBUTE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public LdapUserSearchSessionFactory init() {
|
||||
super.init();
|
||||
connectionPool = createConnectionPool(config, serverSet, timeout, logger);
|
||||
groupResolver = groupResolver(config.settings());
|
||||
return this;
|
||||
}
|
||||
|
||||
private synchronized LDAPConnectionPool connectionPool() throws IOException {
|
||||
if (connectionPool == null) {
|
||||
useConnectionPool = settings.getAsBoolean("user_search.pool.enabled", true);
|
||||
if (useConnectionPool) {
|
||||
connectionPool = createConnectionPool(config, serverSet, timeout, logger);
|
||||
// if it is still null throw an exception
|
||||
if (connectionPool == null) {
|
||||
String msg = "failed to create a connection pool for realm [" + config.name() + "] as no LDAP servers are available";
|
||||
throw new IOException(msg);
|
||||
}
|
||||
} else {
|
||||
connectionPool = null;
|
||||
}
|
||||
|
||||
return connectionPool;
|
||||
}
|
||||
|
||||
static LDAPConnectionPool createConnectionPool(RealmConfig config, ServerSet serverSet, TimeValue timeout, ESLogger logger) {
|
||||
static LDAPConnectionPool createConnectionPool(RealmConfig config, ServerSet serverSet, TimeValue timeout, ESLogger logger)
|
||||
throws LDAPException {
|
||||
Settings settings = config.settings();
|
||||
SimpleBindRequest bindRequest = bindRequest(settings);
|
||||
int initialSize = settings.getAsInt("user_search.pool.initial_size", DEFAULT_CONNECTION_POOL_INITIAL_SIZE);
|
||||
int size = settings.getAsInt("user_search.pool.size", DEFAULT_CONNECTION_POOL_SIZE);
|
||||
final int initialSize = settings.getAsInt("user_search.pool.initial_size", DEFAULT_CONNECTION_POOL_INITIAL_SIZE);
|
||||
final int size = settings.getAsInt("user_search.pool.size", DEFAULT_CONNECTION_POOL_SIZE);
|
||||
LDAPConnectionPool pool = null;
|
||||
boolean success = false;
|
||||
try {
|
||||
LDAPConnectionPool pool = new LDAPConnectionPool(serverSet, bindRequest, initialSize, size);
|
||||
pool = new LDAPConnectionPool(serverSet, bindRequest, initialSize, size);
|
||||
pool.setRetryFailedOperationsDueToInvalidConnections(true);
|
||||
if (settings.getAsBoolean("user_search.pool.health_check.enabled", true)) {
|
||||
String entryDn = settings.get("user_search.pool.health_check.dn", (bindRequest == null) ? null : bindRequest.getBindDN());
|
||||
if (entryDn == null) {
|
||||
pool.close();
|
||||
throw new IllegalArgumentException("[bind_dn] has not been specified so a value must be specified for [user_search" +
|
||||
".pool.health_check.dn] or [user_search.pool.health_check.enabled] must be set to false");
|
||||
final long healthCheckInterval =
|
||||
settings.getAsTime("user_search.pool.health_check.interval", DEFAULT_HEALTH_CHECK_INTERVAL).millis();
|
||||
if (entryDn != null) {
|
||||
// Checks the status of the LDAP connection at a specified interval in the background. We do not check on
|
||||
// on create as the LDAP server may require authentication to get an entry and a bind request has not been executed
|
||||
// yet so we could end up never getting a connection. We do not check on checkout as we always set retry operations
|
||||
// and the pool will handle a bad connection without the added latency on every operation
|
||||
LDAPConnectionPoolHealthCheck healthCheck = new GetEntryLDAPConnectionPoolHealthCheck(entryDn, timeout.millis(),
|
||||
false, false, false, true, false);
|
||||
pool.setHealthCheck(healthCheck);
|
||||
pool.setHealthCheckIntervalMillis(healthCheckInterval);
|
||||
} else {
|
||||
logger.warn("[bind_dn] and [user_search.pool.health_check.dn] have not been specified so no " +
|
||||
"ldap query will be run as a health check");
|
||||
}
|
||||
long healthCheckInterval = settings.getAsTime("user_search.pool.health_check.interval", DEFAULT_HEALTH_CHECK_INTERVAL)
|
||||
.millis();
|
||||
// Checks the status of the LDAP connection at a specified interval in the background. We do not check on
|
||||
// on create as the LDAP server may require authentication to get an entry. We do not check on checkout
|
||||
// as we always set retry operations and the pool will handle a bad connection without the added latency on every operation
|
||||
GetEntryLDAPConnectionPoolHealthCheck healthCheck = new GetEntryLDAPConnectionPoolHealthCheck(entryDn, timeout.millis(),
|
||||
false, false, false, true, false);
|
||||
pool.setHealthCheck(healthCheck);
|
||||
pool.setHealthCheckIntervalMillis(healthCheckInterval);
|
||||
}
|
||||
|
||||
success = true;
|
||||
return pool;
|
||||
} catch (LDAPException e) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("unable to create connection pool for realm [{}]", e, config.name());
|
||||
} else {
|
||||
logger.error("unable to create connection pool for realm [{}]: {}", config.name(), e.getMessage());
|
||||
} finally {
|
||||
if (success == false && pool != null) {
|
||||
pool.close();
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
static SimpleBindRequest bindRequest(Settings settings) {
|
||||
|
@ -126,12 +117,38 @@ public class LdapUserSearchSessionFactory extends SessionFactory {
|
|||
|
||||
@Override
|
||||
protected LdapSession getSession(String user, SecuredString password) throws Exception {
|
||||
if (useConnectionPool) {
|
||||
return getSessionWithPool(user, password);
|
||||
} else {
|
||||
return getSessionWithoutPool(user, password);
|
||||
}
|
||||
}
|
||||
|
||||
private LdapSession getSessionWithPool(String user, SecuredString password) throws Exception {
|
||||
SearchResultEntry searchResult = findUser(user, connectionPool);
|
||||
assert searchResult != null;
|
||||
final String dn = searchResult.getDN();
|
||||
connectionPool.bindAndRevertAuthentication(dn, new String(password.internalChars()));
|
||||
return new LdapSession(logger, connectionPool, dn, groupResolver, timeout, searchResult.getAttributes());
|
||||
}
|
||||
|
||||
private LdapSession getSessionWithoutPool(String user, SecuredString password) throws Exception {
|
||||
boolean success = false;
|
||||
LDAPConnection connection = null;
|
||||
try {
|
||||
String dn = findUserDN(user);
|
||||
tryBind(dn, password);
|
||||
return new LdapSession(logger, connectionPool, dn, groupResolver, timeout);
|
||||
} catch (LDAPException e) {
|
||||
throw Exceptions.authenticationError("failed to authenticate user [{}]", e, user);
|
||||
connection = serverSet.getConnection();
|
||||
connection.bind(bindRequest(config.settings()));
|
||||
SearchResultEntry searchResult = findUser(user, connection);
|
||||
assert searchResult != null;
|
||||
final String dn = searchResult.getDN();
|
||||
connection.bind(dn, new String(password.internalChars()));
|
||||
LdapSession session = new LdapSession(logger, connection, dn, groupResolver, timeout, searchResult.getAttributes());
|
||||
success = true;
|
||||
return session;
|
||||
} finally {
|
||||
if (success == false && connection != null) {
|
||||
connection.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -142,46 +159,45 @@ public class LdapUserSearchSessionFactory extends SessionFactory {
|
|||
|
||||
@Override
|
||||
public LdapSession unauthenticatedSession(String user) throws Exception {
|
||||
LDAPConnection connection = null;
|
||||
boolean success = false;
|
||||
try {
|
||||
String dn = findUserDN(user);
|
||||
return new LdapSession(logger, connectionPool, dn, groupResolver, timeout);
|
||||
} catch (LDAPException e) {
|
||||
throw Exceptions.authenticationError("failed to lookup user [{}]", e, user);
|
||||
final LDAPInterface ldapInterface;
|
||||
if (useConnectionPool) {
|
||||
ldapInterface = connectionPool;
|
||||
} else {
|
||||
connection = serverSet.getConnection();
|
||||
connection.bind(bindRequest(config.settings()));
|
||||
ldapInterface = connection;
|
||||
}
|
||||
|
||||
SearchResultEntry searchResult = findUser(user, ldapInterface);
|
||||
assert searchResult != null;
|
||||
final String dn = searchResult.getDN();
|
||||
LdapSession session = new LdapSession(logger, ldapInterface, dn, groupResolver, timeout, searchResult.getAttributes());
|
||||
success = true;
|
||||
return session;
|
||||
} finally {
|
||||
if (success == false && connection != null) {
|
||||
connection.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private String findUserDN(String user) throws Exception {
|
||||
SearchRequest request = new SearchRequest(userSearchBaseDn, scope.scope(), createEqualityFilter(userAttribute, encodeValue(user))
|
||||
, SearchRequest.NO_ATTRIBUTES);
|
||||
private SearchResultEntry findUser(String user, LDAPInterface ldapInterface) throws Exception {
|
||||
SearchRequest request = new SearchRequest(userSearchBaseDn, scope.scope(), createEqualityFilter(userAttribute, encodeValue(user)),
|
||||
attributesToSearchFor(groupResolver.attributes()));
|
||||
request.setTimeLimitSeconds(Math.toIntExact(timeout.seconds()));
|
||||
LDAPConnectionPool connectionPool = connectionPool();
|
||||
SearchResultEntry entry = searchForEntry(connectionPool, request, logger);
|
||||
SearchResultEntry entry = searchForEntry(ldapInterface, request, logger);
|
||||
if (entry == null) {
|
||||
throw Exceptions.authenticationError("failed to find user [{}] with search base [{}] scope [{}]", user, userSearchBaseDn,
|
||||
scope.toString().toLowerCase(Locale.ENGLISH));
|
||||
}
|
||||
return entry.getDN();
|
||||
}
|
||||
|
||||
private void tryBind(String dn, SecuredString password) throws IOException {
|
||||
LDAPConnection bindConnection;
|
||||
try {
|
||||
bindConnection = serverSet.getConnection();
|
||||
} catch (LDAPException e) {
|
||||
throw new IOException("unable to connect to any LDAP servers for bind", e);
|
||||
}
|
||||
|
||||
try {
|
||||
bindConnection.bind(dn, new String(password.internalChars()));
|
||||
} catch (LDAPException e) {
|
||||
throw Exceptions.authenticationError("failed LDAP authentication for DN [{}]", e, dn);
|
||||
} finally {
|
||||
bindConnection.close();
|
||||
}
|
||||
return entry;
|
||||
}
|
||||
|
||||
/*
|
||||
* This method is used to cleanup the connections for tests
|
||||
* This method is used to cleanup the connections
|
||||
*/
|
||||
void shutdown() {
|
||||
if (connectionPool != null) {
|
||||
|
|
|
@ -12,15 +12,15 @@ import com.unboundid.ldap.sdk.SearchRequest;
|
|||
import com.unboundid.ldap.sdk.SearchResult;
|
||||
import com.unboundid.ldap.sdk.SearchResultEntry;
|
||||
import com.unboundid.ldap.sdk.SearchScope;
|
||||
import org.elasticsearch.common.Strings;
|
||||
import org.elasticsearch.common.logging.ESLogger;
|
||||
import org.elasticsearch.common.settings.Settings;
|
||||
import org.elasticsearch.common.unit.TimeValue;
|
||||
import org.elasticsearch.xpack.security.authc.ldap.support.LdapSearchScope;
|
||||
import org.elasticsearch.xpack.security.authc.ldap.support.LdapSession.GroupsResolver;
|
||||
import org.elasticsearch.xpack.security.support.Exceptions;
|
||||
|
||||
import java.util.LinkedList;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import static org.elasticsearch.xpack.security.authc.ldap.support.LdapUtils.OBJECT_CLASS_PRESENCE_FILTER;
|
||||
|
@ -29,20 +29,21 @@ import static org.elasticsearch.xpack.security.authc.ldap.support.LdapUtils.sear
|
|||
import static org.elasticsearch.xpack.security.authc.ldap.support.LdapUtils.searchForEntry;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
* Resolves the groups for a user by executing a search with a filter usually that contains a group object class with a attribute that
|
||||
* matches an ID of the user
|
||||
*/
|
||||
class SearchGroupsResolver implements GroupsResolver {
|
||||
|
||||
private static final String GROUP_SEARCH_DEFAULT_FILTER = "(&" +
|
||||
"(|(objectclass=groupOfNames)(objectclass=groupOfUniqueNames)(objectclass=group))" +
|
||||
"(|(uniqueMember={0})(member={0})))";
|
||||
"(|(objectclass=groupOfNames)(objectclass=groupOfUniqueNames)(objectclass=group)(objectclass=posixGroup))" +
|
||||
"(|(uniqueMember={0})(member={0})(memberUid={0})))";
|
||||
|
||||
private final String baseDn;
|
||||
private final String filter;
|
||||
private final String userAttribute;
|
||||
private final LdapSearchScope scope;
|
||||
|
||||
public SearchGroupsResolver(Settings settings) {
|
||||
SearchGroupsResolver(Settings settings) {
|
||||
baseDn = settings.get("base_dn");
|
||||
if (baseDn == null) {
|
||||
throw new IllegalArgumentException("base_dn must be specified");
|
||||
|
@ -53,37 +54,60 @@ class SearchGroupsResolver implements GroupsResolver {
|
|||
}
|
||||
|
||||
@Override
|
||||
public List<String> resolve(LDAPInterface connection, String userDn, TimeValue timeout, ESLogger logger) {
|
||||
List<String> groups = new LinkedList<>();
|
||||
|
||||
String userId = userAttribute != null ? readUserAttribute(connection, userDn, timeout, logger) : userDn;
|
||||
try {
|
||||
SearchRequest searchRequest = new SearchRequest(baseDn, scope.scope(), createFilter(filter, userId),
|
||||
SearchRequest.NO_ATTRIBUTES);
|
||||
searchRequest.setTimeLimitSeconds(Math.toIntExact(timeout.seconds()));
|
||||
SearchResult results = search(connection, searchRequest, logger);
|
||||
for (SearchResultEntry entry : results.getSearchEntries()) {
|
||||
groups.add(entry.getDN());
|
||||
}
|
||||
} catch (LDAPException e) {
|
||||
throw Exceptions.authenticationError("could not search for LDAP groups for DN [{}]", e, userDn);
|
||||
public List<String> resolve(LDAPInterface connection, String userDn, TimeValue timeout, ESLogger logger,
|
||||
Collection<Attribute> attributes) throws LDAPException {
|
||||
String userId = getUserId(userDn, attributes, connection, timeout, logger);
|
||||
if (userId == null) {
|
||||
// attributes were queried but the requested wasn't found
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
SearchRequest searchRequest = new SearchRequest(baseDn, scope.scope(), createFilter(filter, userId),
|
||||
SearchRequest.NO_ATTRIBUTES);
|
||||
searchRequest.setTimeLimitSeconds(Math.toIntExact(timeout.seconds()));
|
||||
SearchResult results = search(connection, searchRequest, logger);
|
||||
List<String> groups = new ArrayList<>(results.getSearchEntries().size());
|
||||
for (SearchResultEntry entry : results.getSearchEntries()) {
|
||||
groups.add(entry.getDN());
|
||||
}
|
||||
return groups;
|
||||
}
|
||||
|
||||
String readUserAttribute(LDAPInterface connection, String userDn, TimeValue timeout, ESLogger logger) {
|
||||
try {
|
||||
SearchRequest request = new SearchRequest(userDn, SearchScope.BASE, OBJECT_CLASS_PRESENCE_FILTER, userAttribute);
|
||||
request.setTimeLimitSeconds(Math.toIntExact(timeout.seconds()));
|
||||
SearchResultEntry results = searchForEntry(connection, request, logger);
|
||||
Attribute attribute = results.getAttribute(userAttribute);
|
||||
if (attribute == null) {
|
||||
throw Exceptions.authenticationError("no results returned for DN [{}] attribute [{}]", userDn, userAttribute);
|
||||
}
|
||||
return attribute.getValue();
|
||||
} catch (LDAPException e) {
|
||||
throw Exceptions.authenticationError("could not retrieve attribute [{}] for DN [{}]", e, userAttribute, userDn);
|
||||
public String[] attributes() {
|
||||
if (userAttribute != null) {
|
||||
return new String[] { userAttribute };
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private String getUserId(String dn, Collection<Attribute> attributes, LDAPInterface connection, TimeValue
|
||||
timeout, ESLogger logger) throws LDAPException {
|
||||
if (userAttribute == null) {
|
||||
return dn;
|
||||
}
|
||||
|
||||
if (attributes != null) {
|
||||
for (Attribute attribute : attributes) {
|
||||
if (attribute.getName().equals(userAttribute)) {
|
||||
return attribute.getValue();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return readUserAttribute(connection, dn, timeout, logger);
|
||||
}
|
||||
|
||||
String readUserAttribute(LDAPInterface connection, String userDn, TimeValue timeout, ESLogger logger) throws LDAPException {
|
||||
SearchRequest request = new SearchRequest(userDn, SearchScope.BASE, OBJECT_CLASS_PRESENCE_FILTER, userAttribute);
|
||||
request.setTimeLimitSeconds(Math.toIntExact(timeout.seconds()));
|
||||
SearchResultEntry results = searchForEntry(connection, request, logger);
|
||||
if (results == null) {
|
||||
return null;
|
||||
}
|
||||
Attribute attribute = results.getAttribute(userAttribute);
|
||||
if (attribute == null) {
|
||||
return null;
|
||||
}
|
||||
return attribute.getValue();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,48 +11,64 @@ import com.unboundid.ldap.sdk.LDAPInterface;
|
|||
import com.unboundid.ldap.sdk.SearchRequest;
|
||||
import com.unboundid.ldap.sdk.SearchResultEntry;
|
||||
import com.unboundid.ldap.sdk.SearchScope;
|
||||
import org.elasticsearch.ElasticsearchException;
|
||||
import org.elasticsearch.common.logging.ESLogger;
|
||||
import org.elasticsearch.common.settings.Settings;
|
||||
import org.elasticsearch.common.unit.TimeValue;
|
||||
import org.elasticsearch.xpack.security.authc.ldap.support.LdapSession.GroupsResolver;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
import static org.elasticsearch.xpack.security.authc.ldap.support.LdapUtils.OBJECT_CLASS_PRESENCE_FILTER;
|
||||
import static org.elasticsearch.xpack.security.authc.ldap.support.LdapUtils.searchForEntry;
|
||||
|
||||
/**
|
||||
*
|
||||
* Resolves the groups of a user based on the value of a attribute of the user's ldap entry
|
||||
*/
|
||||
class UserAttributeGroupsResolver implements GroupsResolver {
|
||||
|
||||
private final String attribute;
|
||||
|
||||
public UserAttributeGroupsResolver(Settings settings) {
|
||||
UserAttributeGroupsResolver(Settings settings) {
|
||||
this(settings.get("user_group_attribute", "memberOf"));
|
||||
}
|
||||
|
||||
public UserAttributeGroupsResolver(String attribute) {
|
||||
this.attribute = attribute;
|
||||
private UserAttributeGroupsResolver(String attribute) {
|
||||
this.attribute = Objects.requireNonNull(attribute);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> resolve(LDAPInterface connection, String userDn, TimeValue timeout, ESLogger logger) {
|
||||
try {
|
||||
SearchRequest request = new SearchRequest(userDn, SearchScope.BASE, OBJECT_CLASS_PRESENCE_FILTER, attribute);
|
||||
request.setTimeLimitSeconds(Math.toIntExact(timeout.seconds()));
|
||||
SearchResultEntry result = searchForEntry(connection, request, logger);
|
||||
Attribute attributeReturned = result.getAttribute(attribute);
|
||||
if (attributeReturned == null) {
|
||||
return Collections.emptyList();
|
||||
public List<String> resolve(LDAPInterface connection, String userDn, TimeValue timeout, ESLogger logger,
|
||||
Collection<Attribute> attributes) throws LDAPException {
|
||||
if (attributes != null) {
|
||||
for (Attribute attribute : attributes) {
|
||||
if (attribute.getName().equals(attribute)) {
|
||||
String[] values = attribute.getValues();
|
||||
return Arrays.asList(values);
|
||||
}
|
||||
}
|
||||
String[] values = attributeReturned.getValues();
|
||||
return Arrays.asList(values);
|
||||
} catch (LDAPException e) {
|
||||
throw new ElasticsearchException("could not look up group attributes for DN [{}]", e, userDn);
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
SearchRequest request = new SearchRequest(userDn, SearchScope.BASE, OBJECT_CLASS_PRESENCE_FILTER, attribute);
|
||||
request.setTimeLimitSeconds(Math.toIntExact(timeout.seconds()));
|
||||
SearchResultEntry result = searchForEntry(connection, request, logger);
|
||||
if (result == null) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
Attribute attributeReturned = result.getAttribute(attribute);
|
||||
if (attributeReturned == null) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
String[] values = attributeReturned.getValues();
|
||||
return Arrays.asList(values);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] attributes() {
|
||||
return new String[] { attribute };
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,12 +9,13 @@ import java.util.List;
|
|||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import com.unboundid.ldap.sdk.LDAPException;
|
||||
import org.elasticsearch.xpack.security.user.User;
|
||||
import org.elasticsearch.xpack.security.authc.RealmConfig;
|
||||
import org.elasticsearch.xpack.security.authc.support.CachingUsernamePasswordRealm;
|
||||
import org.elasticsearch.xpack.security.authc.support.DnRoleMapper;
|
||||
import org.elasticsearch.xpack.security.authc.support.RefreshListener;
|
||||
import org.elasticsearch.xpack.security.authc.support.UsernamePasswordToken;
|
||||
import org.elasticsearch.xpack.security.user.User;
|
||||
|
||||
/**
|
||||
* Supporting class for LDAP realms
|
||||
|
@ -86,7 +87,7 @@ public abstract class AbstractLdapRealm extends CachingUsernamePasswordRealm {
|
|||
}
|
||||
}
|
||||
|
||||
private User createUser(String principal, LdapSession session) {
|
||||
private User createUser(String principal, LdapSession session) throws LDAPException {
|
||||
List<String> groupDNs = session.groups();
|
||||
Set<String> roles = roleMapper.resolveRoles(session.userDn(), groupDNs);
|
||||
return new User(principal, roles.toArray(new String[roles.size()]));
|
||||
|
|
|
@ -5,12 +5,15 @@
|
|||
*/
|
||||
package org.elasticsearch.xpack.security.authc.ldap.support;
|
||||
|
||||
import com.unboundid.ldap.sdk.Attribute;
|
||||
import com.unboundid.ldap.sdk.LDAPConnection;
|
||||
import com.unboundid.ldap.sdk.LDAPException;
|
||||
import com.unboundid.ldap.sdk.LDAPInterface;
|
||||
import org.elasticsearch.common.logging.ESLogger;
|
||||
import org.elasticsearch.common.unit.TimeValue;
|
||||
|
||||
import java.io.Closeable;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
|
@ -23,6 +26,7 @@ public class LdapSession implements Closeable {
|
|||
protected final String userDn;
|
||||
protected final GroupsResolver groupsResolver;
|
||||
protected final TimeValue timeout;
|
||||
protected final Collection<Attribute> attributes;
|
||||
|
||||
/**
|
||||
* This object is intended to be constructed by the LdapConnectionFactory
|
||||
|
@ -32,12 +36,14 @@ public class LdapSession implements Closeable {
|
|||
* outside of and be reused across all connections. We can't keep a static logger in this class
|
||||
* since we want the logger to be contextual (i.e. aware of the settings and its environment).
|
||||
*/
|
||||
public LdapSession(ESLogger logger, LDAPInterface connection, String userDn, GroupsResolver groupsResolver, TimeValue timeout) {
|
||||
public LdapSession(ESLogger logger, LDAPInterface connection, String userDn, GroupsResolver groupsResolver, TimeValue timeout,
|
||||
Collection<Attribute> attributes) {
|
||||
this.logger = logger;
|
||||
this.ldap = connection;
|
||||
this.userDn = userDn;
|
||||
this.groupsResolver = groupsResolver;
|
||||
this.timeout = timeout;
|
||||
this.attributes = attributes;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -61,13 +67,15 @@ public class LdapSession implements Closeable {
|
|||
/**
|
||||
* @return List of fully distinguished group names
|
||||
*/
|
||||
public List<String> groups() {
|
||||
return groupsResolver.resolve(ldap, userDn, timeout, logger);
|
||||
public List<String> groups() throws LDAPException {
|
||||
return groupsResolver.resolve(ldap, userDn, timeout, logger, attributes);
|
||||
}
|
||||
|
||||
public interface GroupsResolver {
|
||||
|
||||
List<String> resolve(LDAPInterface ldapConnection, String userDn, TimeValue timeout, ESLogger logger);
|
||||
List<String> resolve(LDAPInterface ldapConnection, String userDn, TimeValue timeout, ESLogger logger,
|
||||
Collection<Attribute> attributes) throws LDAPException;
|
||||
|
||||
String[] attributes();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -46,7 +46,7 @@ public final class LdapUtils {
|
|||
|
||||
/**
|
||||
* This method performs a LDAPConnection.search(...) operation while handling referral exceptions. This is necessary
|
||||
* to maintain backwards compatibility
|
||||
* to maintain backwards compatibility with the original JNDI implementation
|
||||
*/
|
||||
public static SearchResult search(LDAPInterface ldap, SearchRequest searchRequest, ESLogger logger) throws LDAPException {
|
||||
SearchResult results;
|
||||
|
@ -68,7 +68,7 @@ public final class LdapUtils {
|
|||
|
||||
/**
|
||||
* This method performs a LDAPConnection.searchForEntry(...) operation while handling referral exceptions. This is necessary
|
||||
* to maintain backwards compatibility
|
||||
* to maintain backwards compatibility with the original JNDI implementation
|
||||
*/
|
||||
public static SearchResultEntry searchForEntry(LDAPInterface ldap, SearchRequest searchRequest, ESLogger logger) throws LDAPException {
|
||||
SearchResultEntry entry;
|
||||
|
@ -93,6 +93,10 @@ public final class LdapUtils {
|
|||
new StringBuffer(), null).toString());
|
||||
}
|
||||
|
||||
public static String[] attributesToSearchFor(String[] attributes) {
|
||||
return attributes == null ? new String[] { SearchRequest.NO_ATTRIBUTES } : attributes;
|
||||
}
|
||||
|
||||
static String[] encodeFilterValues(String... arguments) {
|
||||
for (int i = 0; i < arguments.length; i++) {
|
||||
arguments[i] = Filter.encodeValue(arguments[i]);
|
||||
|
|
|
@ -48,18 +48,16 @@ public abstract class SessionFactory {
|
|||
private static final Pattern STARTS_WITH_LDAP = Pattern.compile("^ldap:.*", Pattern.CASE_INSENSITIVE);
|
||||
|
||||
protected final ESLogger logger;
|
||||
protected final ESLogger connectionLogger;
|
||||
protected final RealmConfig config;
|
||||
protected final TimeValue timeout;
|
||||
protected final ClientSSLService sslService;
|
||||
|
||||
protected ServerSet serverSet;
|
||||
protected boolean sslUsed;
|
||||
protected final ServerSet serverSet;
|
||||
protected final boolean sslUsed;
|
||||
|
||||
protected SessionFactory(RealmConfig config, ClientSSLService sslService) {
|
||||
this.config = config;
|
||||
this.logger = config.logger(getClass());
|
||||
this.connectionLogger = config.logger(getClass());
|
||||
TimeValue searchTimeout = config.settings().getAsTime(TIMEOUT_LDAP_SETTING, TIMEOUT_DEFAULT);
|
||||
if (searchTimeout.millis() < 1000L) {
|
||||
logger.warn("ldap_search timeout [{}] is less than the minimum supported search timeout of 1s. using 1s",
|
||||
|
@ -68,6 +66,9 @@ public abstract class SessionFactory {
|
|||
}
|
||||
this.timeout = searchTimeout;
|
||||
this.sslService = sslService;
|
||||
LDAPServers ldapServers = ldapServers(config.settings());
|
||||
this.serverSet = serverSet(config.settings(), sslService, ldapServers);
|
||||
this.sslUsed = ldapServers.ssl;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -80,9 +81,6 @@ public abstract class SessionFactory {
|
|||
* @throws Exception if an error occurred when creating the session
|
||||
*/
|
||||
public final LdapSession session(String user, SecuredString password) throws Exception {
|
||||
if (serverSet == null) {
|
||||
throw new IllegalStateException("session factory is not initialized");
|
||||
}
|
||||
return getSession(user, password);
|
||||
}
|
||||
|
||||
|
@ -119,19 +117,11 @@ public abstract class SessionFactory {
|
|||
throw new UnsupportedOperationException("unauthenticated sessions are not supported");
|
||||
}
|
||||
|
||||
public <T extends SessionFactory> T init() {
|
||||
LDAPServers ldapServers = ldapServers(config.settings());
|
||||
this.serverSet = serverSet(config.settings(), sslService, ldapServers);
|
||||
this.sslUsed = ldapServers.ssl;
|
||||
return (T) this;
|
||||
}
|
||||
|
||||
protected static LDAPConnectionOptions connectionOptions(Settings settings) {
|
||||
LDAPConnectionOptions options = new LDAPConnectionOptions();
|
||||
options.setConnectTimeoutMillis(Math.toIntExact(settings.getAsTime(TIMEOUT_TCP_CONNECTION_SETTING, TIMEOUT_DEFAULT).millis()));
|
||||
options.setFollowReferrals(settings.getAsBoolean(FOLLOW_REFERRALS_SETTING, true));
|
||||
options.setResponseTimeoutMillis(settings.getAsTime(TIMEOUT_TCP_READ_SETTING, TIMEOUT_DEFAULT).millis());
|
||||
options.setAutoReconnect(true);
|
||||
options.setAllowConcurrentSocketFactoryUse(true);
|
||||
if (settings.getAsBoolean(HOSTNAME_VERIFICATION_SETTING, true)) {
|
||||
options.setSSLSocketVerifier(new HostNameSSLSocketVerifier(true));
|
||||
|
@ -139,15 +129,19 @@ public abstract class SessionFactory {
|
|||
return options;
|
||||
}
|
||||
|
||||
protected LDAPServers ldapServers(Settings settings) {
|
||||
protected final LDAPServers ldapServers(Settings settings) {
|
||||
// Parse LDAP urls
|
||||
String[] ldapUrls = settings.getAsArray(URLS_SETTING);
|
||||
String[] ldapUrls = settings.getAsArray(URLS_SETTING, getDefaultLdapUrls(settings));
|
||||
if (ldapUrls == null || ldapUrls.length == 0) {
|
||||
throw new IllegalArgumentException("missing required LDAP setting [" + URLS_SETTING + "]");
|
||||
}
|
||||
return new LDAPServers(ldapUrls);
|
||||
}
|
||||
|
||||
protected String[] getDefaultLdapUrls(Settings settings) {
|
||||
return null;
|
||||
}
|
||||
|
||||
protected ServerSet serverSet(Settings settings, ClientSSLService clientSSLService, LDAPServers ldapServers) {
|
||||
SocketFactory socketFactory = null;
|
||||
if (ldapServers.ssl()) {
|
||||
|
|
|
@ -22,12 +22,15 @@ import org.elasticsearch.common.xcontent.XContentHelper;
|
|||
import org.elasticsearch.common.xcontent.XContentParser;
|
||||
import org.elasticsearch.common.xcontent.json.JsonXContent;
|
||||
import org.elasticsearch.xpack.security.support.Validation;
|
||||
import org.elasticsearch.xpack.security.support.MetadataUtils;
|
||||
import org.elasticsearch.xpack.common.xcontent.XContentUtils;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* A holder for a Role that contains user-readable information about the Role
|
||||
|
@ -39,16 +42,26 @@ public class RoleDescriptor implements ToXContent {
|
|||
private final String[] clusterPrivileges;
|
||||
private final IndicesPrivileges[] indicesPrivileges;
|
||||
private final String[] runAs;
|
||||
private final Map<String, Object> metadata;
|
||||
|
||||
public RoleDescriptor(String name,
|
||||
@Nullable String[] clusterPrivileges,
|
||||
@Nullable IndicesPrivileges[] indicesPrivileges,
|
||||
@Nullable String[] runAs) {
|
||||
this(name, clusterPrivileges, indicesPrivileges, runAs, null);
|
||||
}
|
||||
|
||||
public RoleDescriptor(String name,
|
||||
@Nullable String[] clusterPrivileges,
|
||||
@Nullable IndicesPrivileges[] indicesPrivileges,
|
||||
@Nullable String[] runAs,
|
||||
@Nullable Map<String, Object> metadata) {
|
||||
|
||||
this.name = name;
|
||||
this.clusterPrivileges = clusterPrivileges != null ? clusterPrivileges : Strings.EMPTY_ARRAY;
|
||||
this.indicesPrivileges = indicesPrivileges != null ? indicesPrivileges : IndicesPrivileges.NONE;
|
||||
this.runAs = runAs != null ? runAs : Strings.EMPTY_ARRAY;
|
||||
this.metadata = metadata != null ? Collections.unmodifiableMap(metadata) : Collections.emptyMap();
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
|
@ -67,6 +80,10 @@ public class RoleDescriptor implements ToXContent {
|
|||
return this.runAs;
|
||||
}
|
||||
|
||||
public Map<String, Object> getMetadata() {
|
||||
return metadata;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder sb = new StringBuilder("Role[");
|
||||
|
@ -77,6 +94,8 @@ public class RoleDescriptor implements ToXContent {
|
|||
sb.append(group.toString()).append(",");
|
||||
}
|
||||
sb.append("], runAs=[").append(Strings.arrayToCommaDelimitedString(runAs));
|
||||
sb.append("], metadata=[");
|
||||
MetadataUtils.writeValue(sb, metadata);
|
||||
sb.append("]]");
|
||||
return sb.toString();
|
||||
}
|
||||
|
@ -91,6 +110,7 @@ public class RoleDescriptor implements ToXContent {
|
|||
if (!name.equals(that.name)) return false;
|
||||
if (!Arrays.equals(clusterPrivileges, that.clusterPrivileges)) return false;
|
||||
if (!Arrays.equals(indicesPrivileges, that.indicesPrivileges)) return false;
|
||||
if (!metadata.equals(that.getMetadata())) return false;
|
||||
return Arrays.equals(runAs, that.runAs);
|
||||
}
|
||||
|
||||
|
@ -100,16 +120,18 @@ public class RoleDescriptor implements ToXContent {
|
|||
result = 31 * result + Arrays.hashCode(clusterPrivileges);
|
||||
result = 31 * result + Arrays.hashCode(indicesPrivileges);
|
||||
result = 31 * result + Arrays.hashCode(runAs);
|
||||
result = 31 * result + metadata.hashCode();
|
||||
return result;
|
||||
}
|
||||
|
||||
public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
|
||||
builder.startObject();
|
||||
builder.field("cluster", (Object[]) clusterPrivileges);
|
||||
builder.field("indices", (Object[]) indicesPrivileges);
|
||||
builder.field(Fields.CLUSTER.getPreferredName(), (Object[]) clusterPrivileges);
|
||||
builder.field(Fields.INDICES.getPreferredName(), (Object[]) indicesPrivileges);
|
||||
if (runAs != null) {
|
||||
builder.field("run_as", runAs);
|
||||
builder.field(Fields.RUN_AS.getPreferredName(), runAs);
|
||||
}
|
||||
builder.field(Fields.METADATA.getPreferredName(), metadata);
|
||||
return builder.endObject();
|
||||
}
|
||||
|
||||
|
@ -122,7 +144,8 @@ public class RoleDescriptor implements ToXContent {
|
|||
indicesPrivileges[i] = IndicesPrivileges.createFrom(in);
|
||||
}
|
||||
String[] runAs = in.readStringArray();
|
||||
return new RoleDescriptor(name, clusterPrivileges, indicesPrivileges, runAs);
|
||||
Map<String, Object> metadata = in.readMap();
|
||||
return new RoleDescriptor(name, clusterPrivileges, indicesPrivileges, runAs, metadata);
|
||||
}
|
||||
|
||||
public static void writeTo(RoleDescriptor descriptor, StreamOutput out) throws IOException {
|
||||
|
@ -133,6 +156,7 @@ public class RoleDescriptor implements ToXContent {
|
|||
group.writeTo(out);
|
||||
}
|
||||
out.writeStringArray(descriptor.runAs);
|
||||
out.writeMap(descriptor.metadata);
|
||||
}
|
||||
|
||||
public static RoleDescriptor parse(String name, BytesReference source) throws IOException {
|
||||
|
@ -160,6 +184,7 @@ public class RoleDescriptor implements ToXContent {
|
|||
IndicesPrivileges[] indicesPrivileges = null;
|
||||
String[] clusterPrivileges = null;
|
||||
String[] runAsUsers = null;
|
||||
Map<String, Object> metadata = null;
|
||||
while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) {
|
||||
if (token == XContentParser.Token.FIELD_NAME) {
|
||||
currentFieldName = parser.currentName();
|
||||
|
@ -169,11 +194,17 @@ public class RoleDescriptor implements ToXContent {
|
|||
runAsUsers = readStringArray(name, parser, true);
|
||||
} else if (ParseFieldMatcher.STRICT.match(currentFieldName, Fields.CLUSTER)) {
|
||||
clusterPrivileges = readStringArray(name, parser, true);
|
||||
} else if (ParseFieldMatcher.STRICT.match(currentFieldName, Fields.METADATA)) {
|
||||
if (token != XContentParser.Token.START_OBJECT) {
|
||||
throw new ElasticsearchParseException(
|
||||
"expected field [{}] to be of type object, but found [{}] instead", currentFieldName, token);
|
||||
}
|
||||
metadata = parser.map();
|
||||
} else {
|
||||
throw new ElasticsearchParseException("failed to parse role [{}]. unexpected field [{}]", name, currentFieldName);
|
||||
}
|
||||
}
|
||||
return new RoleDescriptor(name, clusterPrivileges, indicesPrivileges, runAsUsers);
|
||||
return new RoleDescriptor(name, clusterPrivileges, indicesPrivileges, runAsUsers, metadata);
|
||||
}
|
||||
|
||||
private static String[] readStringArray(String roleName, XContentParser parser, boolean allowNull) throws IOException {
|
||||
|
@ -355,9 +386,7 @@ public class RoleDescriptor implements ToXContent {
|
|||
this.indices = in.readStringArray();
|
||||
this.fields = in.readOptionalStringArray();
|
||||
this.privileges = in.readStringArray();
|
||||
if (in.readBoolean()) {
|
||||
this.query = new BytesArray(in.readByteArray());
|
||||
}
|
||||
this.query = in.readOptionalBytesReference();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -365,12 +394,7 @@ public class RoleDescriptor implements ToXContent {
|
|||
out.writeStringArray(indices);
|
||||
out.writeOptionalStringArray(fields);
|
||||
out.writeStringArray(privileges);
|
||||
if (query != null) {
|
||||
out.writeBoolean(true);
|
||||
out.writeByteArray(BytesReference.toBytes(query));
|
||||
} else {
|
||||
out.writeBoolean(false);
|
||||
}
|
||||
out.writeOptionalBytesReference(query);
|
||||
}
|
||||
|
||||
public static class Builder {
|
||||
|
@ -424,5 +448,6 @@ public class RoleDescriptor implements ToXContent {
|
|||
ParseField QUERY = new ParseField("query");
|
||||
ParseField PRIVILEGES = new ParseField("privileges");
|
||||
ParseField FIELDS = new ParseField("fields");
|
||||
ParseField METADATA = new ParseField("metadata");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,6 +9,7 @@ import org.elasticsearch.xpack.monitoring.action.MonitoringBulkAction;
|
|||
import org.elasticsearch.xpack.security.authz.RoleDescriptor;
|
||||
import org.elasticsearch.xpack.security.authz.privilege.ClusterPrivilege;
|
||||
import org.elasticsearch.xpack.security.authz.privilege.Privilege.Name;
|
||||
import org.elasticsearch.xpack.security.support.MetadataUtils;
|
||||
|
||||
/**
|
||||
*
|
||||
|
@ -20,7 +21,8 @@ public class KibanaRole extends Role {
|
|||
RoleDescriptor.IndicesPrivileges.builder().indices(".kibana*", ".reporting-*").privileges("all").build() };
|
||||
|
||||
public static final String NAME = "kibana";
|
||||
public static final RoleDescriptor DESCRIPTOR = new RoleDescriptor(NAME, CLUSTER_PRIVILEGES, INDICES_PRIVILEGES, null);
|
||||
public static final RoleDescriptor DESCRIPTOR =
|
||||
new RoleDescriptor(NAME, CLUSTER_PRIVILEGES, INDICES_PRIVILEGES, null, MetadataUtils.DEFAULT_RESERVED_METADATA);
|
||||
public static final KibanaRole INSTANCE = new KibanaRole();
|
||||
|
||||
private KibanaRole() {
|
||||
|
|
|
@ -8,6 +8,7 @@ package org.elasticsearch.xpack.security.authz.permission;
|
|||
import org.elasticsearch.xpack.security.authz.RoleDescriptor;
|
||||
import org.elasticsearch.xpack.security.authz.privilege.ClusterPrivilege;
|
||||
import org.elasticsearch.xpack.security.authz.privilege.Privilege.Name;
|
||||
import org.elasticsearch.xpack.security.support.MetadataUtils;
|
||||
|
||||
public class KibanaUserRole extends Role {
|
||||
|
||||
|
@ -16,7 +17,8 @@ public class KibanaUserRole extends Role {
|
|||
RoleDescriptor.IndicesPrivileges.builder().indices(".kibana*").privileges("manage", "read", "index", "delete").build() };
|
||||
|
||||
public static final String NAME = "kibana_user";
|
||||
public static final RoleDescriptor DESCRIPTOR = new RoleDescriptor(NAME, CLUSTER_PRIVILEGES, INDICES_PRIVILEGES, null);
|
||||
public static final RoleDescriptor DESCRIPTOR =
|
||||
new RoleDescriptor(NAME, CLUSTER_PRIVILEGES, INDICES_PRIVILEGES, null, MetadataUtils.DEFAULT_RESERVED_METADATA);
|
||||
public static final KibanaUserRole INSTANCE = new KibanaUserRole();
|
||||
|
||||
private KibanaUserRole() {
|
||||
|
|
|
@ -9,6 +9,7 @@ import org.elasticsearch.xpack.security.authz.RoleDescriptor;
|
|||
import org.elasticsearch.xpack.security.authz.privilege.ClusterPrivilege;
|
||||
import org.elasticsearch.xpack.security.authz.privilege.GeneralPrivilege;
|
||||
import org.elasticsearch.xpack.security.authz.privilege.Privilege.Name;
|
||||
import org.elasticsearch.xpack.security.support.MetadataUtils;
|
||||
|
||||
/**
|
||||
*
|
||||
|
@ -19,7 +20,8 @@ public class SuperuserRole extends Role {
|
|||
public static final RoleDescriptor DESCRIPTOR = new RoleDescriptor(NAME, new String[] { "all" },
|
||||
new RoleDescriptor.IndicesPrivileges[] {
|
||||
RoleDescriptor.IndicesPrivileges.builder().indices("*").privileges("all").build()},
|
||||
new String[] { "*" });
|
||||
new String[] { "*" },
|
||||
MetadataUtils.DEFAULT_RESERVED_METADATA);
|
||||
public static final SuperuserRole INSTANCE = new SuperuserRole();
|
||||
|
||||
private SuperuserRole() {
|
||||
|
|
|
@ -8,6 +8,7 @@ package org.elasticsearch.xpack.security.authz.permission;
|
|||
import org.elasticsearch.xpack.security.authz.RoleDescriptor;
|
||||
import org.elasticsearch.xpack.security.authz.privilege.ClusterPrivilege;
|
||||
import org.elasticsearch.xpack.security.authz.privilege.Privilege.Name;
|
||||
import org.elasticsearch.xpack.security.support.MetadataUtils;
|
||||
|
||||
/**
|
||||
* Reserved role for the transport client
|
||||
|
@ -17,7 +18,8 @@ public class TransportClientRole extends Role {
|
|||
public static final String NAME = "transport_client";
|
||||
private static final String[] CLUSTER_PRIVILEGES = new String[] { "transport_client" };
|
||||
|
||||
public static final RoleDescriptor DESCRIPTOR = new RoleDescriptor(NAME, CLUSTER_PRIVILEGES, null, null);
|
||||
public static final RoleDescriptor DESCRIPTOR =
|
||||
new RoleDescriptor(NAME, CLUSTER_PRIVILEGES, null, null, MetadataUtils.DEFAULT_RESERVED_METADATA);
|
||||
public static final TransportClientRole INSTANCE = new TransportClientRole();
|
||||
|
||||
private TransportClientRole() {
|
||||
|
|
|
@ -5,9 +5,13 @@
|
|||
*/
|
||||
package org.elasticsearch.xpack.security.ssl;
|
||||
|
||||
import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
|
||||
import org.bouncycastle.asn1.pkcs.PrivateKeyInfo;
|
||||
import org.bouncycastle.asn1.x500.X500Name;
|
||||
import org.bouncycastle.asn1.x509.AuthorityKeyIdentifier;
|
||||
import org.bouncycastle.asn1.x509.BasicConstraints;
|
||||
import org.bouncycastle.asn1.x509.Extension;
|
||||
import org.bouncycastle.asn1.x509.ExtensionsGenerator;
|
||||
import org.bouncycastle.asn1.x509.GeneralName;
|
||||
import org.bouncycastle.asn1.x509.GeneralNames;
|
||||
import org.bouncycastle.asn1.x509.Time;
|
||||
|
@ -24,6 +28,8 @@ import org.bouncycastle.openssl.jcajce.JcaPEMKeyConverter;
|
|||
import org.bouncycastle.openssl.jcajce.JcePEMDecryptorProviderBuilder;
|
||||
import org.bouncycastle.operator.ContentSigner;
|
||||
import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder;
|
||||
import org.bouncycastle.pkcs.PKCS10CertificationRequest;
|
||||
import org.bouncycastle.pkcs.jcajce.JcaPKCS10CertificationRequestBuilder;
|
||||
import org.elasticsearch.common.Nullable;
|
||||
import org.elasticsearch.common.Strings;
|
||||
import org.elasticsearch.common.SuppressForbidden;
|
||||
|
@ -40,6 +46,7 @@ import javax.net.ssl.TrustManager;
|
|||
import javax.net.ssl.TrustManagerFactory;
|
||||
import javax.net.ssl.X509ExtendedKeyManager;
|
||||
import javax.net.ssl.X509ExtendedTrustManager;
|
||||
import javax.security.auth.x500.X500Principal;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.Reader;
|
||||
import java.math.BigInteger;
|
||||
|
@ -60,6 +67,7 @@ import java.util.HashSet;
|
|||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Set;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
class CertUtils {
|
||||
|
||||
|
@ -155,7 +163,7 @@ class CertUtils {
|
|||
}
|
||||
}
|
||||
|
||||
static PrivateKey readPrivateKey(Reader reader, char[] keyPassword) throws Exception {
|
||||
static PrivateKey readPrivateKey(Reader reader, Supplier<char[]> passwordSupplier) throws Exception {
|
||||
try (PEMParser parser = new PEMParser(reader)) {
|
||||
Object parsed;
|
||||
List<Object> list = new ArrayList<>(1);
|
||||
|
@ -173,6 +181,7 @@ class CertUtils {
|
|||
PrivateKeyInfo privateKeyInfo;
|
||||
Object parsedObject = list.get(0);
|
||||
if (parsedObject instanceof PEMEncryptedKeyPair) {
|
||||
char[] keyPassword = passwordSupplier.get();
|
||||
if (keyPassword == null) {
|
||||
throw new IllegalArgumentException("cannot read encrypted key without a password");
|
||||
}
|
||||
|
@ -195,30 +204,65 @@ class CertUtils {
|
|||
}
|
||||
}
|
||||
|
||||
static X509Certificate generateSignedCertificate(boolean resolveHostname, String nodeName, Set<InetAddress> addresses, KeyPair keyPair,
|
||||
Certificate caCert, PrivateKey caPrivKey) throws Exception {
|
||||
static X509Certificate generateCACertificate(X500Principal x500Principal, KeyPair keyPair) throws Exception {
|
||||
return generateSignedCertificate(x500Principal, null, keyPair, null, null, true);
|
||||
}
|
||||
|
||||
static X509Certificate generateSignedCertificate(X500Principal principal, GeneralNames subjectAltNames, KeyPair keyPair,
|
||||
X509Certificate caCert, PrivateKey caPrivKey) throws Exception {
|
||||
return generateSignedCertificate(principal, subjectAltNames, keyPair, caCert, caPrivKey, false);
|
||||
}
|
||||
|
||||
private static X509Certificate generateSignedCertificate(X500Principal principal, GeneralNames subjectAltNames, KeyPair keyPair,
|
||||
X509Certificate caCert, PrivateKey caPrivKey, boolean ca) throws Exception {
|
||||
final DateTime notBefore = new DateTime(DateTimeZone.UTC);
|
||||
final DateTime notAfter = notBefore.plusYears(1);
|
||||
final BigInteger serial = getSerial();
|
||||
|
||||
X509Certificate x509CACert = (X509Certificate) caCert;
|
||||
X500Name subject = new X500Name("CN=" + nodeName);
|
||||
JcaX509v3CertificateBuilder builder =
|
||||
new JcaX509v3CertificateBuilder(X500Name.getInstance(x509CACert.getIssuerX500Principal().getEncoded()), serial,
|
||||
new Time(notBefore.toDate(), Locale.ROOT), new Time(notAfter.toDate(), Locale.ROOT), subject, keyPair.getPublic());
|
||||
|
||||
final BigInteger serial = CertUtils.getSerial();
|
||||
JcaX509ExtensionUtils extUtils = new JcaX509ExtensionUtils();
|
||||
builder.addExtension(Extension.subjectKeyIdentifier, false, extUtils.createSubjectKeyIdentifier(keyPair.getPublic()));
|
||||
builder.addExtension(Extension.authorityKeyIdentifier, false, extUtils.createAuthorityKeyIdentifier(x509CACert));
|
||||
if (addresses.isEmpty() == false) {
|
||||
builder.addExtension(Extension.subjectAlternativeName, false, getSubjectAlternativeNames(resolveHostname, addresses));
|
||||
|
||||
X500Name subject = X500Name.getInstance(principal.getEncoded());
|
||||
final X500Name issuer;
|
||||
final AuthorityKeyIdentifier authorityKeyIdentifier;
|
||||
if (caCert != null) {
|
||||
if (caCert.getBasicConstraints() < 0) {
|
||||
throw new IllegalArgumentException("ca certificate is not a CA!");
|
||||
}
|
||||
issuer = X500Name.getInstance(caCert.getIssuerX500Principal().getEncoded());
|
||||
authorityKeyIdentifier = extUtils.createAuthorityKeyIdentifier(caCert);
|
||||
} else {
|
||||
issuer = subject;
|
||||
authorityKeyIdentifier =
|
||||
extUtils.createAuthorityKeyIdentifier(keyPair.getPublic(), new X500Principal(issuer.toString()), serial);
|
||||
}
|
||||
|
||||
ContentSigner signer = new JcaContentSignerBuilder("SHA256withRSA").build(caPrivKey);
|
||||
JcaX509v3CertificateBuilder builder =
|
||||
new JcaX509v3CertificateBuilder(issuer, serial,
|
||||
new Time(notBefore.toDate(), Locale.ROOT), new Time(notAfter.toDate(), Locale.ROOT), subject, keyPair.getPublic());
|
||||
|
||||
builder.addExtension(Extension.subjectKeyIdentifier, false, extUtils.createSubjectKeyIdentifier(keyPair.getPublic()));
|
||||
builder.addExtension(Extension.authorityKeyIdentifier, false, authorityKeyIdentifier);
|
||||
if (subjectAltNames != null) {
|
||||
builder.addExtension(Extension.subjectAlternativeName, false, subjectAltNames);
|
||||
}
|
||||
builder.addExtension(Extension.basicConstraints, ca, new BasicConstraints(ca));
|
||||
|
||||
PrivateKey signingKey = caPrivKey != null ? caPrivKey : keyPair.getPrivate();
|
||||
ContentSigner signer = new JcaContentSignerBuilder("SHA256withRSA").build(signingKey);
|
||||
X509CertificateHolder certificateHolder = builder.build(signer);
|
||||
return new JcaX509CertificateConverter().getCertificate(certificateHolder);
|
||||
}
|
||||
|
||||
static PKCS10CertificationRequest generateCSR(KeyPair keyPair, X500Principal principal, GeneralNames sanList) throws Exception {
|
||||
JcaPKCS10CertificationRequestBuilder builder = new JcaPKCS10CertificationRequestBuilder(principal, keyPair.getPublic());
|
||||
if (sanList != null) {
|
||||
ExtensionsGenerator extGen = new ExtensionsGenerator();
|
||||
extGen.addExtension(Extension.subjectAlternativeName, false, sanList);
|
||||
builder.addAttribute(PKCSObjectIdentifiers.pkcs_9_at_extensionRequest, extGen.generate());
|
||||
}
|
||||
|
||||
return builder.build(new JcaContentSignerBuilder("SHA256withRSA").setProvider(CertUtils.BC_PROV).build(keyPair.getPrivate()));
|
||||
}
|
||||
|
||||
static BigInteger getSerial() {
|
||||
SecureRandom random = new SecureRandom();
|
||||
BigInteger serial = new BigInteger(SERIAL_BIT_LENGTH, random);
|
||||
|
@ -226,10 +270,10 @@ class CertUtils {
|
|||
return serial;
|
||||
}
|
||||
|
||||
static KeyPair generateKeyPair() throws Exception {
|
||||
static KeyPair generateKeyPair(int keysize) throws Exception {
|
||||
// generate a private key
|
||||
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
|
||||
keyPairGenerator.initialize(2048);
|
||||
keyPairGenerator.initialize(keysize);
|
||||
return keyPairGenerator.generateKeyPair();
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,627 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
package org.elasticsearch.xpack.security.ssl;
|
||||
|
||||
import joptsimple.OptionSet;
|
||||
import joptsimple.OptionSpec;
|
||||
import org.bouncycastle.asn1.DERIA5String;
|
||||
import org.bouncycastle.asn1.x509.GeneralName;
|
||||
import org.bouncycastle.asn1.x509.GeneralNames;
|
||||
import org.bouncycastle.openssl.PEMEncryptor;
|
||||
import org.bouncycastle.openssl.jcajce.JcaPEMWriter;
|
||||
import org.bouncycastle.openssl.jcajce.JcePEMEncryptorBuilder;
|
||||
import org.bouncycastle.pkcs.PKCS10CertificationRequest;
|
||||
import org.elasticsearch.ExceptionsHelper;
|
||||
import org.elasticsearch.cli.SettingCommand;
|
||||
import org.elasticsearch.cli.Terminal;
|
||||
import org.elasticsearch.common.ParseField;
|
||||
import org.elasticsearch.common.ParseFieldMatcher;
|
||||
import org.elasticsearch.common.ParseFieldMatcherSupplier;
|
||||
import org.elasticsearch.common.Strings;
|
||||
import org.elasticsearch.common.network.InetAddresses;
|
||||
import org.elasticsearch.common.settings.Settings;
|
||||
import org.elasticsearch.common.xcontent.ConstructingObjectParser;
|
||||
import org.elasticsearch.common.xcontent.ObjectParser;
|
||||
import org.elasticsearch.common.xcontent.XContentParser;
|
||||
import org.elasticsearch.common.xcontent.XContentType;
|
||||
import org.elasticsearch.env.Environment;
|
||||
import org.elasticsearch.node.internal.InternalSettingsPreparer;
|
||||
import org.elasticsearch.xpack.XPackPlugin;
|
||||
|
||||
import javax.security.auth.x500.X500Principal;
|
||||
import java.io.OutputStream;
|
||||
import java.io.Reader;
|
||||
import java.security.cert.Certificate;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStreamWriter;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.StandardOpenOption;
|
||||
import java.security.KeyPair;
|
||||
import java.security.PrivateKey;
|
||||
import java.security.cert.X509Certificate;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
import java.util.zip.ZipEntry;
|
||||
import java.util.zip.ZipOutputStream;
|
||||
|
||||
/**
|
||||
* CLI tool to make generation of certificates or certificate requests easier for users
|
||||
*/
|
||||
public class CertificateTool extends SettingCommand {
|
||||
|
||||
private static final String AUTO_GEN_CA_DN = "CN=Elastic Certificate Tool Autogenerated CA";
|
||||
private static final String DESCRIPTION = "Simplifies certificate creation for use with the Elastic Stack";
|
||||
private static final String DEFAULT_CSR_FILE = "csr-bundle.zip";
|
||||
private static final String DEFAULT_CERT_FILE = "certificate-bundle.zip";
|
||||
private static final Pattern ALLOWED_FILENAME_CHAR_PATTERN = Pattern.compile("([a-zA-Z0-9!@#$%^&{}\\[\\]()_+\\-=,.~'` ]+)");
|
||||
private static final int DEFAULT_KEY_SIZE = 2048;
|
||||
private static final int FILE_EXTENSION_LENGTH = 4;
|
||||
static final int MAX_FILENAME_LENGTH = 255 - FILE_EXTENSION_LENGTH;
|
||||
private static final ObjectParser<List<CertificateInformation>, CertInfoParseContext> PARSER = new ObjectParser<>("certgen");
|
||||
static {
|
||||
ConstructingObjectParser<CertificateInformation, CertInfoParseContext> instanceParser =
|
||||
new ConstructingObjectParser<>("instances",
|
||||
a -> new CertificateInformation((String) a[0], (List<String>) a[1], (List<String>) a[2]));
|
||||
instanceParser.declareString(ConstructingObjectParser.constructorArg(), new ParseField("name"));
|
||||
instanceParser.declareStringArray(ConstructingObjectParser.optionalConstructorArg(), new ParseField("ip"));
|
||||
instanceParser.declareStringArray(ConstructingObjectParser.optionalConstructorArg(), new ParseField("dns"));
|
||||
|
||||
PARSER.declareObjectArray(List::addAll, instanceParser, new ParseField("instances"));
|
||||
}
|
||||
|
||||
private final OptionSpec<String> outputPathSpec;
|
||||
private final OptionSpec<Void> csrSpec;
|
||||
private final OptionSpec<String> caCertPathSpec;
|
||||
private final OptionSpec<String> caKeyPathSpec;
|
||||
private final OptionSpec<String> caPasswordSpec;
|
||||
private final OptionSpec<String> caDnSpec;
|
||||
private final OptionSpec<Integer> keysizeSpec;
|
||||
private final OptionSpec<String> inputFileSpec;
|
||||
|
||||
CertificateTool() {
|
||||
super(DESCRIPTION);
|
||||
outputPathSpec = parser.accepts("out", "path of the zip file that the output should be written to")
|
||||
.withRequiredArg();
|
||||
csrSpec = parser.accepts("csr", "only generate certificate signing requests");
|
||||
caCertPathSpec = parser.accepts("cert", "path to an existing ca certificate").availableUnless(csrSpec).withRequiredArg();
|
||||
caKeyPathSpec = parser.accepts("key", "path to an existing ca private key")
|
||||
.availableIf(caCertPathSpec)
|
||||
.requiredIf(caCertPathSpec)
|
||||
.withRequiredArg();
|
||||
caPasswordSpec = parser.accepts("pass", "password for an existing ca private key or the generated ca private key")
|
||||
.availableUnless(csrSpec)
|
||||
.withOptionalArg();
|
||||
caDnSpec = parser.accepts("dn", "distinguished name to use for the generated ca. defaults to " + AUTO_GEN_CA_DN)
|
||||
.availableUnless(caCertPathSpec)
|
||||
.withRequiredArg();
|
||||
keysizeSpec = parser.accepts("keysize", "size in bits of RSA keys").withRequiredArg().ofType(Integer.class);
|
||||
inputFileSpec = parser.accepts("in", "file containing details of the instances in yaml format").withRequiredArg();
|
||||
}
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
new CertificateTool().main(args, Terminal.DEFAULT);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void execute(Terminal terminal, OptionSet options, Map<String, String> settings) throws Exception {
|
||||
Environment env = InternalSettingsPreparer.prepareEnvironment(Settings.EMPTY, terminal, settings);
|
||||
final boolean csrOnly = options.has(csrSpec);
|
||||
printIntro(terminal, csrOnly);
|
||||
final Path outputFile = getOutputFile(terminal, outputPathSpec.value(options), env, csrOnly ? DEFAULT_CSR_FILE : DEFAULT_CERT_FILE);
|
||||
final String inputFile = inputFileSpec.value(options);
|
||||
final int keysize = options.has(keysizeSpec) ? keysizeSpec.value(options) : DEFAULT_KEY_SIZE;
|
||||
if (csrOnly) {
|
||||
Collection<CertificateInformation> certificateInformations = getCertificateInformationList(terminal, inputFile, env);
|
||||
generateAndWriteCsrs(outputFile, certificateInformations, keysize);
|
||||
} else {
|
||||
final String dn = options.has(caDnSpec) ? caDnSpec.value(options) : AUTO_GEN_CA_DN;
|
||||
final boolean prompt = options.has(caPasswordSpec);
|
||||
final char[] keyPass = options.hasArgument(caPasswordSpec) ? caPasswordSpec.value(options).toCharArray() : null;
|
||||
CAInfo caInfo =
|
||||
getCAInfo(terminal, dn, caCertPathSpec.value(options), caKeyPathSpec.value(options), keyPass, prompt, env, keysize);
|
||||
Collection<CertificateInformation> certificateInformations = getCertificateInformationList(terminal, inputFile, env);
|
||||
generateAndWriteSignedCertificates(outputFile, certificateInformations, caInfo, keysize);
|
||||
}
|
||||
printConclusion(terminal, csrOnly, outputFile);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void printAdditionalHelp(Terminal terminal) {
|
||||
terminal.println("Simplifies the generation of certificate signing requests and signed");
|
||||
terminal.println("certificates. The tool runs interactively unless the 'in' and 'out' parameters");
|
||||
terminal.println("are specified. In the interactive mode, the tool will prompt for required");
|
||||
terminal.println("values that have not been provided through the use of command line options.");
|
||||
terminal.println("");
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks for output file in the user specified options or prompts the user for the output file
|
||||
*
|
||||
* @param terminal terminal to communicate with a user
|
||||
* @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
|
||||
*/
|
||||
static Path getOutputFile(Terminal terminal, String outputPath, Environment env, String defaultFilename) throws IOException {
|
||||
Path file;
|
||||
if (outputPath != null) {
|
||||
file = XPackPlugin.resolveConfigFile(env, Strings.cleanPath(outputPath));
|
||||
} else {
|
||||
file = XPackPlugin.resolveConfigFile(env, defaultFilename);
|
||||
String input = terminal.readText("Please enter the desired output file [" + file + "]: ");
|
||||
if (input.isEmpty() == false) {
|
||||
file = XPackPlugin.resolveConfigFile(env, Strings.cleanPath(input));
|
||||
}
|
||||
}
|
||||
return file;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
* @param terminal the terminal to use for user interaction
|
||||
* @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
|
||||
*/
|
||||
static Collection<CertificateInformation> getCertificateInformationList(Terminal terminal, String inputFile, Environment env)
|
||||
throws Exception {
|
||||
if (inputFile != null) {
|
||||
return parseFile(XPackPlugin.resolveConfigFile(env, inputFile));
|
||||
}
|
||||
Map<String, CertificateInformation> map = new HashMap<>();
|
||||
boolean done = false;
|
||||
while (done == false) {
|
||||
String name = terminal.readText("Enter instance name: ");
|
||||
if (name.isEmpty() == false) {
|
||||
String ipAddresses = terminal.readText("Enter IP Addresses for instance (comma-separated if more than one) []: ");
|
||||
String dnsNames = terminal.readText("Enter DNS names for instance (comma-separated if more than one) []: ");
|
||||
List<String> ipList = Arrays.asList(Strings.splitStringByCommaToArray(ipAddresses));
|
||||
List<String> dnsList = Arrays.asList(Strings.splitStringByCommaToArray(dnsNames));
|
||||
|
||||
CertificateInformation information = new CertificateInformation(name, ipList, dnsList);
|
||||
List<String> validationErrors = information.validate();
|
||||
if (validationErrors.isEmpty()) {
|
||||
if (map.containsKey(name)) {
|
||||
terminal.println("Overwriting previously defined instance information [" + name + "]");
|
||||
}
|
||||
map.put(name, information);
|
||||
} else {
|
||||
for (String validationError : validationErrors) {
|
||||
terminal.println(validationError);
|
||||
}
|
||||
terminal.println("Skipping entry as invalid values were found");
|
||||
}
|
||||
} else {
|
||||
terminal.println("A name must be provided");
|
||||
}
|
||||
|
||||
String exit = terminal.readText("Would you like to specify another instance? Press 'y' to continue entering instance " +
|
||||
"information: ");
|
||||
if ("y".equals(exit) == false) {
|
||||
done = true;
|
||||
}
|
||||
}
|
||||
return map.values();
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses the input file to retrieve the certificate information
|
||||
* @param file the file to parse
|
||||
* @return a collection of certificate information
|
||||
*/
|
||||
static Collection<CertificateInformation> parseFile(Path file) throws Exception {
|
||||
try (Reader reader = Files.newBufferedReader(file)) {
|
||||
XContentParser xContentParser = XContentType.YAML.xContent().createParser(reader);
|
||||
return PARSER.parse(xContentParser, new ArrayList<>(), new CertInfoParseContext());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates certificate signing requests and writes them out to the specified file in zip format
|
||||
* @param outputFile the file to write the output to. This file must not already exist
|
||||
* @param certInfo the details to use in the certificate signing requests
|
||||
*/
|
||||
static void generateAndWriteCsrs(Path outputFile, Collection<CertificateInformation> certInfo, int keysize) throws Exception {
|
||||
fullyWriteFile(outputFile, (outputStream, pemWriter) -> {
|
||||
for (CertificateInformation certificateInformation : certInfo) {
|
||||
KeyPair keyPair = CertUtils.generateKeyPair(keysize);
|
||||
GeneralNames sanList = getSubjectAlternativeNamesValue(certificateInformation.ipAddresses, certificateInformation.dnsNames);
|
||||
PKCS10CertificationRequest csr = CertUtils.generateCSR(keyPair, certificateInformation.name.x500Principal, sanList);
|
||||
|
||||
final String dirName = certificateInformation.name.filename + "/";
|
||||
ZipEntry zipEntry = new ZipEntry(dirName);
|
||||
assert zipEntry.isDirectory();
|
||||
outputStream.putNextEntry(zipEntry);
|
||||
|
||||
// write csr
|
||||
outputStream.putNextEntry(new ZipEntry(dirName + certificateInformation.name.filename + ".csr"));
|
||||
pemWriter.writeObject(csr);
|
||||
pemWriter.flush();
|
||||
outputStream.closeEntry();
|
||||
|
||||
// write private key
|
||||
outputStream.putNextEntry(new ZipEntry(dirName + certificateInformation.name.filename + ".key"));
|
||||
pemWriter.writeObject(keyPair.getPrivate());
|
||||
pemWriter.flush();
|
||||
outputStream.closeEntry();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the CA certificate and private key that will be used to sign certificates. These may be specified by the user or
|
||||
* automatically generated
|
||||
*
|
||||
* @param terminal the terminal to use for prompting the user
|
||||
* @param dn the distinguished name to use for the CA
|
||||
* @param caCertPath the path to the CA certificate or {@code null} if not provided
|
||||
* @param caKeyPath the path to the CA private key or {@code null} if not provided
|
||||
* @param keyPass the password to the private key. If not present and the key is encrypted the user will be prompted
|
||||
* @param env the environment for this tool to resolve files with
|
||||
* @return CA cert and private key
|
||||
*/
|
||||
static CAInfo getCAInfo(Terminal terminal, String dn, String caCertPath, String caKeyPath, char[] keyPass, boolean prompt,
|
||||
Environment env, int keysize) throws Exception {
|
||||
if (caCertPath != null) {
|
||||
assert caKeyPath != null;
|
||||
Certificate[] certificates = CertUtils.readCertificates(Collections.singletonList(caCertPath), env);
|
||||
if (certificates.length != 1) {
|
||||
throw new IllegalArgumentException("expected a single certificate in file [" + caCertPath + "] but found [" +
|
||||
certificates.length + "]");
|
||||
}
|
||||
Certificate caCert = certificates[0];
|
||||
PrivateKey privateKey = readPrivateKey(caKeyPath, keyPass, terminal, env, prompt);
|
||||
return new CAInfo((X509Certificate) caCert, privateKey);
|
||||
}
|
||||
|
||||
// generate the CA keys and cert
|
||||
X500Principal x500Principal = new X500Principal(dn);
|
||||
KeyPair keyPair = CertUtils.generateKeyPair(keysize);
|
||||
Certificate caCert = CertUtils.generateCACertificate(x500Principal, keyPair);
|
||||
final char[] password;
|
||||
if (prompt) {
|
||||
password = terminal.readSecret("Enter password for CA private key: ");
|
||||
} else {
|
||||
password = keyPass;
|
||||
}
|
||||
return new CAInfo((X509Certificate) caCert, keyPair.getPrivate(), true, password);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates signed certificates in PEM format stored in a zip file
|
||||
* @param outputFile the file that the certificates will be written to. This file must not exist
|
||||
* @param certificateInformations details for creation of the certificates
|
||||
* @param caInfo the CA information to sign the certificates with
|
||||
*/
|
||||
static void generateAndWriteSignedCertificates(Path outputFile, Collection<CertificateInformation> certificateInformations,
|
||||
CAInfo caInfo, int keysize) throws Exception {
|
||||
fullyWriteFile(outputFile, (outputStream, pemWriter) -> {
|
||||
// write out the CA info first if it was generated
|
||||
writeCAInfoIfGenerated(outputStream, pemWriter, caInfo);
|
||||
|
||||
for (CertificateInformation certificateInformation : certificateInformations) {
|
||||
KeyPair keyPair = CertUtils.generateKeyPair(keysize);
|
||||
Certificate certificate = CertUtils.generateSignedCertificate(certificateInformation.name.x500Principal,
|
||||
getSubjectAlternativeNamesValue(certificateInformation.ipAddresses, certificateInformation.dnsNames),
|
||||
keyPair, caInfo.caCert, caInfo.privateKey);
|
||||
|
||||
final String dirName = certificateInformation.name.filename + "/";
|
||||
ZipEntry zipEntry = new ZipEntry(dirName);
|
||||
assert zipEntry.isDirectory();
|
||||
outputStream.putNextEntry(zipEntry);
|
||||
|
||||
// write cert
|
||||
outputStream.putNextEntry(new ZipEntry(dirName + certificateInformation.name.filename + ".crt"));
|
||||
pemWriter.writeObject(certificate);
|
||||
pemWriter.flush();
|
||||
outputStream.closeEntry();
|
||||
|
||||
// write private key
|
||||
outputStream.putNextEntry(new ZipEntry(dirName + certificateInformation.name.filename + ".key"));
|
||||
pemWriter.writeObject(keyPair.getPrivate());
|
||||
pemWriter.flush();
|
||||
outputStream.closeEntry();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* This method handles the deletion of a file in the case of a partial write
|
||||
* @param file the file that is being written to
|
||||
* @param writer writes the contents of the file
|
||||
*/
|
||||
private static void fullyWriteFile(Path file, Writer writer) throws Exception {
|
||||
boolean success = false;
|
||||
try (OutputStream outputStream = Files.newOutputStream(file, StandardOpenOption.CREATE_NEW);
|
||||
ZipOutputStream zipOutputStream = new ZipOutputStream(outputStream, StandardCharsets.UTF_8);
|
||||
JcaPEMWriter pemWriter = new JcaPEMWriter(new OutputStreamWriter(zipOutputStream, StandardCharsets.UTF_8))) {
|
||||
writer.write(zipOutputStream, pemWriter);
|
||||
success = true;
|
||||
} finally {
|
||||
if (success == false) {
|
||||
Files.delete(file);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This method handles writing out the certificate authority cert and private key if the certificate authority was generated by
|
||||
* this invocation of the tool
|
||||
* @param outputStream the output stream to write to
|
||||
* @param pemWriter the writer for PEM objects
|
||||
* @param info the certificate authority information
|
||||
*/
|
||||
private static void writeCAInfoIfGenerated(ZipOutputStream outputStream, JcaPEMWriter pemWriter, CAInfo info) throws Exception {
|
||||
if (info.generated) {
|
||||
final String caDirName = "ca/";
|
||||
ZipEntry zipEntry = new ZipEntry(caDirName);
|
||||
assert zipEntry.isDirectory();
|
||||
outputStream.putNextEntry(zipEntry);
|
||||
outputStream.putNextEntry(new ZipEntry(caDirName + "ca.crt"));
|
||||
pemWriter.writeObject(info.caCert);
|
||||
pemWriter.flush();
|
||||
outputStream.closeEntry();
|
||||
outputStream.putNextEntry(new ZipEntry(caDirName + "ca.key"));
|
||||
if (info.password != null && info.password.length > 0) {
|
||||
try {
|
||||
PEMEncryptor encryptor = new JcePEMEncryptorBuilder("DES-EDE3-CBC").setProvider(CertUtils.BC_PROV).build(info.password);
|
||||
pemWriter.writeObject(info.privateKey, encryptor);
|
||||
} finally {
|
||||
// we can safely nuke the password chars now
|
||||
Arrays.fill(info.password, (char) 0);
|
||||
}
|
||||
} else {
|
||||
pemWriter.writeObject(info.privateKey);
|
||||
}
|
||||
pemWriter.flush();
|
||||
outputStream.closeEntry();
|
||||
}
|
||||
}
|
||||
|
||||
private static void printIntro(Terminal terminal, boolean csr) {
|
||||
terminal.println("This tool assists you in the generation of X.509 certificates and certificate");
|
||||
terminal.println("signing requests for use with SSL in the Elastic stack. Depending on the command");
|
||||
terminal.println("line option specified, you may be prompted for the following:");
|
||||
terminal.println("");
|
||||
terminal.println("* The path to the output file");
|
||||
if (csr) {
|
||||
terminal.println(" * The output file is a zip file containing the certificate signing requests");
|
||||
terminal.println(" and private keys for each instance.");
|
||||
} else {
|
||||
terminal.println(" * The output file is a zip file containing the signed certificates and");
|
||||
terminal.println(" private keys for each instance. If a Certificate Authority was generated,");
|
||||
terminal.println(" the certificate and private key will also be included in the output file.");
|
||||
}
|
||||
terminal.println("* Information about each instance");
|
||||
terminal.println(" * An instance is any piece of the Elastic Stack that requires a SSL certificate.");
|
||||
terminal.println(" Depending on your configuration, Elasticsearch, Logstash, Kibana, and Beats");
|
||||
terminal.println(" may all require a certificate and private key.");
|
||||
terminal.println(" * The minimum required value for each instance is a name. This can simply be the");
|
||||
terminal.println(" hostname, which will be used as the Common Name of the certificate. A full");
|
||||
terminal.println(" distinguished name may also be used.");
|
||||
terminal.println(" * IP addresses and DNS names are optional. Multiple values can be specified as a");
|
||||
terminal.println(" comma separated string. If no IP addresses or DNS names are provided, you may");
|
||||
terminal.println(" disable hostname verification in your SSL configuration.");
|
||||
|
||||
if (csr == false) {
|
||||
terminal.println("* Certificate Authority private key password");
|
||||
terminal.println(" * The password may be left empty if desired.");
|
||||
}
|
||||
terminal.println("");
|
||||
terminal.println("Let's get started...");
|
||||
terminal.println("");
|
||||
}
|
||||
|
||||
private static void printConclusion(Terminal terminal, boolean csr, Path outputFile) {
|
||||
if (csr) {
|
||||
terminal.println("Certificate signing requests written to " + outputFile);
|
||||
terminal.println("");
|
||||
terminal.println("This file should be properly secured as it contains the private keys for all");
|
||||
terminal.println("instances.");
|
||||
terminal.println("");
|
||||
terminal.println("After unzipping the file, there will be a directory for each instance containing");
|
||||
terminal.println("the certificate signing request and the private key. Provide the certificate");
|
||||
terminal.println("signing requests to your certificate authority. Once you have received the");
|
||||
terminal.println("signed certificate, copy the signed certificate, key, and CA certificate to the");
|
||||
terminal.println("configuration directory of the Elastic product that they will be used for and");
|
||||
terminal.println("follow the SSL configuration instructions in the product guide.");
|
||||
} else {
|
||||
terminal.println("Certificates written to " + outputFile);
|
||||
terminal.println("");
|
||||
terminal.println("This file should be properly secured as it contains the private keys for all");
|
||||
terminal.println("instances and the certificate authority.");
|
||||
terminal.println("");
|
||||
terminal.println("After unzipping the file, there will be a directory for each instance containing");
|
||||
terminal.println("the certificate and private key. Copy the certificate, key, and CA certificate");
|
||||
terminal.println("to the configuration directory of the Elastic product that they will be used for");
|
||||
terminal.println("and follow the SSL configuration instructions in the product guide.");
|
||||
terminal.println("");
|
||||
terminal.println("For client applications, you may only need to copy the CA certificate and");
|
||||
terminal.println("configure the client to trust this certificate.");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper method to read a private key and support prompting of user for a key. To avoid passwords being placed as an argument we
|
||||
* can prompt the user for their password if we encounter an encrypted key.
|
||||
* @param path the path to the private key
|
||||
* @param password the password provided by the user or {@code null}
|
||||
* @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
|
||||
* @return the {@link PrivateKey} that was read from the file
|
||||
*/
|
||||
private static PrivateKey readPrivateKey(String path, char[] password, Terminal terminal, Environment env, boolean prompt)
|
||||
throws Exception {
|
||||
AtomicReference<char[]> passwordReference = new AtomicReference<>(password);
|
||||
try (Reader reader = Files.newBufferedReader(XPackPlugin.resolveConfigFile(env, path), StandardCharsets.UTF_8)) {
|
||||
return CertUtils.readPrivateKey(reader, () -> {
|
||||
if (password != null || prompt == false) {
|
||||
return password;
|
||||
}
|
||||
char[] promptedValue = terminal.readSecret("Enter password for CA private key: ");
|
||||
passwordReference.set(promptedValue);
|
||||
return promptedValue;
|
||||
});
|
||||
} finally {
|
||||
if (passwordReference.get() != null) {
|
||||
Arrays.fill(passwordReference.get(), (char) 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static GeneralNames getSubjectAlternativeNamesValue(List<String> ipAddresses, List<String> dnsNames) {
|
||||
Set<GeneralName> generalNameList = new HashSet<>();
|
||||
for (String ip : ipAddresses) {
|
||||
generalNameList.add(new GeneralName(GeneralName.iPAddress, ip));
|
||||
}
|
||||
|
||||
for (String dns : dnsNames) {
|
||||
generalNameList.add(new GeneralName(GeneralName.dNSName, dns));
|
||||
}
|
||||
|
||||
if (generalNameList.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
return new GeneralNames(generalNameList.toArray(new GeneralName[0]));
|
||||
}
|
||||
|
||||
static class CertificateInformation {
|
||||
final Name name;
|
||||
final List<String> ipAddresses;
|
||||
final List<String> dnsNames;
|
||||
|
||||
CertificateInformation(String name, List<String> ipAddresses, List<String> dnsNames) {
|
||||
this.name = Name.fromUserProvidedName(name);
|
||||
this.ipAddresses = ipAddresses == null ? Collections.emptyList() : ipAddresses;
|
||||
this.dnsNames = dnsNames == null ? Collections.emptyList() : dnsNames;
|
||||
}
|
||||
|
||||
List<String> validate() {
|
||||
List<String> errors = new ArrayList<>();
|
||||
if (name.error != null) {
|
||||
errors.add(name.error);
|
||||
}
|
||||
for (String ip : ipAddresses) {
|
||||
if (InetAddresses.isInetAddress(ip) == false) {
|
||||
errors.add("[" + ip + "] is not a valid IP address");
|
||||
}
|
||||
}
|
||||
for (String dnsName : dnsNames) {
|
||||
if (DERIA5String.isIA5String(dnsName) == false) {
|
||||
errors.add("[" + dnsName + "] is not a valid DNS name");
|
||||
}
|
||||
}
|
||||
return errors;
|
||||
}
|
||||
}
|
||||
|
||||
static class Name {
|
||||
|
||||
final String originalName;
|
||||
final X500Principal x500Principal;
|
||||
final String filename;
|
||||
final String error;
|
||||
|
||||
private Name(String name, X500Principal x500Principal, String filename, String error) {
|
||||
this.originalName = name;
|
||||
this.x500Principal = x500Principal;
|
||||
this.filename = filename;
|
||||
this.error = error;
|
||||
}
|
||||
|
||||
static Name fromUserProvidedName(String name) {
|
||||
if ("ca".equals(name)) {
|
||||
return new Name(name, null, null, "[ca] may not be used as an instance name");
|
||||
}
|
||||
|
||||
final X500Principal principal;
|
||||
try {
|
||||
if (name.contains("=")) {
|
||||
principal = new X500Principal(name);
|
||||
} else {
|
||||
principal = new X500Principal("CN=" + name);
|
||||
}
|
||||
} catch (IllegalArgumentException e) {
|
||||
String error = "[" + name + "] could not be converted to a valid DN\n" + e.getMessage() + "\n"
|
||||
+ ExceptionsHelper.stackTrace(e);
|
||||
return new Name(name, null, null, error);
|
||||
}
|
||||
|
||||
String filename = attemptToConvertFilename(Strings.cleanPath(name));
|
||||
if (filename == null) {
|
||||
return new Name(name, principal, null, "could not convert [" + name + "] to a valid filename");
|
||||
}
|
||||
return new Name(name, principal, filename, null);
|
||||
}
|
||||
|
||||
static String attemptToConvertFilename(String name) {
|
||||
StringBuilder builder = new StringBuilder();
|
||||
Matcher matcher = ALLOWED_FILENAME_CHAR_PATTERN.matcher(name);
|
||||
while (matcher.find()) {
|
||||
builder.append(matcher.group(1));
|
||||
}
|
||||
|
||||
if (builder.length() > MAX_FILENAME_LENGTH) {
|
||||
return builder.substring(0, MAX_FILENAME_LENGTH);
|
||||
}
|
||||
|
||||
if (builder.length() > 0) {
|
||||
return builder.toString();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
static class CAInfo {
|
||||
final X509Certificate caCert;
|
||||
final PrivateKey privateKey;
|
||||
final boolean generated;
|
||||
final char[] password;
|
||||
|
||||
CAInfo(X509Certificate caCert, PrivateKey privateKey) {
|
||||
this(caCert, privateKey, false, null);
|
||||
}
|
||||
|
||||
CAInfo(X509Certificate caCert, PrivateKey privateKey, boolean generated, char[] password) {
|
||||
this.caCert = caCert;
|
||||
this.privateKey = privateKey;
|
||||
this.generated = generated;
|
||||
this.password = password;
|
||||
}
|
||||
}
|
||||
|
||||
private static class CertInfoParseContext implements ParseFieldMatcherSupplier {
|
||||
|
||||
private final ParseFieldMatcher parseFieldMatcher;
|
||||
|
||||
CertInfoParseContext() {
|
||||
this.parseFieldMatcher = new ParseFieldMatcher(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ParseFieldMatcher getParseFieldMatcher() {
|
||||
return parseFieldMatcher;
|
||||
}
|
||||
}
|
||||
|
||||
private interface Writer {
|
||||
void write(ZipOutputStream zipOutputStream, JcaPEMWriter pemWriter) throws Exception;
|
||||
}
|
||||
}
|
|
@ -19,6 +19,7 @@ import java.nio.file.Path;
|
|||
import java.security.PrivateKey;
|
||||
import java.security.cert.Certificate;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
class PEMKeyConfig extends KeyConfig {
|
||||
|
@ -48,9 +49,13 @@ class PEMKeyConfig extends KeyConfig {
|
|||
}
|
||||
|
||||
PrivateKey readPrivateKey(Path keyPath) throws Exception {
|
||||
char[] password = keyPassword == null ? null : keyPassword.toCharArray();
|
||||
try (Reader reader = Files.newBufferedReader(keyPath, StandardCharsets.UTF_8)) {
|
||||
char[] password = keyPassword == null ? null : keyPassword.toCharArray();
|
||||
return CertUtils.readPrivateKey(reader, password);
|
||||
return CertUtils.readPrivateKey(reader, () -> password);
|
||||
} finally {
|
||||
if (password != null) {
|
||||
Arrays.fill(password, (char) 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,71 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
package org.elasticsearch.xpack.security.support;
|
||||
|
||||
import java.lang.reflect.Array;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
|
||||
public class MetadataUtils {
|
||||
|
||||
public static final String RESERVED_PREFIX = "_";
|
||||
public static final String RESERVED_METADATA_KEY = RESERVED_PREFIX + "reserved";
|
||||
public static final Map<String, Object> DEFAULT_RESERVED_METADATA = Collections.singletonMap(RESERVED_METADATA_KEY, true);
|
||||
|
||||
private MetadataUtils() {}
|
||||
|
||||
public static void writeValue(StringBuilder sb, Object object) {
|
||||
if (object instanceof Map) {
|
||||
sb.append("{");
|
||||
for (Map.Entry<String, Object> entry : ((Map<String, Object>)object).entrySet()) {
|
||||
sb.append(entry.getKey()).append("=");
|
||||
writeValue(sb, entry.getValue());
|
||||
}
|
||||
sb.append("}");
|
||||
|
||||
} else if (object instanceof Collection) {
|
||||
sb.append("[");
|
||||
boolean first = true;
|
||||
for (Object item : (Collection) object) {
|
||||
if (!first) {
|
||||
sb.append(",");
|
||||
}
|
||||
writeValue(sb, item);
|
||||
first = false;
|
||||
}
|
||||
sb.append("]");
|
||||
} else if (object.getClass().isArray()) {
|
||||
sb.append("[");
|
||||
for (int i = 0; i < Array.getLength(object); i++) {
|
||||
if (i != 0) {
|
||||
sb.append(",");
|
||||
}
|
||||
writeValue(sb, Array.get(object, i));
|
||||
}
|
||||
sb.append("]");
|
||||
} else {
|
||||
sb.append(object);
|
||||
}
|
||||
}
|
||||
|
||||
public static void verifyNoReservedMetadata(Map<String, Object> metadata) {
|
||||
for (String key : metadata.keySet()) {
|
||||
if (key.startsWith(RESERVED_PREFIX)) {
|
||||
throw new IllegalArgumentException("invalid user metadata. [" + key + "] is a reserved for internal uses");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean containsReservedMetadata(Map<String, Object> metadata) {
|
||||
for (String key : metadata.keySet()) {
|
||||
if (key.startsWith(RESERVED_PREFIX)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
|
@ -14,12 +14,11 @@ import org.elasticsearch.common.io.stream.StreamOutput;
|
|||
import org.elasticsearch.common.xcontent.ToXContent;
|
||||
import org.elasticsearch.common.xcontent.XContentBuilder;
|
||||
import org.elasticsearch.xpack.security.authc.esnative.ReservedRealm;
|
||||
import org.elasticsearch.xpack.security.support.MetadataUtils;
|
||||
|
||||
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.Array;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
|
||||
|
@ -28,8 +27,6 @@ import java.util.Map;
|
|||
*/
|
||||
public class User implements ToXContent {
|
||||
|
||||
static final String RESERVED_PREFIX = "_";
|
||||
|
||||
private final String username;
|
||||
private final String[] roles;
|
||||
private final User runAs;
|
||||
|
@ -53,7 +50,7 @@ public class User implements ToXContent {
|
|||
this.fullName = fullName;
|
||||
this.email = email;
|
||||
this.runAs = null;
|
||||
verifyNoReservedMetadata(this.username, this.metadata);
|
||||
verifyNoReservedMetadata(this.metadata);
|
||||
}
|
||||
|
||||
public User(String username, String[] roles, String fullName, String email, Map<String, Object> metadata, User runAs) {
|
||||
|
@ -67,7 +64,7 @@ public class User implements ToXContent {
|
|||
throw new ElasticsearchSecurityException("invalid run_as user");
|
||||
}
|
||||
this.runAs = runAs;
|
||||
verifyNoReservedMetadata(this.username, this.metadata);
|
||||
verifyNoReservedMetadata(this.metadata);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -125,7 +122,7 @@ public class User implements ToXContent {
|
|||
sb.append(",fullName=").append(fullName);
|
||||
sb.append(",email=").append(email);
|
||||
sb.append(",metadata=");
|
||||
append(sb, metadata);
|
||||
MetadataUtils.writeValue(sb, metadata);
|
||||
if (runAs != null) {
|
||||
sb.append(",runAs=[").append(runAs.toString()).append("]");
|
||||
}
|
||||
|
@ -172,16 +169,12 @@ public class User implements ToXContent {
|
|||
return builder.endObject();
|
||||
}
|
||||
|
||||
void verifyNoReservedMetadata(String principal, Map<String, Object> metadata) {
|
||||
void verifyNoReservedMetadata(Map<String, Object> metadata) {
|
||||
if (this instanceof ReservedUser) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (String key : metadata.keySet()) {
|
||||
if (key.startsWith(RESERVED_PREFIX)) {
|
||||
throw new IllegalArgumentException("invalid user metadata. [" + key + "] is a reserved for internal uses");
|
||||
}
|
||||
}
|
||||
MetadataUtils.verifyNoReservedMetadata(metadata);
|
||||
}
|
||||
|
||||
public static User readFrom(StreamInput input) throws IOException {
|
||||
|
@ -245,49 +238,10 @@ public class User implements ToXContent {
|
|||
}
|
||||
}
|
||||
|
||||
public static void append(StringBuilder sb, Object object) {
|
||||
if (object == null) {
|
||||
sb.append((Object) null);
|
||||
}
|
||||
if (object instanceof Map) {
|
||||
sb.append("{");
|
||||
for (Map.Entry<String, Object> entry : ((Map<String, Object>)object).entrySet()) {
|
||||
sb.append(entry.getKey()).append("=");
|
||||
append(sb, entry.getValue());
|
||||
}
|
||||
sb.append("}");
|
||||
|
||||
} else if (object instanceof Collection) {
|
||||
sb.append("[");
|
||||
boolean first = true;
|
||||
for (Object item : (Collection) object) {
|
||||
if (!first) {
|
||||
sb.append(",");
|
||||
}
|
||||
append(sb, item);
|
||||
first = false;
|
||||
}
|
||||
sb.append("]");
|
||||
} else if (object.getClass().isArray()) {
|
||||
sb.append("[");
|
||||
for (int i = 0; i < Array.getLength(object); i++) {
|
||||
if (i != 0) {
|
||||
sb.append(",");
|
||||
}
|
||||
append(sb, Array.get(object, i));
|
||||
}
|
||||
sb.append("]");
|
||||
} else {
|
||||
sb.append(object);
|
||||
}
|
||||
}
|
||||
|
||||
abstract static class ReservedUser extends User {
|
||||
|
||||
private static final String RESERVED_KEY = User.RESERVED_PREFIX + "reserved";
|
||||
|
||||
ReservedUser(String username, String... roles) {
|
||||
super(username, roles, null, null, Collections.singletonMap(RESERVED_KEY, true));
|
||||
super(username, roles, null, null, MetadataUtils.DEFAULT_RESERVED_METADATA);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -86,6 +86,10 @@
|
|||
},
|
||||
"run_as" : {
|
||||
"type" : "keyword"
|
||||
},
|
||||
"metadata" : {
|
||||
"type" : "object",
|
||||
"dynamic" : true
|
||||
}
|
||||
}
|
||||
},
|
||||
|
|
|
@ -48,6 +48,7 @@ import org.elasticsearch.xpack.MockNetty3Plugin;
|
|||
import org.elasticsearch.xpack.XPackPlugin;
|
||||
import org.elasticsearch.xpack.graph.GraphLicensee;
|
||||
import org.elasticsearch.xpack.monitoring.MonitoringLicensee;
|
||||
import org.elasticsearch.xpack.XPackTransportClient;
|
||||
import org.elasticsearch.xpack.security.Security;
|
||||
import org.elasticsearch.xpack.security.SecurityLicenseState;
|
||||
import org.elasticsearch.xpack.security.SecurityLicensee;
|
||||
|
@ -208,7 +209,7 @@ public class LicensingTests extends SecurityIntegTestCase {
|
|||
builder.remove(ThreadContext.PREFIX + "." + UsernamePasswordToken.BASIC_AUTH_HEADER);
|
||||
|
||||
// basic has no auth
|
||||
try (TransportClient client = TransportClient.builder().settings(builder).addPlugin(XPackPlugin.class).build()) {
|
||||
try (TransportClient client = new XPackTransportClient(builder.build())) {
|
||||
client.addTransportAddress(internalCluster().getDataNodeInstance(Transport.class).boundAddress().publishAddress());
|
||||
assertGreenClusterState(client);
|
||||
}
|
||||
|
@ -217,7 +218,7 @@ public class LicensingTests extends SecurityIntegTestCase {
|
|||
OperationMode mode = randomFrom(OperationMode.GOLD, OperationMode.TRIAL, OperationMode.PLATINUM, OperationMode.STANDARD);
|
||||
enableLicensing(mode);
|
||||
|
||||
try (TransportClient client = TransportClient.builder().settings(builder).addPlugin(XPackPlugin.class).build()) {
|
||||
try (TransportClient client = new XPackTransportClient(builder.build())) {
|
||||
client.addTransportAddress(internalCluster().getDataNodeInstance(Transport.class).boundAddress().publishAddress());
|
||||
client.admin().cluster().prepareHealth().get();
|
||||
fail("should not have been able to connect to a node!");
|
||||
|
|
|
@ -16,7 +16,6 @@ import org.elasticsearch.action.ActionRequest;
|
|||
import org.elasticsearch.action.ActionRequestBuilder;
|
||||
import org.elasticsearch.action.ActionResponse;
|
||||
import org.elasticsearch.client.Client;
|
||||
import org.elasticsearch.client.FilterClient;
|
||||
import org.elasticsearch.client.transport.TransportClient;
|
||||
import org.elasticsearch.cluster.node.DiscoveryNode;
|
||||
import org.elasticsearch.cluster.service.ClusterService;
|
||||
|
@ -26,6 +25,7 @@ import org.elasticsearch.rest.RestRequest;
|
|||
import org.elasticsearch.test.ESTestCase;
|
||||
import org.elasticsearch.threadpool.TestThreadPool;
|
||||
import org.elasticsearch.threadpool.ThreadPool;
|
||||
import org.elasticsearch.transport.MockTransportClient;
|
||||
import org.elasticsearch.transport.TransportMessage;
|
||||
import org.elasticsearch.xpack.security.InternalClient;
|
||||
import org.elasticsearch.xpack.security.audit.index.IndexAuditTrail.State;
|
||||
|
@ -60,7 +60,7 @@ public class IndexAuditTrailMutedTests extends ESTestCase {
|
|||
when(clusterService.localNode()).thenReturn(localNode);
|
||||
|
||||
threadPool = new TestThreadPool("index audit trail tests");
|
||||
transportClient = TransportClient.builder().settings(Settings.builder().put("transport.type", "local")).build();
|
||||
transportClient = new MockTransportClient(Settings.EMPTY);
|
||||
clientCalled = new AtomicBoolean(false);
|
||||
class IClient extends InternalClient {
|
||||
IClient(Client transportClient){
|
||||
|
|
|
@ -23,7 +23,7 @@ import org.elasticsearch.xpack.security.authc.support.UsernamePasswordToken;
|
|||
import org.elasticsearch.xpack.security.transport.netty3.SecurityNetty3Transport;
|
||||
import org.elasticsearch.test.SecurityIntegTestCase;
|
||||
import org.elasticsearch.test.SecuritySettingsSource;
|
||||
import org.elasticsearch.xpack.XPackPlugin;
|
||||
import org.elasticsearch.xpack.XPackTransportClient;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
|
@ -233,10 +233,7 @@ public class RunAsIntegTests extends SecurityIntegTestCase {
|
|||
.put(SecurityNetty3Transport.SSL_SETTING.getKey(), false)
|
||||
.build();
|
||||
|
||||
return TransportClient.builder()
|
||||
.settings(settings)
|
||||
.addPlugin(XPackPlugin.class)
|
||||
.build()
|
||||
return new XPackTransportClient(settings)
|
||||
.addTransportAddress(publishAddress);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -31,7 +31,8 @@ public class ActiveDirectoryGroupsResolverTests extends GroupsResolverTestCase {
|
|||
.put("scope", LdapSearchScope.SUB_TREE)
|
||||
.build();
|
||||
ActiveDirectoryGroupsResolver resolver = new ActiveDirectoryGroupsResolver(settings, "DC=ad,DC=test,DC=elasticsearch,DC=com");
|
||||
List<String> groups = resolver.resolve(ldapConnection, BRUCE_BANNER_DN, TimeValue.timeValueSeconds(10), NoOpLogger.INSTANCE);
|
||||
List<String> groups = resolver.resolve(ldapConnection, BRUCE_BANNER_DN, TimeValue.timeValueSeconds(10), NoOpLogger.INSTANCE,
|
||||
null);
|
||||
assertThat(groups, containsInAnyOrder(
|
||||
containsString("Avengers"),
|
||||
containsString("SHIELD"),
|
||||
|
@ -48,7 +49,8 @@ public class ActiveDirectoryGroupsResolverTests extends GroupsResolverTestCase {
|
|||
.put("base_dn", "CN=Builtin, DC=ad, DC=test, DC=elasticsearch,DC=com")
|
||||
.build();
|
||||
ActiveDirectoryGroupsResolver resolver = new ActiveDirectoryGroupsResolver(settings, "DC=ad,DC=test,DC=elasticsearch,DC=com");
|
||||
List<String> groups = resolver.resolve(ldapConnection, BRUCE_BANNER_DN, TimeValue.timeValueSeconds(10), NoOpLogger.INSTANCE);
|
||||
List<String> groups = resolver.resolve(ldapConnection, BRUCE_BANNER_DN, TimeValue.timeValueSeconds(10), NoOpLogger.INSTANCE,
|
||||
null);
|
||||
assertThat(groups, hasItem(containsString("Users")));
|
||||
}
|
||||
|
||||
|
@ -58,7 +60,8 @@ public class ActiveDirectoryGroupsResolverTests extends GroupsResolverTestCase {
|
|||
.put("base_dn", "CN=Users, CN=Builtin, DC=ad, DC=test, DC=elasticsearch, DC=com")
|
||||
.build();
|
||||
ActiveDirectoryGroupsResolver resolver = new ActiveDirectoryGroupsResolver(settings, "DC=ad,DC=test,DC=elasticsearch,DC=com");
|
||||
List<String> groups = resolver.resolve(ldapConnection, BRUCE_BANNER_DN, TimeValue.timeValueSeconds(10), NoOpLogger.INSTANCE);
|
||||
List<String> groups = resolver.resolve(ldapConnection, BRUCE_BANNER_DN, TimeValue.timeValueSeconds(10), NoOpLogger.INSTANCE,
|
||||
null);
|
||||
assertThat(groups, hasItem(containsString("CN=Users,CN=Builtin")));
|
||||
}
|
||||
|
||||
|
@ -69,8 +72,9 @@ public class ActiveDirectoryGroupsResolverTests extends GroupsResolverTestCase {
|
|||
"S-1-5-32-545", //Default Users group
|
||||
"S-1-5-21-3510024162-210737641-214529065-513" //Default Domain Users group
|
||||
};
|
||||
Filter query = ActiveDirectoryGroupsResolver.buildGroupQuery(ldapConnection,
|
||||
"CN=Jarvis, CN=Users, DC=ad, DC=test, DC=elasticsearch, DC=com", TimeValue.timeValueSeconds(10), NoOpLogger.INSTANCE);
|
||||
final String dn = "CN=Jarvis, CN=Users, DC=ad, DC=test, DC=elasticsearch, DC=com";
|
||||
Filter query =
|
||||
ActiveDirectoryGroupsResolver.buildGroupQuery(ldapConnection, dn, TimeValue.timeValueSeconds(10), NoOpLogger.INSTANCE);
|
||||
assertValidSidQuery(query, expectedSids);
|
||||
}
|
||||
|
||||
|
@ -80,8 +84,9 @@ public class ActiveDirectoryGroupsResolverTests extends GroupsResolverTestCase {
|
|||
"S-1-5-32-545", //Default Users group
|
||||
"S-1-5-21-3510024162-210737641-214529065-513", //Default Domain Users group
|
||||
"S-1-5-21-3510024162-210737641-214529065-1117"}; //Gods group
|
||||
Filter query = ActiveDirectoryGroupsResolver.buildGroupQuery(ldapConnection,
|
||||
"CN=Odin, CN=Users, DC=ad, DC=test, DC=elasticsearch, DC=com", TimeValue.timeValueSeconds(10), NoOpLogger.INSTANCE);
|
||||
final String dn = "CN=Odin, CN=Users, DC=ad, DC=test, DC=elasticsearch, DC=com";
|
||||
Filter query =
|
||||
ActiveDirectoryGroupsResolver.buildGroupQuery(ldapConnection, dn, TimeValue.timeValueSeconds(10), NoOpLogger.INSTANCE);
|
||||
assertValidSidQuery(query, expectedSids);
|
||||
}
|
||||
|
||||
|
@ -95,9 +100,10 @@ public class ActiveDirectoryGroupsResolverTests extends GroupsResolverTestCase {
|
|||
"S-1-5-21-3510024162-210737641-214529065-1108", //Geniuses
|
||||
"S-1-5-21-3510024162-210737641-214529065-1106", //SHIELD
|
||||
"S-1-5-21-3510024162-210737641-214529065-1105"};//Avengers
|
||||
Filter query = ActiveDirectoryGroupsResolver.buildGroupQuery(ldapConnection,
|
||||
"CN=Bruce Banner, CN=Users, DC=ad, DC=test, DC=elasticsearch, DC=com", TimeValue.timeValueSeconds(10),
|
||||
NoOpLogger.INSTANCE);
|
||||
|
||||
final String dn = BRUCE_BANNER_DN;
|
||||
Filter query =
|
||||
ActiveDirectoryGroupsResolver.buildGroupQuery(ldapConnection, dn, TimeValue.timeValueSeconds(10), NoOpLogger.INSTANCE);
|
||||
assertValidSidQuery(query, expectedSids);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -110,7 +110,7 @@ public class ActiveDirectoryRealmTests extends ESTestCase {
|
|||
public void testAuthenticateUserPrincipleName() throws Exception {
|
||||
Settings settings = settings();
|
||||
RealmConfig config = new RealmConfig("testAuthenticateUserPrincipleName", settings, globalSettings);
|
||||
ActiveDirectorySessionFactory sessionFactory = new ActiveDirectorySessionFactory(config, null).init();
|
||||
ActiveDirectorySessionFactory sessionFactory = new ActiveDirectorySessionFactory(config, null);
|
||||
DnRoleMapper roleMapper = new DnRoleMapper(ActiveDirectoryRealm.TYPE, config, resourceWatcherService, null);
|
||||
ActiveDirectoryRealm realm = new ActiveDirectoryRealm(config, sessionFactory, roleMapper);
|
||||
|
||||
|
@ -122,7 +122,7 @@ public class ActiveDirectoryRealmTests extends ESTestCase {
|
|||
public void testAuthenticateSAMAccountName() throws Exception {
|
||||
Settings settings = settings();
|
||||
RealmConfig config = new RealmConfig("testAuthenticateSAMAccountName", settings, globalSettings);
|
||||
ActiveDirectorySessionFactory sessionFactory = new ActiveDirectorySessionFactory(config, null).init();
|
||||
ActiveDirectorySessionFactory sessionFactory = new ActiveDirectorySessionFactory(config, null);
|
||||
DnRoleMapper roleMapper = new DnRoleMapper(ActiveDirectoryRealm.TYPE, config, resourceWatcherService, null);
|
||||
ActiveDirectoryRealm realm = new ActiveDirectoryRealm(config, sessionFactory, roleMapper);
|
||||
|
||||
|
@ -144,7 +144,7 @@ public class ActiveDirectoryRealmTests extends ESTestCase {
|
|||
public void testAuthenticateCachesSuccesfulAuthentications() throws Exception {
|
||||
Settings settings = settings();
|
||||
RealmConfig config = new RealmConfig("testAuthenticateCachesSuccesfulAuthentications", settings, globalSettings);
|
||||
ActiveDirectorySessionFactory sessionFactory = spy(new ActiveDirectorySessionFactory(config, null).init());
|
||||
ActiveDirectorySessionFactory sessionFactory = spy(new ActiveDirectorySessionFactory(config, null));
|
||||
DnRoleMapper roleMapper = new DnRoleMapper(ActiveDirectoryRealm.TYPE, config, resourceWatcherService, null);
|
||||
ActiveDirectoryRealm realm = new ActiveDirectoryRealm(config, sessionFactory, roleMapper);
|
||||
|
||||
|
@ -160,7 +160,7 @@ public class ActiveDirectoryRealmTests extends ESTestCase {
|
|||
public void testAuthenticateCachingCanBeDisabled() throws Exception {
|
||||
Settings settings = settings(Settings.builder().put(CachingUsernamePasswordRealm.CACHE_TTL_SETTING, -1).build());
|
||||
RealmConfig config = new RealmConfig("testAuthenticateCachingCanBeDisabled", settings, globalSettings);
|
||||
ActiveDirectorySessionFactory sessionFactory = spy(new ActiveDirectorySessionFactory(config, null).init());
|
||||
ActiveDirectorySessionFactory sessionFactory = spy(new ActiveDirectorySessionFactory(config, null));
|
||||
DnRoleMapper roleMapper = new DnRoleMapper(ActiveDirectoryRealm.TYPE, config, resourceWatcherService, null);
|
||||
ActiveDirectoryRealm realm = new ActiveDirectoryRealm(config, sessionFactory, roleMapper);
|
||||
|
||||
|
@ -176,7 +176,7 @@ public class ActiveDirectoryRealmTests extends ESTestCase {
|
|||
public void testAuthenticateCachingClearsCacheOnRoleMapperRefresh() throws Exception {
|
||||
Settings settings = settings();
|
||||
RealmConfig config = new RealmConfig("testAuthenticateCachingClearsCacheOnRoleMapperRefresh", settings, globalSettings);
|
||||
ActiveDirectorySessionFactory sessionFactory = spy(new ActiveDirectorySessionFactory(config, null).init());
|
||||
ActiveDirectorySessionFactory sessionFactory = spy(new ActiveDirectorySessionFactory(config, null));
|
||||
DnRoleMapper roleMapper = new DnRoleMapper(ActiveDirectoryRealm.TYPE, config, resourceWatcherService, null);
|
||||
ActiveDirectoryRealm realm = new ActiveDirectoryRealm(config, sessionFactory, roleMapper);
|
||||
|
||||
|
@ -203,7 +203,7 @@ public class ActiveDirectoryRealmTests extends ESTestCase {
|
|||
.put(DnRoleMapper.ROLE_MAPPING_FILE_SETTING, getDataPath("role_mapping.yml"))
|
||||
.build());
|
||||
RealmConfig config = new RealmConfig("testRealmMapsGroupsToRoles", settings, globalSettings);
|
||||
ActiveDirectorySessionFactory sessionFactory = new ActiveDirectorySessionFactory(config, null).init();
|
||||
ActiveDirectorySessionFactory sessionFactory = new ActiveDirectorySessionFactory(config, null);
|
||||
DnRoleMapper roleMapper = new DnRoleMapper(ActiveDirectoryRealm.TYPE, config, resourceWatcherService, null);
|
||||
ActiveDirectoryRealm realm = new ActiveDirectoryRealm(config, sessionFactory, roleMapper);
|
||||
|
||||
|
@ -217,7 +217,7 @@ public class ActiveDirectoryRealmTests extends ESTestCase {
|
|||
.put(DnRoleMapper.ROLE_MAPPING_FILE_SETTING, getDataPath("role_mapping.yml"))
|
||||
.build());
|
||||
RealmConfig config = new RealmConfig("testRealmMapsGroupsToRoles", settings, globalSettings);
|
||||
ActiveDirectorySessionFactory sessionFactory = new ActiveDirectorySessionFactory(config, null).init();
|
||||
ActiveDirectorySessionFactory sessionFactory = new ActiveDirectorySessionFactory(config, null);
|
||||
DnRoleMapper roleMapper = new DnRoleMapper(ActiveDirectoryRealm.TYPE, config, resourceWatcherService, null);
|
||||
ActiveDirectoryRealm realm = new ActiveDirectoryRealm(config, sessionFactory, roleMapper);
|
||||
|
||||
|
|
|
@ -29,7 +29,7 @@ public class ActiveDirectoryRealmUsageTests extends AbstractActiveDirectoryInteg
|
|||
.put("load_balance.type", loadBalanceType)
|
||||
.build();
|
||||
RealmConfig config = new RealmConfig("ad-test", settings, globalSettings);
|
||||
ActiveDirectorySessionFactory sessionFactory = new ActiveDirectorySessionFactory(config, clientSSLService).init();
|
||||
ActiveDirectorySessionFactory sessionFactory = new ActiveDirectorySessionFactory(config, clientSSLService);
|
||||
ActiveDirectoryRealm realm = new ActiveDirectoryRealm(config, sessionFactory, mock(DnRoleMapper.class));
|
||||
|
||||
Map<String, Object> stats = realm.usageStats();
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
*/
|
||||
package org.elasticsearch.xpack.security.authc.activedirectory;
|
||||
|
||||
import org.elasticsearch.ElasticsearchSecurityException;
|
||||
import com.unboundid.ldap.sdk.LDAPException;
|
||||
import org.elasticsearch.common.settings.Settings;
|
||||
import org.elasticsearch.xpack.security.authc.RealmConfig;
|
||||
import org.elasticsearch.xpack.security.authc.ldap.LdapSessionFactory;
|
||||
|
@ -19,7 +19,7 @@ import org.elasticsearch.test.junit.annotations.Network;
|
|||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
|
||||
import static org.elasticsearch.test.SecurityTestsUtils.assertAuthenticationException;
|
||||
import static org.hamcrest.Matchers.anyOf;
|
||||
import static org.hamcrest.Matchers.containsInAnyOrder;
|
||||
import static org.hamcrest.Matchers.containsString;
|
||||
import static org.hamcrest.Matchers.hasItem;
|
||||
|
@ -31,7 +31,7 @@ public class ActiveDirectorySessionFactoryTests extends AbstractActiveDirectoryI
|
|||
@SuppressWarnings("unchecked")
|
||||
public void testAdAuth() throws Exception {
|
||||
RealmConfig config = new RealmConfig("ad-test", buildAdSettings(AD_LDAP_URL, AD_DOMAIN, false), globalSettings);
|
||||
ActiveDirectorySessionFactory sessionFactory = new ActiveDirectorySessionFactory(config, clientSSLService).init();
|
||||
ActiveDirectorySessionFactory sessionFactory = new ActiveDirectorySessionFactory(config, clientSSLService);
|
||||
|
||||
String userName = "ironman";
|
||||
try (LdapSession ldap = sessionFactory.session(userName, SecuredStringTests.build(PASSWORD))) {
|
||||
|
@ -49,29 +49,45 @@ public class ActiveDirectorySessionFactoryTests extends AbstractActiveDirectoryI
|
|||
}
|
||||
}
|
||||
|
||||
@AwaitsFix(bugUrl = "https://github.com/elasticsearch/elasticsearch-shield/issues/499")
|
||||
public void testNetbiosAuth() throws Exception {
|
||||
final String adUrl = randomFrom("ldap://54.213.145.20:3268", "ldaps://54.213.145.20:3269", AD_LDAP_URL);
|
||||
RealmConfig config = new RealmConfig("ad-test", buildAdSettings(adUrl, AD_DOMAIN, false), globalSettings);
|
||||
ActiveDirectorySessionFactory sessionFactory = new ActiveDirectorySessionFactory(config, clientSSLService);
|
||||
|
||||
String userName = "ades\\ironman";
|
||||
try (LdapSession ldap = sessionFactory.session(userName, SecuredStringTests.build(PASSWORD))) {
|
||||
List<String> groups = ldap.groups();
|
||||
assertThat(groups, containsInAnyOrder(
|
||||
containsString("Geniuses"),
|
||||
containsString("Billionaire"),
|
||||
containsString("Playboy"),
|
||||
containsString("Philanthropists"),
|
||||
containsString("Avengers"),
|
||||
containsString("SHIELD"),
|
||||
containsString("CN=Users,CN=Builtin"),
|
||||
containsString("Domain Users"),
|
||||
containsString("Supers")));
|
||||
}
|
||||
}
|
||||
|
||||
public void testTcpReadTimeout() throws Exception {
|
||||
Settings settings = Settings.builder()
|
||||
.put(buildAdSettings(AD_LDAP_URL, AD_DOMAIN, false))
|
||||
.put("group_search.filter", "(objectClass=*)")
|
||||
.put(SessionFactory.HOSTNAME_VERIFICATION_SETTING, false)
|
||||
.put(SessionFactory.TIMEOUT_TCP_READ_SETTING, "1ms")
|
||||
.build();
|
||||
RealmConfig config = new RealmConfig("ad-test", settings, globalSettings);
|
||||
ActiveDirectorySessionFactory sessionFactory = new ActiveDirectorySessionFactory(config, clientSSLService).init();
|
||||
ActiveDirectorySessionFactory sessionFactory = new ActiveDirectorySessionFactory(config, clientSSLService);
|
||||
|
||||
try (LdapSession ldap = sessionFactory.session("ironman", SecuredStringTests.build(PASSWORD))) {
|
||||
// In certain cases we may have a successful bind, but a search should take longer and cause a timeout
|
||||
ldap.groups();
|
||||
fail("The TCP connection should timeout before getting groups back");
|
||||
} catch (ElasticsearchSecurityException e) {
|
||||
assertAuthenticationException(e);
|
||||
assertThat(e.getCause().getMessage(), containsString("A client-side timeout was encountered while waiting"));
|
||||
}
|
||||
LDAPException expected = expectThrows(LDAPException.class,
|
||||
() -> sessionFactory.session("ironman", SecuredStringTests.build(PASSWORD)).groups());
|
||||
assertThat(expected.getMessage(), containsString("A client-side timeout was encountered while waiting"));
|
||||
}
|
||||
|
||||
public void testAdAuthAvengers() throws Exception {
|
||||
RealmConfig config = new RealmConfig("ad-test", buildAdSettings(AD_LDAP_URL, AD_DOMAIN, false), globalSettings);
|
||||
ActiveDirectorySessionFactory sessionFactory = new ActiveDirectorySessionFactory(config, clientSSLService).init();
|
||||
ActiveDirectorySessionFactory sessionFactory = new ActiveDirectorySessionFactory(config, clientSSLService);
|
||||
|
||||
String[] users = new String[]{"cap", "hawkeye", "hulk", "ironman", "thor", "blackwidow", };
|
||||
for(String user: users) {
|
||||
|
@ -86,7 +102,7 @@ public class ActiveDirectorySessionFactoryTests extends AbstractActiveDirectoryI
|
|||
Settings settings = buildAdSettings(AD_LDAP_URL, AD_DOMAIN, "CN=Users,DC=ad,DC=test,DC=elasticsearch,DC=com",
|
||||
LdapSearchScope.ONE_LEVEL, false);
|
||||
RealmConfig config = new RealmConfig("ad-test", settings, globalSettings);
|
||||
ActiveDirectorySessionFactory sessionFactory = new ActiveDirectorySessionFactory(config, clientSSLService).init();
|
||||
ActiveDirectorySessionFactory sessionFactory = new ActiveDirectorySessionFactory(config, clientSSLService);
|
||||
|
||||
String userName = "hulk";
|
||||
try (LdapSession ldap = sessionFactory.session(userName, SecuredStringTests.build(PASSWORD))) {
|
||||
|
@ -108,7 +124,7 @@ public class ActiveDirectorySessionFactoryTests extends AbstractActiveDirectoryI
|
|||
Settings settings = buildAdSettings(AD_LDAP_URL, AD_DOMAIN, "CN=Bruce Banner, CN=Users,DC=ad,DC=test,DC=elasticsearch,DC=com",
|
||||
LdapSearchScope.BASE, false);
|
||||
RealmConfig config = new RealmConfig("ad-test", settings, globalSettings);
|
||||
ActiveDirectorySessionFactory sessionFactory = new ActiveDirectorySessionFactory(config, clientSSLService).init();
|
||||
ActiveDirectorySessionFactory sessionFactory = new ActiveDirectorySessionFactory(config, clientSSLService);
|
||||
|
||||
String userName = "hulk";
|
||||
try (LdapSession ldap = sessionFactory.session(userName, SecuredStringTests.build(PASSWORD))) {
|
||||
|
@ -134,7 +150,7 @@ public class ActiveDirectorySessionFactoryTests extends AbstractActiveDirectoryI
|
|||
.put(ActiveDirectorySessionFactory.AD_GROUP_SEARCH_SCOPE_SETTING, LdapSearchScope.BASE)
|
||||
.build();
|
||||
RealmConfig config = new RealmConfig("ad-test", settings, globalSettings);
|
||||
ActiveDirectorySessionFactory sessionFactory = new ActiveDirectorySessionFactory(config, clientSSLService).init();
|
||||
ActiveDirectorySessionFactory sessionFactory = new ActiveDirectorySessionFactory(config, clientSSLService);
|
||||
|
||||
String userName = "hulk";
|
||||
try (LdapSession ldap = sessionFactory.session(userName, SecuredStringTests.build(PASSWORD))) {
|
||||
|
@ -149,7 +165,7 @@ public class ActiveDirectorySessionFactoryTests extends AbstractActiveDirectoryI
|
|||
Settings settings = buildAdSettings(AD_LDAP_URL, AD_DOMAIN, "CN=Users,DC=ad,DC=test,DC=elasticsearch,DC=com",
|
||||
LdapSearchScope.ONE_LEVEL, false);
|
||||
RealmConfig config = new RealmConfig("ad-test", settings, globalSettings);
|
||||
ActiveDirectorySessionFactory sessionFactory = new ActiveDirectorySessionFactory(config, clientSSLService).init();
|
||||
ActiveDirectorySessionFactory sessionFactory = new ActiveDirectorySessionFactory(config, clientSSLService);
|
||||
|
||||
//Login with the UserPrincipalName
|
||||
String userDN = "CN=Erik Selvig,CN=Users,DC=ad,DC=test,DC=elasticsearch,DC=com";
|
||||
|
@ -167,7 +183,7 @@ public class ActiveDirectorySessionFactoryTests extends AbstractActiveDirectoryI
|
|||
Settings settings = buildAdSettings(AD_LDAP_URL, AD_DOMAIN, "CN=Users,DC=ad,DC=test,DC=elasticsearch,DC=com",
|
||||
LdapSearchScope.ONE_LEVEL, false);
|
||||
RealmConfig config = new RealmConfig("ad-test", settings, globalSettings);
|
||||
ActiveDirectorySessionFactory sessionFactory = new ActiveDirectorySessionFactory(config, clientSSLService).init();
|
||||
ActiveDirectorySessionFactory sessionFactory = new ActiveDirectorySessionFactory(config, clientSSLService);
|
||||
|
||||
//login with sAMAccountName
|
||||
String userDN = "CN=Erik Selvig,CN=Users,DC=ad,DC=test,DC=elasticsearch,DC=com";
|
||||
|
@ -191,7 +207,7 @@ public class ActiveDirectorySessionFactoryTests extends AbstractActiveDirectoryI
|
|||
"(&(objectclass=user)(userPrincipalName={0}@ad.test.elasticsearch.com))")
|
||||
.build();
|
||||
RealmConfig config = new RealmConfig("ad-test", settings, globalSettings);
|
||||
ActiveDirectorySessionFactory sessionFactory = new ActiveDirectorySessionFactory(config, clientSSLService).init();
|
||||
ActiveDirectorySessionFactory sessionFactory = new ActiveDirectorySessionFactory(config, clientSSLService);
|
||||
|
||||
//Login with the UserPrincipalName
|
||||
try (LdapSession ldap = sessionFactory.session("erik.selvig", SecuredStringTests.build(PASSWORD))) {
|
||||
|
@ -217,7 +233,7 @@ public class ActiveDirectorySessionFactoryTests extends AbstractActiveDirectoryI
|
|||
.build();
|
||||
}
|
||||
RealmConfig config = new RealmConfig("ad-as-ldap-test", settings, globalSettings);
|
||||
LdapSessionFactory sessionFactory = new LdapSessionFactory(config, clientSSLService).init();
|
||||
LdapSessionFactory sessionFactory = new LdapSessionFactory(config, clientSSLService);
|
||||
|
||||
String user = "Bruce Banner";
|
||||
try (LdapSession ldap = sessionFactory.session(user, SecuredStringTests.build(PASSWORD))) {
|
||||
|
@ -243,7 +259,7 @@ public class ActiveDirectorySessionFactoryTests extends AbstractActiveDirectoryI
|
|||
.build();
|
||||
}
|
||||
RealmConfig config = new RealmConfig("ad-as-ldap-test", settings, globalSettings);
|
||||
LdapSessionFactory sessionFactory = new LdapSessionFactory(config, clientSSLService).init();
|
||||
LdapSessionFactory sessionFactory = new LdapSessionFactory(config, clientSSLService);
|
||||
|
||||
String user = "Bruce Banner";
|
||||
try (LdapSession ldap = sessionFactory.session(user, SecuredStringTests.build(PASSWORD))) {
|
||||
|
@ -259,14 +275,12 @@ public class ActiveDirectorySessionFactoryTests extends AbstractActiveDirectoryI
|
|||
|
||||
public void testAdAuthWithHostnameVerification() throws Exception {
|
||||
RealmConfig config = new RealmConfig("ad-test", buildAdSettings(AD_LDAP_URL, AD_DOMAIN, true), globalSettings);
|
||||
ActiveDirectorySessionFactory sessionFactory = new ActiveDirectorySessionFactory(config, clientSSLService).init();
|
||||
ActiveDirectorySessionFactory sessionFactory = new ActiveDirectorySessionFactory(config, clientSSLService);
|
||||
|
||||
String userName = "ironman";
|
||||
try (LdapSession ldap = sessionFactory.session(userName, SecuredStringTests.build(PASSWORD))) {
|
||||
fail("Test active directory certificate does not have proper hostname/ip address for hostname verification");
|
||||
} catch (IOException e) {
|
||||
assertThat(e.getMessage(), containsString("failed to connect to any active directory servers"));
|
||||
}
|
||||
LDAPException expected =
|
||||
expectThrows(LDAPException.class, () -> sessionFactory.session(userName, SecuredStringTests.build(PASSWORD)));
|
||||
assertThat(expected.getMessage(), anyOf(containsString("Hostname verification failed"), containsString("peer not authenticated")));
|
||||
}
|
||||
|
||||
public void testStandardLdapHostnameVerification() throws Exception {
|
||||
|
@ -277,14 +291,11 @@ public class ActiveDirectorySessionFactoryTests extends AbstractActiveDirectoryI
|
|||
.put(LdapSessionFactory.HOSTNAME_VERIFICATION_SETTING, true)
|
||||
.build();
|
||||
RealmConfig config = new RealmConfig("ad-test", settings, globalSettings);
|
||||
LdapSessionFactory sessionFactory = new LdapSessionFactory(config, clientSSLService).init();
|
||||
LdapSessionFactory sessionFactory = new LdapSessionFactory(config, clientSSLService);
|
||||
|
||||
String user = "Bruce Banner";
|
||||
try (LdapSession ldap = sessionFactory.session(user, SecuredStringTests.build(PASSWORD))) {
|
||||
fail("Test active directory certificate does not have proper hostname/ip address for hostname verification");
|
||||
} catch (IOException e) {
|
||||
assertThat(e.getMessage(), containsString("failed to connect to any LDAP servers"));
|
||||
}
|
||||
LDAPException expected = expectThrows(LDAPException.class, () -> sessionFactory.session(user, SecuredStringTests.build(PASSWORD)));
|
||||
assertThat(expected.getMessage(), anyOf(containsString("Hostname verification failed"), containsString("peer not authenticated")));
|
||||
}
|
||||
|
||||
Settings buildAdSettings(String ldapUrl, String adDomainName, boolean hostnameVerification) {
|
||||
|
|
|
@ -42,7 +42,7 @@ public class ESNativeRealmMigrateToolTests extends CommandTestCase {
|
|||
RoleDescriptor rd = new RoleDescriptor("rolename", cluster, ips, runAs);
|
||||
assertThat(ESNativeRealmMigrateTool.MigrateUserOrRoles.createRoleJson(rd),
|
||||
equalTo("{\"cluster\":[],\"indices\":[{\"names\":[\"i1\",\"i2\",\"i3\"]," +
|
||||
"\"privileges\":[\"all\"],\"fields\":[\"body\"]}],\"run_as\":[]}"));
|
||||
"\"privileges\":[\"all\"],\"fields\":[\"body\"]}],\"run_as\":[],\"metadata\":{}}"));
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -55,7 +55,7 @@ import static org.hamcrest.Matchers.is;
|
|||
import static org.hamcrest.Matchers.notNullValue;
|
||||
|
||||
/**
|
||||
* Tests for the ESNativeUsersStore and ESNativeRolesStore
|
||||
* Tests for the NativeUsersStore and NativeRolesStore
|
||||
*/
|
||||
public class NativeRealmIntegTests extends NativeRealmIntegTestCase {
|
||||
|
||||
|
@ -144,12 +144,14 @@ public class NativeRealmIntegTests extends NativeRealmIntegTestCase {
|
|||
SecurityClient c = securityClient();
|
||||
final List<RoleDescriptor> existingRoles = Arrays.asList(c.prepareGetRoles().get().roles());
|
||||
final int existing = existingRoles.size();
|
||||
final Map<String, Object> metadata = Collections.singletonMap("key", (Object) randomAsciiOfLengthBetween(1, 10));
|
||||
logger.error("--> creating role");
|
||||
c.preparePutRole("test_role")
|
||||
.cluster("all", "none")
|
||||
.runAs("root", "nobody")
|
||||
.addIndices(new String[]{"index"}, new String[]{"read"},
|
||||
new String[]{"body", "title"}, new BytesArray("{\"query\": {\"match_all\": {}}}"))
|
||||
.metadata(metadata)
|
||||
.get();
|
||||
logger.error("--> waiting for .security index");
|
||||
ensureGreen(SecurityTemplateService.SECURITY_INDEX_NAME);
|
||||
|
@ -158,6 +160,8 @@ public class NativeRealmIntegTests extends NativeRealmIntegTestCase {
|
|||
assertTrue("role should exist", resp.hasRoles());
|
||||
RoleDescriptor testRole = resp.roles()[0];
|
||||
assertNotNull(testRole);
|
||||
assertThat(testRole.getMetadata().size(), is(1));
|
||||
assertThat(testRole.getMetadata().get("key"), is(metadata.get("key")));
|
||||
|
||||
c.preparePutRole("test_role2")
|
||||
.cluster("all", "none")
|
||||
|
|
|
@ -45,7 +45,6 @@ public abstract class GroupsResolverTestCase extends ESTestCase {
|
|||
LDAPURL ldapurl = new LDAPURL(ldapUrl());
|
||||
LDAPConnectionOptions options = new LDAPConnectionOptions();
|
||||
options.setFollowReferrals(true);
|
||||
options.setAutoReconnect(true);
|
||||
options.setAllowConcurrentSocketFactoryUse(true);
|
||||
options.setConnectTimeoutMillis(Math.toIntExact(SessionFactory.TIMEOUT_DEFAULT.millis()));
|
||||
options.setResponseTimeoutMillis(SessionFactory.TIMEOUT_DEFAULT.millis());
|
||||
|
|
|
@ -66,7 +66,7 @@ public class LdapRealmTests extends LdapTestCase {
|
|||
String userTemplate = VALID_USER_TEMPLATE;
|
||||
Settings settings = buildLdapSettings(ldapUrls(), userTemplate, groupSearchBase, LdapSearchScope.SUB_TREE);
|
||||
RealmConfig config = new RealmConfig("test-ldap-realm", settings, globalSettings);
|
||||
LdapSessionFactory ldapFactory = new LdapSessionFactory(config, null).init();
|
||||
LdapSessionFactory ldapFactory = new LdapSessionFactory(config, null);
|
||||
LdapRealm ldap = new LdapRealm(config, ldapFactory, buildGroupAsRoleMapper(resourceWatcherService));
|
||||
|
||||
User user = ldap.authenticate(new UsernamePasswordToken(VALID_USERNAME, SecuredStringTests.build(PASSWORD)));
|
||||
|
@ -82,7 +82,7 @@ public class LdapRealmTests extends LdapTestCase {
|
|||
.build();
|
||||
RealmConfig config = new RealmConfig("test-ldap-realm", settings, globalSettings);
|
||||
|
||||
LdapSessionFactory ldapFactory = new LdapSessionFactory(config, null).init();
|
||||
LdapSessionFactory ldapFactory = new LdapSessionFactory(config, null);
|
||||
LdapRealm ldap = new LdapRealm(config, ldapFactory, buildGroupAsRoleMapper(resourceWatcherService));
|
||||
|
||||
User user = ldap.authenticate(new UsernamePasswordToken(VALID_USERNAME, SecuredStringTests.build(PASSWORD)));
|
||||
|
@ -98,7 +98,7 @@ public class LdapRealmTests extends LdapTestCase {
|
|||
.build();
|
||||
RealmConfig config = new RealmConfig("test-ldap-realm", settings, globalSettings);
|
||||
|
||||
LdapSessionFactory ldapFactory = new LdapSessionFactory(config, null).init();
|
||||
LdapSessionFactory ldapFactory = new LdapSessionFactory(config, null);
|
||||
ldapFactory = spy(ldapFactory);
|
||||
LdapRealm ldap = new LdapRealm(config, ldapFactory, buildGroupAsRoleMapper(resourceWatcherService));
|
||||
ldap.authenticate(new UsernamePasswordToken(VALID_USERNAME, SecuredStringTests.build(PASSWORD)));
|
||||
|
@ -116,7 +116,7 @@ public class LdapRealmTests extends LdapTestCase {
|
|||
.build();
|
||||
RealmConfig config = new RealmConfig("test-ldap-realm", settings, globalSettings);
|
||||
|
||||
LdapSessionFactory ldapFactory = new LdapSessionFactory(config, null).init();
|
||||
LdapSessionFactory ldapFactory = new LdapSessionFactory(config, null);
|
||||
DnRoleMapper roleMapper = buildGroupAsRoleMapper(resourceWatcherService);
|
||||
ldapFactory = spy(ldapFactory);
|
||||
LdapRealm ldap = new LdapRealm(config, ldapFactory, roleMapper);
|
||||
|
@ -143,7 +143,7 @@ public class LdapRealmTests extends LdapTestCase {
|
|||
.build();
|
||||
RealmConfig config = new RealmConfig("test-ldap-realm", settings, globalSettings);
|
||||
|
||||
LdapSessionFactory ldapFactory = new LdapSessionFactory(config, null).init();
|
||||
LdapSessionFactory ldapFactory = new LdapSessionFactory(config, null);
|
||||
ldapFactory = spy(ldapFactory);
|
||||
LdapRealm ldap = new LdapRealm(config, ldapFactory, buildGroupAsRoleMapper(resourceWatcherService));
|
||||
ldap.authenticate(new UsernamePasswordToken(VALID_USERNAME, SecuredStringTests.build(PASSWORD)));
|
||||
|
@ -216,7 +216,7 @@ public class LdapRealmTests extends LdapTestCase {
|
|||
.build();
|
||||
RealmConfig config = new RealmConfig("test-ldap-realm-userdn", settings, globalSettings);
|
||||
|
||||
LdapSessionFactory ldapFactory = new LdapSessionFactory(config, null).init();
|
||||
LdapSessionFactory ldapFactory = new LdapSessionFactory(config, null);
|
||||
LdapRealm ldap = new LdapRealm(config, ldapFactory, new DnRoleMapper(LdapRealm.TYPE, config, resourceWatcherService, null));
|
||||
|
||||
User user = ldap.authenticate(new UsernamePasswordToken("Horatio Hornblower", SecuredStringTests.build(PASSWORD)));
|
||||
|
@ -244,7 +244,7 @@ public class LdapRealmTests extends LdapTestCase {
|
|||
|
||||
RealmConfig config = new RealmConfig("ldap-realm", settings.build(), globalSettings);
|
||||
|
||||
LdapSessionFactory ldapFactory = new LdapSessionFactory(config, null).init();
|
||||
LdapSessionFactory ldapFactory = new LdapSessionFactory(config, null);
|
||||
LdapRealm realm = new LdapRealm(config, ldapFactory, new DnRoleMapper(LdapRealm.TYPE, config, resourceWatcherService, null));
|
||||
|
||||
Map<String, Object> stats = realm.usageStats();
|
||||
|
|
|
@ -6,8 +6,8 @@
|
|||
package org.elasticsearch.xpack.security.authc.ldap;
|
||||
|
||||
import com.unboundid.ldap.listener.InMemoryDirectoryServer;
|
||||
import com.unboundid.ldap.sdk.LDAPException;
|
||||
import com.unboundid.ldap.sdk.LDAPURL;
|
||||
import org.elasticsearch.ElasticsearchSecurityException;
|
||||
import org.elasticsearch.common.settings.Settings;
|
||||
import org.elasticsearch.xpack.security.authc.RealmConfig;
|
||||
import org.elasticsearch.xpack.security.authc.ldap.support.LdapSearchScope;
|
||||
|
@ -19,9 +19,9 @@ import org.elasticsearch.xpack.security.authc.support.SecuredStringTests;
|
|||
import org.elasticsearch.test.junit.annotations.Network;
|
||||
import org.junit.Before;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
|
||||
import static org.hamcrest.Matchers.anyOf;
|
||||
import static org.hamcrest.Matchers.contains;
|
||||
import static org.hamcrest.Matchers.containsString;
|
||||
import static org.hamcrest.Matchers.instanceOf;
|
||||
|
@ -49,7 +49,7 @@ public class LdapSessionFactoryTests extends LdapTestCase {
|
|||
.build();
|
||||
|
||||
RealmConfig config = new RealmConfig("ldap_realm", settings, globalSettings);
|
||||
LdapSessionFactory sessionFactory = new LdapSessionFactory(config, null).init();
|
||||
LdapSessionFactory sessionFactory = new LdapSessionFactory(config, null);
|
||||
String user = "Horatio Hornblower";
|
||||
SecuredString userPass = SecuredStringTests.build("pass");
|
||||
|
||||
|
@ -57,15 +57,15 @@ public class LdapSessionFactoryTests extends LdapTestCase {
|
|||
try (LdapSession session = sessionFactory.session(user, userPass)) {
|
||||
fail("expected connection timeout error here");
|
||||
} catch (Exception e) {
|
||||
assertThat(e, instanceOf(ElasticsearchSecurityException.class));
|
||||
assertThat(e.getCause().getMessage(), containsString("A client-side timeout was encountered while waiting "));
|
||||
assertThat(e, instanceOf(LDAPException.class));
|
||||
assertThat(e.getMessage(), containsString("A client-side timeout was encountered while waiting "));
|
||||
} finally {
|
||||
ldapServer.setProcessingDelayMillis(0L);
|
||||
}
|
||||
}
|
||||
|
||||
@Network
|
||||
@AwaitsFix(bugUrl = "https://github.com/elastic/elasticsearch-shield/issues/767")
|
||||
@AwaitsFix(bugUrl = "https://github.com/elastic/x-plugins/issues/2849")
|
||||
public void testConnectTimeout() {
|
||||
// Local sockets connect too fast...
|
||||
String ldapUrl = "ldap://54.200.235.244:389";
|
||||
|
@ -78,19 +78,17 @@ public class LdapSessionFactoryTests extends LdapTestCase {
|
|||
.build();
|
||||
|
||||
RealmConfig config = new RealmConfig("ldap_realm", settings, globalSettings);
|
||||
LdapSessionFactory sessionFactory = new LdapSessionFactory(config, null).init();
|
||||
LdapSessionFactory sessionFactory = new LdapSessionFactory(config, null);
|
||||
String user = "Horatio Hornblower";
|
||||
SecuredString userPass = SecuredStringTests.build("pass");
|
||||
|
||||
long start = System.currentTimeMillis();
|
||||
try (LdapSession session = sessionFactory.session(user, userPass)) {
|
||||
fail("expected connection timeout error here");
|
||||
} catch (Exception e) {
|
||||
long time = System.currentTimeMillis() - start;
|
||||
assertThat(time, lessThan(10000L));
|
||||
assertThat(e, instanceOf(IOException.class));
|
||||
assertThat(e.getCause().getCause().getMessage(), containsString("within the configured timeout of"));
|
||||
}
|
||||
LDAPException expected = expectThrows(LDAPException.class, () -> sessionFactory.session(user, userPass));
|
||||
long time = System.currentTimeMillis() - start;
|
||||
assertThat(time, lessThan(10000L));
|
||||
assertThat(expected, instanceOf(LDAPException.class));
|
||||
assertThat(expected.getCause().getMessage(),
|
||||
anyOf(containsString("within the configured timeout of"), containsString("connect timed out")));
|
||||
}
|
||||
|
||||
public void testBindWithTemplates() throws Exception {
|
||||
|
@ -103,7 +101,7 @@ public class LdapSessionFactoryTests extends LdapTestCase {
|
|||
RealmConfig config = new RealmConfig("ldap_realm", buildLdapSettings(ldapUrls(), userTemplates, groupSearchBase,
|
||||
LdapSearchScope.SUB_TREE), globalSettings);
|
||||
|
||||
LdapSessionFactory sessionFactory = new LdapSessionFactory(config, null).init();
|
||||
LdapSessionFactory sessionFactory = new LdapSessionFactory(config, null);
|
||||
|
||||
String user = "Horatio Hornblower";
|
||||
SecuredString userPass = SecuredStringTests.build("pass");
|
||||
|
@ -124,15 +122,14 @@ public class LdapSessionFactoryTests extends LdapTestCase {
|
|||
RealmConfig config = new RealmConfig("ldap_realm", buildLdapSettings(ldapUrls(), userTemplates, groupSearchBase,
|
||||
LdapSearchScope.SUB_TREE), globalSettings);
|
||||
|
||||
LdapSessionFactory ldapFac = new LdapSessionFactory(config, null).init();
|
||||
LdapSessionFactory ldapFac = new LdapSessionFactory(config, null);
|
||||
|
||||
String user = "Horatio Hornblower";
|
||||
SecuredString userPass = SecuredStringTests.build("pass");
|
||||
try (LdapSession ldapConnection = ldapFac.session(user, userPass)) {
|
||||
fail("Expected ElasticsearchSecurityException");
|
||||
} catch (ElasticsearchSecurityException e) {
|
||||
assertThat(e.getMessage(), is("failed LDAP authentication"));
|
||||
}
|
||||
LDAPException expected = expectThrows(LDAPException.class, () -> ldapFac.session(user, userPass));
|
||||
assertThat(expected.getMessage(), containsString("Unable to bind as user"));
|
||||
Throwable[] suppressed = expected.getSuppressed();
|
||||
assertThat(suppressed.length, is(2));
|
||||
}
|
||||
|
||||
public void testGroupLookupSubtree() throws Exception {
|
||||
|
@ -141,7 +138,7 @@ public class LdapSessionFactoryTests extends LdapTestCase {
|
|||
RealmConfig config = new RealmConfig("ldap_realm", buildLdapSettings(ldapUrls(), userTemplate, groupSearchBase,
|
||||
LdapSearchScope.SUB_TREE), globalSettings);
|
||||
|
||||
LdapSessionFactory ldapFac = new LdapSessionFactory(config, null).init();
|
||||
LdapSessionFactory ldapFac = new LdapSessionFactory(config, null);
|
||||
|
||||
String user = "Horatio Hornblower";
|
||||
SecuredString userPass = SecuredStringTests.build("pass");
|
||||
|
@ -158,7 +155,7 @@ public class LdapSessionFactoryTests extends LdapTestCase {
|
|||
RealmConfig config = new RealmConfig("ldap_realm", buildLdapSettings(ldapUrls(), userTemplate, groupSearchBase,
|
||||
LdapSearchScope.ONE_LEVEL), globalSettings);
|
||||
|
||||
LdapSessionFactory ldapFac = new LdapSessionFactory(config, null).init();
|
||||
LdapSessionFactory ldapFac = new LdapSessionFactory(config, null);
|
||||
|
||||
String user = "Horatio Hornblower";
|
||||
try (LdapSession ldap = ldapFac.session(user, SecuredStringTests.build("pass"))) {
|
||||
|
@ -173,7 +170,7 @@ public class LdapSessionFactoryTests extends LdapTestCase {
|
|||
RealmConfig config = new RealmConfig("ldap_realm", buildLdapSettings(ldapUrls(), userTemplate, groupSearchBase,
|
||||
LdapSearchScope.BASE), globalSettings);
|
||||
|
||||
LdapSessionFactory ldapFac = new LdapSessionFactory(config, null).init();
|
||||
LdapSessionFactory ldapFac = new LdapSessionFactory(config, null);
|
||||
|
||||
String user = "Horatio Hornblower";
|
||||
SecuredString userPass = SecuredStringTests.build("pass");
|
||||
|
|
|
@ -5,12 +5,12 @@
|
|||
*/
|
||||
package org.elasticsearch.xpack.security.authc.ldap;
|
||||
|
||||
import com.carrotsearch.randomizedtesting.ThreadFilter;
|
||||
import com.carrotsearch.randomizedtesting.annotations.ThreadLeakFilters;
|
||||
import com.unboundid.ldap.listener.InMemoryDirectoryServer;
|
||||
import com.unboundid.ldap.sdk.BindRequest;
|
||||
import com.unboundid.ldap.sdk.GetEntryLDAPConnectionPoolHealthCheck;
|
||||
import com.unboundid.ldap.sdk.LDAPConnectionPool;
|
||||
import com.unboundid.ldap.sdk.LDAPConnectionPoolHealthCheck;
|
||||
import com.unboundid.ldap.sdk.LDAPURL;
|
||||
import com.unboundid.ldap.sdk.SimpleBindRequest;
|
||||
import com.unboundid.ldap.sdk.SingleServerSet;
|
||||
import org.elasticsearch.ElasticsearchSecurityException;
|
||||
|
@ -18,8 +18,6 @@ import org.elasticsearch.common.Strings;
|
|||
import org.elasticsearch.common.settings.Settings;
|
||||
import org.elasticsearch.common.unit.TimeValue;
|
||||
import org.elasticsearch.env.Environment;
|
||||
import org.elasticsearch.node.MockNode;
|
||||
import org.elasticsearch.node.Node;
|
||||
import org.elasticsearch.xpack.security.authc.RealmConfig;
|
||||
import org.elasticsearch.xpack.security.authc.activedirectory.ActiveDirectorySessionFactoryTests;
|
||||
import org.elasticsearch.xpack.security.authc.ldap.support.LdapSearchScope;
|
||||
|
@ -31,17 +29,12 @@ import org.elasticsearch.xpack.security.ssl.ClientSSLService;
|
|||
import org.elasticsearch.xpack.security.ssl.SSLConfiguration.Global;
|
||||
import org.elasticsearch.xpack.security.support.NoOpLogger;
|
||||
import org.elasticsearch.test.junit.annotations.Network;
|
||||
import org.elasticsearch.xpack.watcher.Watcher;
|
||||
import org.elasticsearch.xpack.XPackPlugin;
|
||||
import org.junit.Before;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Path;
|
||||
import java.text.MessageFormat;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
|
||||
import static org.elasticsearch.test.SecurityTestsUtils.assertAuthenticationException;
|
||||
import static org.hamcrest.Matchers.containsInAnyOrder;
|
||||
|
@ -51,12 +44,6 @@ import static org.hamcrest.Matchers.hasItem;
|
|||
import static org.hamcrest.Matchers.is;
|
||||
import static org.hamcrest.Matchers.nullValue;
|
||||
|
||||
// thread leak filter for UnboundID's background connect threads. The background connect threads do not always respect the
|
||||
// timeout and linger. Will be fixed in a new version of the library, see
|
||||
// http://sourceforge.net/p/ldap-sdk/discussion/1001257/thread/154e3b71/
|
||||
@ThreadLeakFilters(filters = {
|
||||
LdapUserSearchSessionFactoryTests.BackgroundConnectThreadLeakFilter.class
|
||||
})
|
||||
public class LdapUserSearchSessionFactoryTests extends LdapTestCase {
|
||||
|
||||
private ClientSSLService clientSSLService;
|
||||
|
@ -87,9 +74,10 @@ public class LdapUserSearchSessionFactoryTests extends LdapTestCase {
|
|||
.put("bind_dn", "cn=Horatio Hornblower,ou=people,o=sevenSeas")
|
||||
.put("bind_password", "pass")
|
||||
.put("user_search.attribute", "cn")
|
||||
.put("user_search.pool.enabled", randomBoolean())
|
||||
.build(), globalSettings);
|
||||
|
||||
LdapUserSearchSessionFactory sessionFactory = new LdapUserSearchSessionFactory(config, null).init();
|
||||
LdapUserSearchSessionFactory sessionFactory = new LdapUserSearchSessionFactory(config, null);
|
||||
try {
|
||||
assertThat(sessionFactory.supportsUnauthenticatedSession(), is(true));
|
||||
} finally {
|
||||
|
@ -107,9 +95,10 @@ public class LdapUserSearchSessionFactoryTests extends LdapTestCase {
|
|||
.put("bind_dn", "cn=Horatio Hornblower,ou=people,o=sevenSeas")
|
||||
.put("bind_password", "pass")
|
||||
.put("user_search.attribute", "cn")
|
||||
.put("user_search.pool.enabled", randomBoolean())
|
||||
.build(), globalSettings);
|
||||
|
||||
LdapUserSearchSessionFactory sessionFactory = new LdapUserSearchSessionFactory(config, null).init();
|
||||
LdapUserSearchSessionFactory sessionFactory = new LdapUserSearchSessionFactory(config, null);
|
||||
|
||||
String user = "William Bush";
|
||||
SecuredString userPass = SecuredStringTests.build("pass");
|
||||
|
@ -142,9 +131,10 @@ public class LdapUserSearchSessionFactoryTests extends LdapTestCase {
|
|||
.put("bind_password", "pass")
|
||||
.put("user_search.scope", LdapSearchScope.BASE)
|
||||
.put("user_search.attribute", "cn")
|
||||
.put("user_search.pool.enabled", randomBoolean())
|
||||
.build(), globalSettings);
|
||||
|
||||
LdapUserSearchSessionFactory sessionFactory = new LdapUserSearchSessionFactory(config, null).init();
|
||||
LdapUserSearchSessionFactory sessionFactory = new LdapUserSearchSessionFactory(config, null);
|
||||
|
||||
String user = "William Bush";
|
||||
SecuredString userPass = SecuredStringTests.build("pass");
|
||||
|
@ -181,9 +171,10 @@ public class LdapUserSearchSessionFactoryTests extends LdapTestCase {
|
|||
.put("bind_password", "pass")
|
||||
.put("user_search.scope", LdapSearchScope.BASE)
|
||||
.put("user_search.attribute", "cn")
|
||||
.put("user_search.pool.enabled", randomBoolean())
|
||||
.build(), globalSettings);
|
||||
|
||||
LdapUserSearchSessionFactory sessionFactory = new LdapUserSearchSessionFactory(config, null).init();
|
||||
LdapUserSearchSessionFactory sessionFactory = new LdapUserSearchSessionFactory(config, null);
|
||||
|
||||
String user = "William Bush";
|
||||
SecuredString userPass = SecuredStringTests.build("pass");
|
||||
|
@ -216,9 +207,10 @@ public class LdapUserSearchSessionFactoryTests extends LdapTestCase {
|
|||
.put("bind_password", "pass")
|
||||
.put("user_search.scope", LdapSearchScope.ONE_LEVEL)
|
||||
.put("user_search.attribute", "cn")
|
||||
.put("user_search.pool.enabled", randomBoolean())
|
||||
.build(), globalSettings);
|
||||
|
||||
LdapUserSearchSessionFactory sessionFactory = new LdapUserSearchSessionFactory(config, null).init();
|
||||
LdapUserSearchSessionFactory sessionFactory = new LdapUserSearchSessionFactory(config, null);
|
||||
|
||||
String user = "William Bush";
|
||||
SecuredString userPass = SecuredStringTests.build("pass");
|
||||
|
@ -255,9 +247,10 @@ public class LdapUserSearchSessionFactoryTests extends LdapTestCase {
|
|||
.put("bind_password", "pass")
|
||||
.put("user_search.scope", LdapSearchScope.ONE_LEVEL)
|
||||
.put("user_search.attribute", "cn")
|
||||
.put("user_search.pool.enabled", randomBoolean())
|
||||
.build(), globalSettings);
|
||||
|
||||
LdapUserSearchSessionFactory sessionFactory = new LdapUserSearchSessionFactory(config, null).init();
|
||||
LdapUserSearchSessionFactory sessionFactory = new LdapUserSearchSessionFactory(config, null);
|
||||
|
||||
String user = "William Bush";
|
||||
SecuredString userPass = SecuredStringTests.build("pass");
|
||||
|
@ -289,9 +282,10 @@ public class LdapUserSearchSessionFactoryTests extends LdapTestCase {
|
|||
.put("bind_dn", "cn=Horatio Hornblower,ou=people,o=sevenSeas")
|
||||
.put("bind_password", "pass")
|
||||
.put("user_search.attribute", "uid1")
|
||||
.put("user_search.pool.enabled", randomBoolean())
|
||||
.build(), globalSettings);
|
||||
|
||||
LdapUserSearchSessionFactory sessionFactory = new LdapUserSearchSessionFactory(config, null).init();
|
||||
LdapUserSearchSessionFactory sessionFactory = new LdapUserSearchSessionFactory(config, null);
|
||||
|
||||
String user = "William Bush";
|
||||
SecuredString userPass = SecuredStringTests.build("pass");
|
||||
|
@ -326,9 +320,10 @@ public class LdapUserSearchSessionFactoryTests extends LdapTestCase {
|
|||
.put("user_search.base_dn", userSearchBase)
|
||||
.put("bind_dn", "cn=Horatio Hornblower,ou=people,o=sevenSeas")
|
||||
.put("bind_password", "pass")
|
||||
.put("user_search.pool.enabled", randomBoolean())
|
||||
.build(), globalSettings);
|
||||
|
||||
LdapUserSearchSessionFactory sessionFactory = new LdapUserSearchSessionFactory(config, null).init();
|
||||
LdapUserSearchSessionFactory sessionFactory = new LdapUserSearchSessionFactory(config, null);
|
||||
|
||||
String user = "wbush";
|
||||
SecuredString userPass = SecuredStringTests.build("pass");
|
||||
|
@ -361,9 +356,10 @@ public class LdapUserSearchSessionFactoryTests extends LdapTestCase {
|
|||
.put("bind_dn", "ironman@ad.test.elasticsearch.com")
|
||||
.put("bind_password", ActiveDirectorySessionFactoryTests.PASSWORD)
|
||||
.put("user_search.attribute", "cn")
|
||||
.put("user_search.pool.enabled", randomBoolean())
|
||||
.build();
|
||||
RealmConfig config = new RealmConfig("ad-as-ldap-test", settings, globalSettings);
|
||||
LdapUserSearchSessionFactory sessionFactory = new LdapUserSearchSessionFactory(config, clientSSLService).init();
|
||||
LdapUserSearchSessionFactory sessionFactory = new LdapUserSearchSessionFactory(config, clientSSLService);
|
||||
|
||||
String user = "Bruce Banner";
|
||||
try {
|
||||
|
@ -403,8 +399,9 @@ public class LdapUserSearchSessionFactoryTests extends LdapTestCase {
|
|||
.put("user_search.base_dn", userSearchBase)
|
||||
.put("bind_dn", "uid=blackwidow,ou=people,dc=oldap,dc=test,dc=elasticsearch,dc=com")
|
||||
.put("bind_password", OpenLdapTests.PASSWORD)
|
||||
.put("user_search.pool.enabled", randomBoolean())
|
||||
.build(), globalSettings);
|
||||
LdapUserSearchSessionFactory sessionFactory = new LdapUserSearchSessionFactory(config, clientSSLService).init();
|
||||
LdapUserSearchSessionFactory sessionFactory = new LdapUserSearchSessionFactory(config, clientSSLService);
|
||||
|
||||
String[] users = new String[] { "cap", "hawkeye", "hulk", "ironman", "thor" };
|
||||
try {
|
||||
|
@ -479,7 +476,7 @@ public class LdapUserSearchSessionFactoryTests extends LdapTestCase {
|
|||
}
|
||||
}
|
||||
|
||||
public void testThatEmptyBindDNThrowsExceptionWithHealthCheckEnabled() throws Exception {
|
||||
public void testThatEmptyBindDNWithHealthCheckEnabledDoesNotThrow() throws Exception {
|
||||
String groupSearchBase = "o=sevenSeas";
|
||||
String userSearchBase = "o=sevenSeas";
|
||||
RealmConfig config = new RealmConfig("ldap_realm", Settings.builder()
|
||||
|
@ -488,12 +485,13 @@ public class LdapUserSearchSessionFactoryTests extends LdapTestCase {
|
|||
.put("bind_password", "pass")
|
||||
.build(), globalSettings);
|
||||
|
||||
LdapUserSearchSessionFactory searchSessionFactory = null;
|
||||
try {
|
||||
new LdapUserSearchSessionFactory(config, null).init();
|
||||
fail("expected an exception");
|
||||
} catch (IllegalArgumentException e) {
|
||||
assertThat(e.getMessage(), containsString("[bind_dn] has not been specified so a value must be specified for [user_search" +
|
||||
".pool.health_check.dn] or [user_search.pool.health_check.enabled] must be set to false"));
|
||||
searchSessionFactory = new LdapUserSearchSessionFactory(config, null);
|
||||
} finally {
|
||||
if (searchSessionFactory != null) {
|
||||
searchSessionFactory.shutdown();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -512,12 +510,17 @@ public class LdapUserSearchSessionFactoryTests extends LdapTestCase {
|
|||
assertThat(simpleBindRequest.getBindDN(), is("cn=ironman"));
|
||||
}
|
||||
|
||||
@Network
|
||||
public void testThatLDAPServerConnectErrorDoesNotPreventNodeFromStarting() throws IOException {
|
||||
public void testThatConnectErrorIsNotThrownOnConstruction() throws Exception {
|
||||
String groupSearchBase = "DC=ad,DC=test,DC=elasticsearch,DC=com";
|
||||
String userSearchBase = "CN=Users,DC=ad,DC=test,DC=elasticsearch,DC=com";
|
||||
|
||||
// pick a random ldap server and stop it
|
||||
InMemoryDirectoryServer inMemoryDirectoryServer = randomFrom(ldapServers);
|
||||
String ldapUrl = new LDAPURL("ldap", "localhost", inMemoryDirectoryServer.getListenPort(), null, null, null, null).toString();
|
||||
inMemoryDirectoryServer.shutDown(true);
|
||||
|
||||
Settings ldapSettings = Settings.builder()
|
||||
.put(LdapTestCase.buildLdapSettings(new String[] { "ldaps://elastic.co:636" }, Strings.EMPTY_ARRAY,
|
||||
.put(LdapTestCase.buildLdapSettings(new String[] { ldapUrl }, Strings.EMPTY_ARRAY,
|
||||
groupSearchBase, LdapSearchScope.SUB_TREE))
|
||||
.put("user_search.base_dn", userSearchBase)
|
||||
.put("bind_dn", "ironman@ad.test.elasticsearch.com")
|
||||
|
@ -525,30 +528,18 @@ public class LdapUserSearchSessionFactoryTests extends LdapTestCase {
|
|||
.put("user_search.attribute", "cn")
|
||||
.put("timeout.tcp_connect", "500ms")
|
||||
.put("type", "ldap")
|
||||
.put("user_search.pool.health_check.enabled", false)
|
||||
.put("user_search.pool.enabled", randomBoolean())
|
||||
.build();
|
||||
|
||||
Settings.Builder builder = Settings.builder();
|
||||
for (Map.Entry<String, String> entry : ldapSettings.getAsMap().entrySet()) {
|
||||
builder.put("xpack.security.authc.realms.ldap1." + entry.getKey(), entry.getValue());
|
||||
}
|
||||
builder.put("path.home", createTempDir());
|
||||
|
||||
// disable watcher, because watcher takes some time when starting, which results in problems
|
||||
// having a quick start/stop cycle like below
|
||||
builder.put(XPackPlugin.featureEnabledSetting(Watcher.NAME), false);
|
||||
|
||||
try (Node node = new MockNode(builder.build(), Collections.singletonList(XPackPlugin.class))) {
|
||||
node.start();
|
||||
}
|
||||
}
|
||||
|
||||
public static class BackgroundConnectThreadLeakFilter implements ThreadFilter {
|
||||
@Override
|
||||
public boolean reject(Thread thread) {
|
||||
if (thread.getName().startsWith("Background connect thread for elastic.co")) {
|
||||
return true;
|
||||
RealmConfig config = new RealmConfig("ldap_realm", ldapSettings, globalSettings);
|
||||
LdapUserSearchSessionFactory searchSessionFactory = null;
|
||||
try {
|
||||
searchSessionFactory = new LdapUserSearchSessionFactory(config, null);
|
||||
} finally {
|
||||
if (searchSessionFactory != null) {
|
||||
searchSessionFactory.shutdown();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
*/
|
||||
package org.elasticsearch.xpack.security.authc.ldap;
|
||||
|
||||
import com.unboundid.ldap.sdk.LDAPException;
|
||||
import org.elasticsearch.ElasticsearchException;
|
||||
import org.elasticsearch.common.settings.Settings;
|
||||
import org.elasticsearch.env.Environment;
|
||||
|
@ -25,6 +26,7 @@ import java.io.IOException;
|
|||
import java.nio.file.Path;
|
||||
import java.util.Map;
|
||||
|
||||
import static org.hamcrest.Matchers.anyOf;
|
||||
import static org.hamcrest.Matchers.containsString;
|
||||
import static org.hamcrest.Matchers.hasEntry;
|
||||
import static org.hamcrest.Matchers.hasItem;
|
||||
|
@ -67,7 +69,7 @@ public class OpenLdapTests extends ESTestCase {
|
|||
String userTemplate = "uid={0},ou=people,dc=oldap,dc=test,dc=elasticsearch,dc=com";
|
||||
RealmConfig config = new RealmConfig("oldap-test", buildLdapSettings(OPEN_LDAP_URL, userTemplate, groupSearchBase,
|
||||
LdapSearchScope.ONE_LEVEL), globalSettings);
|
||||
LdapSessionFactory sessionFactory = new LdapSessionFactory(config, clientSSLService).init();
|
||||
LdapSessionFactory sessionFactory = new LdapSessionFactory(config, clientSSLService);
|
||||
|
||||
String[] users = new String[] { "blackwidow", "cap", "hawkeye", "hulk", "ironman", "thor" };
|
||||
for (String user : users) {
|
||||
|
@ -84,7 +86,7 @@ public class OpenLdapTests extends ESTestCase {
|
|||
String userTemplate = "uid={0},ou=people,dc=oldap,dc=test,dc=elasticsearch,dc=com";
|
||||
RealmConfig config = new RealmConfig("oldap-test", buildLdapSettings(OPEN_LDAP_URL, userTemplate, groupSearchBase,
|
||||
LdapSearchScope.BASE), globalSettings);
|
||||
LdapSessionFactory sessionFactory = new LdapSessionFactory(config, clientSSLService).init();
|
||||
LdapSessionFactory sessionFactory = new LdapSessionFactory(config, clientSSLService);
|
||||
|
||||
String[] users = new String[] { "blackwidow", "cap", "hawkeye", "hulk", "ironman", "thor" };
|
||||
for (String user : users) {
|
||||
|
@ -111,7 +113,7 @@ public class OpenLdapTests extends ESTestCase {
|
|||
settings.put("load_balance.type", loadBalanceType);
|
||||
|
||||
RealmConfig config = new RealmConfig("oldap-test", settings.build(), globalSettings);
|
||||
LdapSessionFactory sessionFactory = new LdapSessionFactory(config, clientSSLService).init();
|
||||
LdapSessionFactory sessionFactory = new LdapSessionFactory(config, clientSSLService);
|
||||
LdapRealm realm = new LdapRealm(config, sessionFactory, mock(DnRoleMapper.class));
|
||||
|
||||
Map<String, Object> stats = realm.usageStats();
|
||||
|
@ -133,32 +135,29 @@ public class OpenLdapTests extends ESTestCase {
|
|||
.put("group_search.user_attribute", "uid")
|
||||
.build();
|
||||
RealmConfig config = new RealmConfig("oldap-test", settings, globalSettings);
|
||||
LdapSessionFactory sessionFactory = new LdapSessionFactory(config, clientSSLService).init();
|
||||
LdapSessionFactory sessionFactory = new LdapSessionFactory(config, clientSSLService);
|
||||
|
||||
try (LdapSession ldap = sessionFactory.session("selvig", SecuredStringTests.build(PASSWORD))){
|
||||
assertThat(ldap.groups(), hasItem(containsString("Geniuses")));
|
||||
}
|
||||
}
|
||||
|
||||
@AwaitsFix(bugUrl = "https://github.com/elasticsearch/elasticsearch-shield/issues/499")
|
||||
@AwaitsFix(bugUrl = "https://github.com/elastic/x-plugins/issues/2849")
|
||||
public void testTcpTimeout() throws Exception {
|
||||
String groupSearchBase = "ou=people,dc=oldap,dc=test,dc=elasticsearch,dc=com";
|
||||
String userTemplate = "uid={0},ou=people,dc=oldap,dc=test,dc=elasticsearch,dc=com";
|
||||
Settings settings = Settings.builder()
|
||||
.put(buildLdapSettings(OPEN_LDAP_URL, userTemplate, groupSearchBase, LdapSearchScope.ONE_LEVEL))
|
||||
.put(buildLdapSettings(OPEN_LDAP_URL, userTemplate, groupSearchBase, LdapSearchScope.SUB_TREE))
|
||||
.put("group_search.filter", "(objectClass=*)")
|
||||
.put(SessionFactory.HOSTNAME_VERIFICATION_SETTING, false)
|
||||
.put(SessionFactory.TIMEOUT_TCP_READ_SETTING, "1ms") //1 millisecond
|
||||
.build();
|
||||
RealmConfig config = new RealmConfig("oldap-test", settings, globalSettings);
|
||||
LdapSessionFactory sessionFactory = new LdapSessionFactory(config, clientSSLService).init();
|
||||
LdapSessionFactory sessionFactory = new LdapSessionFactory(config, clientSSLService);
|
||||
|
||||
try (LdapSession ldap = sessionFactory.session("thor", SecuredStringTests.build(PASSWORD))) {
|
||||
// In certain cases we may have a successful bind, but a search should take longer and cause a timeout
|
||||
ldap.groups();
|
||||
fail("The TCP connection should timeout before getting groups back");
|
||||
} catch (ElasticsearchException e) {
|
||||
assertThat(e.getCause().getMessage(), containsString("A client-side timeout was encountered while waiting"));
|
||||
}
|
||||
LDAPException expected = expectThrows(LDAPException.class,
|
||||
() -> sessionFactory.session("thor", SecuredStringTests.build(PASSWORD)).groups());
|
||||
assertThat(expected.getMessage(), containsString("A client-side timeout was encountered while waiting"));
|
||||
}
|
||||
|
||||
public void testStandardLdapConnectionHostnameVerification() throws Exception {
|
||||
|
@ -171,14 +170,11 @@ public class OpenLdapTests extends ESTestCase {
|
|||
.build();
|
||||
|
||||
RealmConfig config = new RealmConfig("oldap-test", settings, globalSettings);
|
||||
LdapSessionFactory sessionFactory = new LdapSessionFactory(config, clientSSLService).init();
|
||||
LdapSessionFactory sessionFactory = new LdapSessionFactory(config, clientSSLService);
|
||||
|
||||
String user = "blackwidow";
|
||||
try (LdapSession ldap = sessionFactory.session(user, SecuredStringTests.build(PASSWORD))) {
|
||||
fail("OpenLDAP certificate does not contain the correct hostname/ip so hostname verification should fail on open");
|
||||
} catch (IOException e) {
|
||||
assertThat(e.getMessage(), containsString("failed to connect to any LDAP servers"));
|
||||
}
|
||||
LDAPException expected = expectThrows(LDAPException.class, () -> sessionFactory.session(user, SecuredStringTests.build(PASSWORD)));
|
||||
assertThat(expected.getMessage(), anyOf(containsString("Hostname verification failed"), containsString("peer not authenticated")));
|
||||
}
|
||||
|
||||
Settings buildLdapSettings(String ldapUrl, String userTemplate, String groupSearchBase, LdapSearchScope scope) {
|
||||
|
|
|
@ -5,7 +5,6 @@
|
|||
*/
|
||||
package org.elasticsearch.xpack.security.authc.ldap;
|
||||
|
||||
import org.elasticsearch.ElasticsearchSecurityException;
|
||||
import org.elasticsearch.common.settings.Settings;
|
||||
import org.elasticsearch.common.unit.TimeValue;
|
||||
import org.elasticsearch.xpack.security.authc.ldap.support.LdapSearchScope;
|
||||
|
@ -31,7 +30,7 @@ public class SearchGroupsResolverTests extends GroupsResolverTestCase {
|
|||
.build();
|
||||
|
||||
SearchGroupsResolver resolver = new SearchGroupsResolver(settings);
|
||||
List<String> groups = resolver.resolve(ldapConnection, BRUCE_BANNER_DN, TimeValue.timeValueSeconds(10), NoOpLogger.INSTANCE);
|
||||
List<String> groups = resolver.resolve(ldapConnection, BRUCE_BANNER_DN, TimeValue.timeValueSeconds(10), NoOpLogger.INSTANCE, null);
|
||||
assertThat(groups, containsInAnyOrder(
|
||||
containsString("Avengers"),
|
||||
containsString("SHIELD"),
|
||||
|
@ -46,7 +45,7 @@ public class SearchGroupsResolverTests extends GroupsResolverTestCase {
|
|||
.build();
|
||||
|
||||
SearchGroupsResolver resolver = new SearchGroupsResolver(settings);
|
||||
List<String> groups = resolver.resolve(ldapConnection, BRUCE_BANNER_DN, TimeValue.timeValueSeconds(10), NoOpLogger.INSTANCE);
|
||||
List<String> groups = resolver.resolve(ldapConnection, BRUCE_BANNER_DN, TimeValue.timeValueSeconds(10), NoOpLogger.INSTANCE, null);
|
||||
assertThat(groups, containsInAnyOrder(
|
||||
containsString("Avengers"),
|
||||
containsString("SHIELD"),
|
||||
|
@ -61,7 +60,7 @@ public class SearchGroupsResolverTests extends GroupsResolverTestCase {
|
|||
.build();
|
||||
|
||||
SearchGroupsResolver resolver = new SearchGroupsResolver(settings);
|
||||
List<String> groups = resolver.resolve(ldapConnection, BRUCE_BANNER_DN, TimeValue.timeValueSeconds(10), NoOpLogger.INSTANCE);
|
||||
List<String> groups = resolver.resolve(ldapConnection, BRUCE_BANNER_DN, TimeValue.timeValueSeconds(10), NoOpLogger.INSTANCE, null);
|
||||
assertThat(groups, hasItem(containsString("Avengers")));
|
||||
}
|
||||
|
||||
|
@ -74,7 +73,19 @@ public class SearchGroupsResolverTests extends GroupsResolverTestCase {
|
|||
|
||||
SearchGroupsResolver resolver = new SearchGroupsResolver(settings);
|
||||
List<String> groups = resolver.resolve(ldapConnection, "uid=selvig,ou=people,dc=oldap,dc=test,dc=elasticsearch,dc=com",
|
||||
TimeValue.timeValueSeconds(10), NoOpLogger.INSTANCE);
|
||||
TimeValue.timeValueSeconds(10), NoOpLogger.INSTANCE, null);
|
||||
assertThat(groups, hasItem(containsString("Geniuses")));
|
||||
}
|
||||
|
||||
public void testFilterIncludesPosixGroups() throws Exception {
|
||||
Settings settings = Settings.builder()
|
||||
.put("base_dn", "dc=oldap,dc=test,dc=elasticsearch,dc=com")
|
||||
.put("user_attribute", "uid")
|
||||
.build();
|
||||
|
||||
SearchGroupsResolver resolver = new SearchGroupsResolver(settings);
|
||||
List<String> groups = resolver.resolve(ldapConnection, "uid=selvig,ou=people,dc=oldap,dc=test,dc=elasticsearch,dc=com",
|
||||
TimeValue.timeValueSeconds(10), NoOpLogger.INSTANCE, null);
|
||||
assertThat(groups, hasItem(containsString("Geniuses")));
|
||||
}
|
||||
|
||||
|
@ -116,12 +127,7 @@ public class SearchGroupsResolverTests extends GroupsResolverTestCase {
|
|||
.put("user_attribute", "doesntExists")
|
||||
.build();
|
||||
SearchGroupsResolver resolver = new SearchGroupsResolver(settings);
|
||||
try {
|
||||
resolver.readUserAttribute(ldapConnection, BRUCE_BANNER_DN, TimeValue.timeValueSeconds(5), NoOpLogger.INSTANCE);
|
||||
fail("searching for a non-existing attribute should throw an LdapException");
|
||||
} catch (ElasticsearchSecurityException e) {
|
||||
assertThat(e.getMessage(), containsString("no results returned"));
|
||||
}
|
||||
assertNull(resolver.readUserAttribute(ldapConnection, BRUCE_BANNER_DN, TimeValue.timeValueSeconds(5), NoOpLogger.INSTANCE));
|
||||
}
|
||||
|
||||
public void testReadBinaryUserAttribute() throws Exception {
|
||||
|
|
|
@ -26,7 +26,7 @@ public class UserAttributeGroupsResolverTests extends GroupsResolverTestCase {
|
|||
public void testResolve() throws Exception {
|
||||
//falling back on the 'memberOf' attribute
|
||||
UserAttributeGroupsResolver resolver = new UserAttributeGroupsResolver(Settings.EMPTY);
|
||||
List<String> groups = resolver.resolve(ldapConnection, BRUCE_BANNER_DN, TimeValue.timeValueSeconds(20), NoOpLogger.INSTANCE);
|
||||
List<String> groups = resolver.resolve(ldapConnection, BRUCE_BANNER_DN, TimeValue.timeValueSeconds(20), NoOpLogger.INSTANCE, null);
|
||||
assertThat(groups, containsInAnyOrder(
|
||||
containsString("Avengers"),
|
||||
containsString("SHIELD"),
|
||||
|
@ -39,7 +39,7 @@ public class UserAttributeGroupsResolverTests extends GroupsResolverTestCase {
|
|||
.put("user_group_attribute", "seeAlso")
|
||||
.build();
|
||||
UserAttributeGroupsResolver resolver = new UserAttributeGroupsResolver(settings);
|
||||
List<String> groups = resolver.resolve(ldapConnection, BRUCE_BANNER_DN, TimeValue.timeValueSeconds(20), NoOpLogger.INSTANCE);
|
||||
List<String> groups = resolver.resolve(ldapConnection, BRUCE_BANNER_DN, TimeValue.timeValueSeconds(20), NoOpLogger.INSTANCE, null);
|
||||
assertThat(groups, hasItems(containsString("Avengers"))); //seeAlso only has Avengers
|
||||
}
|
||||
|
||||
|
@ -48,7 +48,7 @@ public class UserAttributeGroupsResolverTests extends GroupsResolverTestCase {
|
|||
.put("user_group_attribute", "doesntExist")
|
||||
.build();
|
||||
UserAttributeGroupsResolver resolver = new UserAttributeGroupsResolver(settings);
|
||||
List<String> groups = resolver.resolve(ldapConnection, BRUCE_BANNER_DN, TimeValue.timeValueSeconds(20), NoOpLogger.INSTANCE);
|
||||
List<String> groups = resolver.resolve(ldapConnection, BRUCE_BANNER_DN, TimeValue.timeValueSeconds(20), NoOpLogger.INSTANCE, null);
|
||||
assertThat(groups, empty());
|
||||
}
|
||||
|
||||
|
|
|
@ -166,7 +166,7 @@ public class SessionFactoryLoadBalancingTests extends LdapTestCase {
|
|||
LdapSearchScope.SUB_TREE, loadBalancing);
|
||||
RealmConfig config = new RealmConfig("test-session-factory", settings, Settings.builder().put("path.home",
|
||||
createTempDir()).build());
|
||||
return new TestSessionFactory(config, null).init();
|
||||
return new TestSessionFactory(config, null);
|
||||
}
|
||||
|
||||
static class TestSessionFactory extends SessionFactory {
|
||||
|
|
|
@ -63,6 +63,6 @@ public class SessionFactoryTests extends ESTestCase {
|
|||
protected LdapSession getSession(String user, SecuredString password) {
|
||||
return null;
|
||||
}
|
||||
}.init();
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
|
@ -25,7 +25,7 @@ import org.elasticsearch.xpack.security.transport.netty3.SecurityNetty3HttpServe
|
|||
import org.elasticsearch.test.ESIntegTestCase.ClusterScope;
|
||||
import org.elasticsearch.test.SecurityIntegTestCase;
|
||||
import org.elasticsearch.transport.Transport;
|
||||
import org.elasticsearch.xpack.XPackPlugin;
|
||||
import org.elasticsearch.xpack.XPackTransportClient;
|
||||
|
||||
import javax.net.ssl.KeyManagerFactory;
|
||||
import javax.net.ssl.SSLContext;
|
||||
|
@ -155,7 +155,7 @@ public class PkiAuthenticationTests extends SecurityIntegTestCase {
|
|||
.put("cluster.name", internalCluster().getClusterName());
|
||||
builder.remove(Security.USER_SETTING.getKey());
|
||||
builder.remove("request.headers.Authorization");
|
||||
return TransportClient.builder().settings(builder).addPlugin(XPackPlugin.class).build();
|
||||
return new XPackTransportClient(builder.build());
|
||||
}
|
||||
|
||||
private String getNodeUrl() {
|
||||
|
|
|
@ -18,7 +18,7 @@ import org.elasticsearch.common.transport.InetSocketTransportAddress;
|
|||
import org.elasticsearch.test.SecurityIntegTestCase;
|
||||
import org.elasticsearch.test.SecuritySettingsSource;
|
||||
import org.elasticsearch.transport.Transport;
|
||||
import org.elasticsearch.xpack.XPackPlugin;
|
||||
import org.elasticsearch.xpack.XPackTransportClient;
|
||||
import org.elasticsearch.xpack.security.Security;
|
||||
import org.elasticsearch.xpack.security.authc.support.SecuredString;
|
||||
import org.elasticsearch.xpack.security.authc.support.UsernamePasswordToken;
|
||||
|
@ -112,7 +112,7 @@ public class PkiOptionalClientAuthTests extends SecurityIntegTestCase {
|
|||
.build();
|
||||
|
||||
|
||||
try (TransportClient client = TransportClient.builder().settings(settings).addPlugin(XPackPlugin.class).build()) {
|
||||
try (TransportClient client = new XPackTransportClient(settings)) {
|
||||
client.addTransportAddress(new InetSocketTransportAddress(InetAddress.getLoopbackAddress(), port));
|
||||
assertGreenClusterState(client);
|
||||
}
|
||||
|
|
|
@ -7,10 +7,17 @@ package org.elasticsearch.xpack.security.authz;
|
|||
|
||||
import org.elasticsearch.common.Strings;
|
||||
import org.elasticsearch.common.bytes.BytesArray;
|
||||
import org.elasticsearch.common.bytes.BytesReference;
|
||||
import org.elasticsearch.common.io.stream.ByteBufferStreamInput;
|
||||
import org.elasticsearch.common.io.stream.BytesStreamOutput;
|
||||
import org.elasticsearch.common.io.stream.StreamInput;
|
||||
import org.elasticsearch.common.xcontent.ToXContent;
|
||||
import org.elasticsearch.common.xcontent.XContentBuilder;
|
||||
import org.elasticsearch.xpack.security.support.MetadataUtils;
|
||||
import org.elasticsearch.test.ESTestCase;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import static org.elasticsearch.common.xcontent.XContentFactory.jsonBuilder;
|
||||
import static org.hamcrest.core.Is.is;
|
||||
|
||||
|
@ -37,7 +44,7 @@ public class RoleDescriptorTests extends ESTestCase {
|
|||
};
|
||||
RoleDescriptor descriptor = new RoleDescriptor("test", new String[] { "all", "none" }, groups, new String[] { "sudo" });
|
||||
assertThat(descriptor.toString(), is("Role[name=test, cluster=[all,none], indicesPrivileges=[IndicesPrivileges[indices=[i1,i2], " +
|
||||
"privileges=[read], fields=[body,title], query={\"query\": {\"match_all\": {}}}],], runAs=[sudo]]"));
|
||||
"privileges=[read], fields=[body,title], query={\"query\": {\"match_all\": {}}}],], runAs=[sudo], metadata=[{}]]"));
|
||||
}
|
||||
|
||||
public void testToXContent() throws Exception {
|
||||
|
@ -49,7 +56,8 @@ public class RoleDescriptorTests extends ESTestCase {
|
|||
.query("{\"query\": {\"match_all\": {}}}")
|
||||
.build()
|
||||
};
|
||||
RoleDescriptor descriptor = new RoleDescriptor("test", new String[] { "all", "none" }, groups, new String[] { "sudo" });
|
||||
Map<String, Object> metadata = randomBoolean() ? MetadataUtils.DEFAULT_RESERVED_METADATA : null;
|
||||
RoleDescriptor descriptor = new RoleDescriptor("test", new String[] { "all", "none" }, groups, new String[] { "sudo" }, metadata);
|
||||
XContentBuilder builder = descriptor.toXContent(jsonBuilder(), ToXContent.EMPTY_PARAMS);
|
||||
RoleDescriptor parsed = RoleDescriptor.parse("test", builder.bytes());
|
||||
assertThat(parsed, is(descriptor));
|
||||
|
@ -88,5 +96,34 @@ public class RoleDescriptorTests extends ESTestCase {
|
|||
assertEquals(1, rd.getIndicesPrivileges().length);
|
||||
assertArrayEquals(new String[] { "idx1", "idx2" }, rd.getIndicesPrivileges()[0].getIndices());
|
||||
assertArrayEquals(new String[] { "m", "n" }, rd.getRunAs());
|
||||
|
||||
q = "{\"cluster\":[\"a\", \"b\"], \"metadata\":{\"foo\":\"bar\"}}";
|
||||
rd = RoleDescriptor.parse("test", new BytesArray(q));
|
||||
assertEquals("test", rd.getName());
|
||||
assertArrayEquals(new String[] { "a", "b" }, rd.getClusterPrivileges());
|
||||
assertEquals(0, rd.getIndicesPrivileges().length);
|
||||
assertArrayEquals(Strings.EMPTY_ARRAY, rd.getRunAs());
|
||||
assertNotNull(rd.getMetadata());
|
||||
assertThat(rd.getMetadata().size(), is(1));
|
||||
assertThat(rd.getMetadata().get("foo"), is("bar"));
|
||||
}
|
||||
|
||||
public void testSerialization() throws Exception {
|
||||
BytesStreamOutput output = new BytesStreamOutput();
|
||||
RoleDescriptor.IndicesPrivileges[] groups = new RoleDescriptor.IndicesPrivileges[] {
|
||||
RoleDescriptor.IndicesPrivileges.builder()
|
||||
.indices("i1", "i2")
|
||||
.privileges("read")
|
||||
.fields("body", "title")
|
||||
.query("{\"query\": {\"match_all\": {}}}")
|
||||
.build()
|
||||
};
|
||||
Map<String, Object> metadata = randomBoolean() ? MetadataUtils.DEFAULT_RESERVED_METADATA : null;
|
||||
final RoleDescriptor descriptor =
|
||||
new RoleDescriptor("test", new String[] { "all", "none" }, groups, new String[] { "sudo" }, metadata);
|
||||
RoleDescriptor.writeTo(descriptor, output);
|
||||
StreamInput streamInput = ByteBufferStreamInput.wrap(BytesReference.toBytes(output.bytes()));
|
||||
final RoleDescriptor serialized = RoleDescriptor.readFrom(streamInput);
|
||||
assertEquals(descriptor, serialized);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -55,12 +55,11 @@ public class CertUtilsTests extends ESTestCase {
|
|||
}
|
||||
|
||||
public void testGenerateKeyPair() throws Exception {
|
||||
KeyPair keyPair = CertUtils.generateKeyPair();
|
||||
KeyPair keyPair = CertUtils.generateKeyPair(randomFrom(1024, 2048));
|
||||
assertThat(keyPair.getPrivate().getAlgorithm(), is("RSA"));
|
||||
assertThat(keyPair.getPublic().getAlgorithm(), is("RSA"));
|
||||
}
|
||||
|
||||
|
||||
public void testReadKeysCorrectly() throws Exception {
|
||||
// read in keystore version
|
||||
Path keystorePath = getDataPath("/org/elasticsearch/xpack/security/transport/ssl/certs/simple/testnode.jks");
|
||||
|
@ -77,7 +76,7 @@ public class CertUtilsTests extends ESTestCase {
|
|||
try (Reader reader =
|
||||
Files.newBufferedReader(getDataPath("/org/elasticsearch/xpack/security/transport/ssl/certs/simple/testnode.pem"),
|
||||
StandardCharsets.UTF_8)) {
|
||||
privateKey = CertUtils.readPrivateKey(reader, "testnode".toCharArray());
|
||||
privateKey = CertUtils.readPrivateKey(reader, "testnode"::toCharArray);
|
||||
}
|
||||
assertThat(privateKey, notNullValue());
|
||||
assertThat(privateKey, equalTo(key));
|
||||
|
|
|
@ -0,0 +1,390 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
package org.elasticsearch.xpack.security.ssl;
|
||||
|
||||
import org.bouncycastle.asn1.ASN1String;
|
||||
import org.bouncycastle.asn1.DEROctetString;
|
||||
import org.bouncycastle.asn1.pkcs.Attribute;
|
||||
import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
|
||||
import org.bouncycastle.asn1.x509.Extension;
|
||||
import org.bouncycastle.asn1.x509.Extensions;
|
||||
import org.bouncycastle.asn1.x509.GeneralName;
|
||||
import org.bouncycastle.asn1.x509.GeneralNames;
|
||||
import org.bouncycastle.cert.X509CertificateHolder;
|
||||
import org.bouncycastle.openssl.PEMEncryptedKeyPair;
|
||||
import org.bouncycastle.openssl.PEMParser;
|
||||
import org.bouncycastle.pkcs.PKCS10CertificationRequest;
|
||||
import org.elasticsearch.cli.MockTerminal;
|
||||
import org.elasticsearch.common.Strings;
|
||||
import org.elasticsearch.common.network.NetworkAddress;
|
||||
import org.elasticsearch.common.settings.Settings;
|
||||
import org.elasticsearch.env.Environment;
|
||||
import org.elasticsearch.test.ESTestCase;
|
||||
import org.elasticsearch.xpack.XPackPlugin;
|
||||
import org.elasticsearch.xpack.security.ssl.CertificateTool.CAInfo;
|
||||
import org.elasticsearch.xpack.security.ssl.CertificateTool.CertificateInformation;
|
||||
import org.elasticsearch.xpack.security.ssl.CertificateTool.Name;
|
||||
|
||||
import javax.security.auth.x500.X500Principal;
|
||||
import java.io.Reader;
|
||||
import java.net.InetAddress;
|
||||
import java.net.URI;
|
||||
import java.nio.file.FileSystem;
|
||||
import java.nio.file.FileSystems;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.security.KeyPair;
|
||||
import java.security.PrivateKey;
|
||||
import java.security.cert.Certificate;
|
||||
import java.security.cert.CertificateFactory;
|
||||
import java.security.cert.X509Certificate;
|
||||
import java.security.interfaces.RSAKey;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static org.hamcrest.Matchers.containsString;
|
||||
import static org.hamcrest.Matchers.greaterThanOrEqualTo;
|
||||
import static org.hamcrest.Matchers.instanceOf;
|
||||
|
||||
/**
|
||||
* Unit tests for the tool used to simplify SSL certificate generation
|
||||
*/
|
||||
public class CertificateToolTests extends ESTestCase {
|
||||
|
||||
public void testOutputDirectory() throws Exception {
|
||||
Environment env = new Environment(Settings.builder().put("path.home", createTempDir()).build());
|
||||
Path outputDir = createTempDir();
|
||||
Path outputFile = outputDir.resolve("certs.zip");
|
||||
MockTerminal terminal = new MockTerminal();
|
||||
|
||||
// test with a user provided dir
|
||||
Path resolvedOutputFile = CertificateTool.getOutputFile(terminal, outputFile.toString(), env, null);
|
||||
assertEquals(outputFile, resolvedOutputFile);
|
||||
assertTrue(terminal.getOutput().isEmpty());
|
||||
|
||||
// test without a user provided directory
|
||||
Path userPromptedOutputFile = outputDir.resolve("csr");
|
||||
assertFalse(Files.exists(userPromptedOutputFile));
|
||||
terminal.addTextInput(userPromptedOutputFile.toString());
|
||||
resolvedOutputFile = CertificateTool.getOutputFile(terminal, null, env, "out.zip");
|
||||
assertEquals(userPromptedOutputFile, resolvedOutputFile);
|
||||
assertTrue(terminal.getOutput().isEmpty());
|
||||
|
||||
// test with empty user input
|
||||
String defaultFilename = randomAsciiOfLengthBetween(1, 10);
|
||||
Path expectedDefaultPath = XPackPlugin.resolveConfigFile(env, defaultFilename);
|
||||
terminal.addTextInput("");
|
||||
resolvedOutputFile = CertificateTool.getOutputFile(terminal, null, env, defaultFilename);
|
||||
assertEquals(expectedDefaultPath, resolvedOutputFile);
|
||||
assertTrue(terminal.getOutput().isEmpty());
|
||||
}
|
||||
|
||||
public void testPromptingForInstanceInformation() throws Exception {
|
||||
final int numberOfInstances = scaledRandomIntBetween(1, 12);
|
||||
Map<String, Map<String, String>> instanceInput = new HashMap<>(numberOfInstances);
|
||||
for (int i = 0; i < numberOfInstances; i++) {
|
||||
final String name = randomAsciiOfLengthBetween(1, 32);
|
||||
Map<String, String> instanceInfo = new HashMap<>();
|
||||
instanceInput.put(name, instanceInfo);
|
||||
instanceInfo.put("ip", randomFrom("127.0.0.1", "::1", "192.168.1.1,::1", ""));
|
||||
instanceInfo.put("dns", randomFrom("localhost", "localhost.localdomain", "localhost,myhost", ""));
|
||||
}
|
||||
|
||||
int count = 0;
|
||||
MockTerminal terminal = new MockTerminal();
|
||||
for (Entry<String, Map<String, String>> entry : instanceInput.entrySet()) {
|
||||
terminal.addTextInput(entry.getKey());
|
||||
terminal.addTextInput(entry.getValue().get("ip"));
|
||||
terminal.addTextInput(entry.getValue().get("dns"));
|
||||
count++;
|
||||
if (count == numberOfInstances) {
|
||||
terminal.addTextInput("n");
|
||||
} else {
|
||||
terminal.addTextInput("y");
|
||||
}
|
||||
}
|
||||
|
||||
Collection<CertificateInformation> certInfos = CertificateTool.getCertificateInformationList(terminal, null,
|
||||
new Environment(Settings.builder().put("path.home", createTempDir()).build()));
|
||||
assertEquals(numberOfInstances, certInfos.size());
|
||||
for (CertificateInformation certInfo : certInfos) {
|
||||
String name = certInfo.name.originalName;
|
||||
Map<String, String> instanceInfo = instanceInput.get(name);
|
||||
assertNotNull("did not find map for " + name, instanceInfo);
|
||||
List<String> expectedIps = Arrays.asList(Strings.commaDelimitedListToStringArray(instanceInfo.get("ip")));
|
||||
List<String> expectedDns = Arrays.asList(Strings.commaDelimitedListToStringArray(instanceInfo.get("dns")));
|
||||
assertEquals(expectedIps, certInfo.ipAddresses);
|
||||
assertEquals(expectedDns, certInfo.dnsNames);
|
||||
instanceInput.remove(name);
|
||||
}
|
||||
assertEquals(0, instanceInput.size());
|
||||
final String output = terminal.getOutput();
|
||||
assertTrue("Output: " + output, output.isEmpty());
|
||||
}
|
||||
|
||||
public void testParsingFile() throws Exception {
|
||||
Path instanceFile = getDataPath("instances.yml");
|
||||
Collection<CertificateInformation> certInfos = CertificateTool.parseFile(instanceFile);
|
||||
assertEquals(4, certInfos.size());
|
||||
|
||||
Map<String, CertificateInformation> certInfosMap =
|
||||
certInfos.stream().collect(Collectors.toMap((c) -> c.name.originalName, Function.identity()));
|
||||
CertificateInformation certInfo = certInfosMap.get("node1");
|
||||
assertEquals(Collections.singletonList("127.0.0.1"), certInfo.ipAddresses);
|
||||
assertEquals(Collections.singletonList("localhost"), certInfo.dnsNames);
|
||||
|
||||
certInfo = certInfosMap.get("node2");
|
||||
assertEquals(Collections.singletonList("::1"), certInfo.ipAddresses);
|
||||
assertEquals(Collections.emptyList(), certInfo.dnsNames);
|
||||
|
||||
certInfo = certInfosMap.get("node3");
|
||||
assertEquals(Collections.emptyList(), certInfo.ipAddresses);
|
||||
assertEquals(Collections.emptyList(), certInfo.dnsNames);
|
||||
|
||||
certInfo = certInfosMap.get("CN=different value");
|
||||
assertEquals(Collections.emptyList(), certInfo.ipAddresses);
|
||||
assertEquals(Collections.singletonList("node4.mydomain.com"), certInfo.dnsNames);
|
||||
}
|
||||
|
||||
public void testGeneratingCsr() throws Exception {
|
||||
Path tempDir = createTempDir();
|
||||
Path outputFile = tempDir.resolve("out.zip");
|
||||
Path instanceFile = getDataPath("instances.yml");
|
||||
Collection<CertificateInformation> certInfos = CertificateTool.parseFile(instanceFile);
|
||||
assertEquals(4, certInfos.size());
|
||||
|
||||
assertFalse(Files.exists(outputFile));
|
||||
CertificateTool.generateAndWriteCsrs(outputFile, certInfos, randomFrom(1024, 2048));
|
||||
assertTrue(Files.exists(outputFile));
|
||||
|
||||
FileSystem fileSystem = FileSystems.newFileSystem(new URI("jar:" + outputFile.toUri()), Collections.emptyMap());
|
||||
Path zipRoot = fileSystem.getPath("/");
|
||||
|
||||
assertFalse(Files.exists(zipRoot.resolve("ca")));
|
||||
for (CertificateInformation certInfo : certInfos) {
|
||||
String filename = certInfo.name.filename;
|
||||
assertTrue(Files.exists(zipRoot.resolve(filename)));
|
||||
final Path csr = zipRoot.resolve(filename + "/" + filename + ".csr");
|
||||
assertTrue(Files.exists(csr));
|
||||
assertTrue(Files.exists(zipRoot.resolve(filename + "/" + filename + ".key")));
|
||||
PKCS10CertificationRequest request = readCertificateRequest(csr);
|
||||
assertEquals(certInfo.name.x500Principal.getName(), request.getSubject().toString());
|
||||
Attribute[] extensionsReq = request.getAttributes(PKCSObjectIdentifiers.pkcs_9_at_extensionRequest);
|
||||
if (certInfo.ipAddresses.size() > 0 || certInfo.dnsNames.size() > 0) {
|
||||
assertEquals(1, extensionsReq.length);
|
||||
Extensions extensions = Extensions.getInstance(extensionsReq[0].getAttributeValues()[0]);
|
||||
GeneralNames subjAltNames = GeneralNames.fromExtensions(extensions, Extension.subjectAlternativeName);
|
||||
assertSubjAltNames(subjAltNames, certInfo);
|
||||
} else {
|
||||
assertEquals(0, extensionsReq.length);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void testGeneratingSignedCertificates() throws Exception {
|
||||
Path tempDir = createTempDir();
|
||||
Path outputFile = tempDir.resolve("out.zip");
|
||||
Path instanceFile = getDataPath("instances.yml");
|
||||
Collection<CertificateInformation> certInfos = CertificateTool.parseFile(instanceFile);
|
||||
assertEquals(4, certInfos.size());
|
||||
|
||||
final int keysize = randomFrom(1024, 2048);
|
||||
KeyPair keyPair = CertUtils.generateKeyPair(keysize);
|
||||
X509Certificate caCert = CertUtils.generateCACertificate(new X500Principal("CN=test ca"), keyPair);
|
||||
|
||||
final boolean generatedCa = randomBoolean();
|
||||
final char[] keyPassword = randomBoolean() ? "changeme".toCharArray() : null;
|
||||
assertFalse(Files.exists(outputFile));
|
||||
CAInfo caInfo = new CAInfo(caCert, keyPair.getPrivate(), generatedCa, keyPassword);
|
||||
CertificateTool.generateAndWriteSignedCertificates(outputFile, certInfos, caInfo, keysize);
|
||||
assertTrue(Files.exists(outputFile));
|
||||
|
||||
FileSystem fileSystem = FileSystems.newFileSystem(new URI("jar:" + outputFile.toUri()), Collections.emptyMap());
|
||||
Path zipRoot = fileSystem.getPath("/");
|
||||
|
||||
if (generatedCa) {
|
||||
assertTrue(Files.exists(zipRoot.resolve("ca")));
|
||||
assertTrue(Files.exists(zipRoot.resolve("ca").resolve("ca.crt")));
|
||||
assertTrue(Files.exists(zipRoot.resolve("ca").resolve("ca.key")));
|
||||
// check the CA cert
|
||||
try (Reader reader = Files.newBufferedReader(zipRoot.resolve("ca").resolve("ca.crt"))) {
|
||||
X509Certificate parsedCaCert = readX509Certificate(reader);
|
||||
assertThat(parsedCaCert.getSubjectX500Principal().getName(), containsString("test ca"));
|
||||
assertEquals(caCert, parsedCaCert);
|
||||
}
|
||||
|
||||
// check the CA key
|
||||
if (keyPassword != null) {
|
||||
try (Reader reader = Files.newBufferedReader(zipRoot.resolve("ca").resolve("ca.key"))) {
|
||||
PEMParser pemParser = new PEMParser(reader);
|
||||
Object parsed = pemParser.readObject();
|
||||
assertThat(parsed, instanceOf(PEMEncryptedKeyPair.class));
|
||||
char[] zeroChars = new char[keyPassword.length];
|
||||
Arrays.fill(zeroChars, (char) 0);
|
||||
assertArrayEquals(zeroChars, keyPassword);
|
||||
}
|
||||
}
|
||||
|
||||
try (Reader reader = Files.newBufferedReader(zipRoot.resolve("ca").resolve("ca.key"))) {
|
||||
PrivateKey privateKey = CertUtils.readPrivateKey(reader, () -> keyPassword != null ? "changeme".toCharArray() : null);
|
||||
assertEquals(caInfo.privateKey, privateKey);
|
||||
}
|
||||
} else {
|
||||
assertFalse(Files.exists(zipRoot.resolve("ca")));
|
||||
}
|
||||
|
||||
for (CertificateInformation certInfo : certInfos) {
|
||||
String filename = certInfo.name.filename;
|
||||
assertTrue(Files.exists(zipRoot.resolve(filename)));
|
||||
final Path cert = zipRoot.resolve(filename + "/" + filename + ".crt");
|
||||
assertTrue(Files.exists(cert));
|
||||
assertTrue(Files.exists(zipRoot.resolve(filename + "/" + filename + ".key")));
|
||||
try (Reader reader = Files.newBufferedReader(cert)) {
|
||||
X509Certificate certificate = readX509Certificate(reader);
|
||||
assertEquals(certInfo.name.x500Principal.toString(), certificate.getSubjectX500Principal().getName());
|
||||
final int sanCount = certInfo.ipAddresses.size() + certInfo.dnsNames.size();
|
||||
if (sanCount == 0) {
|
||||
assertNull(certificate.getSubjectAlternativeNames());
|
||||
} else {
|
||||
X509CertificateHolder x509CertHolder = new X509CertificateHolder(certificate.getEncoded());
|
||||
GeneralNames subjAltNames =
|
||||
GeneralNames.fromExtensions(x509CertHolder.getExtensions(), Extension.subjectAlternativeName);
|
||||
assertSubjAltNames(subjAltNames, certInfo);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void testGetCAInfo() throws Exception {
|
||||
Environment env = new Environment(Settings.builder().put("path.home", createTempDir()).build());
|
||||
Path testNodeCertPath = getDataPath("/org/elasticsearch/xpack/security/transport/ssl/certs/simple/testnode.crt");
|
||||
Path testNodeKeyPath = getDataPath("/org/elasticsearch/xpack/security/transport/ssl/certs/simple/testnode.pem");
|
||||
final boolean passwordPrompt = randomBoolean();
|
||||
MockTerminal terminal = new MockTerminal();
|
||||
if (passwordPrompt) {
|
||||
terminal.addSecretInput("testnode");
|
||||
}
|
||||
|
||||
CAInfo caInfo = CertificateTool.getCAInfo(terminal, "CN=foo", testNodeCertPath.toString(), testNodeKeyPath.toString(),
|
||||
passwordPrompt ? null : "testnode".toCharArray(), passwordPrompt, env, randomFrom(1024, 2048));
|
||||
assertTrue(terminal.getOutput().isEmpty());
|
||||
assertThat(caInfo.caCert, instanceOf(X509Certificate.class));
|
||||
assertEquals(caInfo.caCert.getSubjectX500Principal().getName(),
|
||||
"CN=Elasticsearch Test Node,OU=elasticsearch,O=org");
|
||||
assertThat(caInfo.privateKey.getAlgorithm(), containsString("RSA"));
|
||||
assertEquals(2048, ((RSAKey) caInfo.privateKey).getModulus().bitLength());
|
||||
assertFalse(caInfo.generated);
|
||||
|
||||
// test generation
|
||||
final boolean passwordProtected = randomBoolean();
|
||||
final char[] password;
|
||||
if (passwordPrompt && passwordProtected) {
|
||||
password = null;
|
||||
terminal.addSecretInput("testnode");
|
||||
} else {
|
||||
password = "testnode".toCharArray();
|
||||
}
|
||||
final int keysize = randomFrom(1024, 2048);
|
||||
caInfo = CertificateTool.getCAInfo(terminal, "CN=foo bar", null, null, password, passwordProtected && passwordPrompt, env, keysize);
|
||||
assertTrue(terminal.getOutput().isEmpty());
|
||||
assertThat(caInfo.caCert, instanceOf(X509Certificate.class));
|
||||
assertEquals(caInfo.caCert.getSubjectX500Principal().getName(), "CN=foo bar");
|
||||
assertThat(caInfo.privateKey.getAlgorithm(), containsString("RSA"));
|
||||
assertTrue(caInfo.generated);
|
||||
assertEquals(keysize, ((RSAKey) caInfo.privateKey).getModulus().bitLength());
|
||||
}
|
||||
|
||||
public void testNameValues() throws Exception {
|
||||
// good name
|
||||
Name name = Name.fromUserProvidedName("my instance");
|
||||
assertEquals("my instance", name.originalName);
|
||||
assertNull(name.error);
|
||||
assertEquals("CN=my instance", name.x500Principal.getName());
|
||||
assertEquals("my instance", name.originalName);
|
||||
|
||||
// too long
|
||||
String userProvidedName = randomAsciiOfLength(CertificateTool.MAX_FILENAME_LENGTH + 1);
|
||||
name = Name.fromUserProvidedName(userProvidedName);
|
||||
assertEquals(userProvidedName, name.originalName);
|
||||
assertNull(name.error);
|
||||
assertEquals(userProvidedName.substring(0, CertificateTool.MAX_FILENAME_LENGTH), name.filename);
|
||||
assertEquals("CN=" + userProvidedName, name.x500Principal.getName());
|
||||
|
||||
// too short
|
||||
name = Name.fromUserProvidedName("");
|
||||
assertEquals("", name.originalName);
|
||||
assertThat(name.error, containsString("valid filename"));
|
||||
assertEquals("CN=", name.x500Principal.getName());
|
||||
assertNull(name.filename);
|
||||
|
||||
// invalid characters only
|
||||
userProvidedName = "<>|<>*|?\"\\";
|
||||
name = Name.fromUserProvidedName(userProvidedName);
|
||||
assertEquals(userProvidedName, name.originalName);
|
||||
assertThat(name.error, containsString("valid DN"));
|
||||
assertNull(name.x500Principal);
|
||||
assertNull(name.filename);
|
||||
|
||||
// invalid for file but DN ok
|
||||
userProvidedName = "*";
|
||||
name = Name.fromUserProvidedName(userProvidedName);
|
||||
assertEquals(userProvidedName, name.originalName);
|
||||
assertThat(name.error, containsString("valid filename"));
|
||||
assertEquals("CN=" + userProvidedName, name.x500Principal.getName());
|
||||
assertNull(name.filename);
|
||||
|
||||
// invalid with valid chars
|
||||
userProvidedName = "*.mydomain.com";
|
||||
name = Name.fromUserProvidedName(userProvidedName);
|
||||
assertEquals(userProvidedName, name.originalName);
|
||||
assertNull(name.error);
|
||||
assertEquals("CN=" + userProvidedName, name.x500Principal.getName());
|
||||
assertEquals(".mydomain.com", name.filename);
|
||||
}
|
||||
|
||||
private PKCS10CertificationRequest readCertificateRequest(Path path) throws Exception {
|
||||
try (Reader reader = Files.newBufferedReader(path);
|
||||
PEMParser pemParser = new PEMParser(reader)) {
|
||||
Object object = pemParser.readObject();
|
||||
assertThat(object, instanceOf(PKCS10CertificationRequest.class));
|
||||
return (PKCS10CertificationRequest) object;
|
||||
}
|
||||
}
|
||||
|
||||
private X509Certificate readX509Certificate(Reader reader) throws Exception {
|
||||
List<Certificate> list = new ArrayList<>(1);
|
||||
CertUtils.readCertificates(reader, list, CertificateFactory.getInstance("X.509"));
|
||||
assertEquals(1, list.size());
|
||||
assertThat(list.get(0), instanceOf(X509Certificate.class));
|
||||
return (X509Certificate) list.get(0);
|
||||
}
|
||||
|
||||
private void assertSubjAltNames(GeneralNames subjAltNames, CertificateInformation certInfo) throws Exception {
|
||||
assertEquals(certInfo.ipAddresses.size() + certInfo.dnsNames.size(), subjAltNames.getNames().length);
|
||||
Collections.sort(certInfo.dnsNames);
|
||||
Collections.sort(certInfo.ipAddresses);
|
||||
for (GeneralName generalName : subjAltNames.getNames()) {
|
||||
if (generalName.getTagNo() == GeneralName.dNSName) {
|
||||
String dns = ((ASN1String)generalName.getName()).getString();
|
||||
assertThat(Collections.binarySearch(certInfo.dnsNames, dns), greaterThanOrEqualTo(0));
|
||||
} else if (generalName.getTagNo() == GeneralName.iPAddress) {
|
||||
byte[] ipBytes = DEROctetString.getInstance(generalName.getName()).getOctets();
|
||||
String ip = NetworkAddress.format(InetAddress.getByAddress(ipBytes));
|
||||
assertThat(Collections.binarySearch(certInfo.ipAddresses, ip), greaterThanOrEqualTo(0));
|
||||
} else {
|
||||
fail("unknown general name with tag " + generalName.getTagNo());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -421,7 +421,7 @@ public class SSLConfigurationTests extends ESTestCase {
|
|||
}
|
||||
});
|
||||
Path updatedKeyPath = tempDir.resolve("updated.pem");
|
||||
KeyPair keyPair = CertUtils.generateKeyPair();
|
||||
KeyPair keyPair = CertUtils.generateKeyPair(randomFrom(1024, 2048));
|
||||
try (OutputStream os = Files.newOutputStream(updatedKeyPath);
|
||||
OutputStreamWriter osWriter = new OutputStreamWriter(os, StandardCharsets.UTF_8);
|
||||
JcaPEMWriter writer = new JcaPEMWriter(osWriter)) {
|
||||
|
|
|
@ -93,7 +93,7 @@ public class SSLReloadIntegTests extends SecurityIntegTestCase {
|
|||
}
|
||||
|
||||
public void testThatSSLConfigurationReloadsOnModification() throws Exception {
|
||||
KeyPair keyPair = CertUtils.generateKeyPair();
|
||||
KeyPair keyPair = CertUtils.generateKeyPair(randomFrom(1024, 2048));
|
||||
X509Certificate certificate = getCertificate(keyPair);
|
||||
KeyStore keyStore = KeyStore.getInstance("jks");
|
||||
keyStore.load(null, null);
|
||||
|
|
|
@ -125,15 +125,17 @@ public class HandshakeWaitingHandlerTests extends ESTestCase {
|
|||
}
|
||||
|
||||
if (failureCause.get() != null) {
|
||||
logger.info("run [{}] produced a failure", i);
|
||||
assertThat(failureCause.get(), anyOf(instanceOf(SSLException.class), instanceOf(AssertionError.class)));
|
||||
break;
|
||||
} else {
|
||||
logger.warn("run [{}] did not produce a failure", i);
|
||||
}
|
||||
}
|
||||
|
||||
assertThat("Expected this test to fail with an SSLException or AssertionError", failureCause.get(), notNullValue());
|
||||
}
|
||||
|
||||
@AwaitsFix(bugUrl = "https://github.com/elasticsearch/elasticsearch-shield/issues/533")
|
||||
public void testWriteBeforeHandshakePassesWithHandshakeWaitingHandler() throws Exception {
|
||||
clientBootstrap.setPipelineFactory(new ChannelPipelineFactory() {
|
||||
|
||||
|
@ -161,14 +163,15 @@ public class HandshakeWaitingHandlerTests extends ESTestCase {
|
|||
// Wait for pending writes to prevent IOExceptions
|
||||
Channel channel = handshakeFuture.getChannel();
|
||||
HandshakeWaitingHandler handler = channel.getPipeline().get(HandshakeWaitingHandler.class);
|
||||
while (handler != null && handler.hasPendingWrites()) {
|
||||
Thread.sleep(10);
|
||||
if (handler != null) {
|
||||
boolean noMoreWrites = awaitBusy(() -> handler.hasPendingWrites() == false);
|
||||
assertTrue(noMoreWrites);
|
||||
}
|
||||
|
||||
channel.close();
|
||||
}
|
||||
|
||||
assertNotFailed();
|
||||
logger.info("run [{}] succeeded", i);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -13,8 +13,8 @@ import org.elasticsearch.common.transport.InetSocketTransportAddress;
|
|||
import org.elasticsearch.common.transport.TransportAddress;
|
||||
import org.elasticsearch.test.SecurityIntegTestCase;
|
||||
import org.elasticsearch.transport.Transport;
|
||||
import org.elasticsearch.xpack.XPackPlugin;
|
||||
import org.elasticsearch.xpack.security.Security;
|
||||
import org.elasticsearch.xpack.XPackTransportClient;
|
||||
|
||||
import java.net.InetSocketAddress;
|
||||
import java.nio.file.Files;
|
||||
|
@ -99,7 +99,7 @@ public class SslHostnameVerificationTests extends SecurityIntegTestCase {
|
|||
.put(SecurityNetty3Transport.HOSTNAME_VERIFICATION_SETTING.getKey(), true)
|
||||
.build();
|
||||
|
||||
try (TransportClient client = TransportClient.builder().addPlugin(XPackPlugin.class).settings(settings).build()) {
|
||||
try (TransportClient client = new XPackTransportClient(settings)) {
|
||||
client.addTransportAddress(new InetSocketTransportAddress(inetSocketAddress.getAddress(), inetSocketAddress.getPort()));
|
||||
client.admin().cluster().prepareHealth().get();
|
||||
fail("Expected a NoNodeAvailableException due to hostname verification failures");
|
||||
|
|
|
@ -20,7 +20,7 @@ import org.elasticsearch.common.settings.Settings;
|
|||
import org.elasticsearch.common.transport.TransportAddress;
|
||||
import org.elasticsearch.test.SecurityIntegTestCase;
|
||||
import org.elasticsearch.transport.Transport;
|
||||
import org.elasticsearch.xpack.XPackPlugin;
|
||||
import org.elasticsearch.xpack.XPackTransportClient;
|
||||
import org.elasticsearch.xpack.security.Security;
|
||||
import org.elasticsearch.xpack.security.ssl.ClientSSLService;
|
||||
import org.elasticsearch.xpack.security.ssl.SSLConfiguration.Global;
|
||||
|
@ -101,7 +101,7 @@ public class SslClientAuthTests extends SecurityIntegTestCase {
|
|||
.put(Security.USER_SETTING.getKey(),
|
||||
transportClientUsername() + ":" + new String(transportClientPassword().internalChars()))
|
||||
.build();
|
||||
try (TransportClient client = TransportClient.builder().settings(settings).addPlugin(XPackPlugin.class).build()) {
|
||||
try (TransportClient client = new XPackTransportClient(settings)) {
|
||||
Transport transport = internalCluster().getDataNodeInstance(Transport.class);
|
||||
TransportAddress transportAddress = transport.boundAddress().publishAddress();
|
||||
client.addTransportAddress(transportAddress);
|
||||
|
|
|
@ -28,7 +28,7 @@ import org.elasticsearch.xpack.security.ssl.SSLConfiguration.Global;
|
|||
import org.elasticsearch.xpack.security.transport.netty3.SecurityNetty3HttpServerTransport;
|
||||
import org.elasticsearch.test.SecurityIntegTestCase;
|
||||
import org.elasticsearch.transport.Transport;
|
||||
import org.elasticsearch.xpack.XPackPlugin;
|
||||
import org.elasticsearch.xpack.XPackTransportClient;
|
||||
|
||||
import javax.net.ssl.SSLContext;
|
||||
import javax.net.ssl.SSLHandshakeException;
|
||||
|
@ -59,12 +59,12 @@ public class SslIntegrationTests extends SecurityIntegTestCase {
|
|||
|
||||
// no SSL exception as this is the exception is returned when connecting
|
||||
public void testThatUnconfiguredCiphersAreRejected() {
|
||||
try (TransportClient transportClient = TransportClient.builder().addPlugin(XPackPlugin.class).settings(Settings.builder()
|
||||
try (TransportClient transportClient = new XPackTransportClient(Settings.builder()
|
||||
.put(transportClientSettings())
|
||||
.put("node.name", "programmatic_transport_client")
|
||||
.put("cluster.name", internalCluster().getClusterName())
|
||||
.putArray("xpack.security.ssl.ciphers", new String[]{"TLS_ECDH_anon_WITH_RC4_128_SHA", "SSL_RSA_WITH_3DES_EDE_CBC_SHA"})
|
||||
.build()).build()) {
|
||||
.build())) {
|
||||
|
||||
TransportAddress transportAddress = randomFrom(internalCluster().getInstance(Transport.class).boundAddress().boundAddresses());
|
||||
transportClient.addTransportAddress(transportAddress);
|
||||
|
@ -78,12 +78,12 @@ public class SslIntegrationTests extends SecurityIntegTestCase {
|
|||
|
||||
// no SSL exception as this is the exception is returned when connecting
|
||||
public void testThatTransportClientUsingSSLv3ProtocolIsRejected() {
|
||||
try(TransportClient transportClient = TransportClient.builder().addPlugin(XPackPlugin.class).settings(Settings.builder()
|
||||
try(TransportClient transportClient = new XPackTransportClient(Settings.builder()
|
||||
.put(transportClientSettings())
|
||||
.put("node.name", "programmatic_transport_client")
|
||||
.put("cluster.name", internalCluster().getClusterName())
|
||||
.putArray("xpack.security.ssl.supported_protocols", new String[]{"SSLv3"})
|
||||
.build()).build()) {
|
||||
.build())) {
|
||||
|
||||
TransportAddress transportAddress = randomFrom(internalCluster().getInstance(Transport.class).boundAddress().boundAddresses());
|
||||
transportClient.addTransportAddress(transportAddress);
|
||||
|
|
|
@ -12,10 +12,9 @@ import org.elasticsearch.common.transport.InetSocketTransportAddress;
|
|||
import org.elasticsearch.common.transport.TransportAddress;
|
||||
import org.elasticsearch.xpack.security.Security;
|
||||
import org.elasticsearch.xpack.security.transport.netty3.SecurityNetty3Transport;
|
||||
import org.elasticsearch.xpack.security.transport.netty3.SecurityNetty3Transport;
|
||||
import org.elasticsearch.test.SecurityIntegTestCase;
|
||||
import org.elasticsearch.transport.Transport;
|
||||
import org.elasticsearch.xpack.XPackPlugin;
|
||||
import org.elasticsearch.xpack.XPackTransportClient;
|
||||
import org.junit.BeforeClass;
|
||||
|
||||
import java.net.InetAddress;
|
||||
|
@ -105,9 +104,7 @@ public class SslMultiPortTests extends SecurityIntegTestCase {
|
|||
.put("cluster.name", internalCluster().getClusterName())
|
||||
.put(additionalSettings)
|
||||
.build();
|
||||
return TransportClient.builder().settings(settings)
|
||||
.addPlugin(XPackPlugin.class)
|
||||
.build();
|
||||
return new XPackTransportClient(settings);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -240,7 +237,7 @@ public class SslMultiPortTests extends SecurityIntegTestCase {
|
|||
.put(SecurityNetty3Transport.SSL_SETTING.getKey(), false)
|
||||
.put("cluster.name", internalCluster().getClusterName())
|
||||
.build();
|
||||
try (TransportClient transportClient = TransportClient.builder().settings(settings).addPlugin(XPackPlugin.class).build()) {
|
||||
try (TransportClient transportClient = new XPackTransportClient(settings)) {
|
||||
transportClient.addTransportAddress(new InetSocketTransportAddress(InetAddress.getLoopbackAddress(), getProfilePort("no_ssl")));
|
||||
assertGreenClusterState(transportClient);
|
||||
}
|
||||
|
@ -255,7 +252,7 @@ public class SslMultiPortTests extends SecurityIntegTestCase {
|
|||
.put(Security.USER_SETTING.getKey(), DEFAULT_USER_NAME + ":" + DEFAULT_PASSWORD)
|
||||
.put("cluster.name", internalCluster().getClusterName())
|
||||
.build();
|
||||
try (TransportClient transportClient = TransportClient.builder().addPlugin(XPackPlugin.class).settings(settings).build()) {
|
||||
try (TransportClient transportClient = new XPackTransportClient(settings)) {
|
||||
transportClient.addTransportAddress(randomFrom(internalCluster().getInstance(Transport.class).boundAddress().boundAddresses()));
|
||||
assertGreenClusterState(transportClient);
|
||||
fail("Expected NoNodeAvailableException");
|
||||
|
@ -273,7 +270,7 @@ public class SslMultiPortTests extends SecurityIntegTestCase {
|
|||
.put(Security.USER_SETTING.getKey(), DEFAULT_USER_NAME + ":" + DEFAULT_PASSWORD)
|
||||
.put("cluster.name", internalCluster().getClusterName())
|
||||
.build();
|
||||
try (TransportClient transportClient = TransportClient.builder().addPlugin(XPackPlugin.class).settings(settings).build()) {
|
||||
try (TransportClient transportClient = new XPackTransportClient(settings)) {
|
||||
transportClient.addTransportAddress(new InetSocketTransportAddress(InetAddress.getLoopbackAddress(), getProfilePort("client")));
|
||||
assertGreenClusterState(transportClient);
|
||||
fail("Expected NoNodeAvailableException");
|
||||
|
@ -291,7 +288,7 @@ public class SslMultiPortTests extends SecurityIntegTestCase {
|
|||
.put(Security.USER_SETTING.getKey(), DEFAULT_USER_NAME + ":" + DEFAULT_PASSWORD)
|
||||
.put("cluster.name", internalCluster().getClusterName())
|
||||
.build();
|
||||
try (TransportClient transportClient = TransportClient.builder().addPlugin(XPackPlugin.class).settings(settings).build()) {
|
||||
try (TransportClient transportClient = new XPackTransportClient(settings)) {
|
||||
transportClient.addTransportAddress(new InetSocketTransportAddress(InetAddress.getLoopbackAddress(),
|
||||
getProfilePort("no_client_auth")));
|
||||
assertGreenClusterState(transportClient);
|
||||
|
@ -315,7 +312,7 @@ public class SslMultiPortTests extends SecurityIntegTestCase {
|
|||
getDataPath("/org/elasticsearch/xpack/security/transport/ssl/certs/simple/truststore-testnode-only.jks"))
|
||||
.put("xpack.security.ssl.truststore.password", "truststore-testnode-only")
|
||||
.build();
|
||||
try (TransportClient transportClient = TransportClient.builder().settings(settings).addPlugin(XPackPlugin.class).build()) {
|
||||
try (TransportClient transportClient = new XPackTransportClient(settings)) {
|
||||
transportClient.addTransportAddress(new InetSocketTransportAddress(InetAddress.getLoopbackAddress(),
|
||||
getProfilePort("no_client_auth")));
|
||||
assertGreenClusterState(transportClient);
|
||||
|
@ -337,7 +334,7 @@ public class SslMultiPortTests extends SecurityIntegTestCase {
|
|||
getDataPath("/org/elasticsearch/xpack/security/transport/ssl/certs/simple/truststore-testnode-only.jks"))
|
||||
.put("xpack.security.ssl.truststore.password", "truststore-testnode-only")
|
||||
.build();
|
||||
try (TransportClient transportClient = TransportClient.builder().addPlugin(XPackPlugin.class).settings(settings).build()) {
|
||||
try (TransportClient transportClient = new XPackTransportClient(settings)) {
|
||||
transportClient.addTransportAddress(new InetSocketTransportAddress(InetAddress.getLoopbackAddress(), getProfilePort("client")));
|
||||
assertGreenClusterState(transportClient);
|
||||
fail("Expected NoNodeAvailableException");
|
||||
|
@ -361,7 +358,7 @@ public class SslMultiPortTests extends SecurityIntegTestCase {
|
|||
getDataPath("/org/elasticsearch/xpack/security/transport/ssl/certs/simple/truststore-testnode-only.jks"))
|
||||
.put("xpack.security.ssl.truststore.password", "truststore-testnode-only")
|
||||
.build();
|
||||
try (TransportClient transportClient = TransportClient.builder().addPlugin(XPackPlugin.class).settings(settings).build()) {
|
||||
try (TransportClient transportClient = new XPackTransportClient(settings)) {
|
||||
transportClient.addTransportAddress(randomFrom(internalCluster().getInstance(Transport.class).boundAddress().boundAddresses()));
|
||||
assertGreenClusterState(transportClient);
|
||||
fail("Expected NoNodeAvailableException");
|
||||
|
@ -384,7 +381,7 @@ public class SslMultiPortTests extends SecurityIntegTestCase {
|
|||
getDataPath("/org/elasticsearch/xpack/security/transport/ssl/certs/simple/truststore-testnode-only.jks"))
|
||||
.put("xpack.security.ssl.truststore.password", "truststore-testnode-only")
|
||||
.build();
|
||||
try (TransportClient transportClient = TransportClient.builder().addPlugin(XPackPlugin.class).settings(settings).build()) {
|
||||
try (TransportClient transportClient = new XPackTransportClient(settings)) {
|
||||
transportClient.addTransportAddress(new InetSocketTransportAddress(InetAddress.getLoopbackAddress(), getProfilePort("no_ssl")));
|
||||
assertGreenClusterState(transportClient);
|
||||
fail("Expected NoNodeAvailableException");
|
||||
|
@ -404,7 +401,7 @@ public class SslMultiPortTests extends SecurityIntegTestCase {
|
|||
.put("cluster.name", internalCluster().getClusterName())
|
||||
.put(SecurityNetty3Transport.SSL_SETTING.getKey(), true)
|
||||
.build();
|
||||
try (TransportClient transportClient = TransportClient.builder().addPlugin(XPackPlugin.class).settings(settings).build()) {
|
||||
try (TransportClient transportClient = new XPackTransportClient(settings)) {
|
||||
transportClient.addTransportAddress(randomFrom(internalCluster().getInstance(Transport.class).boundAddress().boundAddresses()));
|
||||
assertGreenClusterState(transportClient);
|
||||
fail("Expected NoNodeAvailableException");
|
||||
|
@ -424,7 +421,7 @@ public class SslMultiPortTests extends SecurityIntegTestCase {
|
|||
.put("cluster.name", internalCluster().getClusterName())
|
||||
.put(SecurityNetty3Transport.SSL_SETTING.getKey(), true)
|
||||
.build();
|
||||
try (TransportClient transportClient = TransportClient.builder().addPlugin(XPackPlugin.class).settings(settings).build()) {
|
||||
try (TransportClient transportClient = new XPackTransportClient(settings)) {
|
||||
transportClient.addTransportAddress(new InetSocketTransportAddress(InetAddress.getLoopbackAddress(), getProfilePort("client")));
|
||||
assertGreenClusterState(transportClient);
|
||||
fail("Expected NoNodeAvailableException");
|
||||
|
@ -444,7 +441,7 @@ public class SslMultiPortTests extends SecurityIntegTestCase {
|
|||
.put("cluster.name", internalCluster().getClusterName())
|
||||
.put(SecurityNetty3Transport.SSL_SETTING.getKey(), true)
|
||||
.build();
|
||||
try (TransportClient transportClient = TransportClient.builder().addPlugin(XPackPlugin.class).settings(settings).build()) {
|
||||
try (TransportClient transportClient = new XPackTransportClient(settings)) {
|
||||
transportClient.addTransportAddress(new InetSocketTransportAddress(InetAddress.getLoopbackAddress(),
|
||||
getProfilePort("no_client_auth")));
|
||||
assertGreenClusterState(transportClient);
|
||||
|
@ -465,7 +462,7 @@ public class SslMultiPortTests extends SecurityIntegTestCase {
|
|||
.put("cluster.name", internalCluster().getClusterName())
|
||||
.put(SecurityNetty3Transport.SSL_SETTING.getKey(), true)
|
||||
.build();
|
||||
try (TransportClient transportClient = TransportClient.builder().addPlugin(XPackPlugin.class).settings(settings).build()) {
|
||||
try (TransportClient transportClient = new XPackTransportClient(settings)) {
|
||||
transportClient.addTransportAddress(new InetSocketTransportAddress(InetAddress.getLoopbackAddress(), getProfilePort("no_ssl")));
|
||||
assertGreenClusterState(transportClient);
|
||||
fail("Expected NoNodeAvailableException");
|
||||
|
|
|
@ -7,10 +7,9 @@ package org.elasticsearch.xpack.security.user;
|
|||
|
||||
import org.elasticsearch.ElasticsearchSecurityException;
|
||||
import org.elasticsearch.common.Strings;
|
||||
import org.elasticsearch.common.io.stream.ByteBufferStreamInput;
|
||||
import org.elasticsearch.common.io.stream.BytesStreamOutput;
|
||||
import org.elasticsearch.xpack.security.support.MetadataUtils;
|
||||
import org.elasticsearch.test.ESTestCase;
|
||||
import org.elasticsearch.xpack.XPackClient;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
|
@ -127,7 +126,7 @@ public class UserTests extends ESTestCase {
|
|||
|
||||
public void testReservedMetadata() throws Exception {
|
||||
Map<String, Object> validMetadata = Collections.singletonMap("foo", "bar");
|
||||
Map<String, Object> invalidMetadata = Collections.singletonMap(User.RESERVED_PREFIX + "foo", "bar");
|
||||
Map<String, Object> invalidMetadata = Collections.singletonMap(MetadataUtils.RESERVED_PREFIX + "foo", "bar");
|
||||
|
||||
IllegalArgumentException exception = expectThrows(IllegalArgumentException.class, () ->
|
||||
new User("john", Strings.EMPTY_ARRAY, "John Doe", "john@doe.com", invalidMetadata));
|
||||
|
|
|
@ -0,0 +1,11 @@
|
|||
instances:
|
||||
- name: "node1"
|
||||
ip:
|
||||
- "127.0.0.1"
|
||||
dns: "localhost"
|
||||
- name: "node2"
|
||||
ip: "::1"
|
||||
- name: "node3"
|
||||
- name: "CN=different value"
|
||||
dns:
|
||||
- "node4.mydomain.com"
|
|
@ -38,6 +38,10 @@ teardown:
|
|||
body: >
|
||||
{
|
||||
"cluster": ["all"],
|
||||
"metadata": {
|
||||
"key1" : "val1",
|
||||
"key2" : "val2"
|
||||
},
|
||||
"indices": [
|
||||
{
|
||||
"names": "*",
|
||||
|
@ -73,5 +77,7 @@ teardown:
|
|||
xpack.security.get_role:
|
||||
name: "admin_role"
|
||||
- match: { admin_role.cluster.0: "all" }
|
||||
- match: { admin_role.metadata.key1: "val1" }
|
||||
- match: { admin_role.metadata.key2: "val2" }
|
||||
- match: { admin_role.indices.0.names.0: "*" }
|
||||
- match: { admin_role.indices.0.privileges.0: "all" }
|
||||
|
|
|
@ -36,7 +36,6 @@ import org.elasticsearch.common.settings.Settings;
|
|||
import org.elasticsearch.env.Environment;
|
||||
import org.elasticsearch.index.IndexModule;
|
||||
import org.elasticsearch.license.plugin.Licensing;
|
||||
import org.elasticsearch.license.plugin.core.LicensesService;
|
||||
import org.elasticsearch.plugins.ActionPlugin;
|
||||
import org.elasticsearch.plugins.Plugin;
|
||||
import org.elasticsearch.plugins.ScriptPlugin;
|
||||
|
@ -49,7 +48,6 @@ import org.elasticsearch.xpack.action.TransportXPackInfoAction;
|
|||
import org.elasticsearch.xpack.action.TransportXPackUsageAction;
|
||||
import org.elasticsearch.xpack.action.XPackInfoAction;
|
||||
import org.elasticsearch.xpack.action.XPackUsageAction;
|
||||
import org.elasticsearch.xpack.common.ScriptServiceProxy;
|
||||
import org.elasticsearch.xpack.common.http.HttpClient;
|
||||
import org.elasticsearch.xpack.common.http.HttpRequestTemplate;
|
||||
import org.elasticsearch.xpack.common.http.auth.HttpAuthFactory;
|
||||
|
@ -75,6 +73,7 @@ import org.elasticsearch.xpack.security.authc.support.UsernamePasswordToken;
|
|||
import org.elasticsearch.xpack.support.clock.Clock;
|
||||
import org.elasticsearch.xpack.support.clock.SystemClock;
|
||||
import org.elasticsearch.xpack.watcher.Watcher;
|
||||
import org.elasticsearch.xpack.watcher.support.WatcherScript;
|
||||
|
||||
public class XPackPlugin extends Plugin implements ScriptPlugin, ActionPlugin {
|
||||
|
||||
|
@ -231,7 +230,7 @@ public class XPackPlugin extends Plugin implements ScriptPlugin, ActionPlugin {
|
|||
|
||||
@Override
|
||||
public ScriptContext.Plugin getCustomScriptContexts() {
|
||||
return ScriptServiceProxy.INSTANCE;
|
||||
return WatcherScript.CTX_PLUGIN;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -0,0 +1,30 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
package org.elasticsearch.xpack;
|
||||
|
||||
import org.elasticsearch.client.transport.TransportClient;
|
||||
import org.elasticsearch.common.settings.Settings;
|
||||
import org.elasticsearch.plugins.Plugin;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
|
||||
/**
|
||||
* TransportClient.Builder that installs the XPackPlugin by default.
|
||||
*/
|
||||
@SuppressWarnings({"unchecked","varargs"})
|
||||
public class XPackTransportClient extends TransportClient {
|
||||
|
||||
@SafeVarargs
|
||||
public XPackTransportClient(Settings settings, Class<? extends Plugin>... plugins) {
|
||||
this(settings, Arrays.asList(plugins));
|
||||
}
|
||||
|
||||
public XPackTransportClient(Settings settings, Collection<Class<? extends Plugin>> plugins) {
|
||||
super(settings, Settings.EMPTY, addPlugins(plugins, XPackPlugin.class));
|
||||
}
|
||||
}
|
|
@ -1,68 +0,0 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
package org.elasticsearch.xpack.common;
|
||||
|
||||
import org.elasticsearch.cluster.service.ClusterService;
|
||||
import org.elasticsearch.common.inject.Inject;
|
||||
import org.elasticsearch.script.CompiledScript;
|
||||
import org.elasticsearch.script.ExecutableScript;
|
||||
import org.elasticsearch.script.ScriptContext;
|
||||
import org.elasticsearch.script.ScriptService;
|
||||
import org.elasticsearch.xpack.security.SecurityContext;
|
||||
import org.elasticsearch.xpack.security.user.XPackUser;
|
||||
import org.elasticsearch.xpack.watcher.support.Script;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import static java.util.Collections.emptyMap;
|
||||
|
||||
/**
|
||||
* Wraps {@link ScriptService} but ensure that all scripts are run or compiled as {@link XPackUser}.
|
||||
*/
|
||||
public class ScriptServiceProxy {
|
||||
|
||||
private final ScriptService service;
|
||||
private final SecurityContext securityContext;
|
||||
|
||||
@Inject
|
||||
public ScriptServiceProxy(ScriptService service, SecurityContext securityContext) {
|
||||
this.service = service;
|
||||
this.securityContext = securityContext;
|
||||
}
|
||||
|
||||
public CompiledScript compile(Script script) {
|
||||
return compile(new org.elasticsearch.script.Script(script.script(), script.type(), script.lang(), script.params()), emptyMap());
|
||||
}
|
||||
|
||||
public CompiledScript compile(org.elasticsearch.script.Script script, Map<String, String> compileParams) {
|
||||
return securityContext.executeAs(XPackUser.INSTANCE, () ->
|
||||
service.compile(script, WatcherScriptContext.CTX, compileParams));
|
||||
}
|
||||
|
||||
public ExecutableScript executable(CompiledScript compiledScript, Map<String, Object> vars) {
|
||||
return securityContext.executeAs(XPackUser.INSTANCE, () ->
|
||||
service.executable(compiledScript, vars));
|
||||
}
|
||||
|
||||
public static final ScriptContext.Plugin INSTANCE = new ScriptContext.Plugin("xpack", "watch");
|
||||
|
||||
private static class WatcherScriptContext implements ScriptContext {
|
||||
|
||||
public static final ScriptContext CTX = new WatcherScriptContext();
|
||||
|
||||
@Override
|
||||
public String getKey() {
|
||||
return INSTANCE.getKey();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Factory helper method for testing.
|
||||
*/
|
||||
public static ScriptServiceProxy of(ScriptService service) {
|
||||
return new ScriptServiceProxy(service, SecurityContext.Insecure.INSTANCE);
|
||||
}
|
||||
}
|
|
@ -12,8 +12,9 @@ import org.elasticsearch.common.settings.Settings;
|
|||
import org.elasticsearch.common.xcontent.XContentType;
|
||||
import org.elasticsearch.script.CompiledScript;
|
||||
import org.elasticsearch.script.ExecutableScript;
|
||||
import org.elasticsearch.script.Template;
|
||||
import org.elasticsearch.xpack.common.ScriptServiceProxy;
|
||||
import org.elasticsearch.script.Script;
|
||||
import org.elasticsearch.script.ScriptService;
|
||||
import org.elasticsearch.xpack.watcher.support.WatcherScript;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
|
@ -21,10 +22,10 @@ import java.util.Map;
|
|||
|
||||
public class DefaultTextTemplateEngine extends AbstractComponent implements TextTemplateEngine {
|
||||
|
||||
private final ScriptServiceProxy service;
|
||||
private final ScriptService service;
|
||||
|
||||
@Inject
|
||||
public DefaultTextTemplateEngine(Settings settings, ScriptServiceProxy service) {
|
||||
public DefaultTextTemplateEngine(Settings settings, ScriptService service) {
|
||||
super(settings);
|
||||
this.service = service;
|
||||
}
|
||||
|
@ -39,7 +40,7 @@ public class DefaultTextTemplateEngine extends AbstractComponent implements Text
|
|||
Map<String, String> compileParams = compileParams(contentType);
|
||||
template = trimContentType(template);
|
||||
|
||||
CompiledScript compiledScript = service.compile(convert(template, model), compileParams);
|
||||
CompiledScript compiledScript = service.compile(convert(template, model), WatcherScript.CTX, compileParams);
|
||||
ExecutableScript executable = service.executable(compiledScript, model);
|
||||
Object result = executable.run();
|
||||
if (result instanceof BytesReference) {
|
||||
|
@ -79,11 +80,11 @@ public class DefaultTextTemplateEngine extends AbstractComponent implements Text
|
|||
return null;
|
||||
}
|
||||
|
||||
private Template convert(TextTemplate textTemplate, Map<String, Object> model) {
|
||||
private Script convert(TextTemplate textTemplate, Map<String, Object> model) {
|
||||
Map<String, Object> mergedModel = new HashMap<>();
|
||||
mergedModel.putAll(textTemplate.getParams());
|
||||
mergedModel.putAll(model);
|
||||
return new Template(textTemplate.getTemplate(), textTemplate.getType(), "mustache", textTemplate.getContentType(), mergedModel);
|
||||
return new Script(textTemplate.getTemplate(), textTemplate.getType(), "mustache", mergedModel, textTemplate.getContentType());
|
||||
}
|
||||
|
||||
private Map<String, String> compileParams(XContentType contentType) {
|
||||
|
|
|
@ -6,7 +6,6 @@
|
|||
package org.elasticsearch.xpack.common.text;
|
||||
|
||||
import org.elasticsearch.common.inject.AbstractModule;
|
||||
import org.elasticsearch.xpack.common.ScriptServiceProxy;
|
||||
|
||||
/**
|
||||
*
|
||||
|
@ -15,7 +14,6 @@ public class TextTemplateModule extends AbstractModule {
|
|||
|
||||
@Override
|
||||
protected void configure() {
|
||||
bind(ScriptServiceProxy.class).asEagerSingleton();
|
||||
bind(DefaultTextTemplateEngine.class).asEagerSingleton();
|
||||
bind(TextTemplateEngine.class).to(DefaultTextTemplateEngine.class);
|
||||
}
|
||||
|
|
|
@ -14,10 +14,11 @@ import org.elasticsearch.common.xcontent.XContentParser;
|
|||
import org.elasticsearch.common.xcontent.json.JsonXContent;
|
||||
import org.elasticsearch.script.CompiledScript;
|
||||
import org.elasticsearch.script.ExecutableScript;
|
||||
import org.elasticsearch.script.ScriptService;
|
||||
import org.elasticsearch.script.ScriptService.ScriptType;
|
||||
import org.elasticsearch.script.Template;
|
||||
import org.elasticsearch.script.Script;
|
||||
import org.elasticsearch.test.ESTestCase;
|
||||
import org.elasticsearch.xpack.common.ScriptServiceProxy;
|
||||
import org.elasticsearch.xpack.watcher.support.WatcherScript;
|
||||
import org.junit.Before;
|
||||
|
||||
import java.util.Collections;
|
||||
|
@ -37,16 +38,16 @@ import static org.mockito.Mockito.when;
|
|||
|
||||
public class TextTemplateTests extends ESTestCase {
|
||||
|
||||
private ScriptServiceProxy proxy;
|
||||
private ScriptService service;
|
||||
private TextTemplateEngine engine;
|
||||
private ExecutableScript script;
|
||||
private final String lang = "mustache";
|
||||
|
||||
@Before
|
||||
public void init() throws Exception {
|
||||
proxy = mock(ScriptServiceProxy.class);
|
||||
service = mock(ScriptService.class);
|
||||
script = mock(ExecutableScript.class);
|
||||
engine = new DefaultTextTemplateEngine(Settings.EMPTY, proxy);
|
||||
engine = new DefaultTextTemplateEngine(Settings.EMPTY, service);
|
||||
}
|
||||
|
||||
public void testRender() throws Exception {
|
||||
|
@ -59,9 +60,9 @@ public class TextTemplateTests extends ESTestCase {
|
|||
ScriptType type = randomFrom(ScriptType.values());
|
||||
|
||||
CompiledScript compiledScript = mock(CompiledScript.class);
|
||||
when(proxy.compile(new Template(templateText, type, lang, null, merged), Collections.singletonMap("content_type", "text/plain")))
|
||||
.thenReturn(compiledScript);
|
||||
when(proxy.executable(compiledScript, model)).thenReturn(script);
|
||||
when(service.compile(new Script(templateText, type, lang, merged), WatcherScript.CTX,
|
||||
Collections.singletonMap("content_type", "text/plain"))).thenReturn(compiledScript);
|
||||
when(service.executable(compiledScript, model)).thenReturn(script);
|
||||
when(script.run()).thenReturn("rendered_text");
|
||||
|
||||
TextTemplate template = templateBuilder(type, templateText).params(params).build();
|
||||
|
@ -75,10 +76,9 @@ public class TextTemplateTests extends ESTestCase {
|
|||
ScriptType scriptType = randomFrom(ScriptType.values());
|
||||
|
||||
CompiledScript compiledScript = mock(CompiledScript.class);
|
||||
when(proxy.compile(new Template(templateText, scriptType, lang, null, model),
|
||||
Collections.singletonMap("content_type", "text/plain")))
|
||||
.thenReturn(compiledScript);
|
||||
when(proxy.executable(compiledScript, model)).thenReturn(script);
|
||||
when(service.compile(new Script(templateText, scriptType, lang, model), WatcherScript.CTX,
|
||||
Collections.singletonMap("content_type", "text/plain"))).thenReturn(compiledScript);
|
||||
when(service.executable(compiledScript, model)).thenReturn(script);
|
||||
when(script.run()).thenReturn("rendered_text");
|
||||
|
||||
TextTemplate template = templateBuilder(scriptType, templateText).params(params).build();
|
||||
|
@ -90,10 +90,9 @@ public class TextTemplateTests extends ESTestCase {
|
|||
Map<String, Object> model = singletonMap("key", "model_val");
|
||||
|
||||
CompiledScript compiledScript = mock(CompiledScript.class);
|
||||
when(proxy.compile(new Template(templateText, ScriptType.INLINE, lang, null, model),
|
||||
Collections.singletonMap("content_type", "text/plain")))
|
||||
.thenReturn(compiledScript);
|
||||
when(proxy.executable(compiledScript, model)).thenReturn(script);
|
||||
when(service.compile(new Script(templateText, ScriptType.INLINE, lang, model), WatcherScript.CTX,
|
||||
Collections.singletonMap("content_type", "text/plain"))).thenReturn(compiledScript);
|
||||
when(service.executable(compiledScript, model)).thenReturn(script);
|
||||
when(script.run()).thenReturn("rendered_text");
|
||||
|
||||
TextTemplate template = new TextTemplate(templateText);
|
||||
|
|
|
@ -59,15 +59,17 @@ if [ -z "$ES_CLASSPATH" ]; then
|
|||
exit 1
|
||||
fi
|
||||
|
||||
# Try to read package config files
|
||||
if [ -f "/etc/sysconfig/elasticsearch" ]; then
|
||||
CONF_DIR=/etc/elasticsearch
|
||||
if [ -z "$CONF_DIR" ]; then
|
||||
# Try to read package config files
|
||||
if [ -f "/etc/sysconfig/elasticsearch" ]; then
|
||||
CONF_DIR=/etc/elasticsearch
|
||||
|
||||
. "/etc/sysconfig/elasticsearch"
|
||||
elif [ -f "/etc/default/elasticsearch" ]; then
|
||||
CONF_DIR=/etc/elasticsearch
|
||||
. "/etc/sysconfig/elasticsearch"
|
||||
elif [ -f "/etc/default/elasticsearch" ]; then
|
||||
CONF_DIR=/etc/elasticsearch
|
||||
|
||||
. "/etc/default/elasticsearch"
|
||||
. "/etc/default/elasticsearch"
|
||||
fi
|
||||
fi
|
||||
|
||||
export HOSTNAME=`hostname -s`
|
||||
|
|
|
@ -10,7 +10,7 @@ import org.elasticsearch.xpack.watcher.condition.compare.CompareCondition;
|
|||
import org.elasticsearch.xpack.watcher.condition.compare.array.ArrayCompareCondition;
|
||||
import org.elasticsearch.xpack.watcher.condition.never.NeverCondition;
|
||||
import org.elasticsearch.xpack.watcher.condition.script.ScriptCondition;
|
||||
import org.elasticsearch.xpack.watcher.support.Script;
|
||||
import org.elasticsearch.xpack.watcher.support.WatcherScript;
|
||||
|
||||
/**
|
||||
*
|
||||
|
@ -29,14 +29,14 @@ public final class ConditionBuilders {
|
|||
}
|
||||
|
||||
public static ScriptCondition.Builder scriptCondition(String script) {
|
||||
return scriptCondition(Script.inline(script));
|
||||
return scriptCondition(WatcherScript.inline(script));
|
||||
}
|
||||
|
||||
public static ScriptCondition.Builder scriptCondition(Script.Builder script) {
|
||||
public static ScriptCondition.Builder scriptCondition(WatcherScript.Builder script) {
|
||||
return scriptCondition(script.build());
|
||||
}
|
||||
|
||||
public static ScriptCondition.Builder scriptCondition(Script script) {
|
||||
public static ScriptCondition.Builder scriptCondition(WatcherScript script) {
|
||||
return ScriptCondition.builder(script);
|
||||
}
|
||||
|
||||
|
|
|
@ -8,11 +8,14 @@ package org.elasticsearch.xpack.watcher.condition.script;
|
|||
import org.elasticsearch.common.logging.ESLogger;
|
||||
import org.elasticsearch.script.CompiledScript;
|
||||
import org.elasticsearch.script.ExecutableScript;
|
||||
import org.elasticsearch.xpack.common.ScriptServiceProxy;
|
||||
import org.elasticsearch.script.Script;
|
||||
import org.elasticsearch.script.ScriptService;
|
||||
import org.elasticsearch.xpack.watcher.condition.ExecutableCondition;
|
||||
import org.elasticsearch.xpack.watcher.execution.WatchExecutionContext;
|
||||
import org.elasticsearch.xpack.watcher.support.Variables;
|
||||
import org.elasticsearch.xpack.watcher.support.WatcherScript;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
|
||||
import static org.elasticsearch.xpack.watcher.support.Exceptions.invalidScript;
|
||||
|
@ -22,14 +25,16 @@ import static org.elasticsearch.xpack.watcher.support.Exceptions.invalidScript;
|
|||
*/
|
||||
public class ExecutableScriptCondition extends ExecutableCondition<ScriptCondition, ScriptCondition.Result> {
|
||||
|
||||
private final ScriptServiceProxy scriptService;
|
||||
private final ScriptService scriptService;
|
||||
private final CompiledScript compiledScript;
|
||||
|
||||
public ExecutableScriptCondition(ScriptCondition condition, ESLogger logger, ScriptServiceProxy scriptService) {
|
||||
public ExecutableScriptCondition(ScriptCondition condition, ESLogger logger, ScriptService scriptService) {
|
||||
super(condition, logger);
|
||||
this.scriptService = scriptService;
|
||||
try {
|
||||
compiledScript = scriptService.compile(condition.script);
|
||||
Script script = new Script(condition.script.script(), condition.script.type(),
|
||||
condition.script.lang(), condition.script.params());
|
||||
compiledScript = scriptService.compile(script, WatcherScript.CTX, Collections.emptyMap());
|
||||
} catch (Exception e) {
|
||||
throw invalidScript("failed to compile script [{}] with lang [{}] of type [{}]", e, condition.script.script(),
|
||||
condition.script.lang(), condition.script.type(), e);
|
||||
|
|
|
@ -9,7 +9,7 @@ import org.elasticsearch.ElasticsearchParseException;
|
|||
import org.elasticsearch.common.xcontent.XContentBuilder;
|
||||
import org.elasticsearch.common.xcontent.XContentParser;
|
||||
import org.elasticsearch.xpack.watcher.condition.Condition;
|
||||
import org.elasticsearch.xpack.watcher.support.Script;
|
||||
import org.elasticsearch.xpack.watcher.support.WatcherScript;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
|
@ -20,9 +20,9 @@ public class ScriptCondition implements Condition {
|
|||
|
||||
public static final String TYPE = "script";
|
||||
|
||||
final Script script;
|
||||
final WatcherScript script;
|
||||
|
||||
public ScriptCondition(Script script) {
|
||||
public ScriptCondition(WatcherScript script) {
|
||||
this.script = script;
|
||||
}
|
||||
|
||||
|
@ -31,7 +31,7 @@ public class ScriptCondition implements Condition {
|
|||
return TYPE;
|
||||
}
|
||||
|
||||
public Script getScript() {
|
||||
public WatcherScript getScript() {
|
||||
return script;
|
||||
}
|
||||
|
||||
|
@ -57,7 +57,7 @@ public class ScriptCondition implements Condition {
|
|||
|
||||
public static ScriptCondition parse(String watchId, XContentParser parser) throws IOException {
|
||||
try {
|
||||
Script script = Script.parse(parser);
|
||||
WatcherScript script = WatcherScript.parse(parser);
|
||||
return new ScriptCondition(script);
|
||||
} catch (ElasticsearchParseException pe) {
|
||||
throw new ElasticsearchParseException("could not parse [{}] condition for watch [{}]. failed to parse script", pe, TYPE,
|
||||
|
@ -65,7 +65,7 @@ public class ScriptCondition implements Condition {
|
|||
}
|
||||
}
|
||||
|
||||
public static Builder builder(Script script) {
|
||||
public static Builder builder(WatcherScript script) {
|
||||
return new Builder(script);
|
||||
}
|
||||
|
||||
|
@ -86,9 +86,9 @@ public class ScriptCondition implements Condition {
|
|||
|
||||
public static class Builder implements Condition.Builder<ScriptCondition> {
|
||||
|
||||
private final Script script;
|
||||
private final WatcherScript script;
|
||||
|
||||
private Builder(Script script) {
|
||||
private Builder(WatcherScript script) {
|
||||
this.script = script;
|
||||
}
|
||||
|
||||
|
|
|
@ -9,8 +9,8 @@ import org.elasticsearch.common.inject.Inject;
|
|||
import org.elasticsearch.common.logging.Loggers;
|
||||
import org.elasticsearch.common.settings.Settings;
|
||||
import org.elasticsearch.common.xcontent.XContentParser;
|
||||
import org.elasticsearch.script.ScriptService;
|
||||
import org.elasticsearch.xpack.watcher.condition.ConditionFactory;
|
||||
import org.elasticsearch.xpack.common.ScriptServiceProxy;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
|
@ -19,10 +19,10 @@ import java.io.IOException;
|
|||
*/
|
||||
public class ScriptConditionFactory extends ConditionFactory<ScriptCondition, ScriptCondition.Result, ExecutableScriptCondition> {
|
||||
|
||||
private final ScriptServiceProxy scriptService;
|
||||
private final ScriptService scriptService;
|
||||
|
||||
@Inject
|
||||
public ScriptConditionFactory(Settings settings, ScriptServiceProxy service) {
|
||||
public ScriptConditionFactory(Settings settings, ScriptService service) {
|
||||
super(Loggers.getLogger(ExecutableScriptCondition.class, settings));
|
||||
scriptService = service;
|
||||
}
|
||||
|
|
|
@ -11,14 +11,14 @@ import org.elasticsearch.common.logging.ESLogger;
|
|||
import org.elasticsearch.common.xcontent.XContentFactory;
|
||||
import org.elasticsearch.common.xcontent.XContentParser;
|
||||
import org.elasticsearch.common.xcontent.XContentType;
|
||||
import org.elasticsearch.xpack.watcher.execution.WatchExecutionContext;
|
||||
import org.elasticsearch.xpack.watcher.input.ExecutableInput;
|
||||
import org.elasticsearch.xpack.watcher.support.Variables;
|
||||
import org.elasticsearch.xpack.watcher.support.XContentFilterKeysUtils;
|
||||
import org.elasticsearch.xpack.common.http.HttpClient;
|
||||
import org.elasticsearch.xpack.common.http.HttpRequest;
|
||||
import org.elasticsearch.xpack.common.http.HttpResponse;
|
||||
import org.elasticsearch.xpack.common.text.TextTemplateEngine;
|
||||
import org.elasticsearch.xpack.watcher.execution.WatchExecutionContext;
|
||||
import org.elasticsearch.xpack.watcher.input.ExecutableInput;
|
||||
import org.elasticsearch.xpack.watcher.support.Variables;
|
||||
import org.elasticsearch.xpack.watcher.support.XContentFilterKeysUtils;
|
||||
import org.elasticsearch.xpack.watcher.watch.Payload;
|
||||
|
||||
import java.util.HashMap;
|
||||
|
@ -59,42 +59,35 @@ public class ExecutableHttpInput extends ExecutableInput<HttpInput, HttpInput.Re
|
|||
return new HttpInput.Result(request, -1, payload);
|
||||
}
|
||||
|
||||
XContentType contentType = response.xContentType();
|
||||
if (input.getExpectedResponseXContentType() != null) {
|
||||
if (contentType != input.getExpectedResponseXContentType().contentType()) {
|
||||
logger.warn("[{}] [{}] input expected content type [{}] but read [{}] from headers", type(), ctx.id(),
|
||||
input.getExpectedResponseXContentType(), contentType);
|
||||
}
|
||||
if (contentType == null) {
|
||||
contentType = input.getExpectedResponseXContentType().contentType();
|
||||
}
|
||||
final XContentType contentType;
|
||||
XContentType responseContentType = response.xContentType();
|
||||
if (input.getExpectedResponseXContentType() == null) {
|
||||
//Attempt to auto detect content type, if not set in response
|
||||
contentType = responseContentType != null ? responseContentType : XContentFactory.xContentType(response.body());
|
||||
} else {
|
||||
//Attempt to auto detect content type
|
||||
if (contentType == null) {
|
||||
contentType = XContentFactory.xContentType(response.body());
|
||||
}
|
||||
}
|
||||
|
||||
XContentParser parser = null;
|
||||
if (contentType != null) {
|
||||
try {
|
||||
parser = contentType.xContent().createParser(response.body());
|
||||
} catch (Exception e) {
|
||||
throw new ElasticsearchParseException("could not parse response body [{}] it does not appear to be [{}]", type(), ctx.id(),
|
||||
response.body().utf8ToString(), contentType.shortName());
|
||||
contentType = input.getExpectedResponseXContentType().contentType();
|
||||
if (responseContentType != contentType) {
|
||||
logger.warn("[{}] [{}] input expected content type [{}] but read [{}] from headers, using expected one", type(), ctx.id(),
|
||||
input.getExpectedResponseXContentType(), responseContentType);
|
||||
}
|
||||
}
|
||||
|
||||
final Map<String, Object> payloadMap = new HashMap<>();
|
||||
if (input.getExtractKeys() != null) {
|
||||
payloadMap.putAll(XContentFilterKeysUtils.filterMapOrdered(input.getExtractKeys(), parser));
|
||||
} else {
|
||||
if (parser != null) {
|
||||
payloadMap.putAll(parser.mapOrdered());
|
||||
} else {
|
||||
payloadMap.put("_value", response.body().utf8ToString());
|
||||
if (contentType != null) {
|
||||
try (XContentParser parser = contentType.xContent().createParser(response.body())) {
|
||||
if (input.getExtractKeys() != null) {
|
||||
payloadMap.putAll(XContentFilterKeysUtils.filterMapOrdered(input.getExtractKeys(), parser));
|
||||
} else {
|
||||
payloadMap.putAll(parser.mapOrdered());
|
||||
}
|
||||
} catch (Exception e) {
|
||||
throw new ElasticsearchParseException("could not parse response body [{}] it does not appear to be [{}]", type(), ctx.id(),
|
||||
response.body().utf8ToString(), contentType.shortName());
|
||||
}
|
||||
} else {
|
||||
payloadMap.put("_value", response.body().utf8ToString());
|
||||
}
|
||||
|
||||
if (headers.size() > 0) {
|
||||
payloadMap.put("_headers", headers);
|
||||
}
|
||||
|
|
|
@ -13,9 +13,9 @@ import org.elasticsearch.common.unit.TimeValue;
|
|||
import org.elasticsearch.common.xcontent.XContentParser;
|
||||
import org.elasticsearch.index.query.QueryParseContext;
|
||||
import org.elasticsearch.indices.query.IndicesQueriesRegistry;
|
||||
import org.elasticsearch.script.ScriptService;
|
||||
import org.elasticsearch.search.aggregations.AggregatorParsers;
|
||||
import org.elasticsearch.search.suggest.Suggesters;
|
||||
import org.elasticsearch.xpack.common.ScriptServiceProxy;
|
||||
import org.elasticsearch.xpack.security.InternalClient;
|
||||
import org.elasticsearch.xpack.watcher.input.InputFactory;
|
||||
import org.elasticsearch.xpack.watcher.input.simple.ExecutableSimpleInput;
|
||||
|
@ -39,12 +39,12 @@ public class SearchInputFactory extends InputFactory<SearchInput, SearchInput.Re
|
|||
|
||||
@Inject
|
||||
public SearchInputFactory(Settings settings, InternalClient client, IndicesQueriesRegistry queryRegistry,
|
||||
AggregatorParsers aggParsers, Suggesters suggesters, ScriptServiceProxy scriptService) {
|
||||
AggregatorParsers aggParsers, Suggesters suggesters, ScriptService scriptService) {
|
||||
this(settings, new WatcherClientProxy(settings, client), queryRegistry, aggParsers, suggesters, scriptService);
|
||||
}
|
||||
|
||||
public SearchInputFactory(Settings settings, WatcherClientProxy client, IndicesQueriesRegistry queryRegistry,
|
||||
AggregatorParsers aggParsers, Suggesters suggesters, ScriptServiceProxy scriptService) {
|
||||
AggregatorParsers aggParsers, Suggesters suggesters, ScriptService scriptService) {
|
||||
super(Loggers.getLogger(ExecutableSimpleInput.class, settings));
|
||||
this.parseFieldMatcher = new ParseFieldMatcher(settings);
|
||||
this.client = client;
|
||||
|
|
|
@ -12,6 +12,8 @@ import org.elasticsearch.common.ParseFieldMatcher;
|
|||
import org.elasticsearch.common.xcontent.ToXContent;
|
||||
import org.elasticsearch.common.xcontent.XContentBuilder;
|
||||
import org.elasticsearch.common.xcontent.XContentParser;
|
||||
import org.elasticsearch.script.Script;
|
||||
import org.elasticsearch.script.ScriptContext;
|
||||
import org.elasticsearch.script.ScriptService.ScriptType;
|
||||
import org.elasticsearch.script.ScriptSettings;
|
||||
|
||||
|
@ -22,20 +24,22 @@ import java.util.Map;
|
|||
/**
|
||||
*
|
||||
*/
|
||||
public class Script implements ToXContent {
|
||||
public class WatcherScript implements ToXContent {
|
||||
|
||||
public static final String DEFAULT_LANG = ScriptSettings.DEFAULT_LANG;
|
||||
public static final ScriptContext.Plugin CTX_PLUGIN = new ScriptContext.Plugin("xpack", "watch");
|
||||
public static final ScriptContext CTX = new WatcherScriptContext();
|
||||
|
||||
private final String script;
|
||||
@Nullable private final ScriptType type;
|
||||
@Nullable private final String lang;
|
||||
@Nullable private final Map<String, Object> params;
|
||||
|
||||
Script(String script) {
|
||||
WatcherScript(String script) {
|
||||
this(script, null, null, null);
|
||||
}
|
||||
|
||||
Script(String script, @Nullable ScriptType type, @Nullable String lang, @Nullable Map<String, Object> params) {
|
||||
WatcherScript(String script, @Nullable ScriptType type, @Nullable String lang, @Nullable Map<String, Object> params) {
|
||||
this.script = script;
|
||||
this.type = type;
|
||||
this.lang = lang;
|
||||
|
@ -58,12 +62,16 @@ public class Script implements ToXContent {
|
|||
return params != null ? params : Collections.emptyMap();
|
||||
}
|
||||
|
||||
public Script toScript() {
|
||||
return new Script(script(), type(), lang(), params());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
|
||||
Script script1 = (Script) o;
|
||||
WatcherScript script1 = (WatcherScript) o;
|
||||
|
||||
if (!script.equals(script1.script)) return false;
|
||||
if (type != script1.type) return false;
|
||||
|
@ -106,10 +114,10 @@ public class Script implements ToXContent {
|
|||
return builder.endObject();
|
||||
}
|
||||
|
||||
public static Script parse(XContentParser parser) throws IOException {
|
||||
public static WatcherScript parse(XContentParser parser) throws IOException {
|
||||
XContentParser.Token token = parser.currentToken();
|
||||
if (token == XContentParser.Token.VALUE_STRING) {
|
||||
return new Script(parser.text());
|
||||
return new WatcherScript(parser.text());
|
||||
}
|
||||
if (token != XContentParser.Token.START_OBJECT) {
|
||||
throw new ElasticsearchParseException("expected a string value or an object, but found [{}] instead", token);
|
||||
|
@ -170,7 +178,7 @@ public class Script implements ToXContent {
|
|||
Field.INLINE.getPreferredName(), Field.FILE.getPreferredName(), Field.ID.getPreferredName());
|
||||
}
|
||||
assert type != null : "if script is not null, type should definitely not be null";
|
||||
return new Script(script, type, lang, params);
|
||||
return new WatcherScript(script, type, lang, params);
|
||||
}
|
||||
|
||||
public static Builder.Inline inline(String script) {
|
||||
|
@ -211,7 +219,7 @@ public class Script implements ToXContent {
|
|||
return (B) this;
|
||||
}
|
||||
|
||||
public abstract Script build();
|
||||
public abstract WatcherScript build();
|
||||
|
||||
public static class Inline extends Builder<Inline> {
|
||||
|
||||
|
@ -220,8 +228,8 @@ public class Script implements ToXContent {
|
|||
}
|
||||
|
||||
@Override
|
||||
public Script build() {
|
||||
return new Script(script, type, lang, params);
|
||||
public WatcherScript build() {
|
||||
return new WatcherScript(script, type, lang, params);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -232,8 +240,8 @@ public class Script implements ToXContent {
|
|||
}
|
||||
|
||||
@Override
|
||||
public Script build() {
|
||||
return new Script(script, type, lang, params);
|
||||
public WatcherScript build() {
|
||||
return new WatcherScript(script, type, lang, params);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -244,8 +252,8 @@ public class Script implements ToXContent {
|
|||
}
|
||||
|
||||
@Override
|
||||
public Script build() {
|
||||
return new Script(script, type, lang, params);
|
||||
public WatcherScript build() {
|
||||
return new WatcherScript(script, type, lang, params);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -256,8 +264,8 @@ public class Script implements ToXContent {
|
|||
}
|
||||
|
||||
@Override
|
||||
public Script build() {
|
||||
return new Script(script, type, lang, params);
|
||||
public WatcherScript build() {
|
||||
return new WatcherScript(script, type, lang, params);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -271,4 +279,10 @@ public class Script implements ToXContent {
|
|||
}
|
||||
|
||||
|
||||
private static class WatcherScriptContext implements ScriptContext {
|
||||
@Override
|
||||
public String getKey() {
|
||||
return CTX_PLUGIN.getKey();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -20,7 +20,7 @@ import org.elasticsearch.index.query.QueryParseContext;
|
|||
import org.elasticsearch.search.aggregations.AggregatorParsers;
|
||||
import org.elasticsearch.search.builder.SearchSourceBuilder;
|
||||
import org.elasticsearch.search.suggest.Suggesters;
|
||||
import org.elasticsearch.xpack.watcher.support.Script;
|
||||
import org.elasticsearch.xpack.watcher.support.WatcherScript;
|
||||
import org.elasticsearch.xpack.watcher.support.SearchRequestEquivalence;
|
||||
|
||||
import java.io.IOException;
|
||||
|
@ -36,9 +36,9 @@ import java.util.Objects;
|
|||
public class WatcherSearchTemplateRequest implements ToXContent {
|
||||
|
||||
private final SearchRequest request;
|
||||
@Nullable private final Script template;
|
||||
@Nullable private final WatcherScript template;
|
||||
|
||||
public WatcherSearchTemplateRequest(SearchRequest searchRequest, @Nullable Script template) {
|
||||
public WatcherSearchTemplateRequest(SearchRequest searchRequest, @Nullable WatcherScript template) {
|
||||
this.request = Objects.requireNonNull(searchRequest);
|
||||
this.template = template;
|
||||
}
|
||||
|
@ -51,7 +51,7 @@ public class WatcherSearchTemplateRequest implements ToXContent {
|
|||
return request;
|
||||
}
|
||||
|
||||
public Script getTemplate() {
|
||||
public WatcherScript getTemplate() {
|
||||
return template;
|
||||
}
|
||||
|
||||
|
@ -105,7 +105,7 @@ public class WatcherSearchTemplateRequest implements ToXContent {
|
|||
throws IOException {
|
||||
IndicesOptions indicesOptions = DEFAULT_INDICES_OPTIONS;
|
||||
SearchRequest searchRequest = new SearchRequest();
|
||||
Script template = null;
|
||||
WatcherScript template = null;
|
||||
|
||||
XContentParser.Token token;
|
||||
String currentFieldName = null;
|
||||
|
@ -190,7 +190,7 @@ public class WatcherSearchTemplateRequest implements ToXContent {
|
|||
indicesOptions = IndicesOptions.fromOptions(ignoreUnavailable, allowNoIndices, expandOpen, expandClosed,
|
||||
DEFAULT_INDICES_OPTIONS);
|
||||
} else if (ParseFieldMatcher.STRICT.match(currentFieldName, TEMPLATE_FIELD)) {
|
||||
template = Script.parse(parser);
|
||||
template = WatcherScript.parse(parser);
|
||||
} else {
|
||||
throw new ElasticsearchParseException("could not read search request. unexpected object field [" +
|
||||
currentFieldName + "]");
|
||||
|
|
|
@ -17,17 +17,18 @@ import org.elasticsearch.common.xcontent.XContentFactory;
|
|||
import org.elasticsearch.common.xcontent.XContentParser;
|
||||
import org.elasticsearch.index.query.QueryParseContext;
|
||||
import org.elasticsearch.indices.query.IndicesQueriesRegistry;
|
||||
import org.elasticsearch.script.CompiledScript;
|
||||
import org.elasticsearch.script.ScriptService;
|
||||
import org.elasticsearch.search.aggregations.AggregatorParsers;
|
||||
import org.elasticsearch.search.builder.SearchSourceBuilder;
|
||||
import org.elasticsearch.search.suggest.Suggesters;
|
||||
import org.elasticsearch.xpack.common.ScriptServiceProxy;
|
||||
import org.elasticsearch.xpack.watcher.execution.WatchExecutionContext;
|
||||
import org.elasticsearch.xpack.watcher.support.Script;
|
||||
import org.elasticsearch.xpack.watcher.support.WatcherScript;
|
||||
import org.elasticsearch.xpack.watcher.support.Variables;
|
||||
import org.elasticsearch.xpack.watcher.watch.Payload;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
|
||||
import static org.elasticsearch.common.xcontent.XContentFactory.jsonBuilder;
|
||||
|
@ -39,17 +40,17 @@ public class WatcherSearchTemplateService extends AbstractComponent {
|
|||
|
||||
private static final String DEFAULT_LANG = "mustache";
|
||||
|
||||
private final ScriptServiceProxy scriptService;
|
||||
private final ScriptService scriptService;
|
||||
private final ParseFieldMatcher parseFieldMatcher;
|
||||
private final IndicesQueriesRegistry queryRegistry;
|
||||
private final AggregatorParsers aggsParsers;
|
||||
private final Suggesters suggesters;
|
||||
|
||||
@Inject
|
||||
public WatcherSearchTemplateService(Settings settings, ScriptServiceProxy scriptServiceProxy,
|
||||
public WatcherSearchTemplateService(Settings settings, ScriptService scriptService,
|
||||
IndicesQueriesRegistry queryRegistry, AggregatorParsers aggregatorParsers, Suggesters suggesters) {
|
||||
super(settings);
|
||||
this.scriptService = scriptServiceProxy;
|
||||
this.scriptService = scriptService;
|
||||
this.queryRegistry = queryRegistry;
|
||||
this.aggsParsers = aggregatorParsers;
|
||||
this.suggesters = suggesters;
|
||||
|
@ -65,7 +66,7 @@ public class WatcherSearchTemplateService extends AbstractComponent {
|
|||
.indices(prototype.getRequest().indices())
|
||||
.types(prototype.getRequest().types());
|
||||
|
||||
Script template = null;
|
||||
WatcherScript template = null;
|
||||
|
||||
// Due the inconsistency with templates in ES 1.x, we maintain our own template format.
|
||||
// This template format we use now, will become the template structure in ES 2.0
|
||||
|
@ -76,26 +77,26 @@ public class WatcherSearchTemplateService extends AbstractComponent {
|
|||
if (prototype.getRequest().source() != null) {
|
||||
try (XContentBuilder builder = jsonBuilder()) {
|
||||
prototype.getRequest().source().toXContent(builder, ToXContent.EMPTY_PARAMS);
|
||||
template = Script.inline(builder.string()).lang(DEFAULT_LANG).params(watcherContextParams).build();
|
||||
template = WatcherScript.inline(builder.string()).lang(DEFAULT_LANG).params(watcherContextParams).build();
|
||||
}
|
||||
|
||||
} else if (prototype.getTemplate() != null) {
|
||||
// Here we convert watcher template into a ES core templates. Due to the different format we use, we
|
||||
// convert to the template format used in ES core
|
||||
Script templatePrototype = prototype.getTemplate();
|
||||
WatcherScript templatePrototype = prototype.getTemplate();
|
||||
if (templatePrototype.params() != null) {
|
||||
watcherContextParams.putAll(templatePrototype.params());
|
||||
}
|
||||
|
||||
Script.Builder builder;
|
||||
WatcherScript.Builder builder;
|
||||
if (templatePrototype.type() == ScriptService.ScriptType.INLINE) {
|
||||
builder = Script.inline(templatePrototype.script());
|
||||
builder = WatcherScript.inline(templatePrototype.script());
|
||||
} else if (templatePrototype.type() == ScriptService.ScriptType.FILE) {
|
||||
builder = Script.file(templatePrototype.script());
|
||||
builder = WatcherScript.file(templatePrototype.script());
|
||||
} else if (templatePrototype.type() == ScriptService.ScriptType.STORED) {
|
||||
builder = Script.indexed(templatePrototype.script());
|
||||
builder = WatcherScript.indexed(templatePrototype.script());
|
||||
} else {
|
||||
builder = Script.defaultType(templatePrototype.script());
|
||||
builder = WatcherScript.defaultType(templatePrototype.script());
|
||||
}
|
||||
template = builder.lang(templatePrototype.lang()).params(watcherContextParams).build();
|
||||
}
|
||||
|
@ -105,16 +106,16 @@ public class WatcherSearchTemplateService extends AbstractComponent {
|
|||
}
|
||||
|
||||
/**
|
||||
* Converts a {@link Script} to a {@link org.elasticsearch.search.builder.SearchSourceBuilder}
|
||||
* Converts a {@link WatcherScript} to a {@link org.elasticsearch.search.builder.SearchSourceBuilder}
|
||||
*/
|
||||
private SearchSourceBuilder convert(Script template) throws IOException {
|
||||
private SearchSourceBuilder convert(WatcherScript template) throws IOException {
|
||||
SearchSourceBuilder sourceBuilder = SearchSourceBuilder.searchSource();
|
||||
if (template == null) {
|
||||
// falling back to an empty body
|
||||
return sourceBuilder;
|
||||
}
|
||||
|
||||
BytesReference source = (BytesReference) scriptService.executable(scriptService.compile(template), template.params()).run();
|
||||
CompiledScript compiledScript = scriptService.compile(template.toScript(), WatcherScript.CTX, Collections.emptyMap());
|
||||
BytesReference source = (BytesReference) scriptService.executable(compiledScript, template.params()).run();
|
||||
if (source != null && source.length() > 0) {
|
||||
try (XContentParser parser = XContentFactory.xContent(source).createParser(source)) {
|
||||
sourceBuilder.parseXContent(new QueryParseContext(queryRegistry, parser, parseFieldMatcher), aggsParsers, suggesters);
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
package org.elasticsearch.xpack.watcher.transform;
|
||||
|
||||
import org.elasticsearch.action.search.SearchRequest;
|
||||
import org.elasticsearch.xpack.watcher.support.Script;
|
||||
import org.elasticsearch.xpack.watcher.support.WatcherScript;
|
||||
import org.elasticsearch.xpack.watcher.support.search.WatcherSearchTemplateRequest;
|
||||
import org.elasticsearch.xpack.watcher.transform.chain.ChainTransform;
|
||||
import org.elasticsearch.xpack.watcher.transform.script.ScriptTransform;
|
||||
|
@ -29,14 +29,14 @@ public final class TransformBuilders {
|
|||
}
|
||||
|
||||
public static ScriptTransform.Builder scriptTransform(String script) {
|
||||
return scriptTransform(Script.inline(script));
|
||||
return scriptTransform(WatcherScript.inline(script));
|
||||
}
|
||||
|
||||
public static ScriptTransform.Builder scriptTransform(Script.Builder script) {
|
||||
public static ScriptTransform.Builder scriptTransform(WatcherScript.Builder script) {
|
||||
return scriptTransform(script.build());
|
||||
}
|
||||
|
||||
public static ScriptTransform.Builder scriptTransform(Script script) {
|
||||
public static ScriptTransform.Builder scriptTransform(WatcherScript script) {
|
||||
return ScriptTransform.builder(script);
|
||||
}
|
||||
|
||||
|
|
|
@ -8,13 +8,14 @@ package org.elasticsearch.xpack.watcher.transform.script;
|
|||
import org.elasticsearch.common.logging.ESLogger;
|
||||
import org.elasticsearch.script.CompiledScript;
|
||||
import org.elasticsearch.script.ExecutableScript;
|
||||
import org.elasticsearch.script.ScriptService;
|
||||
import org.elasticsearch.xpack.watcher.execution.WatchExecutionContext;
|
||||
import org.elasticsearch.xpack.watcher.support.Script;
|
||||
import org.elasticsearch.xpack.common.ScriptServiceProxy;
|
||||
import org.elasticsearch.xpack.watcher.support.WatcherScript;
|
||||
import org.elasticsearch.xpack.watcher.transform.ExecutableTransform;
|
||||
import org.elasticsearch.xpack.watcher.watch.Payload;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
|
@ -26,15 +27,15 @@ import static org.elasticsearch.xpack.watcher.support.Variables.createCtxModel;
|
|||
*/
|
||||
public class ExecutableScriptTransform extends ExecutableTransform<ScriptTransform, ScriptTransform.Result> {
|
||||
|
||||
private final ScriptServiceProxy scriptService;
|
||||
private final ScriptService scriptService;
|
||||
private final CompiledScript compiledScript;
|
||||
|
||||
public ExecutableScriptTransform(ScriptTransform transform, ESLogger logger, ScriptServiceProxy scriptService) {
|
||||
public ExecutableScriptTransform(ScriptTransform transform, ESLogger logger, ScriptService scriptService) {
|
||||
super(transform, logger);
|
||||
this.scriptService = scriptService;
|
||||
Script script = transform.getScript();
|
||||
WatcherScript script = transform.getScript();
|
||||
try {
|
||||
compiledScript = scriptService.compile(script);
|
||||
compiledScript = scriptService.compile(script.toScript(), WatcherScript.CTX, Collections.emptyMap());
|
||||
} catch (Exception e) {
|
||||
throw invalidScript("failed to compile script [{}] with lang [{}] of type [{}]", e, script.script(), script.lang(),
|
||||
script.type(), e);
|
||||
|
@ -53,7 +54,7 @@ public class ExecutableScriptTransform extends ExecutableTransform<ScriptTransfo
|
|||
|
||||
|
||||
ScriptTransform.Result doExecute(WatchExecutionContext ctx, Payload payload) throws IOException {
|
||||
Script script = transform.getScript();
|
||||
WatcherScript script = transform.getScript();
|
||||
Map<String, Object> model = new HashMap<>();
|
||||
model.putAll(script.params());
|
||||
model.putAll(createCtxModel(ctx, payload));
|
||||
|
|
|
@ -8,7 +8,7 @@ package org.elasticsearch.xpack.watcher.transform.script;
|
|||
import org.elasticsearch.ElasticsearchParseException;
|
||||
import org.elasticsearch.common.xcontent.XContentBuilder;
|
||||
import org.elasticsearch.common.xcontent.XContentParser;
|
||||
import org.elasticsearch.xpack.watcher.support.Script;
|
||||
import org.elasticsearch.xpack.watcher.support.WatcherScript;
|
||||
import org.elasticsearch.xpack.watcher.transform.Transform;
|
||||
import org.elasticsearch.xpack.watcher.watch.Payload;
|
||||
|
||||
|
@ -21,9 +21,9 @@ public class ScriptTransform implements Transform {
|
|||
|
||||
public static final String TYPE = "script";
|
||||
|
||||
private final Script script;
|
||||
private final WatcherScript script;
|
||||
|
||||
public ScriptTransform(Script script) {
|
||||
public ScriptTransform(WatcherScript script) {
|
||||
this.script = script;
|
||||
}
|
||||
|
||||
|
@ -32,7 +32,7 @@ public class ScriptTransform implements Transform {
|
|||
return TYPE;
|
||||
}
|
||||
|
||||
public Script getScript() {
|
||||
public WatcherScript getScript() {
|
||||
return script;
|
||||
}
|
||||
|
||||
|
@ -58,7 +58,7 @@ public class ScriptTransform implements Transform {
|
|||
|
||||
public static ScriptTransform parse(String watchId, XContentParser parser) throws IOException {
|
||||
try {
|
||||
Script script = Script.parse(parser);
|
||||
WatcherScript script = WatcherScript.parse(parser);
|
||||
return new ScriptTransform(script);
|
||||
} catch (ElasticsearchParseException pe) {
|
||||
throw new ElasticsearchParseException("could not parse [{}] transform for watch [{}]. failed to parse script", pe, TYPE,
|
||||
|
@ -66,7 +66,7 @@ public class ScriptTransform implements Transform {
|
|||
}
|
||||
}
|
||||
|
||||
public static Builder builder(Script script) {
|
||||
public static Builder builder(WatcherScript script) {
|
||||
return new Builder(script);
|
||||
}
|
||||
|
||||
|
@ -88,9 +88,9 @@ public class ScriptTransform implements Transform {
|
|||
|
||||
public static class Builder implements Transform.Builder<ScriptTransform> {
|
||||
|
||||
private final Script script;
|
||||
private final WatcherScript script;
|
||||
|
||||
public Builder(Script script) {
|
||||
public Builder(WatcherScript script) {
|
||||
this.script = script;
|
||||
}
|
||||
|
||||
|
|
|
@ -9,7 +9,7 @@ import org.elasticsearch.common.inject.Inject;
|
|||
import org.elasticsearch.common.logging.Loggers;
|
||||
import org.elasticsearch.common.settings.Settings;
|
||||
import org.elasticsearch.common.xcontent.XContentParser;
|
||||
import org.elasticsearch.xpack.common.ScriptServiceProxy;
|
||||
import org.elasticsearch.script.ScriptService;
|
||||
import org.elasticsearch.xpack.watcher.transform.TransformFactory;
|
||||
|
||||
import java.io.IOException;
|
||||
|
@ -19,10 +19,10 @@ import java.io.IOException;
|
|||
*/
|
||||
public class ScriptTransformFactory extends TransformFactory<ScriptTransform, ScriptTransform.Result, ExecutableScriptTransform> {
|
||||
|
||||
private final ScriptServiceProxy scriptService;
|
||||
private final ScriptService scriptService;
|
||||
|
||||
@Inject
|
||||
public ScriptTransformFactory(Settings settings, ScriptServiceProxy scriptService) {
|
||||
public ScriptTransformFactory(Settings settings, ScriptService scriptService) {
|
||||
super(Loggers.getLogger(ExecutableScriptTransform.class, settings));
|
||||
this.scriptService = scriptService;
|
||||
}
|
||||
|
|
|
@ -15,10 +15,10 @@ import org.elasticsearch.common.unit.TimeValue;
|
|||
import org.elasticsearch.common.xcontent.XContentParser;
|
||||
import org.elasticsearch.index.query.QueryParseContext;
|
||||
import org.elasticsearch.indices.query.IndicesQueriesRegistry;
|
||||
import org.elasticsearch.script.ScriptService;
|
||||
import org.elasticsearch.search.aggregations.AggregatorParsers;
|
||||
import org.elasticsearch.search.suggest.Suggesters;
|
||||
import org.elasticsearch.xpack.security.InternalClient;
|
||||
import org.elasticsearch.xpack.common.ScriptServiceProxy;
|
||||
import org.elasticsearch.xpack.watcher.support.init.proxy.WatcherClientProxy;
|
||||
import org.elasticsearch.xpack.watcher.support.search.WatcherSearchTemplateService;
|
||||
import org.elasticsearch.xpack.watcher.transform.TransformFactory;
|
||||
|
@ -38,11 +38,11 @@ public class SearchTransformFactory extends TransformFactory<SearchTransform, Se
|
|||
|
||||
@Inject
|
||||
public SearchTransformFactory(Settings settings, InternalClient client, IndicesQueriesRegistry queryRegistry,
|
||||
AggregatorParsers aggParsers, Suggesters suggesters, ScriptServiceProxy scriptService) {
|
||||
AggregatorParsers aggParsers, Suggesters suggesters, ScriptService scriptService) {
|
||||
this(settings, new WatcherClientProxy(settings, client), queryRegistry, aggParsers, suggesters, scriptService);
|
||||
}
|
||||
public SearchTransformFactory(Settings settings, WatcherClientProxy client, IndicesQueriesRegistry queryRegistry,
|
||||
AggregatorParsers aggParsers, Suggesters suggesters, ScriptServiceProxy scriptService) {
|
||||
AggregatorParsers aggParsers, Suggesters suggesters, ScriptService scriptService) {
|
||||
super(Loggers.getLogger(ExecutableSearchTransform.class, settings));
|
||||
this.client = client;
|
||||
this.parseFieldMatcher = new ParseFieldMatcher(settings);
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue