Merge branch 'master' into reporting-controls

Original commit: elastic/x-pack-elasticsearch@4f0e7501ce
This commit is contained in:
Joe Fleming 2016-06-27 16:39:10 -07:00
commit a66c0cf463
86 changed files with 1680 additions and 1681 deletions

View File

@ -6,9 +6,11 @@
package org.elasticsearch.xpack.security.audit;
import com.carrotsearch.hppc.cursors.ObjectCursor;
import org.apache.http.message.BasicHeader;
import org.elasticsearch.action.admin.indices.template.delete.DeleteIndexTemplateResponse;
import org.elasticsearch.action.admin.indices.template.get.GetIndexTemplatesResponse;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.Response;
import org.elasticsearch.cluster.ClusterState;
import org.elasticsearch.cluster.metadata.IndexTemplateMetaData;
import org.elasticsearch.common.settings.Settings;
@ -19,7 +21,6 @@ import org.elasticsearch.xpack.security.audit.index.IndexAuditTrail;
import org.elasticsearch.xpack.security.authc.support.SecuredString;
import org.elasticsearch.xpack.security.authc.support.UsernamePasswordToken;
import org.elasticsearch.test.ESIntegTestCase;
import org.elasticsearch.test.rest.client.http.HttpResponse;
import org.elasticsearch.xpack.XPackPlugin;
import java.util.Collection;
@ -35,12 +36,12 @@ public class IndexAuditIT extends ESIntegTestCase {
private static final String USER = "test_user";
private static final String PASS = "changeme";
public void testIndexAuditTrailWorking() throws Exception {
HttpResponse response = httpClient().path("/")
.addHeader("Authorization", UsernamePasswordToken.basicAuthHeaderValue(USER, new SecuredString(PASS.toCharArray())))
.execute();
assertThat(response.getStatusCode(), is(200));
public void testShieldIndexAuditTrailWorking() throws Exception {
try (Response response = getRestClient().performRequest("GET", "/", Collections.emptyMap(), null,
new BasicHeader(UsernamePasswordToken.BASIC_AUTH_HEADER,
UsernamePasswordToken.basicAuthHeaderValue(USER, new SecuredString(PASS.toCharArray()))))) {
assertThat(response.getStatusLine().getStatusCode(), is(200));
}
final AtomicReference<ClusterState> lastClusterState = new AtomicReference<>();
final AtomicBoolean indexExists = new AtomicBoolean(false);
boolean found = awaitBusy(() -> {

View File

@ -66,13 +66,10 @@ public class GroovyScriptConditionIT extends AbstractWatcherIntegrationTestCase
}
public void testGroovyClosureWithAggregations() throws Exception {
client().admin().indices().prepareCreate(".monitoring")
.addMapping("cluster_stats", "_timestamp", "enabled=true")
.get();
for (int seconds = 0; seconds < 60; seconds += 5) {
client().prepareIndex(".monitoring", "cluster_stats").setTimestamp("2005-01-01T00:00:" +
String.format(Locale.ROOT, "%02d", seconds)).setSource("status", randomFrom("green", "yellow")).get();
String timestamp = "2005-01-01T00:00:" + String.format(Locale.ROOT, "%02d", seconds);
client().prepareIndex(".monitoring", "cluster_stats")
.setSource("status", randomFrom("green", "yellow"), "@timestamp", timestamp).get();
}
refresh();
@ -80,7 +77,7 @@ public class GroovyScriptConditionIT extends AbstractWatcherIntegrationTestCase
SearchRequestBuilder builder = client().prepareSearch(".monitoring")
.addAggregation(
AggregationBuilders
.dateHistogram("minutes").field("_timestamp").interval(TimeUnit.MILLISECONDS.convert(5, TimeUnit.SECONDS))
.dateHistogram("minutes").field("@timestamp").interval(TimeUnit.MILLISECONDS.convert(5, TimeUnit.SECONDS))
.order(Histogram.Order.COUNT_DESC)
.subAggregation(AggregationBuilders.terms("status").field("status.keyword").size(3)));
SearchResponse unmetResponse = builder.get();
@ -101,8 +98,8 @@ public class GroovyScriptConditionIT extends AbstractWatcherIntegrationTestCase
assertFalse(condition.execute(unmetContext).met());
for (int seconds = 0; seconds < 60; seconds += 5) {
client().prepareIndex(".monitoring", "cluster_stats").setTimestamp("2005-01-01T00:01:" +
String.format(Locale.ROOT, "%02d", seconds)).setSource("status", randomFrom("red")).get();
String timestamp = "2005-01-01T00:01:" + String.format(Locale.ROOT, "%02d", seconds);
client().prepareIndex(".monitoring", "cluster_stats").setSource("status", randomFrom("red"), "@timestamp", timestamp).get();
}
refresh();

View File

@ -20,11 +20,11 @@ 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.common.ScriptServiceProxy;
import org.elasticsearch.xpack.watcher.test.AbstractWatcherIntegrationTestCase;
import org.elasticsearch.xpack.watcher.watch.Payload;
import org.junit.After;
@ -61,18 +61,14 @@ public class ScriptConditionSearchIT extends AbstractWatcherIntegrationTestCase
}
public void testExecuteWithAggs() throws Exception {
client().admin().indices().prepareCreate("my-index")
.addMapping("my-type", "_timestamp", "enabled=true")
.get();
client().prepareIndex("my-index", "my-type").setTimestamp("2005-01-01T00:00").setSource("{}").get();
client().prepareIndex("my-index", "my-type").setTimestamp("2005-01-01T00:10").setSource("{}").get();
client().prepareIndex("my-index", "my-type").setTimestamp("2005-01-01T00:20").setSource("{}").get();
client().prepareIndex("my-index", "my-type").setTimestamp("2005-01-01T00:30").setSource("{}").get();
client().prepareIndex("my-index", "my-type").setSource("@timestamp", "2005-01-01T00:00").get();
client().prepareIndex("my-index", "my-type").setSource("@timestamp", "2005-01-01T00:10").get();
client().prepareIndex("my-index", "my-type").setSource("@timestamp", "2005-01-01T00:20").get();
client().prepareIndex("my-index", "my-type").setSource("@timestamp", "2005-01-01T00:30").get();
refresh();
SearchResponse response = client().prepareSearch("my-index")
.addAggregation(AggregationBuilders.dateHistogram("rate").field("_timestamp")
.addAggregation(AggregationBuilders.dateHistogram("rate").field("@timestamp")
.dateHistogramInterval(DateHistogramInterval.HOUR).order(Histogram.Order.COUNT_DESC))
.get();
@ -83,10 +79,10 @@ public class ScriptConditionSearchIT extends AbstractWatcherIntegrationTestCase
WatchExecutionContext ctx = mockExecutionContext("_name", new Payload.XContent(response));
assertFalse(condition.execute(ctx).met());
client().prepareIndex("my-index", "my-type").setTimestamp("2005-01-01T00:40").setSource("{}").get();
client().prepareIndex("my-index", "my-type").setSource("@timestamp", "2005-01-01T00:40").get();
refresh();
response = client().prepareSearch("my-index").addAggregation(AggregationBuilders.dateHistogram("rate").field("_timestamp")
response = client().prepareSearch("my-index").addAggregation(AggregationBuilders.dateHistogram("rate").field("@timestamp")
.dateHistogramInterval(DateHistogramInterval.HOUR).order(Histogram.Order.COUNT_DESC))
.get();

View File

@ -12,31 +12,40 @@ import org.elasticsearch.common.bytes.BytesArray;
import org.elasticsearch.common.io.Streams;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.unit.TimeValue;
import org.elasticsearch.common.xcontent.ToXContent;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.common.xcontent.XContentParser;
import org.elasticsearch.common.xcontent.XContentType;
import org.elasticsearch.common.xcontent.json.JsonXContent;
import org.elasticsearch.common.xcontent.support.XContentMapValues;
import org.elasticsearch.indices.query.IndicesQueriesRegistry;
import org.elasticsearch.plugins.Plugin;
import org.elasticsearch.script.ScriptService.ScriptType;
import org.elasticsearch.script.Template;
import org.elasticsearch.plugins.ScriptPlugin;
import org.elasticsearch.script.ScriptContext;
import org.elasticsearch.script.ScriptService;
import org.elasticsearch.script.mustache.MustachePlugin;
import org.elasticsearch.search.aggregations.AggregatorParsers;
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;
import org.elasticsearch.xpack.watcher.condition.always.ExecutableAlwaysCondition;
import org.elasticsearch.xpack.watcher.execution.TriggeredExecutionContext;
import org.elasticsearch.xpack.watcher.execution.WatchExecutionContext;
import org.elasticsearch.xpack.watcher.input.Input;
import org.elasticsearch.xpack.watcher.input.search.ExecutableSearchInput;
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.init.proxy.WatcherClientProxy;
import org.elasticsearch.xpack.watcher.support.search.WatcherSearchTemplateRequest;
import org.elasticsearch.xpack.watcher.support.search.WatcherSearchTemplateService;
import org.elasticsearch.xpack.watcher.support.xcontent.XContentSource;
import org.elasticsearch.xpack.watcher.trigger.schedule.IntervalSchedule;
import org.elasticsearch.xpack.watcher.trigger.schedule.ScheduleTrigger;
import org.elasticsearch.xpack.watcher.trigger.schedule.ScheduleTriggerEvent;
@ -67,7 +76,6 @@ import static org.elasticsearch.test.ESIntegTestCase.Scope.SUITE;
import static org.elasticsearch.xpack.watcher.test.WatcherTestUtils.getRandomSupportedSearchType;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.notNullValue;
import static org.joda.time.DateTimeZone.UTC;
/**
@ -81,10 +89,11 @@ public class SearchInputIT extends ESIntegTestCase {
Collection<Class<? extends Plugin>> types = new ArrayList<>();
types.addAll(super.nodePlugins());
types.add(MustachePlugin.class);
types.add(CustomScriptContextPlugin.class);
return types;
}
private final static String TEMPLATE_QUERY = "{\"query\":{\"filtered\":{\"query\":{\"match\":{\"event_type\":{\"query\":\"a\"," +
private final static String TEMPLATE_QUERY = "{\"query\":{\"bool\":{\"must\":{\"match\":{\"event_type\":{\"query\":\"a\"," +
"\"type\":\"boolean\"}}},\"filter\":{\"range\":{\"_timestamp\":" +
"{\"from\":\"{{ctx.trigger.scheduled_time}}||-{{seconds_param}}\",\"to\":\"{{ctx.trigger.scheduled_time}}\"," +
"\"include_lower\":true,\"include_upper\":true}}}}}}";
@ -100,7 +109,6 @@ public class SearchInputIT extends ESIntegTestCase {
throw new RuntimeException("failed to create config dir");
}
String path = "/org/elasticsearch/xpack/watcher/input/search/config/scripts/test_disk_template.mustache";
try (InputStream stream = SearchInputIT.class.getResourceAsStream("/org/elasticsearch/xpack/watcher/input/search/config/scripts" +
"/test_disk_template.mustache");
OutputStream out = Files.newOutputStream(scriptPath.resolve("test_disk_template.mustache"))) {
@ -126,14 +134,15 @@ public class SearchInputIT extends ESIntegTestCase {
SearchSourceBuilder searchSourceBuilder = searchSource().query(
boolQuery().must(matchQuery("event_type", "a")).must(rangeQuery("_timestamp")
.from("{{ctx.trigger.scheduled_time}}||-30s").to("{{ctx.trigger.triggered_time}}")));
SearchRequest request = client()
SearchRequest searchRequest = client()
.prepareSearch()
.setSearchType(ExecutableSearchInput.DEFAULT_SEARCH_TYPE)
.request()
.source(searchSourceBuilder);
WatcherSearchTemplateRequest request = new WatcherSearchTemplateRequest(searchRequest);
ExecutableSearchInput searchInput = new ExecutableSearchInput(new SearchInput(request, null, null, null), logger,
WatcherClientProxy.of(client()), null);
WatcherClientProxy.of(client()), watcherSearchTemplateService(), null);
WatchExecutionContext ctx = new TriggeredExecutionContext(
new Watch("test-watch",
new ScheduleTrigger(new IntervalSchedule(new IntervalSchedule.Interval(1, IntervalSchedule.Interval.Unit.MINUTES))),
@ -149,21 +158,21 @@ public class SearchInputIT extends ESIntegTestCase {
timeValueSeconds(5));
SearchInput.Result result = searchInput.execute(ctx, new Payload.Simple());
assertThat((Integer) XContentMapValues.extractValue("hits.total", result.payload().data()), equalTo(0));
assertThat(XContentMapValues.extractValue("hits.total", result.payload().data()), equalTo(0));
assertNotNull(result.executedRequest());
assertEquals(result.executedRequest().searchType(), request.searchType());
assertArrayEquals(result.executedRequest().indices(), request.indices());
assertEquals(result.executedRequest().indicesOptions(), request.indicesOptions());
assertThat(result.status(), is(Input.Result.Status.SUCCESS));
assertEquals(result.executedRequest().searchType(), request.getRequest().searchType());
assertArrayEquals(result.executedRequest().indices(), request.getRequest().indices());
assertEquals(result.executedRequest().indicesOptions(), request.getRequest().indicesOptions());
XContentSource source = toXContentSource(result);
assertThat(source.getValue("query.bool.must.1.range._timestamp.from"), equalTo("1970-01-01T00:00:00.000Z||-30s"));
assertThat(source.getValue("query.bool.must.1.range._timestamp.to"), equalTo("1970-01-01T00:00:00.000Z"));
}
public void testSearchInlineTemplate() throws Exception {
WatchExecutionContext ctx = createContext();
final String expectedTemplateString = "{\"query\":{\"filtered\":{\"query\":{\"match\":{\"event_type\":{\"query\":\"a\","
+ "\"type\":\"boolean\"}}},\"filter\":{\"range\":{\"_timestamp\":"
+ "{\"from\":\"{{ctx.trigger.scheduled_time}}||-{{seconds_param}}\",\"to\":\"{{ctx.trigger.scheduled_time}}\","
+ "\"include_lower\":true,\"include_upper\":true}}}}}}";
Map<String, Object> triggerParams = new HashMap<String, Object>();
triggerParams.put("triggered_time", new DateTime(1970, 01, 01, 00, 01, 00, 000, ISOChronology.getInstanceUTC()));
triggerParams.put("scheduled_time", new DateTime(1970, 01, 01, 00, 01, 00, 000, ISOChronology.getInstanceUTC()));
@ -178,17 +187,28 @@ public class SearchInputIT extends ESIntegTestCase {
Map<String, Object> expectedParams = new HashMap<String, Object>();
expectedParams.put("seconds_param", "30s");
expectedParams.put("ctx", ctxParams);
Template expectedTemplate = new Template(expectedTemplateString, ScriptType.INLINE, null, XContentType.JSON, expectedParams);
Map<String, Object> params = new HashMap<>();
params.put("seconds_param", "30s");
Template template = new Template(TEMPLATE_QUERY, ScriptType.INLINE, null, XContentType.JSON, params);
Script template = Script.inline(TEMPLATE_QUERY).lang("mustache").params(params).build();
SearchRequest request = client().prepareSearch().setSearchType(ExecutableSearchInput.DEFAULT_SEARCH_TYPE)
.setIndices("test-search-index").setTemplate(template).request();
SearchRequest request = client().prepareSearch()
.setSearchType(ExecutableSearchInput.DEFAULT_SEARCH_TYPE)
.setIndices("test-search-index").request();
SearchInput.Result executedResult = executeSearchInput(request, ctx);
assertThat(executedResult.executedRequest().template(), equalTo(expectedTemplate));
SearchInput.Result executedResult = executeSearchInput(request, template, ctx);
assertNotNull(executedResult.executedRequest());
assertThat(executedResult.status(), is(Input.Result.Status.SUCCESS));
if (getNumShards("test-search-index").numPrimaries > 1) {
assertEquals(executedResult.executedRequest().searchType(), request.searchType());
}
assertArrayEquals(executedResult.executedRequest().indices(), request.indices());
assertEquals(executedResult.executedRequest().indicesOptions(), request.indicesOptions());
XContentSource source = toXContentSource(executedResult);
assertThat(source.getValue("query.bool.filter.0.range._timestamp.from"), equalTo("1970-01-01T00:01:00.000Z||-30s"));
assertThat(source.getValue("query.bool.filter.0.range._timestamp.to"), equalTo("1970-01-01T00:01:00.000Z"));
}
public void testSearchIndexedTemplate() throws Exception {
@ -204,17 +224,26 @@ public class SearchInputIT extends ESIntegTestCase {
Map<String, Object> params = new HashMap<>();
params.put("seconds_param", "30s");
Template template = new Template("test-template", ScriptType.STORED, null, null, params);
Script template = Script.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)
.setIndices("test-search-index").setTemplate(template).request();
.setIndices("test-search-index").request();
SearchInput.Result executedResult = executeSearchInput(request, template, ctx);
assertNotNull(executedResult.executedRequest());
assertThat(executedResult.status(), is(Input.Result.Status.SUCCESS));
if (getNumShards("test-search-index").numPrimaries > 1) {
assertEquals(executedResult.executedRequest().searchType(), request.searchType());
}
assertArrayEquals(executedResult.executedRequest().indices(), request.indices());
assertEquals(executedResult.executedRequest().indicesOptions(), request.indicesOptions());
XContentSource source = toXContentSource(executedResult);
assertThat(source.getValue("query.bool.filter.0.range._timestamp.from"), equalTo("1970-01-01T00:01:00.000Z||-30s"));
assertThat(source.getValue("query.bool.filter.0.range._timestamp.to"), equalTo("1970-01-01T00:01:00.000Z"));
SearchInput.Result executedResult = executeSearchInput(request, ctx);
Template resultTemplate = executedResult.executedRequest().template();
assertThat(resultTemplate, notNullValue());
assertThat(resultTemplate.getScript(), equalTo("test-template"));
assertThat(resultTemplate.getType(), equalTo(ScriptType.STORED));
}
public void testSearchOnDiskTemplate() throws Exception {
@ -223,15 +252,16 @@ public class SearchInputIT extends ESIntegTestCase {
Map<String, Object> params = new HashMap<>();
params.put("seconds_param", "30s");
Template template = new Template("test_disk_template", ScriptType.FILE, null, null, params);
Script template = Script.file("test_disk_template").lang("mustache").params(params).build();
SearchRequest request = client().prepareSearch().setSearchType(ExecutableSearchInput.DEFAULT_SEARCH_TYPE)
.setIndices("test-search-index").setTemplate(template).request();
.setIndices("test-search-index").request();
SearchInput.Result executedResult = executeSearchInput(request, ctx);
Template resultTemplate = executedResult.executedRequest().template();
assertThat(resultTemplate, notNullValue());
assertThat(resultTemplate.getScript(), equalTo("test_disk_template"));
assertThat(resultTemplate.getType(), equalTo(ScriptType.FILE));
SearchInput.Result executedResult = executeSearchInput(request, template, ctx);
assertNotNull(executedResult.executedRequest());
assertThat(executedResult.status(), is(Input.Result.Status.SUCCESS));
assertArrayEquals(executedResult.executedRequest().indices(), request.indices());
assertEquals(executedResult.executedRequest().indicesOptions(), request.indicesOptions());
}
public void testDifferentSearchType() throws Exception {
@ -241,14 +271,16 @@ public class SearchInputIT extends ESIntegTestCase {
);
SearchType searchType = getRandomSupportedSearchType();
SearchRequest request = client()
SearchRequest searchRequest = client()
.prepareSearch()
.setSearchType(searchType)
.request()
.source(searchSourceBuilder);
WatcherSearchTemplateRequest request = new WatcherSearchTemplateRequest(searchRequest);
ExecutableSearchInput searchInput = new ExecutableSearchInput(new SearchInput(request, null, null, null), logger,
WatcherClientProxy.of(client()), null);
WatcherClientProxy.of(client()), watcherSearchTemplateService(), null);
WatchExecutionContext ctx = new TriggeredExecutionContext(
new Watch("test-watch",
new ScheduleTrigger(new IntervalSchedule(new IntervalSchedule.Interval(1, IntervalSchedule.Interval.Unit.MINUTES))),
@ -264,15 +296,20 @@ public class SearchInputIT extends ESIntegTestCase {
timeValueSeconds(5));
SearchInput.Result result = searchInput.execute(ctx, new Payload.Simple());
assertThat((Integer) XContentMapValues.extractValue("hits.total", result.payload().data()), equalTo(0));
assertThat(XContentMapValues.extractValue("hits.total", result.payload().data()), equalTo(0));
assertNotNull(result.executedRequest());
assertThat(result.status(), is(Input.Result.Status.SUCCESS));
assertEquals(result.executedRequest().searchType(), searchType);
assertArrayEquals(result.executedRequest().indices(), request.indices());
assertEquals(result.executedRequest().indicesOptions(), request.indicesOptions());
assertArrayEquals(result.executedRequest().indices(), searchRequest.indices());
assertEquals(result.executedRequest().indicesOptions(), searchRequest.indicesOptions());
XContentSource source = toXContentSource(result);
assertThat(source.getValue("query.bool.must.1.range._timestamp.from"), equalTo("1970-01-01T00:00:00.000Z||-30s"));
assertThat(source.getValue("query.bool.must.1.range._timestamp.to"), equalTo("1970-01-01T00:00:00.000Z"));
}
public void testParserValid() throws Exception {
SearchRequest request = client().prepareSearch()
SearchRequest searchRequest = client().prepareSearch()
.setSearchType(ExecutableSearchInput.DEFAULT_SEARCH_TYPE)
.request()
.source(searchSource()
@ -280,13 +317,14 @@ public class SearchInputIT extends ESIntegTestCase {
.from("{{ctx.trigger.scheduled_time}}||-30s").to("{{ctx.trigger.triggered_time}}"))));
TimeValue timeout = randomBoolean() ? TimeValue.timeValueSeconds(randomInt(10)) : null;
XContentBuilder builder = jsonBuilder().value(new SearchInput(request, null, timeout, null));
XContentBuilder builder = jsonBuilder().value(
new SearchInput(new WatcherSearchTemplateRequest(searchRequest), null, timeout, null));
XContentParser parser = JsonXContent.jsonXContent.createParser(builder.bytes());
parser.nextToken();
IndicesQueriesRegistry indicesQueryRegistry = internalCluster().getInstance(IndicesQueriesRegistry.class);
SearchInputFactory factory = new SearchInputFactory(Settings.EMPTY, WatcherClientProxy.of(client()), indicesQueryRegistry,
null, null);
null, null, scriptService());
SearchInput searchInput = factory.parseInput("_id", parser);
assertEquals(SearchInput.TYPE, searchInput.type());
@ -309,15 +347,47 @@ public class SearchInputIT extends ESIntegTestCase {
timeValueSeconds(5));
}
private SearchInput.Result executeSearchInput(SearchRequest request, WatchExecutionContext ctx) throws IOException {
private SearchInput.Result executeSearchInput(SearchRequest request, Script template, WatchExecutionContext ctx) throws IOException {
createIndex("test-search-index");
ensureGreen("test-search-index");
SearchInput.Builder siBuilder = SearchInput.builder(request);
SearchInput.Builder siBuilder = SearchInput.builder(new WatcherSearchTemplateRequest(request, template));
SearchInput si = siBuilder.build();
ExecutableSearchInput searchInput = new ExecutableSearchInput(si, logger, WatcherClientProxy.of(client()), null);
ExecutableSearchInput searchInput = new ExecutableSearchInput(si, logger, WatcherClientProxy.of(client()),
watcherSearchTemplateService(), null);
return searchInput.execute(ctx, new Payload.Simple());
}
protected WatcherSearchTemplateService watcherSearchTemplateService() {
String master = internalCluster().getMasterName();
return new WatcherSearchTemplateService(internalCluster().clusterService(master).getSettings(),
ScriptServiceProxy.of(internalCluster().getInstance(ScriptService.class, master), internalCluster().clusterService(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), internalCluster().clusterService());
}
private XContentSource toXContentSource(SearchInput.Result result) throws IOException {
try (XContentBuilder builder = jsonBuilder()) {
result.executedRequest().source().toXContent(builder, ToXContent.EMPTY_PARAMS);
return new XContentSource(builder);
}
}
/**
* Custom plugin that registers XPack script context.
*/
public static class CustomScriptContextPlugin extends Plugin implements ScriptPlugin {
@Override
public ScriptContext.Plugin getCustomScriptContexts() {
return ScriptServiceProxy.INSTANCE;
}
}
}

View File

@ -11,25 +11,28 @@ import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.action.search.SearchType;
import org.elasticsearch.client.Requests;
import org.elasticsearch.common.bytes.BytesArray;
import org.elasticsearch.common.bytes.BytesReference;
import org.elasticsearch.common.io.Streams;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.unit.TimeValue;
import org.elasticsearch.common.xcontent.ToXContent;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.common.xcontent.XContentFactory;
import org.elasticsearch.common.xcontent.XContentParser;
import org.elasticsearch.common.xcontent.XContentType;
import org.elasticsearch.common.xcontent.json.JsonXContent;
import org.elasticsearch.common.xcontent.support.XContentMapValues;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.indices.query.IndicesQueriesRegistry;
import org.elasticsearch.plugins.Plugin;
import org.elasticsearch.script.ScriptService.ScriptType;
import org.elasticsearch.script.Template;
import org.elasticsearch.plugins.ScriptPlugin;
import org.elasticsearch.script.ScriptContext;
import org.elasticsearch.script.ScriptService;
import org.elasticsearch.script.mustache.MustachePlugin;
import org.elasticsearch.search.aggregations.AggregatorParsers;
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;
@ -37,7 +40,11 @@ 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.init.proxy.WatcherClientProxy;
import org.elasticsearch.xpack.watcher.support.search.WatcherSearchTemplateRequest;
import org.elasticsearch.xpack.watcher.support.search.WatcherSearchTemplateService;
import org.elasticsearch.xpack.watcher.support.xcontent.XContentSource;
import org.elasticsearch.xpack.watcher.transform.Transform;
import org.elasticsearch.xpack.watcher.transform.TransformBuilders;
import org.elasticsearch.xpack.watcher.transform.search.ExecutableSearchTransform;
@ -100,6 +107,7 @@ public class SearchTransformIT extends ESIntegTestCase {
Collection<Class<? extends Plugin>> types = new ArrayList<>();
types.addAll(super.nodePlugins());
types.add(MustachePlugin.class);
types.add(CustomScriptContextPlugin.class);
return types;
}
@ -154,7 +162,8 @@ public class SearchTransformIT extends ESIntegTestCase {
SearchRequest request = Requests.searchRequest("idx").source(new SearchSourceBuilder().query(QueryBuilders.matchAllQuery()));
SearchTransform searchTransform = TransformBuilders.searchTransform(request).build();
ExecutableSearchTransform transform = new ExecutableSearchTransform(searchTransform, logger, WatcherClientProxy.of(client()), null);
ExecutableSearchTransform transform = new ExecutableSearchTransform(searchTransform, logger, WatcherClientProxy.of(client()),
watcherSearchTemplateService(), null);
WatchExecutionContext ctx = mockExecutionContext("_name", EMPTY_PAYLOAD);
@ -188,7 +197,8 @@ public class SearchTransformIT extends ESIntegTestCase {
new SearchSourceBuilder().query(QueryBuilders.wrapperQuery(jsonBuilder().startObject()
.startObject("_unknown_query_").endObject().endObject().bytes())));
SearchTransform searchTransform = TransformBuilders.searchTransform(request).build();
ExecutableSearchTransform transform = new ExecutableSearchTransform(searchTransform, logger, WatcherClientProxy.of(client()), null);
ExecutableSearchTransform transform = new ExecutableSearchTransform(searchTransform, logger, WatcherClientProxy.of(client()),
watcherSearchTemplateService(), null);
WatchExecutionContext ctx = mockExecutionContext("_name", EMPTY_PAYLOAD);
@ -200,23 +210,27 @@ public class SearchTransformIT extends ESIntegTestCase {
assertThat(result.reason(), containsString("no [query] registered for [_unknown_query_]"));
// extract the base64 encoded query from the template script, path is: query -> wrapper -> query
String jsonQuery = result.executedRequest().template().getScript();
Map<String, Object> map = XContentFactory.xContent(jsonQuery).createParser(jsonQuery).map();
try (XContentBuilder builder = jsonBuilder()) {
result.executedRequest().source().toXContent(builder, ToXContent.EMPTY_PARAMS);
assertThat(map, hasKey("query"));
assertThat(map.get("query"), instanceOf(Map.class));
String jsonQuery = builder.string();
Map<String, Object> map = XContentFactory.xContent(jsonQuery).createParser(jsonQuery).map();
map = (Map<String, Object>) map.get("query");
assertThat(map, hasKey("wrapper"));
assertThat(map.get("wrapper"), instanceOf(Map.class));
assertThat(map, hasKey("query"));
assertThat(map.get("query"), instanceOf(Map.class));
map = (Map<String, Object>) map.get("wrapper");
assertThat(map, hasKey("query"));
assertThat(map.get("query"), instanceOf(String.class));
map = (Map<String, Object>) map.get("query");
assertThat(map, hasKey("wrapper"));
assertThat(map.get("wrapper"), instanceOf(Map.class));
String queryAsBase64 = (String) map.get("query");
String decodedQuery = new String(Base64.getDecoder().decode(queryAsBase64), StandardCharsets.UTF_8);
assertThat(decodedQuery, containsString("_unknown_query_"));
map = (Map<String, Object>) map.get("wrapper");
assertThat(map, hasKey("query"));
assertThat(map.get("query"), instanceOf(String.class));
String queryAsBase64 = (String) map.get("query");
String decodedQuery = new String(Base64.getDecoder().decode(queryAsBase64), StandardCharsets.UTF_8);
assertThat(decodedQuery, containsString("_unknown_query_"));
}
}
public void testExecuteMustacheTemplate() throws Exception {
@ -252,7 +266,8 @@ public class SearchTransformIT extends ESIntegTestCase {
.must(termQuery("value", "{{ctx.payload.value}}"))));
SearchTransform searchTransform = TransformBuilders.searchTransform(request).build();
ExecutableSearchTransform transform = new ExecutableSearchTransform(searchTransform, logger, WatcherClientProxy.of(client()), null);
ExecutableSearchTransform transform = new ExecutableSearchTransform(searchTransform, logger, WatcherClientProxy.of(client()),
watcherSearchTemplateService(), null);
ScheduleTriggerEvent event = new ScheduleTriggerEvent("_name", parseDate("2015-01-04T00:00:00", UTC),
parseDate("2015-01-01T00:00:00", UTC));
@ -319,24 +334,24 @@ public class SearchTransformIT extends ESIntegTestCase {
IndicesQueriesRegistry indicesQueryRegistry = internalCluster().getInstance(IndicesQueriesRegistry.class);
SearchTransformFactory transformFactory = new SearchTransformFactory(Settings.EMPTY, WatcherClientProxy.of(client()),
indicesQueryRegistry, null, null);
indicesQueryRegistry, null, null, scriptService());
ExecutableSearchTransform executable = transformFactory.parseExecutable("_id", parser);
assertThat(executable, notNullValue());
assertThat(executable.type(), is(SearchTransform.TYPE));
assertThat(executable.transform().getRequest(), notNullValue());
if (indices != null) {
assertThat(executable.transform().getRequest().indices(), arrayContainingInAnyOrder(indices));
assertThat(executable.transform().getRequest().getRequest().indices(), arrayContainingInAnyOrder(indices));
}
if (searchType != null) {
assertThat(executable.transform().getRequest().searchType(), is(searchType));
assertThat(executable.transform().getRequest().getRequest().searchType(), is(searchType));
}
if (templateName != null) {
assertThat(executable.transform().getRequest().template(),
equalTo(new Template("template1", ScriptType.FILE, null, null, null)));
assertThat(executable.transform().getRequest().getTemplate(),
equalTo(Script.file("template1").build()));
}
SearchSourceBuilder source = new SearchSourceBuilder().query(QueryBuilders.matchAllQuery());
assertThat(executable.transform().getRequest().source(), equalTo(source));
assertThat(executable.transform().getRequest().getRequest().source(), equalTo(source));
assertThat(executable.transform().getTimeout(), equalTo(readTimeout));
}
@ -348,11 +363,6 @@ public class SearchTransformIT extends ESIntegTestCase {
"{\"from\":\"{{ctx.trigger.scheduled_time}}||-{{seconds_param}}\",\"to\":\"{{ctx.trigger.scheduled_time}}\"," +
"\"include_lower\":true,\"include_upper\":true}}}]}}}";
final String expectedTemplateString = "{\"query\":{\"bool\":{\"must\":[{\"match\":{\"event_type\":{\"query\":\"a\","
+ "\"type\":\"boolean\"}}},{\"range\":{\"_timestamp\":"
+ "{\"from\":\"{{ctx.trigger.scheduled_time}}||-{{seconds_param}}\",\"to\":\"{{ctx.trigger.scheduled_time}}\","
+ "\"include_lower\":true,\"include_upper\":true}}}]}}}";
Map<String, Object> triggerParams = new HashMap<String, Object>();
triggerParams.put("triggered_time", new DateTime(1970, 01, 01, 00, 01, 00, 000, ISOChronology.getInstanceUTC()));
triggerParams.put("scheduled_time", new DateTime(1970, 01, 01, 00, 01, 00, 000, ISOChronology.getInstanceUTC()));
@ -367,18 +377,24 @@ public class SearchTransformIT extends ESIntegTestCase {
Map<String, Object> expectedParams = new HashMap<String, Object>();
expectedParams.put("seconds_param", "30s");
expectedParams.put("ctx", ctxParams);
Template expectedTemplate = new Template(expectedTemplateString, ScriptType.INLINE, null, XContentType.JSON, expectedParams);
Map<String, Object> params = new HashMap<>();
params.put("seconds_param", "30s");
Template template = new Template(templateQuery, ScriptType.INLINE, null, XContentType.JSON, params);
Script template = Script.inline(templateQuery).lang("mustache").params(params).build();
SearchRequest request = client().prepareSearch().setSearchType(ExecutableSearchTransform.DEFAULT_SEARCH_TYPE)
.setIndices("test-search-index").setTemplate(template).request();
.setIndices("test-search-index").request();
SearchTransform.Result executedResult = executeSearchTransform(request, ctx);
SearchTransform.Result executedResult = executeSearchTransform(request, template, ctx);
assertThat(executedResult.executedRequest().template(), equalTo(expectedTemplate));
assertThat(executedResult.status(), is(Transform.Result.Status.SUCCESS));
assertEquals(executedResult.executedRequest().searchType(), request.searchType());
assertArrayEquals(executedResult.executedRequest().indices(), request.indices());
assertEquals(executedResult.executedRequest().indicesOptions(), request.indicesOptions());
XContentSource source = toXContentSource(executedResult);
assertThat(source.getValue("query.bool.must.1.range._timestamp.from"), equalTo("1970-01-01T00:01:00.000Z||-30s"));
assertThat(source.getValue("query.bool.must.1.range._timestamp.to"), equalTo("1970-01-01T00:01:00.000Z"));
}
public void testSearchIndexedTemplate() throws Exception {
@ -399,24 +415,24 @@ public class SearchTransformIT extends ESIntegTestCase {
Map<String, Object> params = new HashMap<>();
params.put("seconds_param", "30s");
BytesReference templateSource = jsonBuilder()
.value(TextTemplate.indexed("test-script").params(params).build())
.bytes();
Template template = new Template("test-script", ScriptType.STORED, null, null, null);
Script template = Script.indexed("test-script").lang("mustache").params(params).build();
SearchRequest request = client()
.prepareSearch()
.setSearchType(ExecutableSearchTransform.DEFAULT_SEARCH_TYPE)
.setIndices("test-search-index")
.setTemplate(template)
.request();
SearchTransform.Result result = executeSearchTransform(request, ctx);
SearchTransform.Result result = executeSearchTransform(request, template, ctx);
assertNotNull(result.executedRequest());
Template resultTemplate = result.executedRequest().template();
assertThat(resultTemplate, notNullValue());
assertThat(resultTemplate.getScript(), equalTo("test-script"));
assertThat(resultTemplate.getType(), equalTo(ScriptType.STORED));
assertThat(result.status(), is(Transform.Result.Status.SUCCESS));
assertArrayEquals(result.executedRequest().indices(), request.indices());
assertEquals(result.executedRequest().indicesOptions(), request.indicesOptions());
XContentSource source = toXContentSource(result);
assertThat(source.getValue("query.bool.must.1.range._timestamp.from"), equalTo("1970-01-01T00:01:00.000Z||-30s"));
assertThat(source.getValue("query.bool.must.1.range._timestamp.to"), equalTo("1970-01-01T00:01:00.000Z"));
}
public void testSearchOnDiskTemplate() throws Exception {
@ -425,16 +441,20 @@ public class SearchTransformIT extends ESIntegTestCase {
Map<String, Object> params = new HashMap<>();
params.put("seconds_param", "30s");
Template template = new Template("test_disk_template", ScriptType.FILE, null, null, null);
Script template = Script.file("test_disk_template").lang("mustache").params(params).build();
SearchRequest request = client().prepareSearch().setSearchType(ExecutableSearchTransform.DEFAULT_SEARCH_TYPE)
.setIndices("test-search-index").setTemplate(template).request();
.setIndices("test-search-index").request();
SearchTransform.Result result = executeSearchTransform(request, template, ctx);
SearchTransform.Result result = executeSearchTransform(request, ctx);
assertNotNull(result.executedRequest());
Template resultTemplate = result.executedRequest().template();
assertThat(resultTemplate, notNullValue());
assertThat(resultTemplate.getScript(), equalTo("test_disk_template"));
assertThat(resultTemplate.getType(), equalTo(ScriptType.FILE));
assertThat(result.status(), is(Transform.Result.Status.SUCCESS));
assertArrayEquals(result.executedRequest().indices(), request.indices());
assertEquals(result.executedRequest().indicesOptions(), request.indicesOptions());
XContentSource source = toXContentSource(result);
assertThat(source.getValue("query.bool.must.1.range._timestamp.from"), equalTo("1970-01-01T00:01:00.000Z||-30s"));
assertThat(source.getValue("query.bool.must.1.range._timestamp.to"), equalTo("1970-01-01T00:01:00.000Z"));
}
public void testDifferentSearchType() throws Exception {
@ -453,13 +473,18 @@ public class SearchTransformIT extends ESIntegTestCase {
.request()
.source(searchSourceBuilder);
SearchTransform.Result result = executeSearchTransform(request, ctx);
SearchTransform.Result result = executeSearchTransform(request, null, ctx);
assertThat((Integer) XContentMapValues.extractValue("hits.total", result.payload().data()), equalTo(0));
assertThat(XContentMapValues.extractValue("hits.total", result.payload().data()), equalTo(0));
assertThat(result.executedRequest(), notNullValue());
assertThat(result.status(), is(Transform.Result.Status.SUCCESS));
assertThat(result.executedRequest().searchType(), is(searchType));
assertThat(result.executedRequest().indices(), arrayContainingInAnyOrder(request.indices()));
assertThat(result.executedRequest().indicesOptions(), equalTo(request.indicesOptions()));
XContentSource source = toXContentSource(result);
assertThat(source.getValue("query.bool.must.1.range._timestamp.from"), equalTo("1970-01-01T00:01:00.000Z||-30s"));
assertThat(source.getValue("query.bool.must.1.range._timestamp.to"), equalTo("1970-01-01T00:01:00.000Z"));
}
private WatchExecutionContext createContext() {
@ -479,17 +504,31 @@ public class SearchTransformIT extends ESIntegTestCase {
timeValueSeconds(5));
}
private SearchTransform.Result executeSearchTransform(SearchRequest request, WatchExecutionContext ctx) throws IOException {
private SearchTransform.Result executeSearchTransform(SearchRequest request, Script template, WatchExecutionContext ctx)
throws IOException {
createIndex("test-search-index");
ensureGreen("test-search-index");
SearchTransform searchTransform = TransformBuilders.searchTransform(request).build();
SearchTransform searchTransform = TransformBuilders.searchTransform(new WatcherSearchTemplateRequest(request, template)).build();
ExecutableSearchTransform executableSearchTransform = new ExecutableSearchTransform(searchTransform, logger,
WatcherClientProxy.of(client()), null);
WatcherClientProxy.of(client()), watcherSearchTemplateService(), null);
return executableSearchTransform.execute(ctx, Payload.Simple.EMPTY);
}
protected WatcherSearchTemplateService watcherSearchTemplateService() {
String master = internalCluster().getMasterName();
return new WatcherSearchTemplateService(internalCluster().clusterService(master).getSettings(),
ScriptServiceProxy.of(internalCluster().getInstance(ScriptService.class, master), internalCluster().clusterService(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), internalCluster().clusterService());
}
private static Map<String, Object> doc(String date, String value) {
Map<String, Object> doc = new HashMap<>();
@ -498,4 +537,21 @@ public class SearchTransformIT extends ESIntegTestCase {
return doc;
}
private XContentSource toXContentSource(SearchTransform.Result result) throws IOException {
try (XContentBuilder builder = jsonBuilder()) {
result.executedRequest().source().toXContent(builder, ToXContent.EMPTY_PARAMS);
return new XContentSource(builder);
}
}
/**
* Custom plugin that registers XPack script context.
*/
public static class CustomScriptContextPlugin extends Plugin implements ScriptPlugin {
@Override
public ScriptContext.Plugin getCustomScriptContexts() {
return ScriptServiceProxy.INSTANCE;
}
}
}

View File

@ -5,27 +5,27 @@
*/
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.common.bytes.BytesArray;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.indices.TermsLookup;
import org.elasticsearch.plugins.Plugin;
import org.elasticsearch.script.ScriptService;
import org.elasticsearch.script.Template;
import org.elasticsearch.script.mustache.MustachePlugin;
import org.elasticsearch.script.mustache.MustacheScriptEngineService;
import org.elasticsearch.xpack.security.authc.support.SecuredString;
import org.elasticsearch.test.SecurityIntegTestCase;
import org.elasticsearch.test.SecuritySettingsSource;
import org.elasticsearch.xpack.security.authc.support.SecuredString;
import org.junit.Before;
import org.junit.BeforeClass;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
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;
@ -72,18 +72,6 @@ public class SecurityCachePermissionIT extends SecurityIntegTestCase {
public void loadData() {
index("data", "a", "1", "{ \"name\": \"John\", \"token\": \"token1\" }");
index("tokens", "tokens", "1", "{ \"group\": \"1\", \"tokens\": [\"token1\", \"token2\"] }");
client().admin().cluster().preparePutStoredScript().setSource(new BytesArray("{\n" +
"\"template\": {\n" +
" \"query\": {\n" +
" \"exists\": {\n" +
" \"field\": \"{{name}}\"\n" +
" }\n" +
" }\n" +
" }\n" +
"}"))
.setScriptLang("mustache")
.setId("testTemplate")
.execute().actionGet();
refresh();
}
@ -96,38 +84,44 @@ public class SecurityCachePermissionIT extends SecurityIntegTestCase {
// Repeat with unauthorized user!!!!
try {
response = client().filterWithHeader(Collections.singletonMap("Authorization", basicAuthHeaderValue(READ_ONE_IDX_USER,
response = client().filterWithHeader(singletonMap("Authorization", basicAuthHeaderValue(READ_ONE_IDX_USER,
new SecuredString("changeme".toCharArray()))))
.prepareSearch("data").setTypes("a").setQuery(QueryBuilders.constantScoreQuery(
QueryBuilders.termsLookupQuery("token", new TermsLookup("tokens", "tokens", "1", "tokens"))))
.execute().actionGet();
fail("search phase exception should have been thrown! response was:\n" + response.toString());
} catch (SearchPhaseExecutionException e) {
} catch (ElasticsearchSecurityException e) {
assertThat(e.toString(), containsString("ElasticsearchSecurityException[action"));
assertThat(e.toString(), containsString("unauthorized"));
}
}
public void testThatScriptServiceDoesntLeakData() {
String source = "{\n" +
"\"template\": {\n" +
" \"query\": {\n" +
" \"exists\": {\n" +
" \"field\": \"{{name}}\"\n" +
" }\n" +
" }\n" +
" }\n" +
"}";
//Template template = new Template(source, INLINE, MustacheScriptEngineService.NAME, null, singletonMap("name", "token"));
SearchResponse response = client().prepareSearch("data").setTypes("a")
.setTemplate(new Template("testTemplate", ScriptService.ScriptType.STORED, MustacheScriptEngineService.NAME, null,
Collections.<String, Object>singletonMap("name", "token")))
.setQuery(QueryBuilders.templateQuery(source, singletonMap("name", "token")))
.execute().actionGet();
assertThat(response.isTimedOut(), is(false));
assertThat(response.getHits().hits().length, is(1));
// Repeat with unauthorized user!!!!
try {
response = client().filterWithHeader(Collections.singletonMap("Authorization", basicAuthHeaderValue(READ_ONE_IDX_USER,
new SecuredString("changeme".toCharArray()))))
ElasticsearchSecurityException e = expectThrows(ElasticsearchSecurityException.class, () -> client()
.filterWithHeader(singletonMap("Authorization", basicAuthHeaderValue(READ_ONE_IDX_USER,
new SecuredString("changeme".toCharArray()))))
.prepareSearch("data").setTypes("a")
.setTemplate(new Template("testTemplate", ScriptService.ScriptType.STORED, MustacheScriptEngineService.NAME, null,
Collections.<String, Object>singletonMap("name", "token")))
.execute().actionGet();
fail("search phase exception should have been thrown! response was:\n" + response.toString());
} catch (SearchPhaseExecutionException e) {
assertThat(e.toString(), containsString("ElasticsearchSecurityException[action"));
assertThat(e.toString(), containsString("unauthorized"));
}
.setQuery(QueryBuilders.templateQuery(source, singletonMap("name", "token")))
.execute().actionGet());
assertThat(e.toString(), containsString("ElasticsearchSecurityException[action"));
assertThat(e.toString(), containsString("unauthorized"));
}
}

View File

@ -5,9 +5,12 @@
*/
package org.elasticsearch.example.realm;
import org.apache.http.message.BasicHeader;
import org.elasticsearch.action.admin.cluster.health.ClusterHealthResponse;
import org.elasticsearch.action.admin.cluster.node.info.NodeInfo;
import org.elasticsearch.action.admin.cluster.node.info.NodesInfoResponse;
import org.elasticsearch.client.Response;
import org.elasticsearch.client.ResponseException;
import org.elasticsearch.client.transport.NoNodeAvailableException;
import org.elasticsearch.client.transport.TransportClient;
import org.elasticsearch.common.settings.Settings;
@ -16,7 +19,6 @@ import org.elasticsearch.common.util.concurrent.ThreadContext;
import org.elasticsearch.env.Environment;
import org.elasticsearch.plugins.Plugin;
import org.elasticsearch.test.ESIntegTestCase;
import org.elasticsearch.test.rest.client.http.HttpResponse;
import org.elasticsearch.xpack.XPackPlugin;
import java.util.Collection;
@ -29,6 +31,7 @@ import static org.hamcrest.Matchers.is;
* Integration test to test authentication with the custom realm
*/
public class CustomRealmIT extends ESIntegTestCase {
@Override
protected Settings externalClusterClientSettings() {
return Settings.builder()
@ -43,18 +46,23 @@ public class CustomRealmIT extends ESIntegTestCase {
}
public void testHttpConnectionWithNoAuthentication() throws Exception {
HttpResponse response = httpClient().path("/").execute();
assertThat(response.getStatusCode(), is(401));
String value = response.getHeaders().get("WWW-Authenticate");
assertThat(value, is("custom-challenge"));
try {
getRestClient().performRequest("GET", "/", Collections.emptyMap(), null);
fail("request should have failed");
} catch(ResponseException e) {
Response response = e.getResponse();
assertThat(response.getStatusLine().getStatusCode(), is(401));
String value = response.getHeader("WWW-Authenticate");
assertThat(value, is("custom-challenge"));
}
}
public void testHttpAuthentication() throws Exception {
HttpResponse response = httpClient().path("/")
.addHeader(CustomRealm.USER_HEADER, CustomRealm.KNOWN_USER)
.addHeader(CustomRealm.PW_HEADER, CustomRealm.KNOWN_PW)
.execute();
assertThat(response.getStatusCode(), is(200));
try (Response response = getRestClient().performRequest("GET", "/", Collections.emptyMap(), null,
new BasicHeader(CustomRealm.USER_HEADER, CustomRealm.KNOWN_USER),
new BasicHeader(CustomRealm.PW_HEADER, CustomRealm.KNOWN_PW))) {
assertThat(response.getStatusLine().getStatusCode(), is(200));
}
}
public void testTransportClient() throws Exception {

View File

@ -7,9 +7,9 @@ package org.elasticsearch.smoketest;
import com.carrotsearch.randomizedtesting.annotations.Name;
import com.carrotsearch.randomizedtesting.annotations.ParametersFactory;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.util.concurrent.ThreadContext;
import org.elasticsearch.xpack.security.authc.support.SecuredString;
import org.elasticsearch.test.rest.ESRestTestCase;
import org.elasticsearch.test.rest.RestTestCandidate;
@ -19,6 +19,7 @@ import java.io.IOException;
import static org.elasticsearch.xpack.security.authc.support.UsernamePasswordToken.basicAuthHeaderValue;
public class GraphWithSecurityIT extends ESRestTestCase {
private final static String TEST_ADMIN_USERNAME = "test_admin";

View File

@ -14,7 +14,7 @@ import org.elasticsearch.common.util.concurrent.ThreadContext;
import org.elasticsearch.xpack.security.authc.support.SecuredString;
import org.elasticsearch.test.rest.ESRestTestCase;
import org.elasticsearch.test.rest.RestTestCandidate;
import org.elasticsearch.test.rest.client.RestClient;
import org.elasticsearch.test.rest.client.RestTestClient;
import org.elasticsearch.test.rest.parser.RestTestParseException;
import org.junit.AfterClass;
import org.junit.BeforeClass;
@ -65,9 +65,9 @@ public class SmokeTestPluginsSslIT extends ESRestTestCase {
String token = basicAuthHeaderValue(USER, new SecuredString(PASS.toCharArray()));
return Settings.builder()
.put(ThreadContext.PREFIX + ".Authorization", token)
.put(RestClient.PROTOCOL, "https")
.put(RestClient.TRUSTSTORE_PATH, keyStore)
.put(RestClient.TRUSTSTORE_PASSWORD, KEYSTORE_PASS)
.put(RestTestClient.PROTOCOL, "https")
.put(RestTestClient.TRUSTSTORE_PATH, keyStore)
.put(RestTestClient.TRUSTSTORE_PASSWORD, KEYSTORE_PASS)
.build();
}
}

View File

@ -5,22 +5,19 @@
*/
package org.elasticsearch.smoketest;
import java.io.IOException;
import java.net.URI;
import java.net.URL;
import com.carrotsearch.randomizedtesting.annotations.Name;
import com.carrotsearch.randomizedtesting.annotations.ParametersFactory;
import org.apache.http.client.methods.HttpPut;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.impl.conn.BasicHttpClientConnectionManager;
import org.elasticsearch.test.rest.ESRestTestCase;
import org.elasticsearch.test.rest.RestTestCandidate;
import org.elasticsearch.test.rest.parser.RestTestParseException;
import org.junit.After;
import org.junit.Before;
import java.io.IOException;
import static java.util.Collections.emptyList;
import static java.util.Collections.emptyMap;
public abstract class WatcherRestTestCase extends ESRestTestCase {
public WatcherRestTestCase(@Name("yaml") RestTestCandidate testCandidate) {
@ -34,19 +31,11 @@ public abstract class WatcherRestTestCase extends ESRestTestCase {
@Before
public void startWatcher() throws Exception {
try(CloseableHttpClient client = HttpClients.createMinimal(new BasicHttpClientConnectionManager())) {
URL url = getClusterUrls()[0];
HttpPut request = new HttpPut(new URI("http", null, url.getHost(), url.getPort(), "/_xpack/watcher/_start", null, null));
client.execute(request);
}
getAdminExecutionContext().callApi("xpack.watcher.start", emptyMap(), emptyList(), emptyMap());
}
@After
public void stopWatcher() throws Exception {
try(CloseableHttpClient client = HttpClients.createMinimal(new BasicHttpClientConnectionManager())) {
URL url = getClusterUrls()[0];
HttpPut request = new HttpPut(new URI("http", null, url.getHost(), url.getPort(), "/_xpack/watcher/_stop", null, null));
client.execute(request);
}
getAdminExecutionContext().callApi("xpack.watcher.stop", emptyMap(), emptyList(), emptyMap());
}
}

View File

@ -7,10 +7,6 @@ package org.elasticsearch.smoketest;
import com.carrotsearch.randomizedtesting.annotations.Name;
import com.carrotsearch.randomizedtesting.annotations.ParametersFactory;
import org.apache.http.client.methods.HttpPut;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.impl.conn.BasicHttpClientConnectionManager;
import org.elasticsearch.test.rest.ESRestTestCase;
import org.elasticsearch.test.rest.RestTestCandidate;
import org.elasticsearch.test.rest.parser.RestTestParseException;
@ -18,8 +14,9 @@ import org.junit.After;
import org.junit.Before;
import java.io.IOException;
import java.net.URI;
import java.net.URL;
import static java.util.Collections.emptyList;
import static java.util.Collections.emptyMap;
public abstract class WatcherRestTestCase extends ESRestTestCase {
@ -34,19 +31,11 @@ public abstract class WatcherRestTestCase extends ESRestTestCase {
@Before
public void startWatcher() throws Exception {
try(CloseableHttpClient client = HttpClients.createMinimal(new BasicHttpClientConnectionManager())) {
URL url = getClusterUrls()[0];
HttpPut request = new HttpPut(new URI("http", null, url.getHost(), url.getPort(), "/_xpack/watcher/_start", null, null));
client.execute(request);
}
getAdminExecutionContext().callApi("xpack.watcher.start", emptyMap(), emptyList(), emptyMap());
}
@After
public void stopWatcher() throws Exception {
try(CloseableHttpClient client = HttpClients.createMinimal(new BasicHttpClientConnectionManager())) {
URL url = getClusterUrls()[0];
HttpPut request = new HttpPut(new URI("http", null, url.getHost(), url.getPort(), "/_xpack/watcher/_stop", null, null));
client.execute(request);
}
getAdminExecutionContext().callApi("xpack.watcher.stop", emptyMap(), emptyList(), emptyMap());
}
}

View File

@ -55,7 +55,7 @@
catch: request_timeout
cluster.health:
wait_for_nodes: 99
timeout: 10s
timeout: 5s
- match: { "timed_out": true }
- do:

View File

@ -53,7 +53,7 @@
catch: request_timeout
cluster.health:
wait_for_nodes: 99
timeout: 10s
timeout: 5s
- match: { "timed_out": true }
- do:

View File

@ -5,22 +5,19 @@
*/
package org.elasticsearch.smoketest;
import java.io.IOException;
import java.net.URI;
import java.net.URL;
import com.carrotsearch.randomizedtesting.annotations.Name;
import com.carrotsearch.randomizedtesting.annotations.ParametersFactory;
import org.apache.http.client.methods.HttpPut;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.impl.conn.BasicHttpClientConnectionManager;
import org.elasticsearch.test.rest.ESRestTestCase;
import org.elasticsearch.test.rest.RestTestCandidate;
import org.elasticsearch.test.rest.parser.RestTestParseException;
import org.junit.After;
import org.junit.Before;
import java.io.IOException;
import static java.util.Collections.emptyList;
import static java.util.Collections.emptyMap;
public abstract class WatcherRestTestCase extends ESRestTestCase {
public WatcherRestTestCase(@Name("yaml") RestTestCandidate testCandidate) {
@ -34,19 +31,11 @@ public abstract class WatcherRestTestCase extends ESRestTestCase {
@Before
public void startWatcher() throws Exception {
try(CloseableHttpClient client = HttpClients.createMinimal(new BasicHttpClientConnectionManager())) {
URL url = getClusterUrls()[0];
HttpPut request = new HttpPut(new URI("http", null, url.getHost(), url.getPort(), "/_xpack/watcher/_start", null, null));
client.execute(request);
}
getAdminExecutionContext().callApi("xpack.watcher.start", emptyMap(), emptyList(), emptyMap());
}
@After
public void stopWatcher() throws Exception {
try(CloseableHttpClient client = HttpClients.createMinimal(new BasicHttpClientConnectionManager())) {
URL url = getClusterUrls()[0];
HttpPut request = new HttpPut(new URI("http", null, url.getHost(), url.getPort(), "/_xpack/watcher/_stop", null, null));
client.execute(request);
}
getAdminExecutionContext().callApi("xpack.watcher.stop", emptyMap(), emptyList(), emptyMap());
}
}

View File

@ -5,28 +5,26 @@
*/
package org.elasticsearch.smoketest;
import java.io.IOException;
import java.net.URI;
import java.net.URL;
import com.carrotsearch.randomizedtesting.annotations.Name;
import com.carrotsearch.randomizedtesting.annotations.ParametersFactory;
import org.apache.http.client.methods.HttpPut;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.impl.conn.BasicHttpClientConnectionManager;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.util.concurrent.ThreadContext;
import org.elasticsearch.xpack.security.authc.support.SecuredString;
import org.elasticsearch.xpack.security.authc.support.UsernamePasswordToken;
import org.elasticsearch.test.rest.ESRestTestCase;
import org.elasticsearch.test.rest.RestTestCandidate;
import org.elasticsearch.test.rest.parser.RestTestParseException;
import org.junit.After;
import org.junit.Before;
import java.io.IOException;
import static java.util.Collections.emptyList;
import static java.util.Collections.emptyMap;
import static org.elasticsearch.xpack.security.authc.support.UsernamePasswordToken.basicAuthHeaderValue;
public class WatcherWithSecurityIT extends ESRestTestCase {
private final static String TEST_ADMIN_USERNAME = "test_admin";
@ -43,24 +41,12 @@ public class WatcherWithSecurityIT extends ESRestTestCase {
@Before
public void startWatcher() throws Exception {
try(CloseableHttpClient client = HttpClients.createMinimal(new BasicHttpClientConnectionManager())) {
URL url = getClusterUrls()[0];
HttpPut request = new HttpPut(new URI("http", null, url.getHost(), url.getPort(), "/_xpack/watcher/_start", null, null));
String token = basicAuthHeaderValue(TEST_ADMIN_USERNAME, new SecuredString(TEST_ADMIN_PASSWORD.toCharArray()));
request.addHeader(UsernamePasswordToken.BASIC_AUTH_HEADER, token);
client.execute(request);
}
getAdminExecutionContext().callApi("xpack.watcher.start", emptyMap(), emptyList(), emptyMap());
}
@After
public void stopWatcher() throws Exception {
try(CloseableHttpClient client = HttpClients.createMinimal(new BasicHttpClientConnectionManager())) {
URL url = getClusterUrls()[0];
HttpPut request = new HttpPut(new URI("http", null, url.getHost(), url.getPort(), "/_xpack/watcher/_stop", null, null));
String token = basicAuthHeaderValue(TEST_ADMIN_USERNAME, new SecuredString(TEST_ADMIN_PASSWORD.toCharArray()));
request.addHeader(UsernamePasswordToken.BASIC_AUTH_HEADER, token);
client.execute(request);
}
getAdminExecutionContext().callApi("xpack.watcher.stop", emptyMap(), emptyList(), emptyMap());
}
@Override

View File

@ -29,7 +29,7 @@ dependencies {
compile 'com.unboundid:unboundid-ldapsdk:2.3.8'
compile 'org.bouncycastle:bcprov-jdk15on:1.54'
compile 'org.bouncycastle:bcpkix-jdk15on:1.54'
testCompile 'com.google.jimfs:jimfs:1.0'
testCompile 'com.google.jimfs:jimfs:1.1'
// watcher deps
compile 'com.googlecode.owasp-java-html-sanitizer:owasp-java-html-sanitizer:r239'

View File

@ -23,6 +23,7 @@ import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.transport.BoundTransportAddress;
import org.elasticsearch.common.transport.DummyTransportAddress;
import org.elasticsearch.common.transport.TransportAddress;
import org.elasticsearch.common.unit.ByteSizeValue;
import org.elasticsearch.common.xcontent.XContentType;
import org.elasticsearch.http.HttpInfo;
import org.elasticsearch.index.Index;
@ -112,7 +113,7 @@ public class ClusterStatsResolverTests extends MonitoringIndexNameResolverTestCa
Settings.EMPTY, DummyOsInfo.INSTANCE, new ProcessInfo(randomInt(), randomBoolean()), JvmInfo.jvmInfo(),
new ThreadPoolInfo(Collections.singletonList(new ThreadPool.Info("test_threadpool", ThreadPool.ThreadPoolType.FIXED, 5))),
new TransportInfo(transportAddress, Collections.emptyMap()), new HttpInfo(transportAddress, randomLong()),
new PluginsAndModules(), new IngestInfo(Collections.emptyList()));
new PluginsAndModules(), new IngestInfo(Collections.emptyList()), new ByteSizeValue(randomIntBetween(1, 1024)));
}

View File

@ -43,6 +43,7 @@ public class NodeStatsTests extends MarvelIntegTestCase {
wipeMarvelIndices();
}
@AwaitsFix(bugUrl = "https://github.com/elastic/x-plugins/issues/2588")
public void testNodeStats() throws Exception {
logger.debug("--> creating some indices for future node stats");
final int numDocs = between(50, 150);

View File

@ -5,21 +5,18 @@
*/
package org.elasticsearch.marvel.security;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.Header;
import org.apache.http.message.BasicHeader;
import org.elasticsearch.client.Response;
import org.elasticsearch.common.network.NetworkModule;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.xcontent.json.JsonXContent;
import org.elasticsearch.http.HttpServerTransport;
import org.elasticsearch.marvel.MonitoringSettings;
import org.elasticsearch.marvel.test.MarvelIntegTestCase;
import org.elasticsearch.xpack.security.authc.support.SecuredString;
import org.elasticsearch.test.rest.client.http.HttpRequestBuilder;
import org.elasticsearch.test.rest.client.http.HttpResponse;
import org.hamcrest.Matchers;
import org.junit.After;
import java.io.IOException;
import java.util.Collections;
import java.util.Map;
import static org.elasticsearch.common.xcontent.support.XContentMapValues.extractValue;
@ -29,13 +26,6 @@ import static org.hamcrest.CoreMatchers.nullValue;
public class MarvelSettingsFilterTests extends MarvelIntegTestCase {
private CloseableHttpClient httpClient = HttpClients.createDefault();
@After
public void cleanup() throws IOException {
httpClient.close();
}
@Override
protected Settings nodeSettings(int nodeOrdinal) {
return Settings.builder()
@ -53,46 +43,35 @@ public class MarvelSettingsFilterTests extends MarvelIntegTestCase {
}
public void testGetSettingsFiltered() throws Exception {
String body = executeRequest("GET", "/_nodes/settings", null, null).getBody();
Map<String, Object> response = JsonXContent.jsonXContent.createParser(body).map();
Map<String, Object> nodes = (Map<String, Object>) response.get("nodes");
for (Object node : nodes.values()) {
Map<String, Object> settings = (Map<String, Object>) ((Map<String, Object>) node).get("settings");
assertThat(extractValue("xpack.monitoring.agent.exporters._http.type", settings), Matchers.<Object>equalTo("http"));
assertThat(extractValue("xpack.monitoring.agent.exporters._http.enabled", settings), Matchers.<Object>equalTo("false"));
assertNullSetting(settings, "xpack.monitoring.agent.exporters._http.auth.username");
assertNullSetting(settings, "xpack.monitoring.agent.exporters._http.auth.password");
assertNullSetting(settings, "xpack.monitoring.agent.exporters._http.ssl.truststore.path");
assertNullSetting(settings, "xpack.monitoring.agent.exporters._http.ssl.truststore.password");
assertNullSetting(settings, "xpack.monitoring.agent.exporters._http.ssl.hostname_verification");
Header[] headers;
if (securityEnabled) {
headers = new Header[] {
new BasicHeader(BASIC_AUTH_HEADER,
basicAuthHeaderValue(SecuritySettings.TEST_USERNAME,
new SecuredString(SecuritySettings.TEST_PASSWORD.toCharArray())))};
} else {
headers = new Header[0];
}
try (Response response = getRestClient().performRequest("GET", "/_nodes/settings",
Collections.emptyMap(), null, headers)) {
Map<String, Object> responseMap = JsonXContent.jsonXContent.createParser(response.getEntity().getContent()).map();
@SuppressWarnings("unchecked")
Map<String, Object> nodes = (Map<String, Object>) responseMap.get("nodes");
for (Object node : nodes.values()) {
@SuppressWarnings("unchecked")
Map<String, Object> settings = (Map<String, Object>) ((Map<String, Object>) node).get("settings");
assertThat(extractValue("xpack.monitoring.agent.exporters._http.type", settings), Matchers.<Object>equalTo("http"));
assertThat(extractValue("xpack.monitoring.agent.exporters._http.enabled", settings), Matchers.<Object>equalTo("false"));
assertNullSetting(settings, "xpack.monitoring.agent.exporters._http.auth.username");
assertNullSetting(settings, "xpack.monitoring.agent.exporters._http.auth.password");
assertNullSetting(settings, "xpack.monitoring.agent.exporters._http.ssl.truststore.path");
assertNullSetting(settings, "xpack.monitoring.agent.exporters._http.ssl.truststore.password");
assertNullSetting(settings, "xpack.monitoring.agent.exporters._http.ssl.hostname_verification");
}
}
}
private void assertNullSetting(Map<String, Object> settings, String setting) {
assertThat(extractValue(setting, settings), nullValue());
}
protected HttpResponse executeRequest(String method, String path, String body, Map<String, String> params) throws IOException {
HttpServerTransport httpServerTransport = internalCluster().getInstance(HttpServerTransport.class,
internalCluster().getMasterName());
HttpRequestBuilder requestBuilder = new HttpRequestBuilder(httpClient)
.httpTransport(httpServerTransport)
.method(method)
.path(path);
if (params != null) {
for (Map.Entry<String, String> entry : params.entrySet()) {
requestBuilder.addParam(entry.getKey(), entry.getValue());
}
}
if (body != null) {
requestBuilder.body(body);
}
if (securityEnabled) {
requestBuilder.addHeader(BASIC_AUTH_HEADER,
basicAuthHeaderValue(SecuritySettings.TEST_USERNAME, new SecuredString(SecuritySettings.TEST_PASSWORD.toCharArray())));
}
return requestBuilder.execute();
}
}

View File

@ -235,10 +235,6 @@ public class RoleDescriptor implements ToXContent {
}
} else if (ParseFieldMatcher.STRICT.match(currentFieldName, Fields.PRIVILEGES)) {
privileges = readStringArray(roleName, parser, true);
if (names.length == 0) {
throw new ElasticsearchParseException("failed to parse indices privileges for role [{}]. [{}] cannot be an empty " +
"array", roleName, currentFieldName);
}
} else if (ParseFieldMatcher.STRICT.match(currentFieldName, Fields.FIELDS)) {
fields = readStringArray(roleName, parser, true);
} else {

View File

@ -5,16 +5,18 @@
*/
package org.elasticsearch.integration;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.elasticsearch.http.HttpServerTransport;
import org.apache.http.HttpEntity;
import org.apache.http.StatusLine;
import org.apache.http.entity.StringEntity;
import org.apache.http.message.BasicHeader;
import org.apache.http.util.EntityUtils;
import org.elasticsearch.client.Response;
import org.elasticsearch.client.ResponseException;
import org.elasticsearch.client.RestClient;
import org.elasticsearch.test.SecurityIntegTestCase;
import org.elasticsearch.xpack.security.authc.support.Hasher;
import org.elasticsearch.xpack.security.authc.support.SecuredString;
import org.elasticsearch.xpack.security.authc.support.UsernamePasswordToken;
import org.elasticsearch.test.SecurityIntegTestCase;
import org.elasticsearch.test.rest.client.http.HttpRequestBuilder;
import org.elasticsearch.test.rest.client.http.HttpResponse;
import org.junit.After;
import java.io.IOException;
import java.util.HashMap;
@ -32,19 +34,16 @@ public abstract class AbstractPrivilegeTestCase extends SecurityIntegTestCase {
protected static final String USERS_PASSWD_HASHED = new String(Hasher.BCRYPT.hash(new SecuredString("passwd".toCharArray())));
private CloseableHttpClient httpClient = HttpClients.createDefault();
@After
public void cleanup() throws IOException {
httpClient.close();
}
protected void assertAccessIsAllowed(String user, String method, String uri, String body,
Map<String, String> params) throws IOException {
HttpResponse response = executeRequest(user, method, uri, body, params);
String message = String.format(Locale.ROOT, "%s %s: Expected no error got %s %s with body %s", method, uri,
response.getStatusCode(), response.getReasonPhrase(), response.getBody());
assertThat(message, response.getStatusCode(), is(not(greaterThanOrEqualTo(400))));
try (Response response = getRestClient().performRequest(method, uri, params, entityOrNull(body),
new BasicHeader(UsernamePasswordToken.BASIC_AUTH_HEADER,
UsernamePasswordToken.basicAuthHeaderValue(user, new SecuredString("passwd".toCharArray()))))) {
StatusLine statusLine = response.getStatusLine();
String message = String.format(Locale.ROOT, "%s %s: Expected no error got %s %s with body %s", method, uri,
statusLine.getStatusCode(), statusLine.getReasonPhrase(), EntityUtils.toString(response.getEntity()));
assertThat(message, statusLine.getStatusCode(), is(not(greaterThanOrEqualTo(400))));
}
}
protected void assertAccessIsAllowed(String user, String method, String uri, String body) throws IOException {
@ -65,28 +64,24 @@ public abstract class AbstractPrivilegeTestCase extends SecurityIntegTestCase {
protected void assertAccessIsDenied(String user, String method, String uri, String body,
Map<String, String> params) throws IOException {
HttpResponse response = executeRequest(user, method, uri, body, params);
String message = String.format(Locale.ROOT, "%s %s body %s: Expected 403, got %s %s with body %s", method, uri, body,
response.getStatusCode(), response.getReasonPhrase(), response.getBody());
assertThat(message, response.getStatusCode(), is(403));
try {
getRestClient().performRequest(method, uri, params, entityOrNull(body),
new BasicHeader(UsernamePasswordToken.BASIC_AUTH_HEADER,
UsernamePasswordToken.basicAuthHeaderValue(user, new SecuredString("passwd".toCharArray()))));
fail("request should have failed");
} catch(ResponseException e) {
StatusLine statusLine = e.getResponse().getStatusLine();
String message = String.format(Locale.ROOT, "%s %s body %s: Expected 403, got %s %s with body %s", method, uri, body,
statusLine.getStatusCode(), statusLine.getReasonPhrase(), e.getResponseBody());
assertThat(message, statusLine.getStatusCode(), is(403));
}
}
protected HttpResponse executeRequest(String user, String method, String uri, String body,
Map<String, String> params) throws IOException {
HttpServerTransport httpServerTransport = internalCluster().getDataNodeInstance(HttpServerTransport.class);
HttpRequestBuilder requestBuilder = new HttpRequestBuilder(httpClient).httpTransport(httpServerTransport);
requestBuilder.path(uri);
requestBuilder.method(method);
for (Map.Entry<String, String> entry : params.entrySet()) {
requestBuilder.addParam(entry.getKey(), entry.getValue());
}
private static HttpEntity entityOrNull(String body) {
HttpEntity entity = null;
if (body != null) {
requestBuilder.body(body);
entity = new StringEntity(body, RestClient.JSON_CONTENT_TYPE);
}
requestBuilder.addHeader(UsernamePasswordToken.BASIC_AUTH_HEADER, UsernamePasswordToken.basicAuthHeaderValue(user,
new SecuredString("passwd".toCharArray())));
return requestBuilder.execute();
return entity;
}
}

View File

@ -5,9 +5,15 @@
*/
package org.elasticsearch.integration;
import org.apache.http.Header;
import org.apache.http.entity.StringEntity;
import org.apache.http.message.BasicHeader;
import org.apache.http.util.EntityUtils;
import org.elasticsearch.action.bulk.BulkResponse;
import org.elasticsearch.action.get.GetResponse;
import org.elasticsearch.action.update.UpdateResponse;
import org.elasticsearch.client.Response;
import org.elasticsearch.client.RestClient;
import org.elasticsearch.common.network.NetworkModule;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.xpack.security.Security;
@ -15,10 +21,10 @@ import org.elasticsearch.xpack.security.authc.support.SecuredString;
import org.elasticsearch.xpack.security.authc.support.UsernamePasswordToken;
import org.elasticsearch.test.SecurityIntegTestCase;
import org.elasticsearch.test.SecuritySettingsSource;
import org.elasticsearch.test.rest.client.http.HttpResponse;
import org.elasticsearch.xpack.XPackPlugin;
import java.io.IOException;
import java.util.Collections;
import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.equalTo;
@ -49,8 +55,8 @@ public class BulkUpdateTests extends SecurityIntegTestCase {
.get().isCreated();
assertThat(created, is(false));
getResponse = internalCluster().transportClient().prepareGet("index1", "type", "1").setFields("test", "not test").get();
assertThat((String) getResponse.getField("test").getValue(), equalTo("test"));
assertThat((String) getResponse.getField("not test").getValue(), equalTo("not test"));
assertThat(getResponse.getField("test").getValue(), equalTo("test"));
assertThat(getResponse.getField("not test").getValue(), equalTo("not test"));
// this part is important. Without this, the document may be read from the translog which would bypass the bug where
// FLS kicks in because the request can't be found and only returns meta fields
@ -62,43 +68,61 @@ public class BulkUpdateTests extends SecurityIntegTestCase {
assertThat(((UpdateResponse)response.getItems()[0].getResponse()).isCreated(), is(false));
getResponse = internalCluster().transportClient().prepareGet("index1", "type", "1").
setFields("test", "not test", "bulk updated").get();
assertThat((String) getResponse.getField("test").getValue(), equalTo("test"));
assertThat((String) getResponse.getField("not test").getValue(), equalTo("not test"));
assertThat((String) getResponse.getField("bulk updated").getValue(), equalTo("bulk updated"));
assertThat(getResponse.getField("test").getValue(), equalTo("test"));
assertThat(getResponse.getField("not test").getValue(), equalTo("not test"));
assertThat(getResponse.getField("bulk updated").getValue(), equalTo("bulk updated"));
}
public void testThatBulkUpdateDoesNotLoseFieldsHttp() throws IOException {
final String path = "/index1/type/1";
final String basicAuthHeader = UsernamePasswordToken.basicAuthHeaderValue(SecuritySettingsSource.DEFAULT_USER_NAME,
new SecuredString(SecuritySettingsSource.DEFAULT_PASSWORD.toCharArray()));
final Header basicAuthHeader = new BasicHeader("Authorization",
UsernamePasswordToken.basicAuthHeaderValue(SecuritySettingsSource.DEFAULT_USER_NAME,
new SecuredString(SecuritySettingsSource.DEFAULT_PASSWORD.toCharArray())));
httpClient().path(path).addHeader("Authorization", basicAuthHeader).method("PUT").body("{\"test\":\"test\"}").execute();
HttpResponse response = httpClient().path(path).addHeader("Authorization", basicAuthHeader).method("GET").execute();
assertThat(response.getBody(), containsString("\"test\":\"test\""));
StringEntity body = new StringEntity("{\"test\":\"test\"}", RestClient.JSON_CONTENT_TYPE);
try (Response response = getRestClient().performRequest("PUT", path, Collections.emptyMap(), body, basicAuthHeader)) {
assertThat(response.getStatusLine().getStatusCode(), equalTo(201));
}
try (Response response = getRestClient().performRequest("GET", path, Collections.emptyMap(), null, basicAuthHeader)) {
assertThat(response.getStatusLine().getStatusCode(), equalTo(200));
assertThat(EntityUtils.toString(response.getEntity()), containsString("\"test\":\"test\""));
}
if (randomBoolean()) {
flushAndRefresh();
}
//update with new field
httpClient().path(path + "/_update").addHeader("Authorization", basicAuthHeader).method("POST").
body("{\"doc\": {\"not test\": \"not test\"}}").execute();
response = httpClient().path(path).addHeader("Authorization", basicAuthHeader).method("GET").execute();
assertThat(response.getBody(), containsString("\"test\":\"test\""));
assertThat(response.getBody(), containsString("\"not test\":\"not test\""));
body = new StringEntity("{\"doc\": {\"not test\": \"not test\"}}", RestClient.JSON_CONTENT_TYPE);
try (Response response = getRestClient().performRequest("POST", path + "/_update",
Collections.emptyMap(), body, basicAuthHeader)) {
assertThat(response.getStatusLine().getStatusCode(), equalTo(200));
}
try (Response response = getRestClient().performRequest("GET", path, Collections.emptyMap(), null, basicAuthHeader)) {
assertThat(response.getStatusLine().getStatusCode(), equalTo(200));
String responseBody = EntityUtils.toString(response.getEntity());
assertThat(responseBody, containsString("\"test\":\"test\""));
assertThat(responseBody, containsString("\"not test\":\"not test\""));
}
// this part is important. Without this, the document may be read from the translog which would bypass the bug where
// FLS kicks in because the request can't be found and only returns meta fields
flushAndRefresh();
// update with bulk
httpClient().path("/_bulk").addHeader("Authorization", basicAuthHeader).method("POST")
.body("{\"update\": {\"_index\": \"index1\", \"_type\": \"type\", \"_id\": \"1\"}}\n{\"doc\": {\"bulk updated\":\"bulk " +
"updated\"}}\n")
.execute();
response = httpClient().path(path).addHeader("Authorization", basicAuthHeader).method("GET").execute();
assertThat(response.getBody(), containsString("\"test\":\"test\""));
assertThat(response.getBody(), containsString("\"not test\":\"not test\""));
assertThat(response.getBody(), containsString("\"bulk updated\":\"bulk updated\""));
body = new StringEntity("{\"update\": {\"_index\": \"index1\", \"_type\": \"type\", \"_id\": \"1\"}}\n" +
"{\"doc\": {\"bulk updated\":\"bulk updated\"}}\n", RestClient.JSON_CONTENT_TYPE);
try (Response response = getRestClient().performRequest("POST", "/_bulk",
Collections.emptyMap(), body, basicAuthHeader)) {
assertThat(response.getStatusLine().getStatusCode(), equalTo(200));
}
try (Response response = getRestClient().performRequest("GET", path, Collections.emptyMap(), null, basicAuthHeader)) {
String responseBody = EntityUtils.toString(response.getEntity());
assertThat(responseBody, containsString("\"test\":\"test\""));
assertThat(responseBody, containsString("\"not test\":\"not test\""));
assertThat(responseBody, containsString("\"bulk updated\":\"bulk updated\""));
}
}
}

View File

@ -5,14 +5,15 @@
*/
package org.elasticsearch.integration;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.message.BasicHeader;
import org.apache.http.util.EntityUtils;
import org.elasticsearch.action.ActionListener;
import org.elasticsearch.client.Response;
import org.elasticsearch.common.Strings;
import org.elasticsearch.common.network.NetworkModule;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.http.HttpServerTransport;
import org.elasticsearch.xpack.security.user.User;
import org.elasticsearch.test.SecurityIntegTestCase;
import org.elasticsearch.test.SecuritySettingsSource;
import org.elasticsearch.xpack.security.action.realm.ClearRealmCacheRequest;
import org.elasticsearch.xpack.security.action.realm.ClearRealmCacheResponse;
import org.elasticsearch.xpack.security.authc.Realm;
@ -22,10 +23,7 @@ import org.elasticsearch.xpack.security.authc.support.SecuredString;
import org.elasticsearch.xpack.security.authc.support.SecuredStringTests;
import org.elasticsearch.xpack.security.authc.support.UsernamePasswordToken;
import org.elasticsearch.xpack.security.client.SecurityClient;
import org.elasticsearch.test.SecurityIntegTestCase;
import org.elasticsearch.test.SecuritySettingsSource;
import org.elasticsearch.test.rest.client.http.HttpRequestBuilder;
import org.elasticsearch.test.rest.client.http.HttpResponse;
import org.elasticsearch.xpack.security.user.User;
import org.junit.BeforeClass;
import java.util.ArrayList;
@ -39,14 +37,10 @@ import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.not;
import static org.hamcrest.Matchers.notNullValue;
import static org.hamcrest.Matchers.sameInstance;
/**
*
*/
public class ClearRealmsCacheTests extends SecurityIntegTestCase {
private static final String USERS_PASSWD_HASHED = new String(Hasher.BCRYPT.hash(new SecuredString("passwd".toCharArray())));
@ -168,21 +162,12 @@ public class ClearRealmsCacheTests extends SecurityIntegTestCase {
}
static void executeHttpRequest(String path, Map<String, String> params) throws Exception {
try (CloseableHttpClient client = HttpClients.createDefault()) {
HttpRequestBuilder requestBuilder = new HttpRequestBuilder(client)
.httpTransport(internalCluster().getDataNodeInstance(HttpServerTransport.class))
.method("POST")
.path(path);
for (Map.Entry<String, String> entry : params.entrySet()) {
requestBuilder.addParam(entry.getKey(), entry.getValue());
}
requestBuilder.addHeader(UsernamePasswordToken.BASIC_AUTH_HEADER,
UsernamePasswordToken.basicAuthHeaderValue(SecuritySettingsSource.DEFAULT_USER_NAME,
new SecuredString(SecuritySettingsSource.DEFAULT_PASSWORD.toCharArray())));
HttpResponse response = requestBuilder.execute();
assertThat(response.hasBody(), is(true));
String body = response.getBody();
assertThat(body.contains("cluster_name"), is(true));
try (Response response = getRestClient().performRequest("POST", path, params, null,
new BasicHeader(UsernamePasswordToken.BASIC_AUTH_HEADER,
UsernamePasswordToken.basicAuthHeaderValue(SecuritySettingsSource.DEFAULT_USER_NAME,
new SecuredString(SecuritySettingsSource.DEFAULT_PASSWORD.toCharArray()))))) {
assertNotNull(response.getEntity());
assertTrue(EntityUtils.toString(response.getEntity()).contains("cluster_name"));
}
}
}

View File

@ -5,9 +5,11 @@
*/
package org.elasticsearch.integration;
import org.apache.http.message.BasicHeader;
import org.elasticsearch.action.delete.DeleteResponse;
import org.elasticsearch.action.update.UpdateResponse;
import org.elasticsearch.client.Client;
import org.elasticsearch.client.Response;
import org.elasticsearch.common.Strings;
import org.elasticsearch.common.network.NetworkModule;
import org.elasticsearch.common.settings.Settings;
@ -23,11 +25,11 @@ import org.elasticsearch.xpack.security.authz.store.NativeRolesStore;
import org.elasticsearch.xpack.security.client.SecurityClient;
import org.elasticsearch.test.NativeRealmIntegTestCase;
import org.elasticsearch.test.SecuritySettingsSource;
import org.elasticsearch.test.rest.client.http.HttpResponse;
import org.junit.Before;
import org.junit.BeforeClass;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import static org.elasticsearch.action.support.WriteRequest.RefreshPolicy.IMMEDIATE;
@ -81,7 +83,7 @@ public class ClearRolesCacheTests extends NativeRealmIntegTestCase {
logger.debug("using poller interval [{}]", pollerInterval);
return Settings.builder()
.put(super.nodeSettings(nodeOrdinal))
.put(NativeRolesStore.POLL_INTERVAL_SETTING.getKey(), pollerInterval)
.put(NativeRolesStore.POLL_INTERVAL_SETTING.getKey(), pollerInterval.getStringRep())
.put(NetworkModule.HTTP_ENABLED.getKey(), true)
.build();
}
@ -128,7 +130,7 @@ public class ClearRolesCacheTests extends NativeRealmIntegTestCase {
final boolean useHttp = randomBoolean();
final boolean clearAll = randomBoolean();
logger.debug("--> starting to clear roles. using http [{}] clearing all [{}]", useHttp, clearAll);
String[] rolesToClear = clearAll ? (randomBoolean() ? roles : null) : toModify.toArray(Strings.EMPTY_ARRAY);
String[] rolesToClear = clearAll ? (randomBoolean() ? roles : null) : toModify.toArray(new String[toModify.size()]);
if (useHttp) {
String path;
if (rolesToClear == null) {
@ -136,12 +138,12 @@ public class ClearRolesCacheTests extends NativeRealmIntegTestCase {
} else {
path = "/_xpack/security/role/" + Strings.arrayToCommaDelimitedString(rolesToClear) + "/_clear_cache";
}
HttpResponse response = httpClient().path(path).method("POST")
.addHeader("Authorization",
try (Response response = getRestClient().performRequest("POST", path, Collections.emptyMap(), null,
new BasicHeader(UsernamePasswordToken.BASIC_AUTH_HEADER,
UsernamePasswordToken.basicAuthHeaderValue(SecuritySettingsSource.DEFAULT_USER_NAME,
new SecuredString(SecuritySettingsSource.DEFAULT_PASSWORD.toCharArray())))
.execute();
assertThat(response.getStatusCode(), is(RestStatus.OK.getStatus()));
new SecuredString(SecuritySettingsSource.DEFAULT_PASSWORD.toCharArray()))))) {
assertThat(response.getStatusLine().getStatusCode(), is(RestStatus.OK.getStatus()));
}
} else {
securityClient.prepareClearRolesCache().names(rolesToClear).get();
}

View File

@ -5,20 +5,21 @@
*/
package org.elasticsearch.integration;
import org.apache.lucene.util.LuceneTestCase.BadApple;
import org.apache.http.message.BasicHeader;
import org.elasticsearch.client.ResponseException;
import org.elasticsearch.common.network.NetworkModule;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.test.ESIntegTestCase;
import org.elasticsearch.test.rest.client.http.HttpResponse;
import org.elasticsearch.xpack.security.authc.support.SecuredString;
import org.elasticsearch.xpack.security.authc.support.UsernamePasswordToken;
import org.junit.Before;
import java.util.Collections;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
import static java.util.Collections.singletonMap;
import static org.hamcrest.Matchers.containsString;
import static org.apache.lucene.util.LuceneTestCase.BadApple;
import static org.hamcrest.Matchers.is;
//test is just too slow, please fix it to not be sleep-based
@ -303,8 +304,14 @@ public class IndexPrivilegeTests extends AbstractPrivilegeTestCase {
}
public void testThatUnknownUserIsRejectedProperly() throws Exception {
HttpResponse response = executeRequest("idonotexist", "GET", "/", null, new HashMap<>());
assertThat(response.getStatusCode(), is(401));
try {
getRestClient().performRequest("GET", "/", Collections.emptyMap(), null,
new BasicHeader(UsernamePasswordToken.BASIC_AUTH_HEADER,
UsernamePasswordToken.basicAuthHeaderValue("idonotexist", new SecuredString("passwd".toCharArray()))));
fail("request should have failed");
} catch(ResponseException e) {
assertThat(e.getResponse().getStatusLine().getStatusCode(), is(401));
}
}
private void assertUserExecutes(String user, String action, String index, boolean userIsAllowed) throws Exception {

View File

@ -14,6 +14,8 @@ import org.elasticsearch.action.admin.cluster.stats.ClusterStatsResponse;
import org.elasticsearch.action.admin.indices.stats.IndicesStatsResponse;
import org.elasticsearch.action.index.IndexResponse;
import org.elasticsearch.client.Client;
import org.elasticsearch.client.Response;
import org.elasticsearch.client.ResponseException;
import org.elasticsearch.client.transport.NoNodeAvailableException;
import org.elasticsearch.client.transport.TransportClient;
import org.elasticsearch.common.component.AbstractComponent;
@ -194,13 +196,20 @@ public class LicensingTests extends SecurityIntegTestCase {
}
public void testRestAuthenticationByLicenseType() throws Exception {
// the default of the licensing tests is basic
assertThat(httpClient().path("/").execute().getStatusCode(), is(200));
try (Response response = getRestClient().performRequest("GET", "/", Collections.emptyMap(), null)) {
// the default of the licensing tests is basic
assertThat(response.getStatusLine().getStatusCode(), is(200));
}
// generate a new license with a mode that enables auth
OperationMode mode = randomFrom(OperationMode.GOLD, OperationMode.TRIAL, OperationMode.PLATINUM, OperationMode.STANDARD);
enableLicensing(mode);
assertThat(httpClient().path("/").execute().getStatusCode(), is(401));
try {
getRestClient().performRequest("GET", "/", Collections.emptyMap(), null);
fail("request should have failed");
} catch(ResponseException e) {
assertThat(e.getResponse().getStatusLine().getStatusCode(), is(401));
}
}
public void testTransportClientAuthenticationByLicenseType() throws Exception {

View File

@ -19,7 +19,6 @@ import org.junit.After;
import org.junit.BeforeClass;
import static org.hamcrest.Matchers.instanceOf;
import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.not;
/**

View File

@ -5,23 +5,21 @@
*/
package org.elasticsearch.xpack.security;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.message.BasicHeader;
import org.elasticsearch.client.Response;
import org.elasticsearch.client.ResponseException;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.http.HttpServerTransport;
import org.elasticsearch.xpack.security.authc.support.SecuredString;
import org.elasticsearch.xpack.security.authc.support.UsernamePasswordToken;
import org.elasticsearch.test.SecurityIntegTestCase;
import org.elasticsearch.test.SecuritySettingsSource;
import org.elasticsearch.test.rest.client.http.HttpRequestBuilder;
import org.elasticsearch.test.rest.client.http.HttpResponse;
import java.io.IOException;
import java.util.Collections;
import static org.elasticsearch.rest.RestStatus.OK;
import static org.elasticsearch.rest.RestStatus.UNAUTHORIZED;
import static org.elasticsearch.xpack.security.authc.support.UsernamePasswordToken.basicAuthHeaderValue;
import static org.hamcrest.Matchers.allOf;
import static org.hamcrest.Matchers.is;
public class SecurityPluginTests extends SecurityIntegTestCase {
@ -35,24 +33,20 @@ public class SecurityPluginTests extends SecurityIntegTestCase {
}
public void testThatPluginIsLoaded() throws IOException {
HttpServerTransport httpServerTransport = internalCluster().getDataNodeInstance(HttpServerTransport.class);
try (CloseableHttpClient httpClient = HttpClients.createDefault()) {
try {
logger.info("executing unauthorized request to /_xpack info");
HttpResponse response = new HttpRequestBuilder(httpClient).httpTransport(httpServerTransport)
.method("GET")
.path("/_xpack")
.execute();
assertThat(response.getStatusCode(), is(UNAUTHORIZED.getStatus()));
getRestClient().performRequest("GET", "/_xpack", Collections.emptyMap(), null);
fail("request should have failed");
} catch(ResponseException e) {
assertThat(e.getResponse().getStatusLine().getStatusCode(), is(UNAUTHORIZED.getStatus()));
}
logger.info("executing authorized request to /_xpack infos");
response = new HttpRequestBuilder(httpClient).httpTransport(httpServerTransport)
.method("GET")
.path("/_xpack")
.addHeader(UsernamePasswordToken.BASIC_AUTH_HEADER,
logger.info("executing authorized request to /_xpack infos");
try (Response response = getRestClient().performRequest("GET", "/_xpack", Collections.emptyMap(), null,
new BasicHeader(UsernamePasswordToken.BASIC_AUTH_HEADER,
basicAuthHeaderValue(SecuritySettingsSource.DEFAULT_USER_NAME,
new SecuredString(SecuritySettingsSource.DEFAULT_PASSWORD.toCharArray())))
.execute();
assertThat(response.getStatusCode(), is(OK.getStatus()));
new SecuredString(SecuritySettingsSource.DEFAULT_PASSWORD.toCharArray()))))) {
assertThat(response.getStatusLine().getStatusCode(), is(OK.getStatus()));
}
}
}

View File

@ -5,10 +5,13 @@
*/
package org.elasticsearch.xpack.security.authc;
import org.apache.http.message.BasicHeader;
import org.elasticsearch.ElasticsearchSecurityException;
import org.elasticsearch.action.admin.cluster.health.ClusterHealthResponse;
import org.elasticsearch.action.admin.cluster.node.info.NodeInfo;
import org.elasticsearch.action.admin.cluster.node.info.NodesInfoResponse;
import org.elasticsearch.client.Response;
import org.elasticsearch.client.ResponseException;
import org.elasticsearch.client.transport.TransportClient;
import org.elasticsearch.common.network.NetworkModule;
import org.elasticsearch.common.settings.Settings;
@ -20,7 +23,6 @@ import org.elasticsearch.xpack.security.authc.support.UsernamePasswordToken;
import org.elasticsearch.xpack.security.transport.netty.SecurityNettyTransport;
import org.elasticsearch.test.SecurityIntegTestCase;
import org.elasticsearch.test.SecuritySettingsSource;
import org.elasticsearch.test.rest.client.http.HttpResponse;
import org.elasticsearch.xpack.XPackPlugin;
import java.util.Collections;
@ -115,30 +117,36 @@ public class RunAsIntegTests extends SecurityIntegTestCase {
public void testUserImpersonationUsingHttp() throws Exception {
// use the transport client user and try to run as
HttpResponse response = httpClient().method("GET")
.path("/_nodes")
.addHeader(UsernamePasswordToken.BASIC_AUTH_HEADER, UsernamePasswordToken.basicAuthHeaderValue(TRANSPORT_CLIENT_USER,
SecuredStringTests.build(SecuritySettingsSource.DEFAULT_PASSWORD)))
.addHeader(InternalAuthenticationService.RUN_AS_USER_HEADER, SecuritySettingsSource.DEFAULT_USER_NAME)
.execute();
assertThat(response.getStatusCode(), is(403));
try {
getRestClient().performRequest("GET", "/_nodes", Collections.emptyMap(), null,
new BasicHeader(UsernamePasswordToken.BASIC_AUTH_HEADER,
UsernamePasswordToken.basicAuthHeaderValue(TRANSPORT_CLIENT_USER,
SecuredStringTests.build(SecuritySettingsSource.DEFAULT_PASSWORD))),
new BasicHeader(InternalAuthenticationService.RUN_AS_USER_HEADER, SecuritySettingsSource.DEFAULT_USER_NAME));
fail("request should have failed");
} catch(ResponseException e) {
assertThat(e.getResponse().getStatusLine().getStatusCode(), is(403));
}
//the run as user shouldn't have access to the nodes api
response = httpClient().method("GET")
.path("/_nodes")
.addHeader(UsernamePasswordToken.BASIC_AUTH_HEADER, UsernamePasswordToken.basicAuthHeaderValue(RUN_AS_USER,
SecuredStringTests.build(SecuritySettingsSource.DEFAULT_PASSWORD)))
.execute();
assertThat(response.getStatusCode(), is(403));
try {
//the run as user shouldn't have access to the nodes api
getRestClient().performRequest("GET", "/_nodes", Collections.emptyMap(), null,
new BasicHeader(UsernamePasswordToken.BASIC_AUTH_HEADER,
UsernamePasswordToken.basicAuthHeaderValue(RUN_AS_USER,
SecuredStringTests.build(SecuritySettingsSource.DEFAULT_PASSWORD))));
fail("request should have failed");
} catch(ResponseException e) {
assertThat(e.getResponse().getStatusLine().getStatusCode(), is(403));
}
// but when running as a different user it should work
response = httpClient().method("GET")
.path("/_nodes")
.addHeader(UsernamePasswordToken.BASIC_AUTH_HEADER, UsernamePasswordToken.basicAuthHeaderValue(RUN_AS_USER,
SecuredStringTests.build(SecuritySettingsSource.DEFAULT_PASSWORD)))
.addHeader(InternalAuthenticationService.RUN_AS_USER_HEADER, SecuritySettingsSource.DEFAULT_USER_NAME)
.execute();
assertThat(response.getStatusCode(), is(200));
try (Response response = getRestClient().performRequest("GET", "/_nodes", Collections.emptyMap(), null,
new BasicHeader(UsernamePasswordToken.BASIC_AUTH_HEADER,
UsernamePasswordToken.basicAuthHeaderValue(RUN_AS_USER,
SecuredStringTests.build(SecuritySettingsSource.DEFAULT_PASSWORD))),
new BasicHeader(InternalAuthenticationService.RUN_AS_USER_HEADER, SecuritySettingsSource.DEFAULT_USER_NAME))) {
assertThat(response.getStatusLine().getStatusCode(), is(200));
}
}
public void testEmptyUserImpersonationHeader() throws Exception {
@ -164,13 +172,16 @@ public class RunAsIntegTests extends SecurityIntegTestCase {
}
public void testEmptyHeaderUsingHttp() throws Exception {
HttpResponse response = httpClient().method("GET")
.path("/_nodes")
.addHeader(UsernamePasswordToken.BASIC_AUTH_HEADER, UsernamePasswordToken.basicAuthHeaderValue(RUN_AS_USER,
SecuredStringTests.build(SecuritySettingsSource.DEFAULT_PASSWORD)))
.addHeader(InternalAuthenticationService.RUN_AS_USER_HEADER, "")
.execute();
assertThat(response.getStatusCode(), is(401));
try {
getRestClient().performRequest("GET", "/_nodes", Collections.emptyMap(), null,
new BasicHeader(UsernamePasswordToken.BASIC_AUTH_HEADER,
UsernamePasswordToken.basicAuthHeaderValue(RUN_AS_USER,
SecuredStringTests.build(SecuritySettingsSource.DEFAULT_PASSWORD))),
new BasicHeader(InternalAuthenticationService.RUN_AS_USER_HEADER, ""));
fail("request should have failed");
} catch(ResponseException e) {
assertThat(e.getResponse().getStatusLine().getStatusCode(), is(401));
}
}
public void testNonExistentRunAsUser() throws Exception {
@ -196,13 +207,16 @@ public class RunAsIntegTests extends SecurityIntegTestCase {
}
public void testNonExistentRunAsUserUsingHttp() throws Exception {
HttpResponse response = httpClient().method("GET")
.path("/_nodes")
.addHeader(UsernamePasswordToken.BASIC_AUTH_HEADER, UsernamePasswordToken.basicAuthHeaderValue(RUN_AS_USER,
SecuredStringTests.build(SecuritySettingsSource.DEFAULT_PASSWORD)))
.addHeader(InternalAuthenticationService.RUN_AS_USER_HEADER, "idontexist")
.execute();
assertThat(response.getStatusCode(), is(403));
try {
getRestClient().performRequest("GET", "/_nodes", Collections.emptyMap(), null,
new BasicHeader(UsernamePasswordToken.BASIC_AUTH_HEADER,
UsernamePasswordToken.basicAuthHeaderValue(RUN_AS_USER,
SecuredStringTests.build(SecuritySettingsSource.DEFAULT_PASSWORD))),
new BasicHeader(InternalAuthenticationService.RUN_AS_USER_HEADER, "idontexist"));
fail("request should have failed");
} catch (ResponseException e) {
assertThat(e.getResponse().getStatusLine().getStatusCode(), is(403));
}
}
// build our own here to better mimic an actual client...

View File

@ -7,11 +7,14 @@ package org.elasticsearch.xpack.security.authc.pki;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.message.BasicHeader;
import org.elasticsearch.client.Response;
import org.elasticsearch.client.ResponseException;
import org.elasticsearch.client.RestClient;
import org.elasticsearch.client.transport.TransportClient;
import org.elasticsearch.common.network.NetworkModule;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.transport.InetSocketTransportAddress;
import org.elasticsearch.http.HttpServerTransport;
import org.elasticsearch.xpack.security.Security;
import org.elasticsearch.xpack.security.authc.support.SecuredString;
import org.elasticsearch.xpack.security.authc.support.UsernamePasswordToken;
@ -20,8 +23,6 @@ import org.elasticsearch.xpack.security.transport.netty.SecurityNettyHttpServerT
import org.elasticsearch.xpack.security.transport.netty.SecurityNettyTransport;
import org.elasticsearch.test.SecurityIntegTestCase;
import org.elasticsearch.test.SecuritySettingsSource;
import org.elasticsearch.test.rest.client.http.HttpRequestBuilder;
import org.elasticsearch.test.rest.client.http.HttpResponse;
import org.elasticsearch.transport.Transport;
import org.elasticsearch.xpack.XPackPlugin;
import org.junit.BeforeClass;
@ -34,6 +35,7 @@ import java.nio.file.Files;
import java.nio.file.Path;
import java.security.KeyStore;
import java.security.SecureRandom;
import java.util.Collections;
import static org.elasticsearch.test.SecuritySettingsSource.DEFAULT_PASSWORD;
import static org.elasticsearch.test.SecuritySettingsSource.DEFAULT_USER_NAME;
@ -78,23 +80,21 @@ public class PkiOptionalClientAuthTests extends SecurityIntegTestCase {
}
public void testRestClientWithoutClientCertificate() throws Exception {
HttpServerTransport httpServerTransport = internalCluster().getDataNodeInstance(HttpServerTransport.class);
CloseableHttpClient httpClient = HttpClients.custom().setSSLContext(getSSLContext()).build();
try (RestClient restClient = createRestClient(httpClient, "https")) {
try {
restClient.performRequest("GET", "_nodes", Collections.emptyMap(), null);
fail("request should have failed");
} catch(ResponseException e) {
assertThat(e.getResponse().getStatusLine().getStatusCode(), is(401));
}
try (CloseableHttpClient httpClient = HttpClients.custom().setSslcontext(getSSLContext()).build()) {
HttpRequestBuilder requestBuilder = new HttpRequestBuilder(httpClient)
.host("localhost")
.port(((InetSocketTransportAddress)httpServerTransport.boundAddress().publishAddress()).address().getPort())
.protocol("https")
.method("GET")
.path("/_nodes");
HttpResponse response = requestBuilder.execute();
assertThat(response.getStatusCode(), is(401));
requestBuilder.addHeader(UsernamePasswordToken.BASIC_AUTH_HEADER,
UsernamePasswordToken.basicAuthHeaderValue(SecuritySettingsSource.DEFAULT_USER_NAME,
new SecuredString(SecuritySettingsSource.DEFAULT_PASSWORD.toCharArray())));
response = requestBuilder.execute();
assertThat(response.getStatusCode(), is(200));
try (Response response = restClient.performRequest("GET", "_nodes", Collections.emptyMap(), null,
new BasicHeader(UsernamePasswordToken.BASIC_AUTH_HEADER,
UsernamePasswordToken.basicAuthHeaderValue(SecuritySettingsSource.DEFAULT_USER_NAME,
new SecuredString(SecuritySettingsSource.DEFAULT_PASSWORD.toCharArray()))))) {
assertThat(response.getStatusLine().getStatusCode(), is(200));
}
}
}

View File

@ -8,27 +8,27 @@ package org.elasticsearch.xpack.security.authc.pki;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.message.BasicHeader;
import org.elasticsearch.client.Client;
import org.elasticsearch.client.Response;
import org.elasticsearch.client.RestClient;
import org.elasticsearch.common.network.NetworkModule;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.transport.InetSocketTransportAddress;
import org.elasticsearch.http.HttpServerTransport;
import org.elasticsearch.test.ESIntegTestCase.ClusterScope;
import org.elasticsearch.test.SecurityIntegTestCase;
import org.elasticsearch.test.SecuritySettingsSource;
import org.elasticsearch.xpack.security.authc.support.SecuredString;
import org.elasticsearch.xpack.security.authc.support.UsernamePasswordToken;
import org.elasticsearch.xpack.security.transport.SSLClientAuth;
import org.elasticsearch.xpack.security.transport.netty.SecurityNettyHttpServerTransport;
import org.elasticsearch.xpack.security.transport.netty.SecurityNettyTransport;
import org.elasticsearch.test.ESIntegTestCase.ClusterScope;
import org.elasticsearch.test.SecurityIntegTestCase;
import org.elasticsearch.test.SecuritySettingsSource;
import org.elasticsearch.test.rest.client.http.HttpRequestBuilder;
import org.elasticsearch.test.rest.client.http.HttpResponse;
import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
import java.security.SecureRandom;
import java.security.cert.X509Certificate;
import java.util.Collections;
import java.util.Locale;
import static org.hamcrest.Matchers.is;
@ -77,21 +77,16 @@ public class PkiWithoutClientAuthenticationTests extends SecurityIntegTestCase {
}
public void testThatHttpWorks() throws Exception {
HttpServerTransport httpServerTransport = internalCluster().getDataNodeInstance(HttpServerTransport.class);
SSLContext sc = SSLContext.getInstance("SSL");
sc.init(null, trustAllCerts, new SecureRandom());
try (CloseableHttpClient httpClient = HttpClients.custom().setSslcontext(sc).build()) {
HttpRequestBuilder requestBuilder = new HttpRequestBuilder(httpClient)
.host("localhost")
.port(((InetSocketTransportAddress)httpServerTransport.boundAddress().publishAddress()).address().getPort())
.protocol("https")
.method("GET")
.path("/_nodes");
requestBuilder.addHeader(UsernamePasswordToken.BASIC_AUTH_HEADER,
UsernamePasswordToken.basicAuthHeaderValue(SecuritySettingsSource.DEFAULT_USER_NAME,
new SecuredString(SecuritySettingsSource.DEFAULT_PASSWORD.toCharArray())));
HttpResponse response = requestBuilder.execute();
assertThat(response.getStatusCode(), is(200));
CloseableHttpClient httpClient = HttpClients.custom().setSSLContext(sc).build();
try (RestClient restClient = createRestClient(httpClient, "https")) {
try (Response response = restClient.performRequest("GET", "/_nodes", Collections.emptyMap(), null,
new BasicHeader(UsernamePasswordToken.BASIC_AUTH_HEADER,
UsernamePasswordToken.basicAuthHeaderValue(SecuritySettingsSource.DEFAULT_USER_NAME,
new SecuredString(SecuritySettingsSource.DEFAULT_PASSWORD.toCharArray()))))) {
assertThat(response.getStatusLine().getStatusCode(), is(200));
}
}
}
}

View File

@ -5,19 +5,18 @@
*/
package org.elasticsearch.xpack.security.authc.pki;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.message.BasicHeader;
import org.elasticsearch.client.Client;
import org.elasticsearch.client.Response;
import org.elasticsearch.common.network.NetworkModule;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.http.HttpServerTransport;
import org.elasticsearch.xpack.security.authc.support.SecuredString;
import org.elasticsearch.xpack.security.authc.support.UsernamePasswordToken;
import org.elasticsearch.test.ESIntegTestCase.ClusterScope;
import org.elasticsearch.test.SecurityIntegTestCase;
import org.elasticsearch.test.SecuritySettingsSource;
import org.elasticsearch.test.rest.client.http.HttpRequestBuilder;
import org.elasticsearch.test.rest.client.http.HttpResponse;
import org.elasticsearch.xpack.security.authc.support.SecuredString;
import org.elasticsearch.xpack.security.authc.support.UsernamePasswordToken;
import java.util.Collections;
import static org.hamcrest.Matchers.is;
@ -44,17 +43,11 @@ public class PkiWithoutSSLTests extends SecurityIntegTestCase {
}
public void testThatHttpWorks() throws Exception {
HttpServerTransport httpServerTransport = internalCluster().getDataNodeInstance(HttpServerTransport.class);
try (CloseableHttpClient httpClient = HttpClients.createDefault()) {
HttpRequestBuilder requestBuilder = new HttpRequestBuilder(httpClient)
.httpTransport(httpServerTransport)
.method("GET")
.path("/_nodes");
requestBuilder.addHeader(UsernamePasswordToken.BASIC_AUTH_HEADER,
UsernamePasswordToken.basicAuthHeaderValue(SecuritySettingsSource.DEFAULT_USER_NAME,
new SecuredString(SecuritySettingsSource.DEFAULT_PASSWORD.toCharArray())));
HttpResponse response = requestBuilder.execute();
assertThat(response.getStatusCode(), is(200));
try (Response response = getRestClient().performRequest("GET", "/_nodes", Collections.emptyMap(), null,
new BasicHeader(UsernamePasswordToken.BASIC_AUTH_HEADER,
UsernamePasswordToken.basicAuthHeaderValue(SecuritySettingsSource.DEFAULT_USER_NAME,
new SecuredString(SecuritySettingsSource.DEFAULT_PASSWORD.toCharArray()))))) {
assertThat(response.getStatusLine().getStatusCode(), is(200));
}
}
}

View File

@ -64,6 +64,7 @@ import java.util.Collections;
import java.util.IdentityHashMap;
import java.util.Set;
import static java.util.Collections.emptyList;
import static java.util.Collections.emptySet;
import static java.util.Collections.singleton;
import static java.util.Collections.singletonMap;
@ -94,7 +95,7 @@ public class SecurityIndexSearcherWrapperUnitTests extends ESTestCase {
Collections.emptyMap(), Collections.emptyMap());
SimilarityService similarityService = new SimilarityService(indexSettings, Collections.emptyMap());
mapperService = new MapperService(indexSettings, analysisService, similarityService,
new IndicesModule(new NamedWriteableRegistry()).getMapperRegistry(), () -> null);
new IndicesModule(new NamedWriteableRegistry(), emptyList()).getMapperRegistry(), () -> null);
ShardId shardId = new ShardId(index, 0);
licenseState = mock(SecurityLicenseState.class);

View File

@ -5,6 +5,10 @@
*/
package org.elasticsearch.xpack.security.rest.action;
import org.apache.http.message.BasicHeader;
import org.apache.http.util.EntityUtils;
import org.elasticsearch.client.Response;
import org.elasticsearch.client.ResponseException;
import org.elasticsearch.common.network.NetworkModule;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.xpack.security.authc.support.SecuredString;
@ -12,10 +16,10 @@ import org.elasticsearch.xpack.security.authz.InternalAuthorizationService;
import org.elasticsearch.xpack.security.user.AnonymousUser;
import org.elasticsearch.test.SecurityIntegTestCase;
import org.elasticsearch.test.SecuritySettingsSource;
import org.elasticsearch.test.rest.client.http.HttpResponse;
import org.elasticsearch.test.rest.json.JsonPath;
import org.junit.BeforeClass;
import java.util.Collections;
import java.util.List;
import static org.elasticsearch.xpack.security.authc.support.UsernamePasswordToken.basicAuthHeaderValue;
@ -47,32 +51,40 @@ public class RestAuthenticateActionTests extends SecurityIntegTestCase {
}
public void testAuthenticateApi() throws Exception {
HttpResponse response = httpClient().method("GET").path("/_xpack/security/_authenticate")
.addHeader("Authorization", basicAuthHeaderValue(SecuritySettingsSource.DEFAULT_USER_NAME,
new SecuredString(SecuritySettingsSource.DEFAULT_PASSWORD.toCharArray())))
.execute();
assertThat(response.getStatusCode(), is(200));
JsonPath jsonPath = new JsonPath(response.getBody());
assertThat(jsonPath.evaluate("username").toString(), equalTo(SecuritySettingsSource.DEFAULT_USER_NAME));
List<String> roles = (List<String>) jsonPath.evaluate("roles");
assertThat(roles.size(), is(1));
assertThat(roles, contains(SecuritySettingsSource.DEFAULT_ROLE));
try (Response response = getRestClient().performRequest(
"GET", "/_xpack/security/_authenticate", Collections.emptyMap(), null,
new BasicHeader("Authorization", basicAuthHeaderValue(SecuritySettingsSource.DEFAULT_USER_NAME,
new SecuredString(SecuritySettingsSource.DEFAULT_PASSWORD.toCharArray()))))) {
assertThat(response.getStatusLine().getStatusCode(), is(200));
JsonPath jsonPath = new JsonPath(EntityUtils.toString(response.getEntity()));
assertThat(jsonPath.evaluate("username").toString(), equalTo(SecuritySettingsSource.DEFAULT_USER_NAME));
@SuppressWarnings("unchecked")
List<String> roles = (List<String>) jsonPath.evaluate("roles");
assertThat(roles.size(), is(1));
assertThat(roles, contains(SecuritySettingsSource.DEFAULT_ROLE));
}
}
public void testAuthenticateApiWithoutAuthentication() throws Exception {
HttpResponse response = httpClient().method("GET").path("/_xpack/security/_authenticate")
.execute();
if (anonymousEnabled) {
assertThat(response.getStatusCode(), is(200));
JsonPath jsonPath = new JsonPath(response.getBody());
assertThat(jsonPath.evaluate("username").toString(), equalTo("anon"));
List<String> roles = (List<String>) jsonPath.evaluate("roles");
assertThat(roles.size(), is(2));
assertThat(roles, contains(SecuritySettingsSource.DEFAULT_ROLE, "foo"));
} else {
assertThat(response.getStatusCode(), is(401));
try (Response response = getRestClient().performRequest("GET", "/_xpack/security/_authenticate",
Collections.emptyMap(), null)) {
if (anonymousEnabled) {
assertThat(response.getStatusLine().getStatusCode(), is(200));
JsonPath jsonPath = new JsonPath(EntityUtils.toString(response.getEntity()));
assertThat(jsonPath.evaluate("username").toString(), equalTo("anon"));
@SuppressWarnings("unchecked")
List<String> roles = (List<String>) jsonPath.evaluate("roles");
assertThat(roles.size(), is(2));
assertThat(roles, contains(SecuritySettingsSource.DEFAULT_ROLE, "foo"));
} else {
fail("request should have failed");
}
} catch(ResponseException e) {
if (anonymousEnabled) {
fail("request should have succeeded");
} else {
assertThat(e.getResponse().getStatusLine().getStatusCode(), is(401));
}
}
}
}

View File

@ -9,20 +9,21 @@ import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.conn.ssl.SSLContexts;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.message.BasicHeader;
import org.apache.http.util.EntityUtils;
import org.elasticsearch.ElasticsearchException;
import org.elasticsearch.client.Response;
import org.elasticsearch.client.RestClient;
import org.elasticsearch.client.transport.TransportClient;
import org.elasticsearch.common.network.NetworkModule;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.transport.TransportAddress;
import org.elasticsearch.http.HttpServerTransport;
import org.elasticsearch.xpack.security.Security;
import org.elasticsearch.xpack.security.ssl.ClientSSLService;
import org.elasticsearch.xpack.security.ssl.SSLConfiguration.Global;
import org.elasticsearch.xpack.security.transport.netty.SecurityNettyHttpServerTransport;
import org.elasticsearch.xpack.security.transport.netty.SecurityNettyTransport;
import org.elasticsearch.test.SecurityIntegTestCase;
import org.elasticsearch.test.rest.client.http.HttpRequestBuilder;
import org.elasticsearch.test.rest.client.http.HttpResponse;
import org.elasticsearch.transport.Transport;
import org.elasticsearch.xpack.XPackPlugin;
@ -30,10 +31,12 @@ import javax.net.ssl.SSLHandshakeException;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Collections;
import static org.elasticsearch.xpack.security.authc.support.UsernamePasswordToken.basicAuthHeaderValue;
import static org.elasticsearch.test.SecuritySettingsSource.getSSLSettingsForStore;
import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.equalTo;
public class SslClientAuthTests extends SecurityIntegTestCase {
@Override
@ -59,14 +62,8 @@ public class SslClientAuthTests extends SecurityIntegTestCase {
SSLContexts.createDefault(),
SSLConnectionSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);
CloseableHttpClient client = HttpClients.custom().setSSLSocketFactory(socketFactory).build();
try {
new HttpRequestBuilder(client)
.httpTransport(internalCluster().getInstance(HttpServerTransport.class))
.method("GET").path("/")
.protocol("https")
.execute();
try (RestClient restClient = createRestClient(HttpClients.custom().setSSLSocketFactory(socketFactory).build(), "https")) {
restClient.performRequest("GET", "/", Collections.emptyMap(), null);
fail("Expected SSLHandshakeException");
} catch (SSLHandshakeException e) {
assertThat(e.getMessage(), containsString("unable to find valid certification path to requested target"));
@ -85,13 +82,13 @@ public class SslClientAuthTests extends SecurityIntegTestCase {
CloseableHttpClient client = HttpClients.custom().setSSLSocketFactory(socketFactory).build();
HttpResponse response = new HttpRequestBuilder(client)
.httpTransport(internalCluster().getInstance(HttpServerTransport.class))
.method("GET").path("/")
.protocol("https")
.addHeader("Authorization", basicAuthHeaderValue(transportClientUsername(), transportClientPassword()))
.execute();
assertThat(response.getBody(), containsString("You Know, for Search"));
try (RestClient restClient = createRestClient(client, "https")) {
try (Response response = restClient.performRequest("GET", "/", Collections.emptyMap(), null,
new BasicHeader("Authorization", basicAuthHeaderValue(transportClientUsername(), transportClientPassword())))) {
assertThat(response.getStatusLine().getStatusCode(), equalTo(200));
assertThat(EntityUtils.toString(response.getEntity()), containsString("You Know, for Search"));
}
}
}
public void testThatTransportWorksWithoutSslClientAuth() throws Exception {

View File

@ -5,25 +5,16 @@
*/
package org.elasticsearch.xpack.security.user;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.elasticsearch.common.io.Streams;
import org.elasticsearch.client.Response;
import org.elasticsearch.client.ResponseException;
import org.elasticsearch.common.network.NetworkModule;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.transport.InetSocketTransportAddress;
import org.elasticsearch.common.transport.TransportAddress;
import org.elasticsearch.http.HttpServerTransport;
import org.elasticsearch.xpack.security.authz.InternalAuthorizationService;
import org.elasticsearch.test.SecurityIntegTestCase;
import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;
import java.util.Locale;
import java.util.Collections;
import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.instanceOf;
import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.notNullValue;
import static org.hamcrest.Matchers.nullValue;
@ -51,28 +42,22 @@ public class AnonymousUserIntegTests extends SecurityIntegTestCase {
}
public void testAnonymousViaHttp() throws Exception {
try (CloseableHttpClient client = HttpClients.createDefault();
CloseableHttpResponse response = client.execute(new HttpGet(getNodeUrl() + "_nodes"))) {
int statusCode = response.getStatusLine().getStatusCode();
String data = Streams.copyToString(new InputStreamReader(response.getEntity().getContent(), StandardCharsets.UTF_8));
try {
getRestClient().performRequest("GET", "/_nodes", Collections.emptyMap(), null);
fail("request should have failed");
} catch(ResponseException e) {
int statusCode = e.getResponse().getStatusLine().getStatusCode();
Response response = e.getResponse();
if (authorizationExceptionsEnabled) {
assertThat(statusCode, is(403));
assertThat(response.getFirstHeader("WWW-Authenticate"), nullValue());
assertThat(data, containsString("security_exception"));
assertThat(response.getHeader("WWW-Authenticate"), nullValue());
assertThat(e.getResponseBody(), containsString("security_exception"));
} else {
assertThat(statusCode, is(401));
assertThat(response.getFirstHeader("WWW-Authenticate"), notNullValue());
assertThat(response.getFirstHeader("WWW-Authenticate").getValue(), containsString("Basic"));
assertThat(data, containsString("security_exception"));
assertThat(response.getHeader("WWW-Authenticate"), notNullValue());
assertThat(response.getHeader("WWW-Authenticate"), containsString("Basic"));
assertThat(e.getResponseBody(), containsString("security_exception"));
}
}
}
private String getNodeUrl() {
TransportAddress transportAddress =
randomFrom(internalCluster().getInstance(HttpServerTransport.class).boundAddress().boundAddresses());
assertThat(transportAddress, is(instanceOf(InetSocketTransportAddress.class)));
InetSocketTransportAddress inetSocketTransportAddress = (InetSocketTransportAddress) transportAddress;
return String.format(Locale.ROOT, "http://%s:%s/", "localhost", inetSocketTransportAddress.address().getPort());
}
}

View File

@ -1,4 +1,3 @@
cluster:admin/render/template/search
cluster:admin/repository/delete
cluster:admin/repository/get
cluster:admin/repository/put

View File

@ -1,4 +1,3 @@
cluster:admin/render/template/search
cluster:admin/snapshot/status[nodes]
cluster:admin/snapshot/status[nodes][n]
cluster:admin/tasks/cancel[n]

View File

@ -22,6 +22,21 @@
}
- match: { role: { created: true } }
- do:
xpack.security.put_role:
name: "backwards_role"
body: >
{
"cluster": ["all"],
"indices": [
{
"privileges": ["all"],
"names": "*"
}
]
}
- match: { role: { created: true } }
- do:
xpack.security.put_user:
username: "joe"

View File

@ -5,7 +5,6 @@
*/
package org.elasticsearch.xpack.common.http;
import com.google.common.collect.ImmutableMap;
import org.elasticsearch.ElasticsearchParseException;
import org.elasticsearch.common.Nullable;
import org.elasticsearch.common.ParseField;
@ -87,11 +86,11 @@ public class HttpResponse implements ToXContent {
* in the payload
*/
public Map<String, List<String>> headers() {
ImmutableMap.Builder<String, List<String>> builder = ImmutableMap.builder();
MapBuilder<String, List<String>> builder = MapBuilder.newMapBuilder();
for (Map.Entry<String, String[]> entry : headers.entrySet()) {
builder.put(entry.getKey().toLowerCase(Locale.ROOT), Arrays.asList(entry.getValue()));
}
return builder.build();
return builder.immutableMap();
}
public String[] header(String header) {

View File

@ -7,6 +7,7 @@ package org.elasticsearch.xpack.notification.email;
import org.elasticsearch.ElasticsearchException;
import org.elasticsearch.common.SuppressForbidden;
import org.elasticsearch.common.inject.Provider;
import org.elasticsearch.common.xcontent.ToXContent;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.common.xcontent.XContentType;
@ -16,19 +17,26 @@ import javax.activation.DataHandler;
import javax.activation.DataSource;
import javax.activation.FileDataSource;
import javax.mail.MessagingException;
import javax.mail.Part;
import javax.mail.internet.MimeBodyPart;
import javax.mail.util.ByteArrayDataSource;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.file.Path;
import static javax.mail.Part.ATTACHMENT;
import static javax.mail.Part.INLINE;
/**
*
*/
public abstract class Attachment extends BodyPartSource {
protected Attachment(String id, String name, String contentType) {
private final boolean inline;
protected Attachment(String id, String name, String contentType, boolean inline) {
super(id, name, contentType);
this.inline = inline;
}
@Override
@ -36,13 +44,17 @@ public abstract class Attachment extends BodyPartSource {
MimeBodyPart part = new MimeBodyPart();
part.setContentID(id);
part.setFileName(name);
part.setDisposition(Part.ATTACHMENT);
part.setDisposition(inline ? INLINE : ATTACHMENT);
writeTo(part);
return part;
}
public abstract String type();
public boolean isInline() {
return inline;
}
/**
* intentionally not emitting path as it may come as an information leak
*/
@ -65,22 +77,22 @@ public abstract class Attachment extends BodyPartSource {
private final Path path;
private final DataSource dataSource;
public File(String id, Path path) {
this(id, path.getFileName().toString(), path);
public File(String id, Path path, boolean inline) {
this(id, path.getFileName().toString(), path, inline);
}
public File(String id, Path path, String contentType) {
this(id, path.getFileName().toString(), path, contentType);
public File(String id, Path path, String contentType, boolean inline) {
this(id, path.getFileName().toString(), path, contentType, inline);
}
@SuppressForbidden(reason = "uses toFile")
public File(String id, String name, Path path) {
this(id, name, path, fileTypeMap.getContentType(path.toFile()));
public File(String id, String name, Path path, boolean inline) {
this(id, name, path, fileTypeMap.getContentType(path.toFile()), inline);
}
@SuppressForbidden(reason = "uses toFile")
public File(String id, String name, Path path, String contentType) {
super(id, name, contentType);
public File(String id, String name, Path path, String contentType, boolean inline) {
super(id, name, contentType, inline);
this.path = path;
this.dataSource = new FileDataSource(path.toFile());
}
@ -105,16 +117,16 @@ public abstract class Attachment extends BodyPartSource {
private final byte[] bytes;
public Bytes(String id, byte[] bytes, String contentType) {
this(id, id, bytes, contentType);
public Bytes(String id, byte[] bytes, String contentType, boolean inline) {
this(id, id, bytes, contentType, inline);
}
public Bytes(String id, String name, byte[] bytes) {
this(id, name, bytes, fileTypeMap.getContentType(name));
public Bytes(String id, String name, byte[] bytes, boolean inline) {
this(id, name, bytes, fileTypeMap.getContentType(name), inline);
}
public Bytes(String id, String name, byte[] bytes, String contentType) {
super(id, name, contentType);
public Bytes(String id, String name, byte[] bytes, String contentType, boolean inline) {
super(id, name, contentType, inline);
this.bytes = bytes;
}
@ -134,6 +146,68 @@ public abstract class Attachment extends BodyPartSource {
}
}
public static class Stream extends Attachment {
static final String TYPE = "stream";
private final Provider<InputStream> source;
public Stream(String id, String name, boolean inline, Provider<InputStream> source) {
this(id, name, fileTypeMap.getContentType(name), inline, source);
}
public Stream(String id, String name, String contentType, boolean inline, Provider<InputStream> source) {
super(id, name, contentType, inline);
this.source = source;
}
@Override
public String type() {
return TYPE;
}
@Override
protected void writeTo(MimeBodyPart part) throws MessagingException {
DataSource ds = new StreamDataSource(name, contentType, source);
DataHandler dh = new DataHandler(ds);
part.setDataHandler(dh);
}
static class StreamDataSource implements DataSource {
private final String name;
private final String contentType;
private final Provider<InputStream> source;
public StreamDataSource(String name, String contentType, Provider<InputStream> source) {
this.name = name;
this.contentType = contentType;
this.source = source;
}
@Override
public InputStream getInputStream() throws IOException {
return source.get();
}
@Override
public OutputStream getOutputStream() throws IOException {
throw new UnsupportedOperationException();
}
@Override
public String getContentType() {
return contentType;
}
@Override
public String getName() {
return name;
}
}
}
public static class XContent extends Bytes {
protected XContent(String id, ToXContent content, XContentType type) {
@ -141,7 +215,7 @@ public abstract class Attachment extends BodyPartSource {
}
protected XContent(String id, String name, ToXContent content, XContentType type) {
super(id, name, bytes(name, content, type), mimeType(type));
super(id, name, bytes(name, content, type), mimeType(type), false);
}
static String mimeType(XContentType type) {

View File

@ -49,11 +49,10 @@ public class Email implements ToXContent {
final String textBody;
final String htmlBody;
final Map<String, Attachment> attachments;
final Map<String, Inline> inlines;
public Email(String id, Address from, AddressList replyTo, Priority priority, DateTime sentDate,
AddressList to, AddressList cc, AddressList bcc, String subject, String textBody, String htmlBody,
Map<String, Attachment> attachments, Map<String, Inline> inlines) {
Map<String, Attachment> attachments) {
this.id = id;
this.from = from;
@ -67,7 +66,6 @@ public class Email implements ToXContent {
this.textBody = textBody;
this.htmlBody = htmlBody;
this.attachments = attachments;
this.inlines = inlines;
}
public String id() {
@ -118,10 +116,6 @@ public class Email implements ToXContent {
return attachments;
}
public Map<String, Inline> inlines() {
return inlines;
}
@Override
public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
builder.startObject();
@ -249,7 +243,6 @@ public class Email implements ToXContent {
private String textBody;
private String htmlBody;
private Map<String, Attachment> attachments = new HashMap<>();
private Map<String, Inline> inlines = new HashMap<>();
private Builder() {
}
@ -267,7 +260,6 @@ public class Email implements ToXContent {
textBody = email.textBody;
htmlBody = email.htmlBody;
attachments.putAll(email.attachments);
inlines.putAll(email.inlines);
return this;
}
@ -358,14 +350,6 @@ public class Email implements ToXContent {
return this;
}
public Builder inline(Inline inline) {
if (inlines == null) {
throw new IllegalStateException("Email has already been built!");
}
inlines.put(inline.id(), inline);
return this;
}
/**
* Build the email. Note that adding items to attachments or inlines
* after this is called is incorrect.
@ -373,9 +357,8 @@ public class Email implements ToXContent {
public Email build() {
assert id != null : "email id should not be null (should be set to the watch id";
Email email = new Email(id, from, replyTo, priority, sentDate, to, cc, bcc, subject, textBody, htmlBody,
unmodifiableMap(attachments), unmodifiableMap(inlines));
unmodifiableMap(attachments));
attachments = null;
inlines = null;
return email;
}

View File

@ -1,189 +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.notification.email;
import org.elasticsearch.common.SuppressForbidden;
import org.elasticsearch.common.bytes.BytesArray;
import org.elasticsearch.common.bytes.BytesReference;
import org.elasticsearch.common.inject.Provider;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.xpack.notification.email.support.BodyPartSource;
import javax.activation.DataHandler;
import javax.activation.DataSource;
import javax.activation.FileDataSource;
import javax.mail.MessagingException;
import javax.mail.Part;
import javax.mail.internet.MimeBodyPart;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.file.Path;
/**
*
*/
public abstract class Inline extends BodyPartSource {
protected Inline(String id, String name, String contentType) {
super(id, name, contentType);
}
public abstract String type();
@Override
public final MimeBodyPart bodyPart() throws MessagingException {
MimeBodyPart part = new MimeBodyPart();
part.setDisposition(Part.INLINE);
part.setContentID(id);
part.setFileName(name);
writeTo(part);
return part;
}
/**
* intentionally not emitting path as it may come as an information leak
*/
@Override
public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
return builder.startObject()
.field("type", type())
.field("id", id)
.field("name", name)
.field("content_type", contentType)
.endObject();
}
protected abstract void writeTo(MimeBodyPart part) throws MessagingException;
public static class File extends Inline {
static final String TYPE = "file";
private final Path path;
private DataSource dataSource;
public File(String id, Path path) {
this(id, path.getFileName().toString(), path);
}
@SuppressForbidden(reason = "uses toFile")
public File(String id, String name, Path path) {
this(id, name, path, fileTypeMap.getContentType(path.toFile()));
}
@SuppressForbidden(reason = "uses toFile")
public File(String id, String name, Path path, String contentType) {
super(id, name, contentType);
this.path = path;
this.dataSource = new FileDataSource(path.toFile());
}
public Path path() {
return path;
}
public String type() {
return TYPE;
}
@Override
public void writeTo(MimeBodyPart part) throws MessagingException {
part.setDataHandler(new DataHandler(dataSource, contentType));
}
}
public static class Stream extends Inline {
static final String TYPE = "stream";
private final Provider<InputStream> source;
public Stream(String id, String name, Provider<InputStream> source) {
this(id, name, fileTypeMap.getContentType(name), source);
}
public Stream(String id, String name, String contentType, Provider<InputStream> source) {
super(id, name, contentType);
this.source = source;
}
@Override
public String type() {
return TYPE;
}
@Override
protected void writeTo(MimeBodyPart part) throws MessagingException {
DataSource ds = new StreamDataSource(name, contentType, source);
DataHandler dh = new DataHandler(ds);
part.setDataHandler(dh);
}
static class StreamDataSource implements DataSource {
private final String name;
private final String contentType;
private final Provider<InputStream> source;
public StreamDataSource(String name, String contentType, Provider<InputStream> source) {
this.name = name;
this.contentType = contentType;
this.source = source;
}
@Override
public InputStream getInputStream() throws IOException {
return source.get();
}
@Override
public OutputStream getOutputStream() throws IOException {
throw new UnsupportedOperationException();
}
@Override
public String getContentType() {
return contentType;
}
@Override
public String getName() {
return name;
}
}
}
public static class Bytes extends Stream {
public Bytes(String id, String name, String contentType, byte[] bytes) {
super(id, name, contentType, new BytesStreamProvider(bytes));
}
public Bytes(String id, String name, String contentType, BytesReference bytes) {
super(id, name, contentType, new BytesStreamProvider(bytes));
}
static class BytesStreamProvider implements Provider<InputStream> {
private final BytesReference bytes;
public BytesStreamProvider(byte[] bytes) {
this(new BytesArray(bytes));
}
public BytesStreamProvider(BytesReference bytes) {
this.bytes = bytes;
}
@Override
public InputStream get() {
return new ByteArrayInputStream(bytes.array(), bytes.arrayOffset(), bytes.length());
}
}
}
}

View File

@ -92,15 +92,13 @@ public enum Profile implements ToXContent {
alternative.addBodyPart(html);
}
if (!email.inlines.isEmpty()) {
for (Inline inline : email.inlines.values()) {
related.addBodyPart(inline.bodyPart());
}
}
if (!email.attachments.isEmpty()) {
for (Attachment attachment : email.attachments.values()) {
mixed.addBodyPart(attachment.bodyPart());
if (attachment.isInline()) {
related.addBodyPart(attachment.bodyPart());
} else {
mixed.addBodyPart(attachment.bodyPart());
}
}
}

View File

@ -54,10 +54,16 @@ public class DataAttachment implements EmailAttachmentParser.EmailAttachment {
return Objects.hash(id, dataAttachment);
}
@Override
public String id() {
return id;
}
@Override
public boolean inline() {
return false;
}
public static Builder builder(String id) {
return new Builder(id);
}

View File

@ -30,6 +30,14 @@ public interface EmailAttachmentParser<T extends EmailAttachmentParser.EmailAtta
* @return The id of this attachment
*/
String id();
/**
* Allows the attachment to decide of it should be of disposition type attachment or inline, which is important
* for being able to display inside of desktop email clients
*
* @return a boolean flagging this attachment as being inline
*/
boolean inline();
}
/**

View File

@ -14,15 +14,15 @@ import org.elasticsearch.common.inject.Inject;
import org.elasticsearch.common.logging.ESLogger;
import org.elasticsearch.common.logging.Loggers;
import org.elasticsearch.common.xcontent.XContentParser;
import org.elasticsearch.xpack.watcher.execution.WatchExecutionContext;
import org.elasticsearch.xpack.watcher.support.Variables;
import org.elasticsearch.xpack.common.http.HttpClient;
import org.elasticsearch.xpack.common.http.HttpRequest;
import org.elasticsearch.xpack.common.http.HttpRequestTemplate;
import org.elasticsearch.xpack.common.http.HttpResponse;
import org.elasticsearch.xpack.common.text.TextTemplateEngine;
import org.elasticsearch.xpack.watcher.watch.Payload;
import org.elasticsearch.xpack.notification.email.Attachment;
import org.elasticsearch.xpack.watcher.execution.WatchExecutionContext;
import org.elasticsearch.xpack.watcher.support.Variables;
import org.elasticsearch.xpack.watcher.watch.Payload;
import java.io.IOException;
import java.util.Map;
@ -30,6 +30,7 @@ import java.util.Map;
public class HttpEmailAttachementParser implements EmailAttachmentParser<HttpRequestAttachment> {
public interface Fields {
ParseField INLINE = new ParseField("inline");
ParseField REQUEST = new ParseField("request");
ParseField CONTENT_TYPE = new ParseField("content_type");
}
@ -56,6 +57,7 @@ public class HttpEmailAttachementParser implements EmailAttachmentParser<HttpReq
@Override
public HttpRequestAttachment parse(String id, XContentParser parser) throws IOException {
boolean inline = false;
String contentType = null;
HttpRequestTemplate requestTemplate = null;
@ -66,6 +68,8 @@ public class HttpEmailAttachementParser implements EmailAttachmentParser<HttpReq
currentFieldName = parser.currentName();
} else if (ParseFieldMatcher.STRICT.match(currentFieldName, Fields.CONTENT_TYPE)) {
contentType = parser.text();
} else if (ParseFieldMatcher.STRICT.match(currentFieldName, Fields.INLINE)) {
inline = parser.booleanValue();
} else if (ParseFieldMatcher.STRICT.match(currentFieldName, Fields.REQUEST)) {
requestTemplate = requestTemplateParser.parse(parser);
} else {
@ -75,7 +79,7 @@ public class HttpEmailAttachementParser implements EmailAttachmentParser<HttpReq
}
if (requestTemplate != null) {
return new HttpRequestAttachment(id, requestTemplate, contentType);
return new HttpRequestAttachment(id, requestTemplate, inline, contentType);
}
throw new ElasticsearchParseException("Could not parse http request attachment");
@ -94,7 +98,7 @@ public class HttpEmailAttachementParser implements EmailAttachmentParser<HttpReq
if (response.hasContent()) {
String contentType = attachment.getContentType();
String attachmentContentType = Strings.hasLength(contentType) ? contentType : response.contentType();
return new Attachment.Bytes(attachment.id(), response.body().toBytes(), attachmentContentType);
return new Attachment.Bytes(attachment.id(), response.body().toBytes(), attachmentContentType, attachment.inline());
} else {
logger.error("Empty response body: [host[{}], port[{}], method[{}], path[{}]: response status [{}]", httpRequest.host(),
httpRequest.port(), httpRequest.method(), httpRequest.path(), response.status());

View File

@ -16,12 +16,14 @@ import java.util.Objects;
public class HttpRequestAttachment implements EmailAttachmentParser.EmailAttachment {
private final HttpRequestTemplate requestTemplate;
private boolean inline;
private final String contentType;
private final String id;
public HttpRequestAttachment(String id, HttpRequestTemplate requestTemplate, @Nullable String contentType) {
public HttpRequestAttachment(String id, HttpRequestTemplate requestTemplate, boolean inline, @Nullable String contentType) {
this.id = id;
this.requestTemplate = requestTemplate;
this.inline = inline;
this.contentType = contentType;
}
@ -38,6 +40,11 @@ public class HttpRequestAttachment implements EmailAttachmentParser.EmailAttachm
return id;
}
@Override
public boolean inline() {
return inline;
}
@Override
public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
builder.startObject(id)
@ -46,6 +53,9 @@ public class HttpRequestAttachment implements EmailAttachmentParser.EmailAttachm
if (Strings.hasLength(contentType)) {
builder.field(HttpEmailAttachementParser.Fields.CONTENT_TYPE.getPreferredName(), contentType);
}
if (inline) {
builder.field(HttpEmailAttachementParser.Fields.INLINE.getPreferredName(), inline);
}
return builder.endObject().endObject();
}
@ -65,12 +75,12 @@ public class HttpRequestAttachment implements EmailAttachmentParser.EmailAttachm
HttpRequestAttachment otherDataAttachment = (HttpRequestAttachment) o;
return Objects.equals(id, otherDataAttachment.id) && Objects.equals(requestTemplate, otherDataAttachment.requestTemplate)
&& Objects.equals(contentType, otherDataAttachment.contentType);
&& Objects.equals(contentType, otherDataAttachment.contentType) && Objects.equals(inline, otherDataAttachment.inline);
}
@Override
public int hashCode() {
return Objects.hash(id, requestTemplate, contentType);
return Objects.hash(id, requestTemplate, contentType, inline);
}
public static class Builder {
@ -78,6 +88,7 @@ public class HttpRequestAttachment implements EmailAttachmentParser.EmailAttachm
private String id;
private HttpRequestTemplate httpRequestTemplate;
private String contentType;
private boolean inline = false;
private Builder(String id) {
this.id = id;
@ -93,8 +104,13 @@ public class HttpRequestAttachment implements EmailAttachmentParser.EmailAttachm
return this;
}
public Builder inline(boolean inline) {
this.inline = inline;
return this;
}
public HttpRequestAttachment build() {
return new HttpRequestAttachment(id, httpRequestTemplate, contentType);
return new HttpRequestAttachment(id, httpRequestTemplate, inline, contentType);
}
}

View File

@ -5,7 +5,6 @@
*/
package org.elasticsearch.xpack.common.http;
import com.google.common.collect.Lists;
import org.elasticsearch.common.bytes.BytesArray;
import org.elasticsearch.common.bytes.BytesReference;
import org.elasticsearch.common.xcontent.ToXContent;
@ -13,9 +12,9 @@ import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.common.xcontent.XContentFactory;
import org.elasticsearch.common.xcontent.XContentParser;
import org.elasticsearch.test.ESTestCase;
import org.elasticsearch.xpack.common.http.HttpResponse;
import java.nio.charset.StandardCharsets;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
@ -29,8 +28,8 @@ import static org.hamcrest.Matchers.hasEntry;
import static org.hamcrest.Matchers.hasKey;
import static org.hamcrest.Matchers.instanceOf;
import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.notNullValue;
import static org.hamcrest.Matchers.not;
import static org.hamcrest.Matchers.notNullValue;
/**
*
@ -122,9 +121,9 @@ public class HttpResponseTests extends ESTestCase {
Map<String, Object> responseHeaders = (Map<String, Object>) responseMap.get("headers");
assertThat(responseHeaders, not(hasKey("es.index")));
assertThat(responseHeaders, hasEntry("es_index", Lists.newArrayList("value")));
assertThat(responseHeaders, hasEntry("es_index", Collections.singletonList("value")));
assertThat(responseHeaders, not(hasKey("es.index.2")));
assertThat(responseHeaders, hasEntry("es_index_2", Lists.newArrayList("value")));
assertThat(responseHeaders, hasEntry("es_index_2", Collections.singletonList("value")));
}
}

View File

@ -41,9 +41,8 @@ public class EmailTests extends ESTestCase {
String textBody = randomFrom("Random Body", "", null);
String htmlBody = randomFrom("<hr /><b>BODY</b><hr />", "", null);
Map<String, Attachment> attachments = null;
Map<String, Inline> inlines = null;
Email email = new Email(id, from, replyTo, priority, sentDate, to, cc, bcc, subject, textBody, htmlBody, attachments, inlines);
Email email = new Email(id, from, replyTo, priority, sentDate, to, cc, bcc, subject, textBody, htmlBody, attachments);
XContentBuilder builder = XContentFactory.jsonBuilder();
email.toXContent(builder, ToXContent.EMPTY_PARAMS);

View File

@ -6,14 +6,13 @@
package org.elasticsearch.xpack.notification.email;
import org.apache.lucene.util.LuceneTestCase.AwaitsFix;
import org.elasticsearch.ElasticsearchException;
import org.elasticsearch.cli.Terminal;
import org.elasticsearch.common.settings.ClusterSettings;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.xcontent.ToXContent;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.xpack.common.secret.SecretService;
import java.io.IOException;
import java.util.Collections;
import java.util.Locale;
@ -26,13 +25,13 @@ public class ManualPublicSmtpServersTester {
public static void main(String[] args) throws Exception {
test(Profile.GMAIL, Settings.builder()
.put("xpack.notification.email.service.account.gmail.smtp.auth", true)
.put("xpack.notification.email.service.account.gmail.smtp.starttls.enable", true)
.put("xpack.notification.email.service.account.gmail.smtp.host", "smtp.gmail.com")
.put("xpack.notification.email.service.account.gmail.smtp.port", 587)
.put("xpack.notification.email.service.account.gmail.smtp.user", terminal.readText("username: "))
.put("xpack.notification.email.service.account.gmail.smtp.password", new String(terminal.readSecret("password: ")))
.put("xpack.notification.email.service.account.gmail.email_defaults.to", terminal.readText("to: "))
.put("xpack.notification.email.account.gmail.smtp.auth", true)
.put("xpack.notification.email.account.gmail.smtp.starttls.enable", true)
.put("xpack.notification.email.account.gmail.smtp.host", "smtp.gmail.com")
.put("xpack.notification.email.account.gmail.smtp.port", 587)
.put("xpack.notification.email.account.gmail.smtp.user", terminal.readText("username: "))
.put("xpack.notification.email.account.gmail.smtp.password", new String(terminal.readSecret("password: ")))
.put("xpack.notification.email.account.gmail.email_defaults.to", terminal.readText("to: "))
);
}
}
@ -41,13 +40,13 @@ public class ManualPublicSmtpServersTester {
public static void main(String[] args) throws Exception {
test(Profile.STANDARD, Settings.builder()
.put("xpack.notification.email.service.account.outlook.smtp.auth", true)
.put("xpack.notification.email.service.account.outlook.smtp.starttls.enable", true)
.put("xpack.notification.email.service.account.outlook.smtp.host", "smtp-mail.outlook.com")
.put("xpack.notification.email.service.account.outlook.smtp.port", 587)
.put("xpack.notification.email.service.account.outlook.smtp.user", "elastic.user@outlook.com")
.put("xpack.notification.email.service.account.outlook.smtp.password", "fantastic42")
.put("xpack.notification.email.service.account.outlook.email_defaults.to", "elastic.user@outlook.com")
.put("xpack.notification.email.account.outlook.smtp.auth", true)
.put("xpack.notification.email.account.outlook.smtp.starttls.enable", true)
.put("xpack.notification.email.account.outlook.smtp.host", "smtp-mail.outlook.com")
.put("xpack.notification.email.account.outlook.smtp.port", 587)
.put("xpack.notification.email.account.outlook.smtp.user", "elastic.user@outlook.com")
.put("xpack.notification.email.account.outlook.smtp.password", "fantastic42")
.put("xpack.notification.email.account.outlook.email_defaults.to", "elastic.user@outlook.com")
.put()
);
}
@ -57,15 +56,15 @@ public class ManualPublicSmtpServersTester {
public static void main(String[] args) throws Exception {
test(Profile.STANDARD, Settings.builder()
.put("xpack.notification.email.service.account.yahoo.smtp.starttls.enable", true)
.put("xpack.notification.email.service.account.yahoo.smtp.auth", true)
.put("xpack.notification.email.service.account.yahoo.smtp.host", "smtp.mail.yahoo.com")
.put("xpack.notification.email.service.account.yahoo.smtp.port", 587)
.put("xpack.notification.email.service.account.yahoo.smtp.user", "elastic.user@yahoo.com")
.put("xpack.notification.email.service.account.yahoo.smtp.password", "fantastic42")
.put("xpack.notification.email.account.yahoo.smtp.starttls.enable", true)
.put("xpack.notification.email.account.yahoo.smtp.auth", true)
.put("xpack.notification.email.account.yahoo.smtp.host", "smtp.mail.yahoo.com")
.put("xpack.notification.email.account.yahoo.smtp.port", 587)
.put("xpack.notification.email.account.yahoo.smtp.user", "elastic.user@yahoo.com")
.put("xpack.notification.email.account.yahoo.smtp.password", "fantastic42")
// note: from must be set to the same authenticated user account
.put("xpack.notification.email.service.account.yahoo.email_defaults.from", "elastic.user@yahoo.com")
.put("xpack.notification.email.service.account.yahoo.email_defaults.to", "elastic.user@yahoo.com")
.put("xpack.notification.email.account.yahoo.email_defaults.from", "elastic.user@yahoo.com")
.put("xpack.notification.email.account.yahoo.email_defaults.to", "elastic.user@yahoo.com")
);
}
}
@ -75,42 +74,43 @@ public class ManualPublicSmtpServersTester {
public static void main(String[] args) throws Exception {
test(Profile.STANDARD, Settings.builder()
.put("xpack.notification.email.service.account.ses.smtp.auth", true)
.put("xpack.notification.email.service.account.ses.smtp.starttls.enable", true)
.put("xpack.notification.email.service.account.ses.smtp.starttls.required", true)
.put("xpack.notification.email.service.account.ses.smtp.host", "email-smtp.us-east-1.amazonaws.com")
.put("xpack.notification.email.service.account.ses.smtp.port", 587)
.put("xpack.notification.email.service.account.ses.smtp.user", terminal.readText("user: "))
.put("xpack.notification.email.service.account.ses.email_defaults.from", "dummy.user@elasticsearch.com")
.put("xpack.notification.email.service.account.ses.email_defaults.to", terminal.readText("to: "))
.put("xpack.notification.email.service.account.ses.smtp.password",
.put("xpack.notification.email.account.ses.smtp.auth", true)
.put("xpack.notification.email.account.ses.smtp.starttls.enable", true)
.put("xpack.notification.email.account.ses.smtp.starttls.required", true)
.put("xpack.notification.email.account.ses.smtp.host", "email-smtp.us-east-1.amazonaws.com")
.put("xpack.notification.email.account.ses.smtp.port", 587)
.put("xpack.notification.email.account.ses.smtp.user", terminal.readText("user: "))
.put("xpack.notification.email.account.ses.email_defaults.from", "dummy.user@elasticsearch.com")
.put("xpack.notification.email.account.ses.email_defaults.to", terminal.readText("to: "))
.put("xpack.notification.email.account.ses.smtp.password",
new String(terminal.readSecret("password: ")))
);
}
}
static void test(Profile profile, Settings.Builder builder) throws Exception {
InternalEmailService service = startEmailService(builder);
static void test(Profile profile, Settings.Builder settingsBuilder) throws Exception {
String path = "/org/elasticsearch/xpack/watcher/actions/email/service/logo.png";
if (InternalEmailServiceTests.class.getResourceAsStream(path) == null) {
throw new ElasticsearchException("Could not find logo at path {}", path);
}
InternalEmailService service = startEmailService(settingsBuilder);
try {
ToXContent content = new ToXContent() {
@Override
public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
return builder.startObject()
.field("key1", "value1")
.field("key2", "value2")
.field("key3", "value3")
.endObject();
}
};
ToXContent content = (xContentBuilder, params) -> xContentBuilder.startObject()
.field("key1", "value1")
.field("key2", "value2")
.field("key3", "value3")
.endObject();
Email email = Email.builder()
.id("_id")
.subject("_subject")
.textBody("_text_body")
.htmlBody("<b>html body</b><p/><p/><img src=\"cid:logo\"/>")
.htmlBody("<b>html body</b><p/><p/><img src=\"cid:logo.png\"/>")
.attach(new Attachment.XContent.Yaml("test.yml", content))
.inline(new Inline.Stream("logo", "logo.jpg", () -> InternalEmailServiceTests.class.getResourceAsStream("logo.png")))
.attach(new Attachment.Stream("logo.png", "logo.png", true,
() -> InternalEmailServiceTests.class.getResourceAsStream(path)))
.build();
EmailService.EmailSent sent = service.send(email, null, profile);

View File

@ -0,0 +1,62 @@
/*
* 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.notification.email;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.test.ESTestCase;
import org.elasticsearch.xpack.common.secret.SecretService;
import javax.mail.BodyPart;
import javax.mail.Part;
import javax.mail.Session;
import javax.mail.internet.MimeMessage;
import javax.mail.internet.MimeMultipart;
import static org.hamcrest.Matchers.instanceOf;
import static org.hamcrest.Matchers.is;
public class ProfileTests extends ESTestCase {
public void testThatInlineAttachmentsAreCreated() throws Exception {
String path = "/org/elasticsearch/xpack/watcher/actions/email/service/logo.png";
Attachment attachment = new Attachment.Stream("inline.png", "inline.png", true,
() -> InternalEmailServiceTests.class.getResourceAsStream(path));
Email email = Email.builder()
.id("foo")
.from("foo@example.org")
.to("bar@example.org")
.subject(randomAsciiOfLength(10))
.attach(attachment)
.build();
Settings settings = Settings.builder()
.put("default_account", "foo")
.put("account.foo.smtp.host", "_host")
.build();
Accounts accounts = new Accounts(settings, SecretService.Insecure.INSTANCE, logger);
Session session = accounts.account("foo").getConfig().createSession();
MimeMessage mimeMessage = Profile.STANDARD.toMimeMessage(email, session);
Object content = ((MimeMultipart) mimeMessage.getContent()).getBodyPart(0).getContent();
assertThat(content, instanceOf(MimeMultipart.class));
MimeMultipart multipart = (MimeMultipart) content;
assertThat(multipart.getCount(), is(2));
boolean foundInlineAttachment = false;
BodyPart bodyPart = null;
for (int i = 0; i < multipart.getCount(); i++) {
bodyPart = multipart.getBodyPart(i);
if (Part.INLINE.equalsIgnoreCase(bodyPart.getDisposition())) {
foundInlineAttachment = true;
break;
}
}
assertThat("Expected to find an inline attachment in mime message, but didnt", foundInlineAttachment, is(true));
}
}

View File

@ -5,7 +5,6 @@
*/
package org.elasticsearch.xpack.notification.email.attachment;
import com.google.common.base.Charsets;
import org.elasticsearch.ElasticsearchParseException;
import org.elasticsearch.common.ParseField;
import org.elasticsearch.common.xcontent.ToXContent;
@ -13,13 +12,14 @@ import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.common.xcontent.XContentParser;
import org.elasticsearch.common.xcontent.json.JsonXContent;
import org.elasticsearch.test.ESTestCase;
import org.elasticsearch.xpack.watcher.execution.WatchExecutionContext;
import org.elasticsearch.xpack.common.http.HttpRequestTemplate;
import org.elasticsearch.xpack.common.http.Scheme;
import org.elasticsearch.xpack.watcher.watch.Payload;
import org.elasticsearch.xpack.notification.email.Attachment;
import org.elasticsearch.xpack.watcher.execution.WatchExecutionContext;
import org.elasticsearch.xpack.watcher.watch.Payload;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
@ -93,7 +93,8 @@ public class EmailAttachmentParsersTests extends ESTestCase {
attachments.add(new DataAttachment("my-name.json", org.elasticsearch.xpack.notification.email.DataAttachment.JSON));
HttpRequestTemplate requestTemplate = HttpRequestTemplate.builder("localhost", 80).scheme(Scheme.HTTP).path("/").build();
HttpRequestAttachment httpRequestAttachment = new HttpRequestAttachment("other-id", requestTemplate, null);
boolean inline = randomBoolean();
HttpRequestAttachment httpRequestAttachment = new HttpRequestAttachment("other-id", requestTemplate, inline, null);
attachments.add(httpRequestAttachment);
EmailAttachments emailAttachments = new EmailAttachments(attachments);
@ -105,6 +106,9 @@ public class EmailAttachmentParsersTests extends ESTestCase {
assertThat(builder.string(), containsString("other-id"));
assertThat(builder.string(), containsString("localhost"));
assertThat(builder.string(), containsString("/"));
if (inline) {
assertThat(builder.string(), containsString("inline"));
}
}
public void testThatTwoAttachmentsWithTheSameIdThrowError() throws Exception {
@ -161,7 +165,8 @@ public class EmailAttachmentParsersTests extends ESTestCase {
@Override
public Attachment toAttachment(WatchExecutionContext ctx, Payload payload, TestEmailAttachment attachment) {
return new Attachment.Bytes(attachment.id(), attachment.getValue().getBytes(Charsets.UTF_8), "personalContentType");
return new Attachment.Bytes(attachment.id(), attachment.getValue().getBytes(StandardCharsets.UTF_8),
"personalContentType", false);
}
}
@ -193,6 +198,11 @@ public class EmailAttachmentParsersTests extends ESTestCase {
return id;
}
@Override
public boolean inline() {
return false;
}
@Override
public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
return builder.startObject(id)

View File

@ -37,15 +37,13 @@ import static org.mockito.Mockito.when;
public class HttpEmailAttachementParserTests extends ESTestCase {
private SecretService.Insecure secretService;
private HttpAuthRegistry authRegistry;
private HttpRequestTemplate.Parser httpRequestTemplateParser;
private HttpClient httpClient;
@Before
public void init() throws Exception {
secretService = SecretService.Insecure.INSTANCE;
authRegistry = new HttpAuthRegistry(singletonMap(BasicAuth.TYPE, new BasicAuthFactory(secretService)));
SecretService.Insecure secretService = SecretService.Insecure.INSTANCE;
HttpAuthRegistry authRegistry = new HttpAuthRegistry(singletonMap(BasicAuth.TYPE, new BasicAuthFactory(secretService)));
httpRequestTemplateParser = new HttpRequestTemplate.Parser(authRegistry);
httpClient = mock(HttpClient.class);
@ -77,9 +75,12 @@ public class HttpEmailAttachementParserTests extends ESTestCase {
if (configureContentType) {
builder.field("content_type", "application/foo");
}
boolean isInline = randomBoolean();
if (isInline) {
builder.field("inline", true);
}
builder.endObject().endObject().endObject();
XContentParser parser = JsonXContent.jsonXContent.createParser(builder.bytes());
logger.info("JSON: {}", builder.string());
EmailAttachments emailAttachments = emailAttachmentsParser.parse(parser);
assertThat(emailAttachments.getAttachments(), hasSize(1));
@ -89,6 +90,7 @@ public class HttpEmailAttachementParserTests extends ESTestCase {
attachments.get(0).toXContent(toXcontentBuilder, ToXContent.EMPTY_PARAMS);
toXcontentBuilder.endObject();
assertThat(toXcontentBuilder.string(), is(builder.string()));
}
assertThat(attachments.get(0).inline(), is(isInline));
}
}

View File

@ -5,26 +5,10 @@
*/
package org.elasticsearch.xpack.test.rest;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.URI;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import com.carrotsearch.randomizedtesting.annotations.Name;
import com.carrotsearch.randomizedtesting.annotations.ParametersFactory;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpDelete;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPut;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.impl.conn.BasicHttpClientConnectionManager;
import org.elasticsearch.common.bytes.BytesReference;
import org.elasticsearch.common.io.Streams;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.unit.TimeValue;
import org.elasticsearch.common.util.concurrent.ThreadContext;
@ -33,15 +17,25 @@ import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.common.xcontent.XContentFactory;
import org.elasticsearch.common.xcontent.XContentParser;
import org.elasticsearch.license.plugin.TestUtils;
import org.elasticsearch.xpack.security.authc.support.SecuredString;
import org.elasticsearch.test.rest.ESRestTestCase;
import org.elasticsearch.test.rest.RestTestCandidate;
import org.elasticsearch.test.rest.client.RestTestResponse;
import org.elasticsearch.test.rest.parser.RestTestParseException;
import org.elasticsearch.xpack.security.authc.esnative.ReservedRealm;
import org.elasticsearch.xpack.security.authc.support.SecuredString;
import org.elasticsearch.xpack.security.authz.store.ReservedRolesStore;
import org.junit.After;
import org.junit.Before;
import java.io.IOException;
import java.util.List;
import java.util.Map;
import static java.util.Collections.emptyList;
import static java.util.Collections.emptyMap;
import static java.util.Collections.singletonList;
import static java.util.Collections.singletonMap;
import static org.elasticsearch.xpack.security.authc.support.UsernamePasswordToken.basicAuthHeaderValue;
import static org.hamcrest.Matchers.is;
public abstract class XPackRestTestCase extends ESRestTestCase {
@ -56,46 +50,16 @@ public abstract class XPackRestTestCase extends ESRestTestCase {
return ESRestTestCase.createParameters(0, 1);
}
@Before
public void startWatcher() throws Exception {
try (CloseableHttpClient client = HttpClients.createMinimal(new BasicHttpClientConnectionManager())) {
URL url = getClusterUrls()[0];
HttpPut request = new HttpPut(new URI("http",
null,
url.getHost(),
url.getPort(),
"/_xpack/watcher/_start", null, null));
request.addHeader("Authorization", BASIC_AUTH_VALUE);
try (CloseableHttpResponse response = client.execute(request)) {
}
}
}
@After
public void stopWatcher() throws Exception {
try(CloseableHttpClient client = HttpClients.createMinimal(new BasicHttpClientConnectionManager())) {
URL url = getClusterUrls()[0];
HttpPut request = new HttpPut(new URI("http",
null,
url.getHost(),
url.getPort(),
"/_xpack/watcher/_stop", null, null));
request.addHeader("Authorization", BASIC_AUTH_VALUE);
try (CloseableHttpResponse response = client.execute(request)) {
}
}
}
@Before
public void installLicense() throws Exception {
final XContentBuilder builder = XContentFactory.jsonBuilder();
TestUtils.generateSignedLicense("trial", TimeValue.timeValueHours(2)).toXContent(builder, ToXContent.EMPTY_PARAMS);
final BytesReference bytes = builder.bytes();
try (XContentParser parser = XContentFactory.xContent(bytes).createParser(bytes)) {
final List<Map<String, Object>> bodies = Collections.singletonList(Collections.singletonMap("license",
final List<Map<String, Object>> bodies = singletonList(singletonMap("license",
parser.map()));
getAdminExecutionContext().callApi("license.post", Collections.singletonMap("acknowledge", "true"),
bodies, Collections.singletonMap("Authorization", BASIC_AUTH_VALUE));
getAdminExecutionContext().callApi("license.post", singletonMap("acknowledge", "true"),
bodies, singletonMap("Authorization", BASIC_AUTH_VALUE));
}
}
@ -103,43 +67,21 @@ public abstract class XPackRestTestCase extends ESRestTestCase {
public void clearUsersAndRoles() throws Exception {
// we cannot delete the .security index from a rest test since we aren't the internal user, lets wipe the data
// TODO remove this once the built-in SUPERUSER role is added that can delete the index and we use the built in admin user here
try (CloseableHttpClient client = HttpClients.createMinimal(new BasicHttpClientConnectionManager())) {
final URL url = getClusterUrls()[0];
HttpGet getUsersRequest = new HttpGet(new URI("http", null, url.getHost(), url.getPort(), "/_xpack/security/user", null, null));
getUsersRequest.addHeader("Authorization", BASIC_AUTH_VALUE);
try (CloseableHttpResponse closeableHttpResponse = client.execute(getUsersRequest)) {
assertThat(closeableHttpResponse.getStatusLine().getStatusCode(), is(200));
String response = Streams.copyToString(
new InputStreamReader(closeableHttpResponse.getEntity().getContent(), StandardCharsets.UTF_8));
Map<String, Object> responseMap = XContentFactory.xContent(response).createParser(response).map();
// in the structure of this API, the users are the keyset
for (String user : responseMap.keySet()) {
HttpDelete delete = new HttpDelete(new URI("http", null, url.getHost(), url.getPort(),
"/_xpack/security/user/" + user, null, null));
delete.addHeader("Authorization", BASIC_AUTH_VALUE);
try (CloseableHttpResponse deleteResponse = client.execute(delete)) {
}
}
RestTestResponse response = getAdminExecutionContext().callApi("xpack.security.get_user", emptyMap(), emptyList(), emptyMap());
@SuppressWarnings("unchecked")
Map<String, Object> users = (Map<String, Object>) response.getBody();
for (String user: users.keySet()) {
if (ReservedRealm.isReserved(user) == false) {
getAdminExecutionContext().callApi("xpack.security.delete_user", singletonMap("username", user), emptyList(), emptyMap());
}
}
HttpGet getRolesRequest = new HttpGet(new URI("http", null, url.getHost(), url.getPort(), "/_xpack/security/role",
null, null));
getRolesRequest.addHeader("Authorization", BASIC_AUTH_VALUE);
try (CloseableHttpResponse closeableHttpResponse = client.execute(getRolesRequest)) {
assertThat(closeableHttpResponse.getStatusLine().getStatusCode(), is(200));
String response = Streams.copyToString(
new InputStreamReader(closeableHttpResponse.getEntity().getContent(), StandardCharsets.UTF_8));
Map<String, Object> responseMap = XContentFactory.xContent(response).createParser(response).map();
// in the structure of this API, the users are the keyset
for (String role : responseMap.keySet()) {
HttpDelete delete = new HttpDelete(new URI("http", null, url.getHost(), url.getPort(),
"/_xpack/security/role/" + role, null, null));
delete.addHeader("Authorization", BASIC_AUTH_VALUE);
try (CloseableHttpResponse deleteResponse = client.execute(delete)) {
}
}
response = getAdminExecutionContext().callApi("xpack.security.get_role", emptyMap(), emptyList(), emptyMap());
@SuppressWarnings("unchecked")
Map<String, Object> roles = (Map<String, Object>) response.getBody();
for (String role: roles.keySet()) {
if (ReservedRolesStore.isReserved(role) == false) {
getAdminExecutionContext().callApi("xpack.security.delete_role", singletonMap("name", role), emptyList(), emptyMap());
}
}
}

View File

@ -31,7 +31,6 @@ import org.elasticsearch.xpack.watcher.execution.InternalWatchExecutor;
import org.elasticsearch.xpack.watcher.history.HistoryModule;
import org.elasticsearch.xpack.watcher.history.HistoryStore;
import org.elasticsearch.xpack.watcher.input.InputModule;
import org.elasticsearch.xpack.watcher.input.chain.ChainInputFactory;
import org.elasticsearch.xpack.watcher.rest.action.RestAckWatchAction;
import org.elasticsearch.xpack.watcher.rest.action.RestActivateWatchAction;
import org.elasticsearch.xpack.watcher.rest.action.RestDeleteWatchAction;
@ -266,5 +265,5 @@ public class Watcher {
"[.watcher-history-YYYY.MM.dd] are allowed to be created", value);
}
}

View File

@ -15,7 +15,6 @@ import org.elasticsearch.common.logging.ESLogger;
import org.elasticsearch.common.unit.TimeValue;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.common.xcontent.XContentType;
import org.elasticsearch.index.mapper.internal.TimestampFieldMapper;
import org.elasticsearch.xpack.watcher.actions.Action;
import org.elasticsearch.xpack.watcher.actions.ExecutableAction;
import org.elasticsearch.xpack.watcher.execution.WatchExecutionContext;
@ -24,6 +23,7 @@ import org.elasticsearch.xpack.watcher.support.WatcherDateTimeUtils;
import org.elasticsearch.xpack.watcher.support.init.proxy.WatcherClientProxy;
import org.elasticsearch.xpack.watcher.support.xcontent.XContentSource;
import org.elasticsearch.xpack.watcher.watch.Payload;
import org.joda.time.DateTime;
import java.io.IOException;
import java.util.HashMap;
@ -63,19 +63,9 @@ public class ExecutableIndexAction extends ExecutableAction<IndexAction> {
}
IndexRequest indexRequest = new IndexRequest();
indexRequest.index(action.index);
indexRequest.type(action.docType);
if (action.executionTimeField != null && !TimestampFieldMapper.NAME.equals(action.executionTimeField)) {
if (!(data instanceof HashMap)) {
data = new HashMap<>(data); // ensuring mutability
}
data.put(action.executionTimeField, WatcherDateTimeUtils.formatDate(ctx.executionTime()));
} else {
indexRequest.timestamp(WatcherDateTimeUtils.formatDate(ctx.executionTime()));
}
data = addTimestampToDocument(data, ctx.executionTime());
indexRequest.source(jsonBuilder().prettyPrint().map(data));
if (ctx.simulateAction(actionId)) {
@ -100,14 +90,7 @@ public class ExecutableIndexAction extends ExecutableAction<IndexAction> {
IndexRequest indexRequest = new IndexRequest();
indexRequest.index(action.index);
indexRequest.type(action.docType);
if (action.executionTimeField != null && !TimestampFieldMapper.NAME.equals(action.executionTimeField)) {
if (!(doc instanceof HashMap)) {
doc = new HashMap<>(doc); // ensuring mutability
}
doc.put(action.executionTimeField, WatcherDateTimeUtils.formatDate(ctx.executionTime()));
} else {
indexRequest.timestamp(WatcherDateTimeUtils.formatDate(ctx.executionTime()));
}
doc = addTimestampToDocument(doc, ctx.executionTime());
indexRequest.source(jsonBuilder().prettyPrint().map(doc));
bulkRequest.add(indexRequest);
}
@ -121,6 +104,16 @@ public class ExecutableIndexAction extends ExecutableAction<IndexAction> {
return new IndexAction.Result.Success(new XContentSource(jsonBuilder.bytes(), XContentType.JSON));
}
private Map<String, Object> addTimestampToDocument(Map<String, Object> data, DateTime executionTime) {
if (action.executionTimeField != null) {
if (!(data instanceof HashMap)) {
data = new HashMap<>(data); // ensuring mutability
}
data.put(action.executionTimeField, WatcherDateTimeUtils.formatDate(executionTime));
}
return data;
}
static void indexResponseToXContent(XContentBuilder builder, IndexResponse response) throws IOException {
builder.startObject()
.field("created", response.isCreated())

View File

@ -6,14 +6,14 @@
package org.elasticsearch.xpack.watcher.input;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchRequestBuilder;
import org.elasticsearch.common.collect.MapBuilder;
import org.elasticsearch.xpack.common.http.HttpRequestTemplate;
import org.elasticsearch.xpack.watcher.input.chain.ChainInput;
import org.elasticsearch.xpack.watcher.input.http.HttpInput;
import org.elasticsearch.xpack.watcher.input.none.NoneInput;
import org.elasticsearch.xpack.watcher.input.search.SearchInput;
import org.elasticsearch.xpack.watcher.input.simple.SimpleInput;
import org.elasticsearch.xpack.common.http.HttpRequestTemplate;
import org.elasticsearch.xpack.watcher.support.search.WatcherSearchTemplateRequest;
import org.elasticsearch.xpack.watcher.watch.Payload;
import java.util.HashMap;
@ -31,12 +31,12 @@ public final class InputBuilders {
return NoneInput.builder();
}
public static SearchInput.Builder searchInput(SearchRequest request) {
public static SearchInput.Builder searchInput(WatcherSearchTemplateRequest request) {
return SearchInput.builder(request);
}
public static SearchInput.Builder searchInput(SearchRequestBuilder builder) {
return searchInput(builder.request());
public static SearchInput.Builder searchInput(SearchRequest request) {
return searchInput(new WatcherSearchTemplateRequest(request));
}
public static SimpleInput.Builder simpleInput() {

View File

@ -11,16 +11,15 @@ import org.elasticsearch.action.search.SearchType;
import org.elasticsearch.common.Nullable;
import org.elasticsearch.common.logging.ESLogger;
import org.elasticsearch.common.unit.TimeValue;
import org.elasticsearch.common.xcontent.ToXContent;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.common.xcontent.XContentHelper;
import org.elasticsearch.common.xcontent.XContentParser;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.xpack.watcher.execution.WatchExecutionContext;
import org.elasticsearch.xpack.watcher.input.ExecutableInput;
import org.elasticsearch.xpack.watcher.support.WatcherUtils;
import org.elasticsearch.xpack.watcher.support.XContentFilterKeysUtils;
import org.elasticsearch.xpack.watcher.support.init.proxy.WatcherClientProxy;
import org.elasticsearch.xpack.watcher.support.search.WatcherSearchTemplateService;
import org.elasticsearch.xpack.watcher.watch.Payload;
import java.util.Map;
@ -35,11 +34,14 @@ public class ExecutableSearchInput extends ExecutableInput<SearchInput, SearchIn
public static final SearchType DEFAULT_SEARCH_TYPE = SearchType.QUERY_THEN_FETCH;
private final WatcherClientProxy client;
private final WatcherSearchTemplateService searchTemplateService;
private final @Nullable TimeValue timeout;
public ExecutableSearchInput(SearchInput input, ESLogger logger, WatcherClientProxy client, @Nullable TimeValue defaultTimeout) {
public ExecutableSearchInput(SearchInput input, ESLogger logger, WatcherClientProxy client,
WatcherSearchTemplateService searchTemplateService, @Nullable TimeValue defaultTimeout) {
super(input, logger);
this.client = client;
this.searchTemplateService = searchTemplateService;
this.timeout = input.getTimeout() != null ? input.getTimeout() : defaultTimeout;
}
@ -47,7 +49,7 @@ public class ExecutableSearchInput extends ExecutableInput<SearchInput, SearchIn
public SearchInput.Result execute(WatchExecutionContext ctx, Payload payload) {
SearchRequest request = null;
try {
request = WatcherUtils.createSearchRequestFromPrototype(input.getSearchRequest(), ctx, payload);
request = searchTemplateService.createSearchRequestFromPrototype(input.getRequest(), ctx, payload);
return doExecute(ctx, request);
} catch (Exception e) {
logger.error("failed to execute [{}] input for [{}]", e, SearchInput.TYPE, ctx.watch());
@ -57,8 +59,7 @@ public class ExecutableSearchInput extends ExecutableInput<SearchInput, SearchIn
SearchInput.Result doExecute(WatchExecutionContext ctx, SearchRequest request) throws Exception {
if (logger.isTraceEnabled()) {
ToXContent source = request.source() != null ? request.source() : request.template();
logger.trace("[{}] running query for [{}] [{}]", ctx.id(), ctx.watch().id(), XContentHelper.toString(source));
logger.trace("[{}] running query for [{}] [{}]", ctx.id(), ctx.watch().id(), XContentHelper.toString(request.source()));
}
SearchResponse response = client.search(request, timeout);

View File

@ -17,9 +17,8 @@ import org.elasticsearch.index.query.QueryParseContext;
import org.elasticsearch.search.aggregations.AggregatorParsers;
import org.elasticsearch.search.suggest.Suggesters;
import org.elasticsearch.xpack.watcher.input.Input;
import org.elasticsearch.xpack.watcher.support.SearchRequestEquivalence;
import org.elasticsearch.xpack.watcher.support.WatcherDateTimeUtils;
import org.elasticsearch.xpack.watcher.support.WatcherUtils;
import org.elasticsearch.xpack.watcher.support.search.WatcherSearchTemplateRequest;
import org.elasticsearch.xpack.watcher.watch.Payload;
import org.joda.time.DateTimeZone;
@ -38,14 +37,14 @@ public class SearchInput implements Input {
public static final String TYPE = "search";
private final SearchRequest searchRequest;
private final WatcherSearchTemplateRequest request;
private final @Nullable Set<String> extractKeys;
private final @Nullable TimeValue timeout;
private final @Nullable DateTimeZone dynamicNameTimeZone;
public SearchInput(SearchRequest searchRequest, @Nullable Set<String> extractKeys,
public SearchInput(WatcherSearchTemplateRequest request, @Nullable Set<String> extractKeys,
@Nullable TimeValue timeout, @Nullable DateTimeZone dynamicNameTimeZone) {
this.searchRequest = searchRequest;
this.request = request;
this.extractKeys = extractKeys;
this.timeout = timeout;
this.dynamicNameTimeZone = dynamicNameTimeZone;
@ -63,7 +62,7 @@ public class SearchInput implements Input {
SearchInput that = (SearchInput) o;
if (!SearchRequestEquivalence.INSTANCE.equivalent(searchRequest, this.searchRequest)) return false;
if (request != null ? !request.equals(that.request) : that.request != null) return false;
if (extractKeys != null ? !extractKeys.equals(that.extractKeys) : that.extractKeys != null) return false;
if (timeout != null ? !timeout.equals(that.timeout) : that.timeout != null) return false;
return !(dynamicNameTimeZone != null ? !dynamicNameTimeZone.equals(that.dynamicNameTimeZone) : that.dynamicNameTimeZone != null);
@ -71,15 +70,15 @@ public class SearchInput implements Input {
@Override
public int hashCode() {
int result = searchRequest.hashCode();
int result = request != null ? request.hashCode() : 0;
result = 31 * result + (extractKeys != null ? extractKeys.hashCode() : 0);
result = 31 * result + (timeout != null ? timeout.hashCode() : 0);
result = 31 * result + (dynamicNameTimeZone != null ? dynamicNameTimeZone.hashCode() : 0);
return result;
}
public SearchRequest getSearchRequest() {
return searchRequest;
public WatcherSearchTemplateRequest getRequest() {
return request;
}
public Set<String> getExtractKeys() {
@ -97,8 +96,9 @@ public class SearchInput implements Input {
@Override
public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
builder.startObject();
builder.field(Field.REQUEST.getPreferredName());
builder = WatcherUtils.writeSearchRequest(searchRequest, builder, params);
if (request != null) {
builder.field(Field.REQUEST.getPreferredName(), request);
}
if (extractKeys != null) {
builder.field(Field.EXTRACT.getPreferredName(), extractKeys);
}
@ -115,7 +115,7 @@ public class SearchInput implements Input {
public static SearchInput parse(String watchId, XContentParser parser, QueryParseContext context,
AggregatorParsers aggParsers, Suggesters suggesters)
throws IOException {
SearchRequest request = null;
WatcherSearchTemplateRequest request = null;
Set<String> extract = null;
TimeValue timeout = null;
DateTimeZone dynamicNameTimeZone = null;
@ -127,7 +127,7 @@ public class SearchInput implements Input {
currentFieldName = parser.currentName();
} else if (ParseFieldMatcher.STRICT.match(currentFieldName, Field.REQUEST)) {
try {
request = WatcherUtils.readSearchRequest(parser, ExecutableSearchInput.DEFAULT_SEARCH_TYPE, context,
request = WatcherSearchTemplateRequest.fromXContent(parser, ExecutableSearchInput.DEFAULT_SEARCH_TYPE, context,
aggParsers, suggesters);
} catch (ElasticsearchParseException srpe) {
throw new ElasticsearchParseException("could not parse [{}] input for watch [{}]. failed to parse [{}]", srpe, TYPE,
@ -170,7 +170,7 @@ public class SearchInput implements Input {
return new SearchInput(request, extract, timeout, dynamicNameTimeZone);
}
public static Builder builder(SearchRequest request) {
public static Builder builder(WatcherSearchTemplateRequest request) {
return new Builder(request);
}
@ -198,20 +198,19 @@ public class SearchInput implements Input {
return builder;
}
builder.startObject(type);
builder.field(Field.REQUEST.getPreferredName());
WatcherUtils.writeSearchRequest(request, builder, params);
builder.field(Field.REQUEST.getPreferredName(), new WatcherSearchTemplateRequest(request));
return builder.endObject();
}
}
public static class Builder implements Input.Builder<SearchInput> {
private final SearchRequest request;
private final WatcherSearchTemplateRequest request;
private final Set<String> extractKeys = new HashSet<>();
private TimeValue timeout;
private DateTimeZone dynamicNameTimeZone;
private Builder(SearchRequest request) {
private Builder(WatcherSearchTemplateRequest request) {
this.request = request;
}

View File

@ -5,8 +5,6 @@
*/
package org.elasticsearch.xpack.watcher.input.search;
import java.io.IOException;
import org.elasticsearch.common.ParseFieldMatcher;
import org.elasticsearch.common.inject.Inject;
import org.elasticsearch.common.logging.Loggers;
@ -17,10 +15,14 @@ import org.elasticsearch.index.query.QueryParseContext;
import org.elasticsearch.indices.query.IndicesQueriesRegistry;
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;
import org.elasticsearch.xpack.watcher.support.init.proxy.WatcherClientProxy;
import org.elasticsearch.xpack.watcher.support.search.WatcherSearchTemplateService;
import java.io.IOException;
/**
*
@ -33,15 +35,16 @@ public class SearchInputFactory extends InputFactory<SearchInput, SearchInput.Re
private final AggregatorParsers aggParsers;
private final Suggesters suggesters;
private final ParseFieldMatcher parseFieldMatcher;
private final WatcherSearchTemplateService searchTemplateService;
@Inject
public SearchInputFactory(Settings settings, InternalClient client, IndicesQueriesRegistry queryRegistry,
AggregatorParsers aggParsers, Suggesters suggesters) {
this(settings, new WatcherClientProxy(settings, client), queryRegistry, aggParsers, suggesters);
AggregatorParsers aggParsers, Suggesters suggesters, ScriptServiceProxy scriptService) {
this(settings, new WatcherClientProxy(settings, client), queryRegistry, aggParsers, suggesters, scriptService);
}
public SearchInputFactory(Settings settings, WatcherClientProxy client, IndicesQueriesRegistry queryRegistry,
AggregatorParsers aggParsers, Suggesters suggesters) {
AggregatorParsers aggParsers, Suggesters suggesters, ScriptServiceProxy scriptService) {
super(Loggers.getLogger(ExecutableSimpleInput.class, settings));
this.parseFieldMatcher = new ParseFieldMatcher(settings);
this.client = client;
@ -49,6 +52,7 @@ public class SearchInputFactory extends InputFactory<SearchInput, SearchInput.Re
this.aggParsers = aggParsers;
this.suggesters = suggesters;
this.defaultTimeout = settings.getAsTime("xpack.watcher.input.search.default_timeout", null);
this.searchTemplateService = new WatcherSearchTemplateService(settings, scriptService, queryRegistry, aggParsers, suggesters);
}
@Override
@ -64,6 +68,6 @@ public class SearchInputFactory extends InputFactory<SearchInput, SearchInput.Re
@Override
public ExecutableSearchInput createExecutable(SearchInput input) {
return new ExecutableSearchInput(input, inputLogger, client, defaultTimeout);
return new ExecutableSearchInput(input, inputLogger, client, searchTemplateService, defaultTimeout);
}
}

View File

@ -33,6 +33,9 @@ public class RestAckWatchAction extends WatcherRestHandler {
super(settings, client);
controller.registerHandler(RestRequest.Method.PUT, URI_BASE + "/watch/{id}/_ack", this);
controller.registerHandler(RestRequest.Method.POST, URI_BASE + "/watch/{id}/_ack", this);
controller.registerHandler(RestRequest.Method.POST, URI_BASE + "/watch/{id}/_ack/{actions}", this);
controller.registerHandler(RestRequest.Method.PUT, URI_BASE + "/watch/{id}/_ack/{actions}", this);
// these are going to be removed in 6.0
controller.registerHandler(RestRequest.Method.PUT, URI_BASE + "/watch/{id}/{actions}/_ack", this);
controller.registerHandler(RestRequest.Method.POST, URI_BASE + "/watch/{id}/{actions}/_ack", this);
}

View File

@ -5,7 +5,6 @@
*/
package org.elasticsearch.xpack.watcher.support;
import com.google.common.base.Charsets;
import org.elasticsearch.action.admin.indices.template.put.PutIndexTemplateRequest;
import org.elasticsearch.action.admin.indices.template.put.PutIndexTemplateResponse;
import org.elasticsearch.cluster.ClusterChangedEvent;
@ -21,9 +20,10 @@ import org.elasticsearch.common.unit.TimeValue;
import org.elasticsearch.gateway.GatewayService;
import org.elasticsearch.threadpool.ThreadPool;
import org.elasticsearch.xpack.security.InternalClient;
import org.elasticsearch.xpack.watcher.support.init.proxy.WatcherClientProxy;
import org.elasticsearch.xpack.template.TemplateUtils;
import org.elasticsearch.xpack.watcher.support.init.proxy.WatcherClientProxy;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.Executor;
@ -168,7 +168,7 @@ public class WatcherIndexTemplateRegistry extends AbstractComponent implements C
}
executor.execute(() -> {
final byte[] template = TemplateUtils.loadTemplate("/" + config.getFileName()+ ".json", INDEX_TEMPLATE_VERSION,
Pattern.quote("${xpack.watcher.template.version}")).getBytes(Charsets.UTF_8);
Pattern.quote("${xpack.watcher.template.version}")).getBytes(StandardCharsets.UTF_8);
PutIndexTemplateRequest request = new PutIndexTemplateRequest(config.getTemplateName()).source(template);
request.masterNodeTimeout(TimeValue.timeValueMinutes(1));

View File

@ -5,54 +5,22 @@
*/
package org.elasticsearch.xpack.watcher.support;
import static org.elasticsearch.common.xcontent.XContentFactory.jsonBuilder;
import static org.elasticsearch.xpack.watcher.support.WatcherDateTimeUtils.formatDate;
import java.io.IOException;
import java.lang.reflect.Array;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import org.elasticsearch.ElasticsearchParseException;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchType;
import org.elasticsearch.action.support.IndicesOptions;
import org.elasticsearch.common.ParseField;
import org.elasticsearch.common.ParseFieldMatcher;
import org.elasticsearch.common.Strings;
import org.elasticsearch.common.unit.TimeValue;
import org.elasticsearch.common.xcontent.ToXContent;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.common.xcontent.XContentHelper;
import org.elasticsearch.common.xcontent.XContentParser;
import org.elasticsearch.index.query.QueryParseContext;
import org.elasticsearch.script.ScriptService.ScriptType;
import org.elasticsearch.script.Template;
import org.elasticsearch.search.aggregations.AggregatorParsers;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.suggest.Suggesters;
import org.elasticsearch.xpack.watcher.execution.WatchExecutionContext;
import org.elasticsearch.xpack.watcher.watch.Payload;
import org.joda.time.DateTime;
/**
*/
import java.io.IOException;
import java.lang.reflect.Array;
import java.util.HashMap;
import java.util.Map;
import static org.elasticsearch.common.xcontent.XContentFactory.jsonBuilder;
import static org.elasticsearch.xpack.watcher.support.WatcherDateTimeUtils.formatDate;
public final class WatcherUtils {
static final ParseField INDICES_FIELD = new ParseField("indices");
static final ParseField TYPES_FIELD = new ParseField("types");
static final ParseField BODY_FIELD = new ParseField("body");
static final ParseField SEARCH_TYPE_FIELD = new ParseField("search_type");
static final ParseField INDICES_OPTIONS_FIELD = new ParseField("indices_options");
static final ParseField EXPAND_WILDCARDS_FIELD = new ParseField("expand_wildcards");
static final ParseField IGNORE_UNAVAILABLE_FIELD = new ParseField("ignore_unavailable");
static final ParseField ALLOW_NO_INDICES_FIELD = new ParseField("allow_no_indices");
static final ParseField TEMPLATE_FIELD = new ParseField("template");
public final static IndicesOptions DEFAULT_INDICES_OPTIONS = IndicesOptions.lenientExpandOpen();
private WatcherUtils() {
}
@ -62,214 +30,6 @@ public final class WatcherUtils {
return XContentHelper.convertToMap(builder.bytes(), false).v2();
}
public static SearchRequest createSearchRequestFromPrototype(SearchRequest requestPrototype, WatchExecutionContext ctx,
Payload payload) throws IOException {
SearchRequest request = new SearchRequest()
.indicesOptions(requestPrototype.indicesOptions())
.searchType(requestPrototype.searchType())
.indices(requestPrototype.indices())
.types(requestPrototype.types());
// 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
Map<String, Object> watcherContextParams = Variables.createCtxModel(ctx, payload);
if (requestPrototype.source() != null) {
// Here we convert a watch search request body into an inline search template,
// this way if any Watcher related context variables are used, they will get resolved,
// by ES search template support
XContentBuilder builder = jsonBuilder();
requestPrototype.source().toXContent(builder, ToXContent.EMPTY_PARAMS);
Template template = new Template(builder.string(), ScriptType.INLINE, null, builder.contentType(), watcherContextParams);
request.template(template);
} else if (requestPrototype.template() != 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
Template template = requestPrototype.template();
if (template.getParams() != null) {
watcherContextParams.putAll(template.getParams());
}
template = new Template(template.getScript(), template.getType(), template.getLang(), template.getContentType(),
watcherContextParams);
request.template(template);
// }
}
// falling back to an empty body
return request;
}
/**
* Reads a new search request instance for the specified parser.
*/
public static SearchRequest readSearchRequest(XContentParser parser, SearchType searchType, QueryParseContext context,
AggregatorParsers aggParsers, Suggesters suggesters)
throws IOException {
IndicesOptions indicesOptions = DEFAULT_INDICES_OPTIONS;
SearchRequest searchRequest = new SearchRequest();
XContentParser.Token token;
String currentFieldName = null;
while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) {
if (token == XContentParser.Token.FIELD_NAME) {
currentFieldName = parser.currentName();
if (ParseFieldMatcher.STRICT.match(currentFieldName, BODY_FIELD)) {
searchRequest.source(SearchSourceBuilder.fromXContent(context, aggParsers, suggesters));
}
} else if (token == XContentParser.Token.START_ARRAY) {
if (ParseFieldMatcher.STRICT.match(currentFieldName, INDICES_FIELD)) {
List<String> indices = new ArrayList<>();
while ((token = parser.nextToken()) != XContentParser.Token.END_ARRAY) {
if (token == XContentParser.Token.VALUE_STRING) {
indices.add(parser.textOrNull());
} else {
throw new ElasticsearchParseException("could not read search request. expected string values in [" +
currentFieldName + "] field, but instead found [" + token + "]");
}
}
searchRequest.indices(indices.toArray(new String[indices.size()]));
} else if (ParseFieldMatcher.STRICT.match(currentFieldName, TYPES_FIELD)) {
List<String> types = new ArrayList<>();
while ((token = parser.nextToken()) != XContentParser.Token.END_ARRAY) {
if (token == XContentParser.Token.VALUE_STRING) {
types.add(parser.textOrNull());
} else {
throw new ElasticsearchParseException("could not read search request. expected string values in [" +
currentFieldName + "] field, but instead found [" + token + "]");
}
}
searchRequest.types(types.toArray(new String[types.size()]));
} else {
throw new ElasticsearchParseException("could not read search request. unexpected array field [" +
currentFieldName + "]");
}
} else if (token == XContentParser.Token.START_OBJECT) {
if (ParseFieldMatcher.STRICT.match(currentFieldName, INDICES_OPTIONS_FIELD)) {
boolean expandOpen = DEFAULT_INDICES_OPTIONS.expandWildcardsOpen();
boolean expandClosed = DEFAULT_INDICES_OPTIONS.expandWildcardsClosed();
boolean allowNoIndices = DEFAULT_INDICES_OPTIONS.allowNoIndices();
boolean ignoreUnavailable = DEFAULT_INDICES_OPTIONS.ignoreUnavailable();
while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) {
if (token == XContentParser.Token.FIELD_NAME) {
currentFieldName = parser.currentName();
} else if (token.isValue()) {
if (ParseFieldMatcher.STRICT.match(currentFieldName, EXPAND_WILDCARDS_FIELD)) {
switch (parser.text()) {
case "all":
expandOpen = true;
expandClosed = true;
break;
case "open":
expandOpen = true;
expandClosed = false;
break;
case "closed":
expandOpen = false;
expandClosed = true;
break;
case "none":
expandOpen = false;
expandClosed = false;
break;
default:
throw new ElasticsearchParseException("could not read search request. unknown value [" +
parser.text() + "] for [" + currentFieldName + "] field ");
}
} else if (ParseFieldMatcher.STRICT.match(currentFieldName, IGNORE_UNAVAILABLE_FIELD)) {
ignoreUnavailable = parser.booleanValue();
} else if (ParseFieldMatcher.STRICT.match(currentFieldName, ALLOW_NO_INDICES_FIELD)) {
allowNoIndices = parser.booleanValue();
} else {
throw new ElasticsearchParseException("could not read search request. unexpected index option [" +
currentFieldName + "]");
}
} else {
throw new ElasticsearchParseException("could not read search request. unexpected object field [" +
currentFieldName + "]");
}
}
indicesOptions = IndicesOptions.fromOptions(ignoreUnavailable, allowNoIndices, expandOpen, expandClosed,
DEFAULT_INDICES_OPTIONS);
} else if (ParseFieldMatcher.STRICT.match(currentFieldName, TEMPLATE_FIELD)) {
Template template = Template.parse(parser, ParseFieldMatcher.STRICT);
searchRequest.template(template);
} else {
throw new ElasticsearchParseException("could not read search request. unexpected object field [" +
currentFieldName + "]");
}
} else if (token == XContentParser.Token.VALUE_STRING) {
if (ParseFieldMatcher.STRICT.match(currentFieldName, INDICES_FIELD)) {
String indicesStr = parser.text();
searchRequest.indices(Strings.delimitedListToStringArray(indicesStr, ",", " \t"));
} else if (ParseFieldMatcher.STRICT.match(currentFieldName, TYPES_FIELD)) {
String typesStr = parser.text();
searchRequest.types(Strings.delimitedListToStringArray(typesStr, ",", " \t"));
} else if (ParseFieldMatcher.STRICT.match(currentFieldName, SEARCH_TYPE_FIELD)) {
searchType = SearchType.fromString(parser.text().toLowerCase(Locale.ROOT), ParseFieldMatcher.EMPTY);
} else {
throw new ElasticsearchParseException("could not read search request. unexpected string field [" +
currentFieldName + "]");
}
} else {
throw new ElasticsearchParseException("could not read search request. unexpected token [" + token + "]");
}
}
if (searchRequest.indices() == null) {
searchRequest.indices(Strings.EMPTY_ARRAY);
}
searchRequest.searchType(searchType);
searchRequest.indicesOptions(indicesOptions);
return searchRequest;
}
/**
* Writes the searchRequest to the specified builder.
*/
public static XContentBuilder writeSearchRequest(SearchRequest searchRequest, XContentBuilder builder,
ToXContent.Params params) throws IOException {
if (searchRequest == null) {
builder.nullValue();
return builder;
}
builder.startObject();
if (searchRequest.searchType() != null) {
builder.field(SEARCH_TYPE_FIELD.getPreferredName(), searchRequest.searchType().toString().toLowerCase(Locale.ENGLISH));
}
if (searchRequest.indices() != null) {
builder.array(INDICES_FIELD.getPreferredName(), searchRequest.indices());
}
if (searchRequest.types() != null) {
builder.array(TYPES_FIELD.getPreferredName(), searchRequest.types());
}
if (searchRequest.source() != null) {
builder.field(BODY_FIELD.getPreferredName(), searchRequest.source());
}
if (searchRequest.template() != null) {
builder.field(TEMPLATE_FIELD.getPreferredName(), searchRequest.template());
}
if (searchRequest.indicesOptions() != DEFAULT_INDICES_OPTIONS) {
IndicesOptions options = searchRequest.indicesOptions();
builder.startObject(INDICES_OPTIONS_FIELD.getPreferredName());
String value;
if (options.expandWildcardsClosed() && options.expandWildcardsOpen()) {
value = "all";
} else if (options.expandWildcardsOpen()) {
value = "open";
} else if (options.expandWildcardsClosed()) {
value = "closed";
} else {
value = "none";
}
builder.field(EXPAND_WILDCARDS_FIELD.getPreferredName(), value);
builder.field(IGNORE_UNAVAILABLE_FIELD.getPreferredName(), options.ignoreUnavailable());
builder.field(ALLOW_NO_INDICES_FIELD.getPreferredName(), options.allowNoIndices());
builder.endObject();
}
return builder.endObject();
}
public static Map<String, Object> flattenModel(Map<String, Object> map) {
Map<String, Object> result = new HashMap<>();
flattenModel("", map, result);

View File

@ -0,0 +1,254 @@
/*
* 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.watcher.support.search;
import org.elasticsearch.ElasticsearchParseException;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchType;
import org.elasticsearch.action.support.IndicesOptions;
import org.elasticsearch.common.Nullable;
import org.elasticsearch.common.ParseField;
import org.elasticsearch.common.ParseFieldMatcher;
import org.elasticsearch.common.Strings;
import org.elasticsearch.common.xcontent.ToXContent;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.common.xcontent.XContentParser;
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.SearchRequestEquivalence;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import java.util.Objects;
/**
* A {@link WatcherSearchTemplateRequest} contains the search request and the eventual template that will
* be rendered as a script by {@link WatcherSearchTemplateService} before being executed.
*/
public class WatcherSearchTemplateRequest implements ToXContent {
private final SearchRequest request;
private final @Nullable Script template;
public WatcherSearchTemplateRequest(SearchRequest searchRequest, @Nullable Script template) {
this.request = Objects.requireNonNull(searchRequest);
this.template = template;
}
public WatcherSearchTemplateRequest(SearchRequest request) {
this(request, null);
}
public SearchRequest getRequest() {
return request;
}
public Script getTemplate() {
return template;
}
@Override
public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
builder.startObject();
if (request != null) {
if (request.searchType() != null) {
builder.field(SEARCH_TYPE_FIELD.getPreferredName(), request.searchType().toString().toLowerCase(Locale.ENGLISH));
}
if (request.indices() != null) {
builder.array(INDICES_FIELD.getPreferredName(), request.indices());
}
if (request.types() != null) {
builder.array(TYPES_FIELD.getPreferredName(), request.types());
}
if (request.source() != null) {
builder.field(BODY_FIELD.getPreferredName(), request.source());
}
if (request.indicesOptions() != DEFAULT_INDICES_OPTIONS) {
IndicesOptions options = request.indicesOptions();
builder.startObject(INDICES_OPTIONS_FIELD.getPreferredName());
String value;
if (options.expandWildcardsClosed() && options.expandWildcardsOpen()) {
value = "all";
} else if (options.expandWildcardsOpen()) {
value = "open";
} else if (options.expandWildcardsClosed()) {
value = "closed";
} else {
value = "none";
}
builder.field(EXPAND_WILDCARDS_FIELD.getPreferredName(), value);
builder.field(IGNORE_UNAVAILABLE_FIELD.getPreferredName(), options.ignoreUnavailable());
builder.field(ALLOW_NO_INDICES_FIELD.getPreferredName(), options.allowNoIndices());
builder.endObject();
}
}
if (template != null) {
builder.field(TEMPLATE_FIELD.getPreferredName(), template);
}
return builder.endObject();
}
/**
* Reads a new watcher search request instance for the specified parser.
*/
public static WatcherSearchTemplateRequest fromXContent(XContentParser parser, SearchType searchType, QueryParseContext context,
AggregatorParsers aggParsers, Suggesters suggesters)
throws IOException {
IndicesOptions indicesOptions = DEFAULT_INDICES_OPTIONS;
SearchRequest searchRequest = new SearchRequest();
Script template = null;
XContentParser.Token token;
String currentFieldName = null;
while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) {
if (token == XContentParser.Token.FIELD_NAME) {
currentFieldName = parser.currentName();
if (ParseFieldMatcher.STRICT.match(currentFieldName, BODY_FIELD)) {
searchRequest.source(SearchSourceBuilder.fromXContent(context, aggParsers, suggesters));
}
} else if (token == XContentParser.Token.START_ARRAY) {
if (ParseFieldMatcher.STRICT.match(currentFieldName, INDICES_FIELD)) {
List<String> indices = new ArrayList<>();
while ((token = parser.nextToken()) != XContentParser.Token.END_ARRAY) {
if (token == XContentParser.Token.VALUE_STRING) {
indices.add(parser.textOrNull());
} else {
throw new ElasticsearchParseException("could not read search request. expected string values in [" +
currentFieldName + "] field, but instead found [" + token + "]");
}
}
searchRequest.indices(indices.toArray(new String[indices.size()]));
} else if (ParseFieldMatcher.STRICT.match(currentFieldName, TYPES_FIELD)) {
List<String> types = new ArrayList<>();
while ((token = parser.nextToken()) != XContentParser.Token.END_ARRAY) {
if (token == XContentParser.Token.VALUE_STRING) {
types.add(parser.textOrNull());
} else {
throw new ElasticsearchParseException("could not read search request. expected string values in [" +
currentFieldName + "] field, but instead found [" + token + "]");
}
}
searchRequest.types(types.toArray(new String[types.size()]));
} else {
throw new ElasticsearchParseException("could not read search request. unexpected array field [" +
currentFieldName + "]");
}
} else if (token == XContentParser.Token.START_OBJECT) {
if (ParseFieldMatcher.STRICT.match(currentFieldName, INDICES_OPTIONS_FIELD)) {
boolean expandOpen = DEFAULT_INDICES_OPTIONS.expandWildcardsOpen();
boolean expandClosed = DEFAULT_INDICES_OPTIONS.expandWildcardsClosed();
boolean allowNoIndices = DEFAULT_INDICES_OPTIONS.allowNoIndices();
boolean ignoreUnavailable = DEFAULT_INDICES_OPTIONS.ignoreUnavailable();
while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) {
if (token == XContentParser.Token.FIELD_NAME) {
currentFieldName = parser.currentName();
} else if (token.isValue()) {
if (ParseFieldMatcher.STRICT.match(currentFieldName, EXPAND_WILDCARDS_FIELD)) {
switch (parser.text()) {
case "all":
expandOpen = true;
expandClosed = true;
break;
case "open":
expandOpen = true;
expandClosed = false;
break;
case "closed":
expandOpen = false;
expandClosed = true;
break;
case "none":
expandOpen = false;
expandClosed = false;
break;
default:
throw new ElasticsearchParseException("could not read search request. unknown value [" +
parser.text() + "] for [" + currentFieldName + "] field ");
}
} else if (ParseFieldMatcher.STRICT.match(currentFieldName, IGNORE_UNAVAILABLE_FIELD)) {
ignoreUnavailable = parser.booleanValue();
} else if (ParseFieldMatcher.STRICT.match(currentFieldName, ALLOW_NO_INDICES_FIELD)) {
allowNoIndices = parser.booleanValue();
} else {
throw new ElasticsearchParseException("could not read search request. unexpected index option [" +
currentFieldName + "]");
}
} else {
throw new ElasticsearchParseException("could not read search request. unexpected object field [" +
currentFieldName + "]");
}
}
indicesOptions = IndicesOptions.fromOptions(ignoreUnavailable, allowNoIndices, expandOpen, expandClosed,
DEFAULT_INDICES_OPTIONS);
} else if (ParseFieldMatcher.STRICT.match(currentFieldName, TEMPLATE_FIELD)) {
template = Script.parse(parser);
} else {
throw new ElasticsearchParseException("could not read search request. unexpected object field [" +
currentFieldName + "]");
}
} else if (token == XContentParser.Token.VALUE_STRING) {
if (ParseFieldMatcher.STRICT.match(currentFieldName, INDICES_FIELD)) {
String indicesStr = parser.text();
searchRequest.indices(Strings.delimitedListToStringArray(indicesStr, ",", " \t"));
} else if (ParseFieldMatcher.STRICT.match(currentFieldName, TYPES_FIELD)) {
String typesStr = parser.text();
searchRequest.types(Strings.delimitedListToStringArray(typesStr, ",", " \t"));
} else if (ParseFieldMatcher.STRICT.match(currentFieldName, SEARCH_TYPE_FIELD)) {
searchType = SearchType.fromString(parser.text().toLowerCase(Locale.ROOT), ParseFieldMatcher.EMPTY);
} else {
throw new ElasticsearchParseException("could not read search request. unexpected string field [" +
currentFieldName + "]");
}
} else {
throw new ElasticsearchParseException("could not read search request. unexpected token [" + token + "]");
}
}
if (searchRequest.indices() == null) {
searchRequest.indices(Strings.EMPTY_ARRAY);
}
searchRequest.searchType(searchType);
searchRequest.indicesOptions(indicesOptions);
return new WatcherSearchTemplateRequest(searchRequest, template);
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
WatcherSearchTemplateRequest that = (WatcherSearchTemplateRequest) o;
if (!SearchRequestEquivalence.INSTANCE.equivalent(request, that.request)) return false;
return template != null ? template.equals(that.template) : that.template == null;
}
@Override
public int hashCode() {
int result = request != null ? request.hashCode() : 0;
result = 31 * result + (template != null ? template.hashCode() : 0);
return result;
}
static final ParseField INDICES_FIELD = new ParseField("indices");
static final ParseField TYPES_FIELD = new ParseField("types");
static final ParseField BODY_FIELD = new ParseField("body");
static final ParseField SEARCH_TYPE_FIELD = new ParseField("search_type");
static final ParseField INDICES_OPTIONS_FIELD = new ParseField("indices_options");
static final ParseField EXPAND_WILDCARDS_FIELD = new ParseField("expand_wildcards");
static final ParseField IGNORE_UNAVAILABLE_FIELD = new ParseField("ignore_unavailable");
static final ParseField ALLOW_NO_INDICES_FIELD = new ParseField("allow_no_indices");
static final ParseField TEMPLATE_FIELD = new ParseField("template");
public final static IndicesOptions DEFAULT_INDICES_OPTIONS = IndicesOptions.lenientExpandOpen();
}

View File

@ -0,0 +1,125 @@
/*
* 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.watcher.support.search;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.common.ParseFieldMatcher;
import org.elasticsearch.common.bytes.BytesReference;
import org.elasticsearch.common.component.AbstractComponent;
import org.elasticsearch.common.inject.Inject;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.xcontent.ToXContent;
import org.elasticsearch.common.xcontent.XContentBuilder;
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.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.Variables;
import org.elasticsearch.xpack.watcher.watch.Payload;
import java.io.IOException;
import java.util.Map;
import static org.elasticsearch.common.xcontent.XContentFactory.jsonBuilder;
/**
* {@link WatcherSearchTemplateService} renders {@link WatcherSearchTemplateRequest} before their execution.
*/
public class WatcherSearchTemplateService extends AbstractComponent {
private static final String DEFAULT_LANG = "mustache";
private final ScriptServiceProxy scriptService;
private final ParseFieldMatcher parseFieldMatcher;
private final IndicesQueriesRegistry queryRegistry;
private final AggregatorParsers aggsParsers;
private final Suggesters suggesters;
@Inject
public WatcherSearchTemplateService(Settings settings, ScriptServiceProxy scriptServiceProxy,
IndicesQueriesRegistry queryRegistry, AggregatorParsers aggregatorParsers, Suggesters suggesters) {
super(settings);
this.scriptService = scriptServiceProxy;
this.queryRegistry = queryRegistry;
this.aggsParsers = aggregatorParsers;
this.suggesters = suggesters;
this.parseFieldMatcher = new ParseFieldMatcher(settings);
}
public SearchRequest createSearchRequestFromPrototype(WatcherSearchTemplateRequest prototype, WatchExecutionContext ctx,
Payload payload) throws IOException {
SearchRequest request = new SearchRequest()
.indicesOptions(prototype.getRequest().indicesOptions())
.searchType(prototype.getRequest().searchType())
.indices(prototype.getRequest().indices())
.types(prototype.getRequest().types());
Script 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
Map<String, Object> watcherContextParams = Variables.createCtxModel(ctx, payload);
// Here we convert a watch search request body into an inline search template,
// this way if any Watcher related context variables are used, they will get resolved.
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();
}
} 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();
if (templatePrototype.params() != null) {
watcherContextParams.putAll(templatePrototype.params());
}
Script.Builder builder;
if (templatePrototype.type() == ScriptService.ScriptType.INLINE) {
builder = Script.inline(templatePrototype.script());
} else if (templatePrototype.type() == ScriptService.ScriptType.FILE) {
builder = Script.file(templatePrototype.script());
} else if (templatePrototype.type() == ScriptService.ScriptType.STORED) {
builder = Script.indexed(templatePrototype.script());
} else {
builder = Script.defaultType(templatePrototype.script());
}
template = builder.lang(templatePrototype.lang()).params(watcherContextParams).build();
}
request.source(convert(template));
return request;
}
/**
* Converts a {@link Script} to a {@link org.elasticsearch.search.builder.SearchSourceBuilder}
*/
private SearchSourceBuilder convert(Script 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();
if (source != null && source.length() > 0) {
try (XContentParser parser = XContentFactory.xContent(source).createParser(source)) {
sourceBuilder.parseXContent(new QueryParseContext(queryRegistry, parser, parseFieldMatcher), aggsParsers, suggesters);
}
}
return sourceBuilder;
}
}

View File

@ -6,8 +6,8 @@
package org.elasticsearch.xpack.watcher.transform;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchRequestBuilder;
import org.elasticsearch.xpack.watcher.support.Script;
import org.elasticsearch.xpack.watcher.support.search.WatcherSearchTemplateRequest;
import org.elasticsearch.xpack.watcher.transform.chain.ChainTransform;
import org.elasticsearch.xpack.watcher.transform.script.ScriptTransform;
import org.elasticsearch.xpack.watcher.transform.search.SearchTransform;
@ -20,12 +20,12 @@ public final class TransformBuilders {
private TransformBuilders() {
}
public static SearchTransform.Builder searchTransform(SearchRequest request) {
public static SearchTransform.Builder searchTransform(WatcherSearchTemplateRequest request) {
return SearchTransform.builder(request);
}
public static SearchTransform.Builder searchTransform(SearchRequestBuilder request) {
return searchTransform(request.request());
public static SearchTransform.Builder searchTransform(SearchRequest request) {
return searchTransform(new WatcherSearchTemplateRequest(request));
}
public static ScriptTransform.Builder scriptTransform(String script) {

View File

@ -12,8 +12,8 @@ import org.elasticsearch.common.Nullable;
import org.elasticsearch.common.logging.ESLogger;
import org.elasticsearch.common.unit.TimeValue;
import org.elasticsearch.xpack.watcher.execution.WatchExecutionContext;
import org.elasticsearch.xpack.watcher.support.WatcherUtils;
import org.elasticsearch.xpack.watcher.support.init.proxy.WatcherClientProxy;
import org.elasticsearch.xpack.watcher.support.search.WatcherSearchTemplateService;
import org.elasticsearch.xpack.watcher.transform.ExecutableTransform;
import org.elasticsearch.xpack.watcher.watch.Payload;
@ -25,12 +25,14 @@ public class ExecutableSearchTransform extends ExecutableTransform<SearchTransfo
public static final SearchType DEFAULT_SEARCH_TYPE = SearchType.QUERY_THEN_FETCH;
protected final WatcherClientProxy client;
private final WatcherSearchTemplateService searchTemplateService;
protected final @Nullable TimeValue timeout;
public ExecutableSearchTransform(SearchTransform transform, ESLogger logger, WatcherClientProxy client,
@Nullable TimeValue defaultTimeout) {
WatcherSearchTemplateService searchTemplateService, @Nullable TimeValue defaultTimeout) {
super(transform, logger);
this.client = client;
this.searchTemplateService = searchTemplateService;
this.timeout = transform.getTimeout() != null ? transform.getTimeout() : defaultTimeout;
}
@ -38,7 +40,7 @@ public class ExecutableSearchTransform extends ExecutableTransform<SearchTransfo
public SearchTransform.Result execute(WatchExecutionContext ctx, Payload payload) {
SearchRequest request = null;
try {
request = WatcherUtils.createSearchRequestFromPrototype(transform.getRequest(), ctx, payload);
request = searchTemplateService.createSearchRequestFromPrototype(transform.getRequest(), ctx, payload);
SearchResponse resp = client.search(request, timeout);
return new SearchTransform.Result(request, new Payload.XContent(resp));
} catch (Exception e) {

View File

@ -16,9 +16,8 @@ import org.elasticsearch.common.xcontent.XContentParser;
import org.elasticsearch.index.query.QueryParseContext;
import org.elasticsearch.search.aggregations.AggregatorParsers;
import org.elasticsearch.search.suggest.Suggesters;
import org.elasticsearch.xpack.watcher.support.SearchRequestEquivalence;
import org.elasticsearch.xpack.watcher.support.WatcherDateTimeUtils;
import org.elasticsearch.xpack.watcher.support.WatcherUtils;
import org.elasticsearch.xpack.watcher.support.search.WatcherSearchTemplateRequest;
import org.elasticsearch.xpack.watcher.transform.Transform;
import org.elasticsearch.xpack.watcher.watch.Payload;
import org.joda.time.DateTimeZone;
@ -32,11 +31,11 @@ public class SearchTransform implements Transform {
public static final String TYPE = "search";
private final SearchRequest request;
private final WatcherSearchTemplateRequest request;
private final @Nullable TimeValue timeout;
private final @Nullable DateTimeZone dynamicNameTimeZone;
public SearchTransform(SearchRequest request, @Nullable TimeValue timeout, @Nullable DateTimeZone dynamicNameTimeZone) {
public SearchTransform(WatcherSearchTemplateRequest request, @Nullable TimeValue timeout, @Nullable DateTimeZone dynamicNameTimeZone) {
this.request = request;
this.timeout = timeout;
this.dynamicNameTimeZone = dynamicNameTimeZone;
@ -47,7 +46,7 @@ public class SearchTransform implements Transform {
return TYPE;
}
public SearchRequest getRequest() {
public WatcherSearchTemplateRequest getRequest() {
return request;
}
@ -66,14 +65,14 @@ public class SearchTransform implements Transform {
SearchTransform that = (SearchTransform) o;
if (!SearchRequestEquivalence.INSTANCE.equivalent(request, this.request)) return false;
if (request != null ? !request.equals(that.request) : that.request != null) return false;
if (timeout != null ? !timeout.equals(that.timeout) : that.timeout != null) return false;
return !(dynamicNameTimeZone != null ? !dynamicNameTimeZone.equals(that.dynamicNameTimeZone) : that.dynamicNameTimeZone != null);
}
@Override
public int hashCode() {
int result = request.hashCode();
int result = request != null ? request.hashCode() : 0;
result = 31 * result + (timeout != null ? timeout.hashCode() : 0);
result = 31 * result + (dynamicNameTimeZone != null ? dynamicNameTimeZone.hashCode() : 0);
return result;
@ -82,8 +81,9 @@ public class SearchTransform implements Transform {
@Override
public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
builder.startObject();
builder.field(Field.REQUEST.getPreferredName());
builder = WatcherUtils.writeSearchRequest(request, builder, params);
if (request != null) {
builder.field(Field.REQUEST.getPreferredName(), request);
}
if (timeout != null) {
builder.field(Field.TIMEOUT.getPreferredName(), timeout);
}
@ -97,7 +97,7 @@ public class SearchTransform implements Transform {
public static SearchTransform parse(String watchId, XContentParser parser, QueryParseContext context,
AggregatorParsers aggParsers, Suggesters suggesters)
throws IOException {
SearchRequest request = null;
WatcherSearchTemplateRequest request = null;
TimeValue timeout = null;
DateTimeZone dynamicNameTimeZone = null;
@ -108,7 +108,7 @@ public class SearchTransform implements Transform {
currentFieldName = parser.currentName();
} else if (ParseFieldMatcher.STRICT.match(currentFieldName, Field.REQUEST)) {
try {
request = WatcherUtils.readSearchRequest(parser, ExecutableSearchTransform.DEFAULT_SEARCH_TYPE, context,
request = WatcherSearchTemplateRequest.fromXContent(parser, ExecutableSearchTransform.DEFAULT_SEARCH_TYPE, context,
aggParsers, suggesters);
} catch (ElasticsearchParseException srpe) {
throw new ElasticsearchParseException("could not parse [{}] transform for watch [{}]. failed to parse [{}]", srpe,
@ -136,7 +136,7 @@ public class SearchTransform implements Transform {
return new SearchTransform(request, timeout, dynamicNameTimeZone);
}
public static Builder builder(SearchRequest request) {
public static Builder builder(WatcherSearchTemplateRequest request) {
return new Builder(request);
}
@ -162,8 +162,7 @@ public class SearchTransform implements Transform {
protected XContentBuilder typeXContent(XContentBuilder builder, Params params) throws IOException {
if (request != null) {
builder.startObject(type);
builder.field(Field.REQUEST.getPreferredName());
WatcherUtils.writeSearchRequest(request, builder, params);
builder.field(Field.REQUEST.getPreferredName(), new WatcherSearchTemplateRequest(request));
builder.endObject();
}
return builder;
@ -172,11 +171,11 @@ public class SearchTransform implements Transform {
public static class Builder implements Transform.Builder<SearchTransform> {
private final SearchRequest request;
private final WatcherSearchTemplateRequest request;
private TimeValue timeout;
private DateTimeZone dynamicNameTimeZone;
public Builder(SearchRequest request) {
public Builder(WatcherSearchTemplateRequest request) {
this.request = request;
}

View File

@ -18,7 +18,9 @@ import org.elasticsearch.indices.query.IndicesQueriesRegistry;
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;
/**
@ -32,14 +34,15 @@ public class SearchTransformFactory extends TransformFactory<SearchTransform, Se
private final AggregatorParsers aggParsers;
private final Suggesters suggesters;
private final ParseFieldMatcher parseFieldMatcher;
private final WatcherSearchTemplateService searchTemplateService;
@Inject
public SearchTransformFactory(Settings settings, InternalClient client, IndicesQueriesRegistry queryRegistry,
AggregatorParsers aggParsers, Suggesters suggesters) {
this(settings, new WatcherClientProxy(settings, client), queryRegistry, aggParsers, suggesters);
AggregatorParsers aggParsers, Suggesters suggesters, ScriptServiceProxy scriptService) {
this(settings, new WatcherClientProxy(settings, client), queryRegistry, aggParsers, suggesters, scriptService);
}
public SearchTransformFactory(Settings settings, WatcherClientProxy client, IndicesQueriesRegistry queryRegistry,
AggregatorParsers aggParsers, Suggesters suggesters) {
AggregatorParsers aggParsers, Suggesters suggesters, ScriptServiceProxy scriptService) {
super(Loggers.getLogger(ExecutableSearchTransform.class, settings));
this.client = client;
this.parseFieldMatcher = new ParseFieldMatcher(settings);
@ -47,6 +50,7 @@ public class SearchTransformFactory extends TransformFactory<SearchTransform, Se
this.aggParsers = aggParsers;
this.suggesters = suggesters;
this.defaultTimeout = settings.getAsTime("xpack.watcher.transform.search.default_timeout", null);
this.searchTemplateService = new WatcherSearchTemplateService(settings, scriptService, queryRegistry, aggParsers, suggesters);
}
@Override
@ -62,6 +66,6 @@ public class SearchTransformFactory extends TransformFactory<SearchTransform, Se
@Override
public ExecutableSearchTransform createExecutable(SearchTransform transform) {
return new ExecutableSearchTransform(transform, transformLogger, client, defaultTimeout);
return new ExecutableSearchTransform(transform, transformLogger, client, searchTemplateService, defaultTimeout);
}
}

View File

@ -38,7 +38,7 @@ public class HistoryIntegrationTests extends AbstractWatcherIntegrationTestCase
SearchRequestBuilder searchRequestBuilder = client().prepareSearch("foo").addSort(SortBuilders.fieldSort("inner.date").order
(SortOrder.DESC));
builder.input(InputBuilders.chainInput().add("first", InputBuilders.searchInput(searchRequestBuilder)));
builder.input(InputBuilders.chainInput().add("first", InputBuilders.searchInput(searchRequestBuilder.request())));
PutWatchResponse response = watcherClient().preparePutWatch("test_watch").setSource(builder).get();
assertThat(response.isCreated(), is(true));

View File

@ -6,10 +6,9 @@
package org.elasticsearch.xpack.watcher;
import org.apache.http.HttpStatus;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.elasticsearch.action.admin.cluster.node.info.NodeInfo;
import org.elasticsearch.action.admin.cluster.node.info.NodesInfoResponse;
import org.elasticsearch.client.ResponseException;
import org.elasticsearch.common.network.NetworkModule;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.http.HttpServerTransport;
@ -18,12 +17,10 @@ import org.elasticsearch.plugins.Plugin;
import org.elasticsearch.xpack.security.Security;
import org.elasticsearch.test.ESIntegTestCase;
import org.elasticsearch.test.ESIntegTestCase.ClusterScope;
import org.elasticsearch.test.rest.client.http.HttpRequestBuilder;
import org.elasticsearch.test.rest.client.http.HttpResponse;
import org.elasticsearch.threadpool.ThreadPool;
import org.elasticsearch.threadpool.ThreadPoolInfo;
import org.elasticsearch.xpack.watcher.execution.InternalWatchExecutor;
import org.elasticsearch.xpack.XPackPlugin;
import org.elasticsearch.xpack.watcher.execution.InternalWatchExecutor;
import java.util.Collection;
import java.util.Collections;
@ -70,12 +67,11 @@ public class WatcherPluginDisableTests extends ESIntegTestCase {
public void testRestEndpoints() throws Exception {
HttpServerTransport httpServerTransport = internalCluster().getDataNodeInstance(HttpServerTransport.class);
try (CloseableHttpClient httpClient = HttpClients.createDefault()) {
HttpRequestBuilder request = new HttpRequestBuilder(httpClient).httpTransport(httpServerTransport)
.method("GET")
.path("/_xpack/watcher");
HttpResponse response = request.execute();
assertThat(response.getStatusCode(), is(HttpStatus.SC_BAD_REQUEST));
try {
getRestClient().performRequest("GET", "/_xpack/watcher", Collections.emptyMap(), null);
fail("request should have failed");
} catch(ResponseException e) {
assertThat(e.getResponse().getStatusLine().getStatusCode(), is(HttpStatus.SC_BAD_REQUEST));
}
}

View File

@ -5,7 +5,6 @@
*/
package org.elasticsearch.xpack.watcher.actions.email;
import com.google.common.base.Charsets;
import org.elasticsearch.ElasticsearchParseException;
import org.elasticsearch.common.bytes.BytesReference;
import org.elasticsearch.common.collect.MapBuilder;
@ -55,6 +54,7 @@ import org.junit.Before;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
@ -478,7 +478,7 @@ public class EmailActionTests extends ESTestCase {
Attachment externalAttachment = attachments.get(attachmentId);
assertThat(externalAttachment.bodyPart(), is(notNullValue()));
InputStream is = externalAttachment.bodyPart().getInputStream();
String data = Streams.copyToString(new InputStreamReader(is, Charsets.UTF_8));
String data = Streams.copyToString(new InputStreamReader(is, StandardCharsets.UTF_8));
assertThat(data, is(content));
}
@ -610,7 +610,8 @@ public class EmailActionTests extends ESTestCase {
when(httpClient.execute(any(HttpRequest.class))).thenReturn(mockResponse);
HttpRequestTemplate template = HttpRequestTemplate.builder("localhost", 1234).build();
attachments.add(new HttpRequestAttachment(randomAsciiOfLength(10), template, randomFrom("my/custom-type", null)));
attachments.add(new HttpRequestAttachment(randomAsciiOfLength(10), template,
randomBoolean(), randomFrom("my/custom-type", null)));
} else if ("data".equals(attachmentType)) {
attachments.add(new org.elasticsearch.xpack.notification.email.attachment.DataAttachment(randomAsciiOfLength(10),
randomFrom(DataAttachment.JSON, DataAttachment.YAML)));

View File

@ -207,7 +207,4 @@ public class EmailAttachmentTests extends AbstractWatcherIntegrationTestCase {
fail("waited too long for email to be received");
}
}
}

View File

@ -6,6 +6,7 @@
package org.elasticsearch.xpack.watcher.actions.index;
import org.elasticsearch.ElasticsearchParseException;
import org.elasticsearch.action.search.SearchRequestBuilder;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.unit.TimeValue;
@ -64,14 +65,8 @@ public class IndexActionTests extends ESIntegTestCase {
}
public void testIndexActionExecuteSingleDoc() throws Exception {
String timestampField = randomFrom(null, "_timestamp", "@timestamp");
boolean customTimestampField = "@timestamp".equals(timestampField);
if (timestampField == null || "_timestamp".equals(timestampField)) {
assertThat(prepareCreate("test-index")
.addMapping("test-type", "{ \"test-type\" : { \"_timestamp\" : { \"enabled\" : \"true\" }}}")
.get().isAcknowledged(), is(true));
}
String timestampField = randomFrom(null, "@timestamp");
boolean customTimestampField = timestampField != null;
IndexAction action = new IndexAction("test-index", "test-type", timestampField, null, null);
ExecutableIndexAction executable = new ExecutableIndexAction(action, logger, WatcherClientProxy.of(client()), null);
@ -92,12 +87,15 @@ public class IndexActionTests extends ESIntegTestCase {
refresh(); //Manually refresh to make sure data is available
SearchResponse searchResponse = client().prepareSearch("test-index")
SearchRequestBuilder searchRequestbuilder = client().prepareSearch("test-index")
.setTypes("test-type")
.setSource(searchSource()
.query(matchAllQuery())
.aggregation(terms("timestamps").field(customTimestampField ? timestampField : "_timestamp")))
.get();
.setSource(searchSource().query(matchAllQuery()));
if (customTimestampField) {
searchRequestbuilder.addAggregation(terms("timestamps").field(timestampField));
}
SearchResponse searchResponse = searchRequestbuilder.get();
assertThat(searchResponse.getHits().totalHits(), equalTo(1L));
SearchHit hit = searchResponse.getHits().getAt(0);
@ -106,28 +104,24 @@ public class IndexActionTests extends ESIntegTestCase {
assertThat(hit.getSource().size(), is(2));
assertThat(hit.getSource(), hasEntry("foo", (Object) "bar"));
assertThat(hit.getSource(), hasEntry(timestampField, (Object) WatcherDateTimeUtils.formatDate(executionTime)));
Terms terms = searchResponse.getAggregations().get("timestamps");
assertThat(terms, notNullValue());
assertThat(terms.getBuckets(), hasSize(1));
assertThat(terms.getBuckets().get(0).getKeyAsNumber().longValue(), is(executionTime.getMillis()));
assertThat(terms.getBuckets().get(0).getDocCount(), is(1L));
} else {
assertThat(hit.getSource().size(), is(1));
assertThat(hit.getSource(), hasEntry("foo", (Object) "bar"));
}
Terms terms = searchResponse.getAggregations().get("timestamps");
assertThat(terms, notNullValue());
assertThat(terms.getBuckets(), hasSize(1));
assertThat(terms.getBuckets().get(0).getKeyAsNumber().longValue(), is(executionTime.getMillis()));
assertThat(terms.getBuckets().get(0).getDocCount(), is(1L));
}
public void testIndexActionExecuteMultiDoc() throws Exception {
String timestampField = randomFrom(null, "_timestamp", "@timestamp");
String timestampField = randomFrom(null, "@timestamp");
boolean customTimestampField = "@timestamp".equals(timestampField);
if (timestampField == null || "_timestamp".equals(timestampField)) {
assertAcked(prepareCreate("test-index")
.addMapping("test-type", "_timestamp", "enabled=true", "foo", "type=keyword"));
} else {
assertAcked(prepareCreate("test-index")
.addMapping("test-type", "foo", "type=keyword"));
}
assertAcked(prepareCreate("test-index")
.addMapping("test-type", "foo", "type=keyword"));
Object list = randomFrom(
new Map[] { singletonMap("foo", "bar"), singletonMap("foo", "bar1") },
@ -160,8 +154,7 @@ public class IndexActionTests extends ESIntegTestCase {
SearchResponse searchResponse = client().prepareSearch("test-index")
.setTypes("test-type")
.setSource(searchSource().sort("foo", SortOrder.ASC)
.query(matchAllQuery())
.aggregation(terms("timestamps").field(customTimestampField ? timestampField : "_timestamp")))
.query(matchAllQuery()))
.get();
assertThat(searchResponse.getHits().totalHits(), equalTo(2L));

View File

@ -31,6 +31,7 @@ import org.elasticsearch.xpack.watcher.support.init.proxy.WatcherClientProxy;
import org.elasticsearch.xpack.common.secret.SecretService;
import org.elasticsearch.xpack.common.text.TextTemplate;
import org.elasticsearch.xpack.common.text.TextTemplateEngine;
import org.elasticsearch.xpack.watcher.support.search.WatcherSearchTemplateService;
import org.elasticsearch.xpack.watcher.test.AbstractWatcherIntegrationTestCase;
import org.elasticsearch.xpack.watcher.test.MockTextTemplateEngine;
import org.elasticsearch.xpack.watcher.test.WatcherTestUtils;
@ -238,7 +239,7 @@ public class WebhookActionTests extends ESTestCase {
ExecutableWebhookAction executable = new ExecutableWebhookAction(action, logger, httpClient, templateEngine);
String watchId = "test_url_encode" + randomAsciiOfLength(10);
Watch watch = createWatch(watchId, mock(WatcherClientProxy.class), "account1");
Watch watch = createWatch(watchId, "account1");
WatchExecutionContext ctx = new TriggeredExecutionContext(watch, new DateTime(UTC),
new ScheduleTriggerEvent(watchId, new DateTime(UTC), new DateTime(UTC)), timeValueSeconds(5));
executable.execute("_id", ctx, new Payload.Simple());
@ -264,16 +265,16 @@ public class WebhookActionTests extends ESTestCase {
ExecutableWebhookAction executable = new ExecutableWebhookAction(action, logger, client, templateEngine);
Watch watch = createWatch(watchId, mock(WatcherClientProxy.class), "account1");
Watch watch = createWatch(watchId, "account1");
WatchExecutionContext ctx = new TriggeredExecutionContext(watch, new DateTime(UTC),
new ScheduleTriggerEvent(watchId, new DateTime(UTC), new DateTime(UTC)), timeValueSeconds(5));
Action.Result result = executable.execute("_id", ctx, new Payload.Simple());
assertThat(result, Matchers.instanceOf(WebhookAction.Result.Success.class));
}
private Watch createWatch(String watchId, WatcherClientProxy client, final String account) throws AddressException, IOException {
private Watch createWatch(String watchId, final String account) throws AddressException, IOException {
return WatcherTestUtils.createTestWatch(watchId,
client,
mock(WatcherClientProxy.class),
ExecuteScenario.Success.client(),
new AbstractWatcherIntegrationTestCase.NoopEmailService() {
@Override
@ -286,6 +287,7 @@ public class WebhookActionTests extends ESTestCase {
return new EmailSent(account, email);
}
},
mock(WatcherSearchTemplateService.class),
logger);
};

View File

@ -39,18 +39,14 @@ public class CompareConditionSearchTests extends AbstractWatcherIntegrationTestC
}
public void testExecuteWithAggs() throws Exception {
client().admin().indices().prepareCreate("my-index")
.addMapping("my-type", "_timestamp", "enabled=true")
.get();
client().prepareIndex("my-index", "my-type").setTimestamp("2005-01-01T00:00").setSource("{}").get();
client().prepareIndex("my-index", "my-type").setTimestamp("2005-01-01T00:10").setSource("{}").get();
client().prepareIndex("my-index", "my-type").setTimestamp("2005-01-01T00:20").setSource("{}").get();
client().prepareIndex("my-index", "my-type").setTimestamp("2005-01-01T00:30").setSource("{}").get();
client().prepareIndex("my-index", "my-type").setSource("@timestamp", "2005-01-01T00:00").get();
client().prepareIndex("my-index", "my-type").setSource("@timestamp", "2005-01-01T00:10").get();
client().prepareIndex("my-index", "my-type").setSource("@timestamp", "2005-01-01T00:20").get();
client().prepareIndex("my-index", "my-type").setSource("@timestamp", "2005-01-01T00:30").get();
refresh();
SearchResponse response = client().prepareSearch("my-index")
.addAggregation(AggregationBuilders.dateHistogram("rate").field("_timestamp")
.addAggregation(AggregationBuilders.dateHistogram("rate").field("@timestamp")
.dateHistogramInterval(DateHistogramInterval.HOUR).order(Histogram.Order.COUNT_DESC))
.get();
@ -65,12 +61,12 @@ public class CompareConditionSearchTests extends AbstractWatcherIntegrationTestC
assertThat(resolvedValues.size(), is(1));
assertThat(resolvedValues, hasEntry("ctx.payload.aggregations.rate.buckets.0.doc_count", (Object) 4));
client().prepareIndex("my-index", "my-type").setTimestamp("2005-01-01T00:40").setSource("{}").get();
client().prepareIndex("my-index", "my-type").setSource("@timestamp", "2005-01-01T00:40").get();
refresh();
response = client().prepareSearch("my-index")
.addAggregation(AggregationBuilders.dateHistogram("rate")
.field("_timestamp").dateHistogramInterval(DateHistogramInterval.HOUR).order(Histogram.Order.COUNT_DESC))
.field("@timestamp").dateHistogramInterval(DateHistogramInterval.HOUR).order(Histogram.Order.COUNT_DESC))
.get();
ctx = mockExecutionContext("_name", new Payload.XContent(response));

View File

@ -1,105 +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.watcher.condition.script;
import org.apache.lucene.util.LuceneTestCase.AwaitsFix;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.action.search.ShardSearchFailure;
import org.elasticsearch.common.text.Text;
import org.elasticsearch.index.Index;
import org.elasticsearch.search.SearchShardTarget;
import org.elasticsearch.search.aggregations.AggregationBuilders;
import org.elasticsearch.search.aggregations.bucket.histogram.DateHistogramInterval;
import org.elasticsearch.search.aggregations.bucket.histogram.Histogram;
import org.elasticsearch.search.internal.InternalSearchHit;
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.watcher.execution.WatchExecutionContext;
import org.elasticsearch.xpack.watcher.support.Script;
import org.elasticsearch.xpack.common.ScriptServiceProxy;
import org.elasticsearch.xpack.watcher.test.AbstractWatcherIntegrationTestCase;
import org.elasticsearch.xpack.watcher.test.WatcherTestUtils;
import org.elasticsearch.xpack.watcher.watch.Payload;
import org.junit.After;
import org.junit.Before;
import static org.elasticsearch.xpack.watcher.test.WatcherTestUtils.mockExecutionContext;
import static org.hamcrest.Matchers.is;
import static org.mockito.Mockito.when;
/**
*/
@AwaitsFix(bugUrl = "https://github.com/elastic/x-plugins/issues/724")
public class ScriptConditionSearchTests extends AbstractWatcherIntegrationTestCase {
private ThreadPool tp = null;
private ScriptServiceProxy scriptService;
@Before
public void init() throws Exception {
tp = new TestThreadPool(ThreadPool.Names.SAME);
scriptService = WatcherTestUtils.getScriptServiceProxy(tp);
}
@After
public void cleanup() {
tp.shutdownNow();
}
public void testExecuteWithAggs() throws Exception {
client().admin().indices().prepareCreate("my-index")
.addMapping("my-type", "_timestamp", "enabled=true")
.get();
client().prepareIndex("my-index", "my-type").setTimestamp("2005-01-01T00:00").setSource("{}").get();
client().prepareIndex("my-index", "my-type").setTimestamp("2005-01-01T00:10").setSource("{}").get();
client().prepareIndex("my-index", "my-type").setTimestamp("2005-01-01T00:20").setSource("{}").get();
client().prepareIndex("my-index", "my-type").setTimestamp("2005-01-01T00:30").setSource("{}").get();
refresh();
SearchResponse response = client().prepareSearch("my-index")
.addAggregation(AggregationBuilders.dateHistogram("rate")
.field("_timestamp").dateHistogramInterval(DateHistogramInterval.HOUR).order(Histogram.Order.COUNT_DESC))
.get();
ExecutableScriptCondition condition = new ExecutableScriptCondition(
new ScriptCondition(Script.inline("ctx.payload.aggregations.rate.buckets[0]?.doc_count >= 5").build()),
logger, scriptService);
WatchExecutionContext ctx = mockExecutionContext("_name", new Payload.XContent(response));
assertFalse(condition.execute(ctx).met());
client().prepareIndex("my-index", "my-type").setTimestamp("2005-01-01T00:40").setSource("{}").get();
refresh();
response = client().prepareSearch("my-index")
.addAggregation(AggregationBuilders.dateHistogram("rate")
.field("_timestamp").dateHistogramInterval(DateHistogramInterval.HOUR).order(Histogram.Order.COUNT_DESC))
.get();
ctx = mockExecutionContext("_name", new Payload.XContent(response));
assertThat(condition.execute(ctx).met(), is(true));
}
public void testExecuteAccessHits() throws Exception {
ExecutableScriptCondition condition = new ExecutableScriptCondition(
new ScriptCondition(Script.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));
InternalSearchResponse internalSearchResponse = new InternalSearchResponse(
new InternalSearchHits(new InternalSearchHit[]{hit}, 1L, 1f), null, null, null, false, false);
SearchResponse response = new SearchResponse(internalSearchResponse, "", 3, 3, 500L, new ShardSearchFailure[0]);
WatchExecutionContext ctx = mockExecutionContext("_watch_name", new Payload.XContent(response));
assertThat(condition.execute(ctx).met(), is(true));
hit.score(2f);
when(ctx.payload()).thenReturn(new Payload.XContent(response));
assertThat(condition.execute(ctx).met(), is(false));
}
}

View File

@ -8,6 +8,7 @@ package org.elasticsearch.xpack.watcher.execution;
import org.elasticsearch.common.xcontent.ToXContent;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.common.xcontent.XContentFactory;
import org.elasticsearch.xpack.watcher.support.search.WatcherSearchTemplateService;
import org.elasticsearch.xpack.watcher.test.AbstractWatcherIntegrationTestCase;
import org.elasticsearch.xpack.watcher.test.WatcherTestUtils;
import org.elasticsearch.xpack.watcher.trigger.schedule.ScheduleTriggerEvent;
@ -21,7 +22,8 @@ import static org.hamcrest.Matchers.equalTo;
*/
public class TriggeredWatchTests extends AbstractWatcherIntegrationTestCase {
public void testParser() throws Exception {
Watch watch = WatcherTestUtils.createTestWatch("fired_test", watcherHttpClient(), noopEmailService(), logger);
Watch watch = WatcherTestUtils.createTestWatch("fired_test", watcherHttpClient(), noopEmailService(),
watcherSearchTemplateService(), logger);
ScheduleTriggerEvent event = new ScheduleTriggerEvent(watch.id(), DateTime.now(DateTimeZone.UTC), DateTime.now(DateTimeZone.UTC));
Wid wid = new Wid("_record", randomLong(), DateTime.now(DateTimeZone.UTC));
TriggeredWatch triggeredWatch = new TriggeredWatch(wid, event);
@ -38,4 +40,8 @@ public class TriggeredWatchTests extends AbstractWatcherIntegrationTestCase {
private TriggeredWatch.Parser triggeredWatchParser() {
return internalCluster().getInstance(TriggeredWatch.Parser.class);
}
protected WatcherSearchTemplateService watcherSearchTemplateService() {
return internalCluster().getInstance(WatcherSearchTemplateService.class);
}
}

View File

@ -5,7 +5,6 @@
*/
package org.elasticsearch.xpack.watcher.rest.action;
import com.google.common.collect.Lists;
import org.elasticsearch.client.Client;
import org.elasticsearch.common.bytes.BytesArray;
import org.elasticsearch.common.bytes.BytesReference;
@ -18,6 +17,8 @@ import org.elasticsearch.xpack.watcher.client.WatcherClient;
import org.elasticsearch.xpack.watcher.transport.actions.execute.ExecuteWatchRequestBuilder;
import org.elasticsearch.xpack.watcher.trigger.TriggerService;
import java.util.Arrays;
import static org.hamcrest.core.Is.is;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
@ -32,9 +33,9 @@ public class RestExecuteWatchActionTests extends ESTestCase {
public void testThatFlagsCanBeSpecifiedViaParameters() throws Exception {
String randomId = randomAsciiOfLength(10);
for (String recordExecution : Lists.newArrayList("true", "false", null)) {
for (String ignoreCondition : Lists.newArrayList("true", "false", null)) {
for (String debugCondition : Lists.newArrayList("true", "false", null)) {
for (String recordExecution : Arrays.asList("true", "false", null)) {
for (String ignoreCondition : Arrays.asList("true", "false", null)) {
for (String debugCondition : Arrays.asList("true", "false", null)) {
ExecuteWatchRequestBuilder builder = new ExecuteWatchRequestBuilder(client);
when(watcherClient.prepareExecuteWatch()).thenReturn(builder);

View File

@ -6,23 +6,6 @@
package org.elasticsearch.xpack.watcher.support;
import static java.util.Collections.singletonMap;
import static org.elasticsearch.common.xcontent.XContentFactory.jsonBuilder;
import static org.elasticsearch.xpack.watcher.support.WatcherDateTimeUtils.formatDate;
import static org.elasticsearch.xpack.watcher.support.WatcherUtils.DEFAULT_INDICES_OPTIONS;
import static org.elasticsearch.xpack.watcher.support.WatcherUtils.flattenModel;
import static org.elasticsearch.xpack.watcher.test.WatcherTestUtils.getRandomSupportedSearchType;
import static org.hamcrest.Matchers.arrayContainingInAnyOrder;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.hasEntry;
import static org.hamcrest.Matchers.is;
import java.io.IOException;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchType;
import org.elasticsearch.action.support.IndicesOptions;
@ -40,15 +23,30 @@ import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.index.query.QueryParseContext;
import org.elasticsearch.index.query.QueryParser;
import org.elasticsearch.indices.query.IndicesQueriesRegistry;
import org.elasticsearch.script.ScriptService.ScriptType;
import org.elasticsearch.script.Template;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.test.ESTestCase;
import org.elasticsearch.xpack.watcher.input.search.ExecutableSearchInput;
import org.elasticsearch.xpack.support.clock.SystemClock;
import org.elasticsearch.xpack.common.text.TextTemplate;
import org.elasticsearch.xpack.watcher.support.search.WatcherSearchTemplateRequest;
import org.joda.time.DateTime;
import java.io.IOException;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
import static java.util.Collections.singletonMap;
import static org.elasticsearch.common.xcontent.XContentFactory.jsonBuilder;
import static org.elasticsearch.xpack.watcher.input.search.ExecutableSearchInput.DEFAULT_SEARCH_TYPE;
import static org.elasticsearch.xpack.watcher.support.WatcherDateTimeUtils.formatDate;
import static org.elasticsearch.xpack.watcher.support.WatcherUtils.flattenModel;
import static org.elasticsearch.xpack.watcher.test.WatcherTestUtils.getRandomSupportedSearchType;
import static org.hamcrest.Matchers.arrayContainingInAnyOrder;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.hasEntry;
import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.notNullValue;
/**
*
*/
@ -100,6 +98,7 @@ public class WatcherUtilsTests extends ESTestCase {
public void testSerializeSearchRequest() throws Exception {
String[] randomIndices = generateRandomStringArray(5, 5, false);
SearchRequest expectedRequest = new SearchRequest(randomIndices);
Script expectedTemplate = null;
if (randomBoolean()) {
String[] randomTypes = generateRandomStringArray(2, 5, false);
@ -107,7 +106,7 @@ public class WatcherUtilsTests extends ESTestCase {
}
expectedRequest.indicesOptions(IndicesOptions.fromOptions(randomBoolean(), randomBoolean(), randomBoolean(),
randomBoolean(), WatcherUtils.DEFAULT_INDICES_OPTIONS));
randomBoolean(), WatcherSearchTemplateRequest.DEFAULT_INDICES_OPTIONS));
expectedRequest.searchType(getRandomSupportedSearchType());
SearchSourceBuilder searchSourceBuilder = SearchSourceBuilder.searchSource().query(QueryBuilders.matchAllQuery()).size(11);
@ -122,30 +121,29 @@ public class WatcherUtilsTests extends ESTestCase {
}
}
String text = randomAsciiOfLengthBetween(1, 5);
Template template = randomFrom(
new Template(text, ScriptType.INLINE, null, null, params),
new Template(text, ScriptType.FILE, null, null, params),
new Template(text, ScriptType.STORED, null, null, params)
);
expectedRequest.template(template);
expectedTemplate = randomFrom(Script.inline(text), Script.file(text), Script.indexed(text)).params(params).build();
}
WatcherSearchTemplateRequest request = new WatcherSearchTemplateRequest(expectedRequest, expectedTemplate);
XContentBuilder builder = jsonBuilder();
builder = WatcherUtils.writeSearchRequest(expectedRequest, builder, ToXContent.EMPTY_PARAMS);
request.toXContent(builder, ToXContent.EMPTY_PARAMS);
XContentParser parser = XContentHelper.createParser(builder.bytes());
assertThat(parser.nextToken(), equalTo(XContentParser.Token.START_OBJECT));
IndicesQueriesRegistry registry = new IndicesQueriesRegistry();
QueryParser<MatchAllQueryBuilder> queryParser = MatchAllQueryBuilder::fromXContent;
registry.register(queryParser, MatchAllQueryBuilder.QUERY_NAME_FIELD);
QueryParseContext context = new QueryParseContext(registry, parser, ParseFieldMatcher.STRICT);
SearchRequest result = WatcherUtils.readSearchRequest(parser, ExecutableSearchInput.DEFAULT_SEARCH_TYPE, context, null, null);
WatcherSearchTemplateRequest result = WatcherSearchTemplateRequest.fromXContent(parser, DEFAULT_SEARCH_TYPE, context, null, null);
assertThat(result.indices(), arrayContainingInAnyOrder(expectedRequest.indices()));
assertThat(result.types(), arrayContainingInAnyOrder(expectedRequest.types()));
assertThat(result.indicesOptions(), equalTo(expectedRequest.indicesOptions()));
assertThat(result.searchType(), equalTo(expectedRequest.searchType()));
assertThat(result.source(), equalTo(searchSourceBuilder));
assertThat(result.template(), equalTo(expectedRequest.template()));
assertThat(result.getRequest(), is(notNullValue()));
assertThat(result.getRequest().indices(), arrayContainingInAnyOrder(expectedRequest.indices()));
assertThat(result.getRequest().types(), arrayContainingInAnyOrder(expectedRequest.types()));
assertThat(result.getRequest().indicesOptions(), equalTo(expectedRequest.indicesOptions()));
assertThat(result.getRequest().searchType(), equalTo(expectedRequest.searchType()));
assertThat(result.getRequest().source(), equalTo(searchSourceBuilder));
assertThat(result.getTemplate(), equalTo(expectedTemplate));
}
public void testDeserializeSearchRequest() throws Exception {
@ -172,10 +170,10 @@ public class WatcherUtilsTests extends ESTestCase {
}
}
IndicesOptions indicesOptions = DEFAULT_INDICES_OPTIONS;
IndicesOptions indicesOptions = WatcherSearchTemplateRequest.DEFAULT_INDICES_OPTIONS;
if (randomBoolean()) {
indicesOptions = IndicesOptions.fromOptions(randomBoolean(), randomBoolean(), randomBoolean(),
randomBoolean(), WatcherUtils.DEFAULT_INDICES_OPTIONS);
randomBoolean(), WatcherSearchTemplateRequest.DEFAULT_INDICES_OPTIONS);
builder.startObject("indices_options")
.field("allow_no_indices", indicesOptions.allowNoIndices())
.field("expand_wildcards", indicesOptions.expandWildcardsClosed() && indicesOptions.expandWildcardsOpen() ? "all" :
@ -201,7 +199,7 @@ public class WatcherUtilsTests extends ESTestCase {
source = searchSourceBuilder.buildAsBytes(XContentType.JSON);
builder.rawField("body", source);
}
Template templateSource = null;
Script template = null;
if (randomBoolean()) {
Map<String, Object> params = new HashMap<>();
if (randomBoolean()) {
@ -211,14 +209,8 @@ public class WatcherUtilsTests extends ESTestCase {
}
}
String text = randomAsciiOfLengthBetween(1, 5);
TextTemplate template = randomFrom(
TextTemplate.inline(text).params(params).build(),
TextTemplate.file(text).params(params).build(),
TextTemplate.indexed(text).params(params).build()
);
template = randomFrom(Script.inline(text), Script.file(text), Script.indexed(text)) .params(params).build();
builder.field("template", template);
templateSource = new Template(template.getTemplate(), template.getType(), null, template.getContentType(),
template.getParams());
}
builder.endObject();
@ -228,14 +220,15 @@ public class WatcherUtilsTests extends ESTestCase {
QueryParser<MatchAllQueryBuilder> queryParser = MatchAllQueryBuilder::fromXContent;
registry.register(queryParser, MatchAllQueryBuilder.QUERY_NAME_FIELD);
QueryParseContext context = new QueryParseContext(registry, parser, ParseFieldMatcher.STRICT);
SearchRequest result = WatcherUtils.readSearchRequest(parser, ExecutableSearchInput.DEFAULT_SEARCH_TYPE, context, null, null);
WatcherSearchTemplateRequest result = WatcherSearchTemplateRequest.fromXContent(parser, DEFAULT_SEARCH_TYPE, context, null, null);
assertThat(result.indices(), arrayContainingInAnyOrder(indices));
assertThat(result.types(), arrayContainingInAnyOrder(types));
assertThat(result.indicesOptions(), equalTo(indicesOptions));
assertThat(result.searchType(), equalTo(searchType));
assertThat(result.source(), equalTo(searchSourceBuilder));
assertThat(result.template(), equalTo(templateSource));
assertThat(result.getRequest(), is(notNullValue()));
assertThat(result.getRequest().indices(), arrayContainingInAnyOrder(indices));
assertThat(result.getRequest().types(), arrayContainingInAnyOrder(types));
assertThat(result.getRequest().indicesOptions(), equalTo(indicesOptions));
assertThat(result.getRequest().searchType(), equalTo(searchType));
assertThat(result.getRequest().source(), equalTo(searchSourceBuilder));
assertThat(result.getTemplate(), equalTo(template));
}
}

View File

@ -31,6 +31,13 @@ import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.test.ESIntegTestCase;
import org.elasticsearch.threadpool.ThreadPool;
import org.elasticsearch.watcher.ResourceWatcherService;
import org.elasticsearch.xpack.common.ScriptServiceProxy;
import org.elasticsearch.xpack.common.http.HttpClient;
import org.elasticsearch.xpack.common.http.HttpMethod;
import org.elasticsearch.xpack.common.http.HttpRequestTemplate;
import org.elasticsearch.xpack.common.secret.Secret;
import org.elasticsearch.xpack.common.text.TextTemplate;
import org.elasticsearch.xpack.common.text.TextTemplateEngine;
import org.elasticsearch.xpack.notification.email.Authentication;
import org.elasticsearch.xpack.notification.email.EmailService;
import org.elasticsearch.xpack.notification.email.EmailTemplate;
@ -49,15 +56,9 @@ import org.elasticsearch.xpack.watcher.execution.Wid;
import org.elasticsearch.xpack.watcher.input.search.ExecutableSearchInput;
import org.elasticsearch.xpack.watcher.input.simple.ExecutableSimpleInput;
import org.elasticsearch.xpack.watcher.input.simple.SimpleInput;
import org.elasticsearch.xpack.common.ScriptServiceProxy;
import org.elasticsearch.xpack.watcher.support.WatcherUtils;
import org.elasticsearch.xpack.common.http.HttpClient;
import org.elasticsearch.xpack.common.http.HttpMethod;
import org.elasticsearch.xpack.common.http.HttpRequestTemplate;
import org.elasticsearch.xpack.watcher.support.init.proxy.WatcherClientProxy;
import org.elasticsearch.xpack.common.secret.Secret;
import org.elasticsearch.xpack.common.text.TextTemplate;
import org.elasticsearch.xpack.common.text.TextTemplateEngine;
import org.elasticsearch.xpack.watcher.support.search.WatcherSearchTemplateRequest;
import org.elasticsearch.xpack.watcher.support.search.WatcherSearchTemplateService;
import org.elasticsearch.xpack.watcher.support.xcontent.ObjectPath;
import org.elasticsearch.xpack.watcher.support.xcontent.XContentSource;
import org.elasticsearch.xpack.watcher.transform.search.ExecutableSearchTransform;
@ -76,7 +77,6 @@ import javax.mail.internet.AddressException;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedHashMap;
@ -122,7 +122,7 @@ public final class WatcherTestUtils {
public static SearchRequest newInputSearchRequest(String... indices) {
SearchRequest request = new SearchRequest();
request.indices(indices);
request.indicesOptions(WatcherUtils.DEFAULT_INDICES_OPTIONS);
request.indicesOptions(WatcherSearchTemplateRequest.DEFAULT_INDICES_OPTIONS);
request.searchType(ExecutableSearchInput.DEFAULT_SEARCH_TYPE);
return request;
}
@ -175,13 +175,14 @@ public final class WatcherTestUtils {
public static Watch createTestWatch(String watchName, HttpClient httpClient, EmailService emailService,
ESLogger logger) throws AddressException {
return createTestWatch(watchName, WatcherClientProxy.of(ESIntegTestCase.client()), httpClient, emailService, logger);
WatcherSearchTemplateService searchTemplateService, ESLogger logger) throws AddressException {
WatcherClientProxy client = WatcherClientProxy.of(ESIntegTestCase.client());
return createTestWatch(watchName, client, httpClient, emailService, searchTemplateService, logger);
}
public static Watch createTestWatch(String watchName, WatcherClientProxy client, HttpClient httpClient, EmailService emailService,
ESLogger logger) throws AddressException {
WatcherSearchTemplateService searchTemplateService, ESLogger logger) throws AddressException {
SearchRequest conditionRequest = newInputSearchRequest("my-condition-index").source(searchSource().query(matchAllQuery()));
SearchRequest transformRequest = newInputSearchRequest("my-payload-index").source(searchSource().query(matchAllQuery()));
@ -229,12 +230,15 @@ public final class WatcherTestUtils {
Map<String, ActionStatus> statuses = new HashMap<>();
statuses.put("_webhook", new ActionStatus(now));
statuses.put("_email", new ActionStatus(now));
SearchTransform searchTransform = new SearchTransform(new WatcherSearchTemplateRequest(transformRequest), null, null);
return new Watch(
watchName,
new ScheduleTrigger(new CronSchedule("0/5 * * * * ? *")),
new ExecutableSimpleInput(new SimpleInput(new Payload.Simple(inputData)), logger),
new ExecutableAlwaysCondition(logger),
new ExecutableSearchTransform(new SearchTransform(transformRequest, null, null), logger, client, null),
new ExecutableSearchTransform(searchTransform, logger, client, searchTemplateService, null),
new TimeValue(0),
new ExecutableActions(actions),
metadata,
@ -247,7 +251,7 @@ public final class WatcherTestUtils {
.put("script.indexed", "true")
.put("path.home", createTempDir())
.build();
ScriptContextRegistry scriptContextRegistry = new ScriptContextRegistry(Arrays.asList(ScriptServiceProxy.INSTANCE));
ScriptContextRegistry scriptContextRegistry = new ScriptContextRegistry(Collections.singletonList(ScriptServiceProxy.INSTANCE));
ScriptEngineRegistry scriptEngineRegistry =
new ScriptEngineRegistry(Collections.emptyList());

View File

@ -7,20 +7,16 @@ package org.elasticsearch.xpack.watcher.test.integration;
import org.elasticsearch.ElasticsearchParseException;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchRequestBuilder;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.common.unit.TimeValue;
import org.elasticsearch.common.util.Callback;
import org.elasticsearch.common.xcontent.ToXContent;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.script.ScriptService.ScriptType;
import org.elasticsearch.script.Template;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.xpack.support.clock.SystemClock;
import org.elasticsearch.xpack.watcher.client.WatchSourceBuilder;
import org.elasticsearch.xpack.watcher.client.WatcherClient;
import org.elasticsearch.xpack.watcher.condition.compare.CompareCondition;
import org.elasticsearch.xpack.watcher.support.WatcherUtils;
import org.elasticsearch.xpack.support.clock.SystemClock;
import org.elasticsearch.xpack.watcher.support.Script;
import org.elasticsearch.xpack.watcher.support.search.WatcherSearchTemplateRequest;
import org.elasticsearch.xpack.watcher.support.xcontent.XContentSource;
import org.elasticsearch.xpack.watcher.test.AbstractWatcherIntegrationTestCase;
import org.elasticsearch.xpack.watcher.transport.actions.delete.DeleteWatchResponse;
@ -151,8 +147,8 @@ public class BasicWatcherTests extends AbstractWatcherIntegrationTestCase {
watchSource.field("unknown_field", "x");
watchSource.startObject("schedule").field("cron", "0/5 * * * * ? *").endObject();
watchSource.startObject("condition").startObject("script").field("script", "return true").field("request");
WatcherUtils.writeSearchRequest(newInputSearchRequest(), watchSource, ToXContent.EMPTY_PARAMS);
watchSource.startObject("condition").startObject("script").field("script", "return true");
watchSource.field("request", new WatcherSearchTemplateRequest(newInputSearchRequest()));
watchSource.endObject().endObject();
watchSource.endObject();
@ -252,22 +248,20 @@ public class BasicWatcherTests extends AbstractWatcherIntegrationTestCase {
public void testConditionSearchWithSource() throws Exception {
SearchSourceBuilder searchSourceBuilder = searchSource().query(matchQuery("level", "a"));
testConditionSearch(newInputSearchRequest("events").source(searchSourceBuilder));
testConditionSearch(newInputSearchRequest("events").source(searchSourceBuilder), null);
}
public void testConditionSearchWithIndexedTemplate() throws Exception {
SearchSourceBuilder searchSourceBuilder = searchSource().query(matchQuery("level", "a"));
client().admin().cluster().preparePutStoredScript()
assertAcked(client().admin().cluster().preparePutStoredScript()
.setScriptLang("mustache")
.setId("my-template")
.setSource(jsonBuilder().startObject().field("template").value(searchSourceBuilder).endObject().bytes())
.get();
.get());
Template template = new Template("my-template", ScriptType.STORED, null, null, null);
Script template = Script.indexed("my-template").lang("mustache").build();
SearchRequest searchRequest = newInputSearchRequest("events");
// TODO (2.0 upgrade): move back to BytesReference instead of coverting to a string
searchRequest.template(template);
testConditionSearch(searchRequest);
testConditionSearch(searchRequest, template);
}
public void testInputFiltering() throws Exception {
@ -298,12 +292,7 @@ public class BasicWatcherTests extends AbstractWatcherIntegrationTestCase {
// Check that the input result payload has been filtered
refresh();
SearchResponse searchResponse = searchWatchRecords(new Callback<SearchRequestBuilder>() {
@Override
public void handle(SearchRequestBuilder builder) {
builder.setQuery(matchQuery("watch_id", "_name1"));
}
});
SearchResponse searchResponse = searchWatchRecords(builder -> builder.setQuery(matchQuery("watch_id", "_name1")));
assertHitCount(searchResponse, 1);
XContentSource source = xContentSource(searchResponse.getHits().getAt(0).getSourceRef());
assertThat(source.getValue("result.input.payload.hits.total"), equalTo((Object) 1));
@ -379,17 +368,17 @@ public class BasicWatcherTests extends AbstractWatcherIntegrationTestCase {
}
}
private void testConditionSearch(SearchRequest request) throws Exception {
private void testConditionSearch(SearchRequest request, Script template) throws Exception {
// reset, so we don't miss event docs when we filter over the _timestamp field.
timeWarp().clock().setTime(SystemClock.INSTANCE.nowUTC());
String watchName = "_name";
assertAcked(prepareCreate("events").addMapping("event", "_timestamp", "enabled=true", "level", "type=text"));
assertAcked(prepareCreate("events").addMapping("event", "level", "type=text"));
watcherClient().preparePutWatch(watchName)
.setSource(watchBuilder()
.trigger(schedule(interval("5s")))
.input(searchInput(request))
.input(searchInput(new WatcherSearchTemplateRequest(request, template)))
.condition(compareCondition("ctx.payload.hits.total", CompareCondition.Op.GTE, 3L)))
.get();

View File

@ -5,32 +5,29 @@
*/
package org.elasticsearch.xpack.watcher.test.integration;
import org.apache.http.Header;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.message.BasicHeader;
import org.elasticsearch.client.Response;
import org.elasticsearch.common.network.NetworkModule;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.xcontent.json.JsonXContent;
import org.elasticsearch.common.xcontent.support.XContentMapValues;
import org.elasticsearch.http.HttpServerTransport;
import org.elasticsearch.marvel.test.MarvelIntegTestCase;
import org.elasticsearch.xpack.security.authc.support.SecuredString;
import org.elasticsearch.test.rest.client.http.HttpRequestBuilder;
import org.elasticsearch.test.rest.client.http.HttpResponse;
import org.elasticsearch.xpack.watcher.test.AbstractWatcherIntegrationTestCase;
import org.junit.After;
import java.io.IOException;
import java.util.Collections;
import java.util.Map;
import static org.elasticsearch.xpack.security.authc.support.UsernamePasswordToken.BASIC_AUTH_HEADER;
import static org.elasticsearch.xpack.security.authc.support.UsernamePasswordToken.basicAuthHeaderValue;
import static org.elasticsearch.xpack.watcher.test.AbstractWatcherIntegrationTestCase.SecuritySettings.TEST_PASSWORD;
import static org.elasticsearch.xpack.watcher.test.AbstractWatcherIntegrationTestCase.SecuritySettings.TEST_USERNAME;
import static org.hamcrest.CoreMatchers.nullValue;
import static org.hamcrest.Matchers.is;
/**
*
*/
public class WatcherSettingsFilterTests extends AbstractWatcherIntegrationTestCase {
private CloseableHttpClient httpClient = HttpClients.createDefault();
@ -52,37 +49,26 @@ public class WatcherSettingsFilterTests extends AbstractWatcherIntegrationTestCa
}
public void testGetSettingsSmtpPassword() throws Exception {
String body = executeRequest("GET", "/_nodes/settings", null, null).getBody();
Map<String, Object> response = JsonXContent.jsonXContent.createParser(body).map();
Map<String, Object> nodes = (Map<String, Object>) response.get("nodes");
for (Object node : nodes.values()) {
Map<String, Object> settings = (Map<String, Object>) ((Map<String, Object>) node).get("settings");
assertThat(XContentMapValues.extractValue("xpack.notification.email.account._email.smtp.user", settings),
is((Object) "_user"));
assertThat(XContentMapValues.extractValue("xpack.notification.email.account._email.smtp.password", settings),
nullValue());
Header[] headers;
if (securityEnabled()) {
headers = new Header[] {
new BasicHeader(BASIC_AUTH_HEADER,
basicAuthHeaderValue(MarvelIntegTestCase.SecuritySettings.TEST_USERNAME,
new SecuredString(MarvelIntegTestCase.SecuritySettings.TEST_PASSWORD.toCharArray())))};
} else {
headers = new Header[0];
}
}
protected HttpResponse executeRequest(String method, String path, String body, Map<String, String> params) throws IOException {
HttpServerTransport httpServerTransport = getInstanceFromMaster(HttpServerTransport.class);
HttpRequestBuilder requestBuilder = new HttpRequestBuilder(httpClient)
.httpTransport(httpServerTransport)
.method(method)
.path(path);
if (params != null) {
for (Map.Entry<String, String> entry : params.entrySet()) {
requestBuilder.addParam(entry.getKey(), entry.getValue());
try (Response response = getRestClient().performRequest("GET", "/_nodes/settings",
Collections.emptyMap(), null, headers)) {
Map<String, Object> responseMap = JsonXContent.jsonXContent.createParser(response.getEntity().getContent()).map();
Map<String, Object> nodes = (Map<String, Object>) responseMap.get("nodes");
for (Object node : nodes.values()) {
Map<String, Object> settings = (Map<String, Object>) ((Map<String, Object>) node).get("settings");
assertThat(XContentMapValues.extractValue("xpack.notification.email.account._email.smtp.user", settings),
is((Object) "_user"));
assertThat(XContentMapValues.extractValue("xpack.notification.email.account._email.smtp.password", settings),
nullValue());
}
}
if (body != null) {
requestBuilder.body(body);
}
if (securityEnabled()) {
requestBuilder.addHeader(BASIC_AUTH_HEADER,
basicAuthHeaderValue(TEST_USERNAME, new SecuredString(TEST_PASSWORD.toCharArray())));
}
return requestBuilder.execute();
}
}

View File

@ -5,14 +5,6 @@
*/
package org.elasticsearch.xpack.watcher.watch;
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 org.elasticsearch.ElasticsearchParseException;
import org.elasticsearch.common.bytes.BytesReference;
import org.elasticsearch.common.logging.ESLogger;
@ -25,12 +17,31 @@ import org.elasticsearch.index.query.MatchAllQueryBuilder;
import org.elasticsearch.index.query.QueryParser;
import org.elasticsearch.indices.query.IndicesQueriesRegistry;
import org.elasticsearch.test.ESTestCase;
import org.elasticsearch.xpack.common.ScriptServiceProxy;
import org.elasticsearch.xpack.common.http.HttpClient;
import org.elasticsearch.xpack.common.http.HttpMethod;
import org.elasticsearch.xpack.common.http.HttpRequestTemplate;
import org.elasticsearch.xpack.common.http.auth.HttpAuthRegistry;
import org.elasticsearch.xpack.common.http.auth.basic.BasicAuthFactory;
import org.elasticsearch.xpack.common.secret.SecretService;
import org.elasticsearch.xpack.common.text.TextTemplate;
import org.elasticsearch.xpack.common.text.TextTemplateEngine;
import org.elasticsearch.xpack.notification.email.DataAttachment;
import org.elasticsearch.xpack.notification.email.EmailService;
import org.elasticsearch.xpack.notification.email.EmailTemplate;
import org.elasticsearch.xpack.notification.email.HtmlSanitizer;
import org.elasticsearch.xpack.notification.email.Profile;
import org.elasticsearch.xpack.notification.email.attachment.EmailAttachments;
import org.elasticsearch.xpack.notification.email.attachment.EmailAttachmentsParser;
import org.elasticsearch.xpack.support.clock.Clock;
import org.elasticsearch.xpack.support.clock.ClockMock;
import org.elasticsearch.xpack.support.clock.SystemClock;
import org.elasticsearch.xpack.watcher.WatcherLicensee;
import org.elasticsearch.xpack.watcher.actions.ActionFactory;
import org.elasticsearch.xpack.watcher.actions.ActionRegistry;
import org.elasticsearch.xpack.watcher.actions.ActionStatus;
import org.elasticsearch.xpack.watcher.actions.ActionWrapper;
import org.elasticsearch.xpack.watcher.actions.ExecutableActions;
import org.elasticsearch.xpack.notification.email.DataAttachment;
import org.elasticsearch.xpack.watcher.actions.email.EmailAction;
import org.elasticsearch.xpack.watcher.actions.email.EmailActionFactory;
import org.elasticsearch.xpack.watcher.actions.email.ExecutableEmailAction;
@ -68,28 +79,15 @@ 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.input.simple.SimpleInputFactory;
import org.elasticsearch.xpack.watcher.WatcherLicensee;
import org.elasticsearch.xpack.watcher.support.Script;
import org.elasticsearch.xpack.watcher.support.WatcherUtils;
import org.elasticsearch.xpack.support.clock.Clock;
import org.elasticsearch.xpack.support.clock.ClockMock;
import org.elasticsearch.xpack.support.clock.SystemClock;
import org.elasticsearch.xpack.common.http.HttpClient;
import org.elasticsearch.xpack.common.http.HttpMethod;
import org.elasticsearch.xpack.common.http.HttpRequestTemplate;
import org.elasticsearch.xpack.common.http.auth.HttpAuthRegistry;
import org.elasticsearch.xpack.common.http.auth.basic.BasicAuthFactory;
import org.elasticsearch.xpack.common.ScriptServiceProxy;
import org.elasticsearch.xpack.watcher.support.init.proxy.WatcherClientProxy;
import org.elasticsearch.xpack.common.secret.SecretService;
import org.elasticsearch.xpack.common.text.TextTemplate;
import org.elasticsearch.xpack.common.text.TextTemplateEngine;
import org.elasticsearch.xpack.watcher.support.search.WatcherSearchTemplateRequest;
import org.elasticsearch.xpack.watcher.support.search.WatcherSearchTemplateService;
import org.elasticsearch.xpack.watcher.test.WatcherTestUtils;
import org.elasticsearch.xpack.watcher.transform.ExecutableTransform;
import org.elasticsearch.xpack.watcher.transform.TransformFactory;
import org.elasticsearch.xpack.watcher.transform.TransformRegistry;
import org.elasticsearch.xpack.watcher.transform.chain.ChainTransform;
import org.elasticsearch.xpack.watcher.transform.chain.ChainTransformFactory;
import org.elasticsearch.xpack.watcher.transform.chain.ExecutableChainTransform;
import org.elasticsearch.xpack.watcher.transform.script.ExecutableScriptTransform;
import org.elasticsearch.xpack.watcher.transform.script.ScriptTransform;
@ -116,21 +114,23 @@ import org.elasticsearch.xpack.watcher.trigger.schedule.support.Month;
import org.elasticsearch.xpack.watcher.trigger.schedule.support.MonthTimes;
import org.elasticsearch.xpack.watcher.trigger.schedule.support.WeekTimes;
import org.elasticsearch.xpack.watcher.trigger.schedule.support.YearTimes;
import org.elasticsearch.xpack.notification.email.EmailService;
import org.elasticsearch.xpack.notification.email.EmailTemplate;
import org.elasticsearch.xpack.notification.email.HtmlSanitizer;
import org.elasticsearch.xpack.notification.email.Profile;
import org.elasticsearch.xpack.notification.email.attachment.EmailAttachments;
import org.elasticsearch.xpack.notification.email.attachment.EmailAttachmentsParser;
import org.joda.time.DateTime;
import org.joda.time.DateTimeZone;
import org.junit.Before;
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 static java.util.Collections.singleton;
import static java.util.Collections.singletonMap;
import static java.util.Collections.unmodifiableMap;
import static org.elasticsearch.xpack.watcher.input.InputBuilders.searchInput;
import static org.elasticsearch.xpack.watcher.support.search.WatcherSearchTemplateRequest.DEFAULT_INDICES_OPTIONS;
import static org.elasticsearch.xpack.watcher.test.WatcherTestUtils.matchAllRequest;
import static org.elasticsearch.xpack.watcher.trigger.TriggerBuilders.schedule;
import static org.hamcrest.Matchers.equalTo;
@ -153,6 +153,7 @@ public class WatchTests extends ESTestCase {
private WatcherLicensee watcherLicensee;
private ESLogger logger;
private Settings settings = Settings.EMPTY;
private WatcherSearchTemplateService searchTemplateService;
@Before
public void init() throws Exception {
@ -166,6 +167,7 @@ public class WatchTests extends ESTestCase {
watcherLicensee = mock(WatcherLicensee.class);
authRegistry = new HttpAuthRegistry(singletonMap("basic", new BasicAuthFactory(secretService)));
logger = Loggers.getLogger(WatchTests.class);
searchTemplateService = mock(WatcherSearchTemplateService.class);
}
public void testParserSelfGenerated() throws Exception {
@ -341,7 +343,7 @@ public class WatchTests extends ESTestCase {
switch (type) {
case SearchInput.TYPE:
SearchInput searchInput = searchInput(WatcherTestUtils.newInputSearchRequest("idx")).build();
return new ExecutableSearchInput(searchInput, logger, client, null);
return new ExecutableSearchInput(searchInput, logger, client, searchTemplateService, null);
default:
SimpleInput simpleInput = InputBuilders.simpleInput(singletonMap("_key", "_val")).build();
return new ExecutableSimpleInput(simpleInput, logger);
@ -355,7 +357,7 @@ public class WatchTests extends ESTestCase {
IndicesQueriesRegistry queryRegistry = new IndicesQueriesRegistry();
QueryParser<MatchAllQueryBuilder> queryParser = MatchAllQueryBuilder::fromXContent;
queryRegistry.register(queryParser, MatchAllQueryBuilder.QUERY_NAME_FIELD);
parsers.put(SearchInput.TYPE, new SearchInputFactory(settings, client, queryRegistry, null, null));
parsers.put(SearchInput.TYPE, new SearchInputFactory(settings, client, queryRegistry, null, null, scriptService));
return new InputRegistry(Settings.EMPTY, parsers);
default:
parsers.put(SimpleInput.TYPE, new SimpleInputFactory(settings));
@ -406,15 +408,19 @@ public class WatchTests extends ESTestCase {
case ScriptTransform.TYPE:
return new ExecutableScriptTransform(new ScriptTransform(Script.inline("_script").build()), logger, scriptService);
case SearchTransform.TYPE:
return new ExecutableSearchTransform(new SearchTransform(
matchAllRequest(WatcherUtils.DEFAULT_INDICES_OPTIONS), timeout, timeZone), logger, client, null);
SearchTransform transform = new SearchTransform(
new WatcherSearchTemplateRequest(matchAllRequest(DEFAULT_INDICES_OPTIONS), null), timeout, timeZone);
return new ExecutableSearchTransform(transform, logger, client, searchTemplateService, null);
default: // chain
ChainTransform chainTransform = new ChainTransform(Arrays.asList(
new SearchTransform(matchAllRequest(WatcherUtils.DEFAULT_INDICES_OPTIONS), timeout, timeZone),
new ScriptTransform(Script.inline("_script").build())));
SearchTransform searchTransform = new SearchTransform(
new WatcherSearchTemplateRequest(matchAllRequest(DEFAULT_INDICES_OPTIONS), null), timeout, timeZone);
ScriptTransform scriptTransform = new ScriptTransform(Script.inline("_script").build());
ChainTransform chainTransform = new ChainTransform(Arrays.asList(searchTransform, scriptTransform));
return new ExecutableChainTransform(chainTransform, logger, Arrays.<ExecutableTransform>asList(
new ExecutableSearchTransform(new SearchTransform(
matchAllRequest(WatcherUtils.DEFAULT_INDICES_OPTIONS), timeout, timeZone), logger, client, null),
new WatcherSearchTemplateRequest(matchAllRequest(DEFAULT_INDICES_OPTIONS), null), timeout, timeZone),
logger, client, searchTemplateService, null),
new ExecutableScriptTransform(new ScriptTransform(Script.inline("_script").build()), logger, scriptService)));
}
}
@ -425,7 +431,7 @@ public class WatchTests extends ESTestCase {
queryRegistry.register(queryParser, MatchAllQueryBuilder.QUERY_NAME_FIELD);
Map<String, TransformFactory> factories = new HashMap<>();
factories.put(ScriptTransform.TYPE, new ScriptTransformFactory(settings, scriptService));
factories.put(SearchTransform.TYPE, new SearchTransformFactory(settings, client, queryRegistry, null, null));
factories.put(SearchTransform.TYPE, new SearchTransformFactory(settings, client, queryRegistry, null, null, scriptService));
TransformRegistry registry = new TransformRegistry(Settings.EMPTY, unmodifiableMap(factories));
return registry;
}

View File

@ -4,7 +4,7 @@
"methods": [ "PUT", "POST" ],
"url": {
"path": "/_xpack/watcher/watch/{watch_id}/_ack",
"paths": [ "/_xpack/watcher/watch/{watch_id}/_ack", "/_xpack/watcher/watch/{watch_id}/{action_id}/_ack"],
"paths": [ "/_xpack/watcher/watch/{watch_id}/_ack", "/_xpack/watcher/watch/{watch_id}/_ack/{action_id}"],
"parts": {
"watch_id": {
"type" : "string",