Merge branch 'master' into index-lifecycle

This commit is contained in:
Colin Goodheart-Smithe 2018-05-09 10:50:33 +01:00
commit a03241d590
No known key found for this signature in database
GPG Key ID: F975E7BDD739B3C7
46 changed files with 464 additions and 332 deletions

View File

@ -497,10 +497,15 @@ class BuildPlugin implements Plugin<Project> {
project.afterEvaluate {
project.tasks.withType(JavaCompile) {
final JavaVersion targetCompatibilityVersion = JavaVersion.toVersion(it.targetCompatibility)
// we fork because compiling lots of different classes in a shared jvm can eventually trigger GC overhead limitations
options.fork = true
options.forkOptions.javaHome = new File(project.compilerJavaHome)
options.forkOptions.memoryMaximumSize = "512m"
final compilerJavaHomeFile = new File(project.compilerJavaHome)
// we only fork if the Gradle JDK is not the same as the compiler JDK
if (compilerJavaHomeFile.canonicalPath == Jvm.current().javaHome.canonicalPath) {
options.fork = false
} else {
options.fork = true
options.forkOptions.javaHome = compilerJavaHomeFile
options.forkOptions.memoryMaximumSize = "512m"
}
if (targetCompatibilityVersion == JavaVersion.VERSION_1_8) {
// compile with compact 3 profile by default
// NOTE: this is just a compile time check: does not replace testing with a compact3 JRE

View File

@ -210,7 +210,9 @@ public class RestClient implements Closeable {
* @throws IOException in case of a problem or the connection was aborted
* @throws ClientProtocolException in case of an http protocol error
* @throws ResponseException in case Elasticsearch responded with a status code that indicated an error
* @deprecated prefer {@link #performRequest(Request)}
*/
@Deprecated
public Response performRequest(String method, String endpoint, Header... headers) throws IOException {
Request request = new Request(method, endpoint);
request.setHeaders(headers);
@ -229,7 +231,9 @@ public class RestClient implements Closeable {
* @throws IOException in case of a problem or the connection was aborted
* @throws ClientProtocolException in case of an http protocol error
* @throws ResponseException in case Elasticsearch responded with a status code that indicated an error
* @deprecated prefer {@link #performRequest(Request)}
*/
@Deprecated
public Response performRequest(String method, String endpoint, Map<String, String> params, Header... headers) throws IOException {
Request request = new Request(method, endpoint);
addParameters(request, params);
@ -252,7 +256,9 @@ public class RestClient implements Closeable {
* @throws IOException in case of a problem or the connection was aborted
* @throws ClientProtocolException in case of an http protocol error
* @throws ResponseException in case Elasticsearch responded with a status code that indicated an error
* @deprecated prefer {@link #performRequest(Request)}
*/
@Deprecated
public Response performRequest(String method, String endpoint, Map<String, String> params,
HttpEntity entity, Header... headers) throws IOException {
Request request = new Request(method, endpoint);
@ -289,7 +295,9 @@ public class RestClient implements Closeable {
* @throws IOException in case of a problem or the connection was aborted
* @throws ClientProtocolException in case of an http protocol error
* @throws ResponseException in case Elasticsearch responded with a status code that indicated an error
* @deprecated prefer {@link #performRequest(Request)}
*/
@Deprecated
public Response performRequest(String method, String endpoint, Map<String, String> params,
HttpEntity entity, HttpAsyncResponseConsumerFactory httpAsyncResponseConsumerFactory,
Header... headers) throws IOException {
@ -310,7 +318,9 @@ public class RestClient implements Closeable {
* @param endpoint the path of the request (without host and port)
* @param responseListener the {@link ResponseListener} to notify when the request is completed or fails
* @param headers the optional request headers
* @deprecated prefer {@link #performRequestAsync(Request, ResponseListener)}
*/
@Deprecated
public void performRequestAsync(String method, String endpoint, ResponseListener responseListener, Header... headers) {
Request request;
try {
@ -333,7 +343,9 @@ public class RestClient implements Closeable {
* @param params the query_string parameters
* @param responseListener the {@link ResponseListener} to notify when the request is completed or fails
* @param headers the optional request headers
* @deprecated prefer {@link #performRequestAsync(Request, ResponseListener)}
*/
@Deprecated
public void performRequestAsync(String method, String endpoint, Map<String, String> params,
ResponseListener responseListener, Header... headers) {
Request request;
@ -361,7 +373,9 @@ public class RestClient implements Closeable {
* @param entity the body of the request, null if not applicable
* @param responseListener the {@link ResponseListener} to notify when the request is completed or fails
* @param headers the optional request headers
* @deprecated prefer {@link #performRequestAsync(Request, ResponseListener)}
*/
@Deprecated
public void performRequestAsync(String method, String endpoint, Map<String, String> params,
HttpEntity entity, ResponseListener responseListener, Header... headers) {
Request request;
@ -394,7 +408,9 @@ public class RestClient implements Closeable {
* connection on the client side.
* @param responseListener the {@link ResponseListener} to notify when the request is completed or fails
* @param headers the optional request headers
* @deprecated prefer {@link #performRequestAsync(Request, ResponseListener)}
*/
@Deprecated
public void performRequestAsync(String method, String endpoint, Map<String, String> params,
HttpEntity entity, HttpAsyncResponseConsumerFactory httpAsyncResponseConsumerFactory,
ResponseListener responseListener, Header... headers) {

View File

@ -43,7 +43,6 @@ public final class RestClientBuilder {
public static final int DEFAULT_CONNECT_TIMEOUT_MILLIS = 1000;
public static final int DEFAULT_SOCKET_TIMEOUT_MILLIS = 30000;
public static final int DEFAULT_MAX_RETRY_TIMEOUT_MILLIS = DEFAULT_SOCKET_TIMEOUT_MILLIS;
public static final int DEFAULT_CONNECTION_REQUEST_TIMEOUT_MILLIS = 500;
public static final int DEFAULT_MAX_CONN_PER_ROUTE = 10;
public static final int DEFAULT_MAX_CONN_TOTAL = 30;
@ -196,8 +195,7 @@ public final class RestClientBuilder {
//default timeouts are all infinite
RequestConfig.Builder requestConfigBuilder = RequestConfig.custom()
.setConnectTimeout(DEFAULT_CONNECT_TIMEOUT_MILLIS)
.setSocketTimeout(DEFAULT_SOCKET_TIMEOUT_MILLIS)
.setConnectionRequestTimeout(DEFAULT_CONNECTION_REQUEST_TIMEOUT_MILLIS);
.setSocketTimeout(DEFAULT_SOCKET_TIMEOUT_MILLIS);
if (requestConfigCallback != null) {
requestConfigBuilder = requestConfigCallback.customizeRequestConfig(requestConfigBuilder);
}

View File

@ -177,4 +177,24 @@ public class RestClientBuilderTests extends RestClientTestCase {
}
}
/**
* This test verifies that we don't change the default value for the connection request timeout as that causes problems.
* See https://github.com/elastic/elasticsearch/issues/24069
*/
public void testDefaultConnectionRequestTimeout() throws IOException {
RestClientBuilder builder = RestClient.builder(new HttpHost("localhost", 9200));
builder.setRequestConfigCallback(new RestClientBuilder.RequestConfigCallback() {
@Override
public RequestConfig.Builder customizeRequestConfig(RequestConfig.Builder requestConfigBuilder) {
RequestConfig requestConfig = requestConfigBuilder.build();
assertEquals(RequestConfig.DEFAULT.getConnectionRequestTimeout(), requestConfig.getConnectionRequestTimeout());
//this way we get notified if the default ever changes
assertEquals(-1, requestConfig.getConnectionRequestTimeout());
return requestConfigBuilder;
}
});
try (RestClient restClient = builder.build()) {
assertNotNull(restClient);
}
}
}

View File

@ -32,6 +32,7 @@ import org.apache.http.entity.ContentType;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.BasicCredentialsProvider;
import org.apache.http.impl.nio.client.HttpAsyncClientBuilder;
import org.apache.http.nio.entity.NStringEntity;
import org.apache.http.util.EntityUtils;
import org.elasticsearch.mocksocket.MockHttpServer;
import org.junit.AfterClass;
@ -48,6 +49,9 @@ import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import static org.elasticsearch.client.RestClientTestUtil.getAllStatusCodes;
import static org.elasticsearch.client.RestClientTestUtil.getHttpMethods;
@ -159,6 +163,42 @@ public class RestClientSingleHostIntegTests extends RestClientTestCase {
httpServer = null;
}
/**
* Tests sending a bunch of async requests works well (e.g. no TimeoutException from the leased pool)
* See https://github.com/elastic/elasticsearch/issues/24069
*/
public void testManyAsyncRequests() throws Exception {
int iters = randomIntBetween(500, 1000);
final CountDownLatch latch = new CountDownLatch(iters);
final List<Exception> exceptions = new CopyOnWriteArrayList<>();
for (int i = 0; i < iters; i++) {
Request request = new Request("PUT", "/200");
request.setEntity(new NStringEntity("{}", ContentType.APPLICATION_JSON));
restClient.performRequestAsync(request, new ResponseListener() {
@Override
public void onSuccess(Response response) {
latch.countDown();
}
@Override
public void onFailure(Exception exception) {
exceptions.add(exception);
latch.countDown();
}
});
}
assertTrue("timeout waiting for requests to be sent", latch.await(10, TimeUnit.SECONDS));
if (exceptions.isEmpty() == false) {
AssertionError error = new AssertionError("expected no failures but got some. see suppressed for first 10 of ["
+ exceptions.size() + "] failures");
for (Exception exception : exceptions.subList(0, Math.min(10, exceptions.size()))) {
error.addSuppressed(exception);
}
throw error;
}
}
/**
* End to end test for headers. We test it explicitly against a real http client as there are different ways
* to set/add headers to the {@link org.apache.http.client.HttpClient}.

View File

@ -3,7 +3,7 @@
[partintro]
--
// To add a release, copy and paste the template text
// To add a release, copy and paste the template text
// and add a link to the new section. Note that release subheads must
// be floated and sections cannot be empty.
@ -104,6 +104,8 @@ ones that the user is authorized to access in case field level security is enabl
[float]
=== Bug Fixes
Use date format in `date_range` mapping before fallback to default ({pull}29310[#29310])
Fix NPE in 'more_like_this' when field has zero tokens ({pull}30365[#30365])
Fixed prerelease version of elasticsearch in the `deb` package to sort before GA versions
@ -139,8 +141,11 @@ coming[6.4.0]
//[float]
//=== Breaking Java Changes
//[float]
//=== Deprecations
[float]
=== Deprecations
Deprecated multi-argument versions of the request methods in the RestClient.
Prefer the "Request" object flavored methods. ({pull}30315[#30315])
[float]
=== New Features
@ -157,8 +162,11 @@ analysis module. ({pull}30397[#30397])
{ref-64}/breaking_64_api_changes.html#copy-source-settings-on-resize[Allow copying source settings on index resize operations] ({pull}30255[#30255])
Added new "Request" object flavored request methods. Prefer these instead of the
multi-argument versions. ({pull}29623[#29623])
Added new "Request" object flavored request methods in the RestClient. Prefer
these instead of the multi-argument versions. ({pull}29623[#29623])
Watcher HTTP client used in watches now allows more parallel connections to the
same endpoint and evicts long running connections. ({pull}30130[#30130])
The cluster state listener to decide if watcher should be
stopped/started/paused now runs far less code in an executor but is more
@ -171,6 +179,8 @@ Added put index template API to the high level rest client ({pull}30400[#30400])
[float]
=== Bug Fixes
Use date format in `date_range` mapping before fallback to default ({pull}29310[#29310])
Fix NPE in 'more_like_this' when field has zero tokens ({pull}30365[#30365])
Do not ignore request analysis/similarity settings on index resize operations when the source index already contains such settings ({pull}30216[#30216])

View File

@ -64,9 +64,10 @@ It is also possible to retrieve information for a particular task:
[source,js]
--------------------------------------------------
GET _tasks/task_id:1 <1>
GET _tasks/task_id <1>
--------------------------------------------------
// CONSOLE
// TEST[s/task_id/node_id:1/]
// TEST[catch:missing]
<1> This will return a 404 if the task isn't found.
@ -75,9 +76,10 @@ Or to retrieve all children of a particular task:
[source,js]
--------------------------------------------------
GET _tasks?parent_task_id=parentTaskId:1 <1>
GET _tasks?parent_task_id=parent_task_id <1>
--------------------------------------------------
// CONSOLE
// TEST[s/=parent_task_id/=node_id:1/]
<1> This won't return a 404 if the parent isn't found.

View File

@ -357,9 +357,10 @@ With the task id you can look up the task directly:
[source,js]
--------------------------------------------------
GET /_tasks/taskId:1
GET /_tasks/task_id
--------------------------------------------------
// CONSOLE
// TEST[s/task_id/node_id:1/]
// TEST[catch:missing]
The advantage of this API is that it integrates with `wait_for_completion=false`
@ -378,8 +379,9 @@ Any Delete By Query can be canceled using the <<tasks,Task Cancel API>>:
[source,js]
--------------------------------------------------
POST _tasks/task_id:1/_cancel
POST _tasks/task_id/_cancel
--------------------------------------------------
// TEST[s/task_id/node_id:1/]
// CONSOLE
The `task_id` can be found using the tasks API above.
@ -397,8 +399,9 @@ using the `_rethrottle` API:
[source,js]
--------------------------------------------------
POST _delete_by_query/task_id:1/_rethrottle?requests_per_second=-1
POST _delete_by_query/task_id/_rethrottle?requests_per_second=-1
--------------------------------------------------
// TEST[s/task_id/node_id:1/]
// CONSOLE
The `task_id` can be found using the tasks API above.

View File

@ -740,9 +740,10 @@ With the task id you can look up the task directly:
[source,js]
--------------------------------------------------
GET /_tasks/taskId:1
GET /_tasks/task_id
--------------------------------------------------
// CONSOLE
// TEST[s/task_id/node_id:1/]
// TEST[catch:missing]
The advantage of this API is that it integrates with `wait_for_completion=false`
@ -761,9 +762,10 @@ Any Reindex can be canceled using the <<tasks,Task Cancel API>>:
[source,js]
--------------------------------------------------
POST _tasks/task_id:1/_cancel
POST _tasks/task_id/_cancel
--------------------------------------------------
// CONSOLE
// TEST[s/task_id/node_id:1/]
The `task_id` can be found using the Tasks API.
@ -780,9 +782,10 @@ the `_rethrottle` API:
[source,js]
--------------------------------------------------
POST _reindex/task_id:1/_rethrottle?requests_per_second=-1
POST _reindex/task_id/_rethrottle?requests_per_second=-1
--------------------------------------------------
// CONSOLE
// TEST[s/task_id/node_id:1/]
The `task_id` can be found using the Tasks API above.

View File

@ -415,9 +415,10 @@ With the task id you can look up the task directly:
[source,js]
--------------------------------------------------
GET /_tasks/taskId:1
GET /_tasks/task_id
--------------------------------------------------
// CONSOLE
// TEST[s/task_id/node_id:1/]
// TEST[catch:missing]
The advantage of this API is that it integrates with `wait_for_completion=false`
@ -436,9 +437,10 @@ Any Update By Query can be canceled using the <<tasks,Task Cancel API>>:
[source,js]
--------------------------------------------------
POST _tasks/task_id:1/_cancel
POST _tasks/task_id/_cancel
--------------------------------------------------
// CONSOLE
// TEST[s/task_id/node_id:1/]
The `task_id` can be found using the tasks API above.
@ -455,9 +457,10 @@ using the `_rethrottle` API:
[source,js]
--------------------------------------------------
POST _update_by_query/task_id:1/_rethrottle?requests_per_second=-1
POST _update_by_query/task_id/_rethrottle?requests_per_second=-1
--------------------------------------------------
// CONSOLE
// TEST[s/task_id/node_id:1/]
The `task_id` can be found using the tasks API above.

View File

@ -287,6 +287,9 @@ public class RangeFieldMapper extends FieldMapper {
public Query rangeQuery(Object lowerTerm, Object upperTerm, boolean includeLower, boolean includeUpper,
ShapeRelation relation, DateTimeZone timeZone, DateMathParser parser, QueryShardContext context) {
failIfNotIndexed();
if (parser == null) {
parser = dateMathParser();
}
return rangeType.rangeQuery(name(), hasDocValues(), lowerTerm, upperTerm, includeLower, includeUpper, relation,
timeZone, parser, context);
}

View File

@ -19,7 +19,6 @@
package org.elasticsearch.index.mapper;
import com.carrotsearch.randomizedtesting.generators.RandomPicks;
import org.apache.lucene.document.DoubleRange;
import org.apache.lucene.document.FloatRange;
import org.apache.lucene.document.InetAddressPoint;
@ -31,13 +30,16 @@ import org.apache.lucene.queries.BinaryDocValuesRangeQuery;
import org.apache.lucene.search.IndexOrDocValuesQuery;
import org.apache.lucene.search.Query;
import org.apache.lucene.util.BytesRef;
import org.elasticsearch.ElasticsearchParseException;
import org.elasticsearch.Version;
import org.elasticsearch.cluster.metadata.IndexMetaData;
import org.elasticsearch.common.geo.ShapeRelation;
import org.elasticsearch.common.joda.FormatDateTimeFormatter;
import org.elasticsearch.common.joda.Joda;
import org.elasticsearch.common.network.InetAddresses;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.index.IndexSettings;
import org.elasticsearch.index.mapper.RangeFieldMapper.RangeFieldType;
import org.elasticsearch.index.mapper.RangeFieldMapper.RangeType;
import org.elasticsearch.index.query.QueryShardContext;
import org.elasticsearch.test.IndexSettingsModule;
@ -55,42 +57,38 @@ public class RangeFieldTypeTests extends FieldTypeTestCase {
@Before
public void setupProperties() {
type = RandomPicks.randomFrom(random(), RangeType.values());
type = randomFrom(RangeType.values());
nowInMillis = randomNonNegativeLong();
if (type == RangeType.DATE) {
addModifier(new Modifier("format", true) {
@Override
public void modify(MappedFieldType ft) {
((RangeFieldMapper.RangeFieldType) ft).setDateTimeFormatter(Joda.forPattern("basic_week_date", Locale.ROOT));
((RangeFieldType) ft).setDateTimeFormatter(Joda.forPattern("basic_week_date", Locale.ROOT));
}
});
addModifier(new Modifier("locale", true) {
@Override
public void modify(MappedFieldType ft) {
((RangeFieldMapper.RangeFieldType) ft).setDateTimeFormatter(Joda.forPattern("date_optional_time", Locale.CANADA));
((RangeFieldType) ft).setDateTimeFormatter(Joda.forPattern("date_optional_time", Locale.CANADA));
}
});
}
}
@Override
protected RangeFieldMapper.RangeFieldType createDefaultFieldType() {
return new RangeFieldMapper.RangeFieldType(type, Version.CURRENT);
protected RangeFieldType createDefaultFieldType() {
return new RangeFieldType(type, Version.CURRENT);
}
public void testRangeQuery() throws Exception {
Settings indexSettings = Settings.builder()
.put(IndexMetaData.SETTING_VERSION_CREATED, Version.CURRENT).build();
IndexSettings idxSettings = IndexSettingsModule.newIndexSettings(randomAlphaOfLengthBetween(1, 10), indexSettings);
QueryShardContext context = new QueryShardContext(0, idxSettings, null, null, null, null, null, xContentRegistry(),
writableRegistry(), null, null, () -> nowInMillis, null);
RangeFieldMapper.RangeFieldType ft = new RangeFieldMapper.RangeFieldType(type, Version.CURRENT);
QueryShardContext context = createContext();
RangeFieldType ft = new RangeFieldType(type, Version.CURRENT);
ft.setName(FIELDNAME);
ft.setIndexOptions(IndexOptions.DOCS);
ShapeRelation relation = RandomPicks.randomFrom(random(), ShapeRelation.values());
boolean includeLower = random().nextBoolean();
boolean includeUpper = random().nextBoolean();
ShapeRelation relation = randomFrom(ShapeRelation.values());
boolean includeLower = randomBoolean();
boolean includeUpper = randomBoolean();
Object from = nextFrom();
Object to = nextTo(from);
@ -98,6 +96,41 @@ public class RangeFieldTypeTests extends FieldTypeTestCase {
ft.rangeQuery(from, to, includeLower, includeUpper, relation, null, null, context));
}
private QueryShardContext createContext() {
Settings indexSettings = Settings.builder()
.put(IndexMetaData.SETTING_VERSION_CREATED, Version.CURRENT).build();
IndexSettings idxSettings = IndexSettingsModule.newIndexSettings(randomAlphaOfLengthBetween(1, 10), indexSettings);
return new QueryShardContext(0, idxSettings, null, null, null, null, null, xContentRegistry(),
writableRegistry(), null, null, () -> nowInMillis, null);
}
public void testDateRangeQueryUsingMappingFormat() {
QueryShardContext context = createContext();
RangeFieldType fieldType = new RangeFieldType(RangeType.DATE, Version.CURRENT);
fieldType.setName(FIELDNAME);
fieldType.setIndexOptions(IndexOptions.DOCS);
fieldType.setHasDocValues(false);
ShapeRelation relation = randomFrom(ShapeRelation.values());
// dates will break the default format
final String from = "2016-15-06T15:29:50+08:00";
final String to = "2016-16-06T15:29:50+08:00";
ElasticsearchParseException ex = expectThrows(ElasticsearchParseException.class,
() -> fieldType.rangeQuery(from, to, true, true, relation, null, null, context));
assertEquals("failed to parse date field [2016-15-06T15:29:50+08:00] with format [strict_date_optional_time||epoch_millis]",
ex.getMessage());
// setting mapping format which is compatible with those dates
final FormatDateTimeFormatter formatter = Joda.forPattern("yyyy-dd-MM'T'HH:mm:ssZZ");
assertEquals(1465975790000L, formatter.parser().parseMillis(from));
assertEquals(1466062190000L, formatter.parser().parseMillis(to));
fieldType.setDateTimeFormatter(formatter);
final Query query = fieldType.rangeQuery(from, to, true, true, relation, null, null, context);
assertEquals("field:<ranges:[1465975790000 : 1466062190000]>", query.toString());
}
private Query getExpectedRangeQuery(ShapeRelation relation, Object from, Object to, boolean includeLower, boolean includeUpper) {
switch (type) {
case DATE:
@ -277,14 +310,10 @@ public class RangeFieldTypeTests extends FieldTypeTestCase {
assertEquals(InetAddresses.forString("::1"), RangeFieldMapper.RangeType.IP.parse(new BytesRef("::1"), randomBoolean()));
}
public void testTermQuery() throws Exception, IllegalArgumentException {
public void testTermQuery() throws Exception {
// See https://github.com/elastic/elasticsearch/issues/25950
Settings indexSettings = Settings.builder()
.put(IndexMetaData.SETTING_VERSION_CREATED, Version.CURRENT).build();
IndexSettings idxSettings = IndexSettingsModule.newIndexSettings(randomAlphaOfLengthBetween(1, 10), indexSettings);
QueryShardContext context = new QueryShardContext(0, idxSettings, null, null, null, null, null, xContentRegistry(),
writableRegistry(), null, null, () -> nowInMillis, null);
RangeFieldMapper.RangeFieldType ft = new RangeFieldMapper.RangeFieldType(type, Version.CURRENT);
QueryShardContext context = createContext();
RangeFieldType ft = new RangeFieldType(type, Version.CURRENT);
ft.setName(FIELDNAME);
ft.setIndexOptions(IndexOptions.DOCS);

View File

@ -28,6 +28,7 @@ import org.elasticsearch.common.util.BigArrays;
import org.elasticsearch.common.util.PageCacheRecycler;
import org.elasticsearch.common.util.concurrent.ThreadContext;
import org.elasticsearch.common.xcontent.NamedXContentRegistry;
import org.elasticsearch.core.internal.io.IOUtils;
import org.elasticsearch.env.Environment;
import org.elasticsearch.env.NodeEnvironment;
import org.elasticsearch.http.HttpServerTransport;
@ -38,6 +39,7 @@ import org.elasticsearch.indices.breaker.CircuitBreakerService;
import org.elasticsearch.ingest.Processor;
import org.elasticsearch.license.LicenseService;
import org.elasticsearch.license.XPackLicenseState;
import org.elasticsearch.persistent.PersistentTasksExecutor;
import org.elasticsearch.plugins.ActionPlugin;
import org.elasticsearch.plugins.AnalysisPlugin;
import org.elasticsearch.plugins.ClusterPlugin;
@ -57,9 +59,9 @@ import org.elasticsearch.threadpool.ThreadPool;
import org.elasticsearch.transport.Transport;
import org.elasticsearch.transport.TransportInterceptor;
import org.elasticsearch.watcher.ResourceWatcherService;
import org.elasticsearch.persistent.PersistentTasksExecutor;
import org.elasticsearch.xpack.core.ssl.SSLService;
import java.io.IOException;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Collection;
@ -391,6 +393,11 @@ public class LocalStateCompositeXPackPlugin extends XPackPlugin implements Scrip
.collect(toList());
}
@Override
public void close() throws IOException {
IOUtils.close(plugins);
}
private <T> List<T> filterPlugins(Class<T> type) {
return plugins.stream().filter(x -> type.isAssignableFrom(x.getClass())).map(p -> ((T)p))
.collect(Collectors.toList());

View File

@ -197,7 +197,7 @@ import org.elasticsearch.xpack.security.rest.action.user.RestGetUsersAction;
import org.elasticsearch.xpack.security.rest.action.user.RestHasPrivilegesAction;
import org.elasticsearch.xpack.security.rest.action.user.RestPutUserAction;
import org.elasticsearch.xpack.security.rest.action.user.RestSetEnabledAction;
import org.elasticsearch.xpack.security.support.IndexLifecycleManager;
import org.elasticsearch.xpack.security.support.SecurityIndexManager;
import org.elasticsearch.xpack.security.transport.SecurityServerTransportInterceptor;
import org.elasticsearch.xpack.security.transport.filter.IPFilter;
import org.elasticsearch.xpack.security.transport.netty4.SecurityNetty4HttpServerTransport;
@ -233,7 +233,7 @@ import static org.elasticsearch.cluster.metadata.IndexMetaData.INDEX_FORMAT_SETT
import static org.elasticsearch.xpack.core.XPackSettings.HTTP_SSL_ENABLED;
import static org.elasticsearch.xpack.core.security.SecurityLifecycleServiceField.SECURITY_TEMPLATE_NAME;
import static org.elasticsearch.xpack.security.SecurityLifecycleService.SECURITY_INDEX_NAME;
import static org.elasticsearch.xpack.security.support.IndexLifecycleManager.INTERNAL_INDEX_FORMAT;
import static org.elasticsearch.xpack.security.support.SecurityIndexManager.INTERNAL_INDEX_FORMAT;
public class Security extends Plugin implements ActionPlugin, IngestPlugin, NetworkPlugin, ClusterPlugin,
DiscoveryPlugin, MapperPlugin, ExtensiblePlugin {
@ -424,8 +424,8 @@ public class Security extends Plugin implements ActionPlugin, IngestPlugin, Netw
components.add(realms);
components.add(reservedRealm);
securityLifecycleService.addSecurityIndexHealthChangeListener(nativeRoleMappingStore::onSecurityIndexHealthChange);
securityLifecycleService.addSecurityIndexOutOfDateListener(nativeRoleMappingStore::onSecurityIndexOutOfDateChange);
securityLifecycleService.securityIndex().addIndexHealthChangeListener(nativeRoleMappingStore::onSecurityIndexHealthChange);
securityLifecycleService.securityIndex().addIndexOutOfDateListener(nativeRoleMappingStore::onSecurityIndexOutOfDateChange);
AuthenticationFailureHandler failureHandler = null;
String extensionName = null;
@ -458,8 +458,8 @@ public class Security extends Plugin implements ActionPlugin, IngestPlugin, Netw
}
final CompositeRolesStore allRolesStore = new CompositeRolesStore(settings, fileRolesStore, nativeRolesStore,
reservedRolesStore, rolesProviders, threadPool.getThreadContext(), getLicenseState());
securityLifecycleService.addSecurityIndexHealthChangeListener(allRolesStore::onSecurityIndexHealthChange);
securityLifecycleService.addSecurityIndexOutOfDateListener(allRolesStore::onSecurityIndexOutOfDateChange);
securityLifecycleService.securityIndex().addIndexHealthChangeListener(allRolesStore::onSecurityIndexHealthChange);
securityLifecycleService.securityIndex().addIndexOutOfDateListener(allRolesStore::onSecurityIndexOutOfDateChange);
// to keep things simple, just invalidate all cached entries on license change. this happens so rarely that the impact should be
// minimal
getLicenseState().addListener(allRolesStore::invalidateAll);
@ -886,7 +886,7 @@ public class Security extends Plugin implements ActionPlugin, IngestPlugin, Netw
templates.remove(SECURITY_TEMPLATE_NAME);
final XContent xContent = XContentFactory.xContent(XContentType.JSON);
final byte[] auditTemplate = TemplateUtils.loadTemplate("/" + IndexAuditTrail.INDEX_TEMPLATE_NAME + ".json",
Version.CURRENT.toString(), IndexLifecycleManager.TEMPLATE_VERSION_PATTERN).getBytes(StandardCharsets.UTF_8);
Version.CURRENT.toString(), SecurityIndexManager.TEMPLATE_VERSION_PATTERN).getBytes(StandardCharsets.UTF_8);
try (XContentParser parser = xContent
.createParser(NamedXContentRegistry.EMPTY, LoggingDeprecationHandler.INSTANCE, auditTemplate)) {

View File

@ -22,7 +22,7 @@ import org.elasticsearch.common.util.concurrent.AbstractRunnable;
import org.elasticsearch.gateway.GatewayService;
import org.elasticsearch.threadpool.ThreadPool;
import org.elasticsearch.xpack.security.audit.index.IndexAuditTrail;
import org.elasticsearch.xpack.security.support.IndexLifecycleManager;
import org.elasticsearch.xpack.security.support.SecurityIndexManager;
import java.util.Arrays;
import java.util.Collections;
@ -46,7 +46,7 @@ import java.util.function.Predicate;
*/
public class SecurityLifecycleService extends AbstractComponent implements ClusterStateListener {
public static final String INTERNAL_SECURITY_INDEX = IndexLifecycleManager.INTERNAL_SECURITY_INDEX;
public static final String INTERNAL_SECURITY_INDEX = SecurityIndexManager.INTERNAL_SECURITY_INDEX;
public static final String SECURITY_INDEX_NAME = ".security";
private static final Version MIN_READ_VERSION = Version.V_5_0_0;
@ -55,7 +55,7 @@ public class SecurityLifecycleService extends AbstractComponent implements Clust
private final ThreadPool threadPool;
private final IndexAuditTrail indexAuditTrail;
private final IndexLifecycleManager securityIndex;
private final SecurityIndexManager securityIndex;
public SecurityLifecycleService(Settings settings, ClusterService clusterService,
ThreadPool threadPool, Client client,
@ -64,7 +64,7 @@ public class SecurityLifecycleService extends AbstractComponent implements Clust
this.settings = settings;
this.threadPool = threadPool;
this.indexAuditTrail = indexAuditTrail;
this.securityIndex = new IndexLifecycleManager(settings, client, SECURITY_INDEX_NAME);
this.securityIndex = new SecurityIndexManager(settings, client, SECURITY_INDEX_NAME);
clusterService.addListener(this);
clusterService.addLifecycleListener(new LifecycleListener() {
@Override
@ -110,69 +110,10 @@ public class SecurityLifecycleService extends AbstractComponent implements Clust
}
}
IndexLifecycleManager securityIndex() {
public SecurityIndexManager securityIndex() {
return securityIndex;
}
/**
* Returns {@code true} if the security index exists
*/
public boolean isSecurityIndexExisting() {
return securityIndex.indexExists();
}
/**
* Returns <code>true</code> if the security index does not exist or it exists and has the current
* value for the <code>index.format</code> index setting
*/
public boolean isSecurityIndexUpToDate() {
return securityIndex.isIndexUpToDate();
}
/**
* Returns <code>true</code> if the security index exists and all primary shards are active
*/
public boolean isSecurityIndexAvailable() {
return securityIndex.isAvailable();
}
/**
* Returns <code>true</code> if the security index does not exist or the mappings are up to date
* based on the version in the <code>_meta</code> field
*/
public boolean isSecurityIndexMappingUpToDate() {
return securityIndex().isMappingUpToDate();
}
/**
* Test whether the effective (active) version of the security mapping meets the
* <code>requiredVersion</code>.
*
* @return <code>true</code> if the effective version passes the predicate, or the security
* mapping does not exist (<code>null</code> version). Otherwise, <code>false</code>.
*/
public boolean checkSecurityMappingVersion(Predicate<Version> requiredVersion) {
return securityIndex.checkMappingVersion(requiredVersion);
}
/**
* Adds a listener which will be notified when the security index health changes. The previous and
* current health will be provided to the listener so that the listener can determine if any action
* needs to be taken.
*/
public void addSecurityIndexHealthChangeListener(BiConsumer<ClusterIndexHealth, ClusterIndexHealth> listener) {
securityIndex.addIndexHealthChangeListener(listener);
}
/**
* Adds a listener which will be notified when the security index out of date value changes. The previous and
* current value will be provided to the listener so that the listener can determine if any action
* needs to be taken.
*/
void addSecurityIndexOutOfDateListener(BiConsumer<Boolean, Boolean> listener) {
securityIndex.addIndexOutOfDateListener(listener);
}
// this is called in a lifecycle listener beforeStop on the cluster service
private void close() {
if (indexAuditTrail != null) {
@ -193,29 +134,13 @@ public class SecurityLifecycleService extends AbstractComponent implements Clust
}
private static boolean checkMappingVersions(ClusterState clusterState, Logger logger, Predicate<Version> versionPredicate) {
return IndexLifecycleManager.checkIndexMappingVersionMatches(SECURITY_INDEX_NAME, clusterState, logger, versionPredicate);
return SecurityIndexManager.checkIndexMappingVersionMatches(SECURITY_INDEX_NAME, clusterState, logger, versionPredicate);
}
public static List<String> indexNames() {
return Collections.unmodifiableList(Arrays.asList(SECURITY_INDEX_NAME, INTERNAL_SECURITY_INDEX));
}
/**
* Prepares the security index by creating it if it doesn't exist or updating the mappings if the mappings are
* out of date. After any tasks have been executed, the runnable is then executed.
*/
public void prepareIndexIfNeededThenExecute(final Consumer<Exception> consumer, final Runnable andThen) {
securityIndex.prepareIndexIfNeededThenExecute(consumer, andThen);
}
/**
* Checks if the security index is out of date with the current version. If the index does not exist
* we treat the index as up to date as we expect it to be created with the current format.
*/
public boolean isSecurityIndexOutOfDate() {
return securityIndex.isIndexUpToDate() == false;
}
/**
* Is the move from {@code previousHealth} to {@code currentHealth} a move from an unhealthy ("RED") index state to a healthy
* ("non-RED") state.

View File

@ -57,7 +57,7 @@ import org.elasticsearch.xpack.core.template.TemplateUtils;
import org.elasticsearch.xpack.security.audit.AuditLevel;
import org.elasticsearch.xpack.security.audit.AuditTrail;
import org.elasticsearch.xpack.security.rest.RemoteHostHeader;
import org.elasticsearch.xpack.security.support.IndexLifecycleManager;
import org.elasticsearch.xpack.security.support.SecurityIndexManager;
import org.elasticsearch.xpack.security.transport.filter.SecurityIpFilterRule;
import org.joda.time.DateTime;
import org.joda.time.DateTimeZone;
@ -105,7 +105,7 @@ import static org.elasticsearch.xpack.security.audit.AuditLevel.parse;
import static org.elasticsearch.xpack.security.audit.AuditUtil.indices;
import static org.elasticsearch.xpack.security.audit.AuditUtil.restRequestContent;
import static org.elasticsearch.xpack.security.audit.index.IndexNameResolver.resolve;
import static org.elasticsearch.xpack.security.support.IndexLifecycleManager.SECURITY_VERSION_STRING;
import static org.elasticsearch.xpack.security.support.SecurityIndexManager.SECURITY_VERSION_STRING;
/**
* Audit trail implementation that writes events into an index.
@ -1001,7 +1001,7 @@ public class IndexAuditTrail extends AbstractComponent implements AuditTrail {
private PutIndexTemplateRequest getPutIndexTemplateRequest(Settings customSettings) {
final byte[] template = TemplateUtils.loadTemplate("/" + INDEX_TEMPLATE_NAME + ".json",
Version.CURRENT.toString(), IndexLifecycleManager.TEMPLATE_VERSION_PATTERN).getBytes(StandardCharsets.UTF_8);
Version.CURRENT.toString(), SecurityIndexManager.TEMPLATE_VERSION_PATTERN).getBytes(StandardCharsets.UTF_8);
final PutIndexTemplateRequest request = new PutIndexTemplateRequest(INDEX_TEMPLATE_NAME).source(template, XContentType.JSON);
if (customSettings != null && customSettings.names().size() > 0) {
Settings updatedSettings = Settings.builder()

View File

@ -96,7 +96,7 @@ public final class InternalRealms {
map.put(FileRealmSettings.TYPE, config -> new FileRealm(config, resourceWatcherService));
map.put(NativeRealmSettings.TYPE, config -> {
final NativeRealm nativeRealm = new NativeRealm(config, nativeUsersStore);
securityLifecycleService.addSecurityIndexHealthChangeListener(nativeRealm::onSecurityIndexHealthChange);
securityLifecycleService.securityIndex().addIndexHealthChangeListener(nativeRealm::onSecurityIndexHealthChange);
return nativeRealm;
});
map.put(LdapRealmSettings.AD_TYPE, config -> new LdapRealm(LdapRealmSettings.AD_TYPE, config, sslService,

View File

@ -250,7 +250,7 @@ public final class TokenService extends AbstractComponent {
.setSource(builder)
.setRefreshPolicy(RefreshPolicy.WAIT_UNTIL)
.request();
lifecycleService.prepareIndexIfNeededThenExecute(listener::onFailure, () ->
lifecycleService.securityIndex().prepareIndexIfNeededThenExecute(listener::onFailure, () ->
executeAsyncWithOrigin(client, SECURITY_ORIGIN, IndexAction.INSTANCE, request,
ActionListener.wrap(indexResponse -> listener.onResponse(new Tuple<>(userToken, refreshToken)),
listener::onFailure))
@ -354,7 +354,7 @@ public final class TokenService extends AbstractComponent {
if (version.onOrAfter(Version.V_6_2_0)) {
// we only have the id and need to get the token from the doc!
decryptTokenId(in, cipher, version, ActionListener.wrap(tokenId ->
lifecycleService.prepareIndexIfNeededThenExecute(listener::onFailure, () -> {
lifecycleService.securityIndex().prepareIndexIfNeededThenExecute(listener::onFailure, () -> {
final GetRequest getRequest =
client.prepareGet(SecurityLifecycleService.SECURITY_INDEX_NAME, TYPE,
getTokenDocumentId(tokenId)).request();
@ -524,7 +524,7 @@ public final class TokenService extends AbstractComponent {
.request();
final String tokenDocId = getTokenDocumentId(userToken);
final Version version = userToken.getVersion();
lifecycleService.prepareIndexIfNeededThenExecute(listener::onFailure, () ->
lifecycleService.securityIndex().prepareIndexIfNeededThenExecute(listener::onFailure, () ->
executeAsyncWithOrigin(client.threadPool().getThreadContext(), SECURITY_ORIGIN, indexRequest,
ActionListener.<IndexResponse>wrap(indexResponse -> {
ActionListener<Boolean> wrappedListener =
@ -566,7 +566,7 @@ public final class TokenService extends AbstractComponent {
.setVersion(documentVersion)
.setRefreshPolicy(RefreshPolicy.WAIT_UNTIL)
.request();
lifecycleService.prepareIndexIfNeededThenExecute(listener::onFailure, () ->
lifecycleService.securityIndex().prepareIndexIfNeededThenExecute(listener::onFailure, () ->
executeAsyncWithOrigin(client.threadPool().getThreadContext(), SECURITY_ORIGIN, request,
ActionListener.<UpdateResponse>wrap(updateResponse -> {
if (updateResponse.getGetResult() != null
@ -665,7 +665,7 @@ public final class TokenService extends AbstractComponent {
.setVersion(true)
.request();
lifecycleService.prepareIndexIfNeededThenExecute(listener::onFailure, () ->
lifecycleService.securityIndex().prepareIndexIfNeededThenExecute(listener::onFailure, () ->
executeAsyncWithOrigin(client.threadPool().getThreadContext(), SECURITY_ORIGIN, request,
ActionListener.<SearchResponse>wrap(searchResponse -> {
if (searchResponse.isTimedOut()) {
@ -847,7 +847,7 @@ public final class TokenService extends AbstractComponent {
.request();
final Supplier<ThreadContext.StoredContext> supplier = client.threadPool().getThreadContext().newRestorableContext(false);
lifecycleService.prepareIndexIfNeededThenExecute(listener::onFailure, () ->
lifecycleService.securityIndex().prepareIndexIfNeededThenExecute(listener::onFailure, () ->
ScrollHelper.fetchAllByEntity(client, request, new ContextPreservingActionListener<>(supplier, listener), this::parseHit));
}
@ -914,11 +914,11 @@ public final class TokenService extends AbstractComponent {
* have been explicitly cleared.
*/
private void checkIfTokenIsRevoked(UserToken userToken, ActionListener<UserToken> listener) {
if (lifecycleService.isSecurityIndexExisting() == false) {
if (lifecycleService.securityIndex().indexExists() == false) {
// index doesn't exist so the token is considered valid.
listener.onResponse(userToken);
} else {
lifecycleService.prepareIndexIfNeededThenExecute(listener::onFailure, () -> {
lifecycleService.securityIndex().prepareIndexIfNeededThenExecute(listener::onFailure, () -> {
MultiGetRequest mGetRequest = client.prepareMultiGet()
.add(SecurityLifecycleService.SECURITY_INDEX_NAME, TYPE, getInvalidatedTokenDocumentId(userToken))
.add(SecurityLifecycleService.SECURITY_INDEX_NAME, TYPE, getTokenDocumentId(userToken))
@ -989,7 +989,7 @@ public final class TokenService extends AbstractComponent {
}
private void maybeStartTokenRemover() {
if (lifecycleService.isSecurityIndexAvailable()) {
if (lifecycleService.securityIndex().isAvailable()) {
if (client.threadPool().relativeTimeInMillis() - lastExpirationRunMs > deleteInterval.getMillis()) {
expiredTokenRemover.submit(client.threadPool());
lastExpirationRunMs = client.threadPool().relativeTimeInMillis();

View File

@ -114,7 +114,7 @@ public class NativeUsersStore extends AbstractComponent {
}
};
if (securityLifecycleService.isSecurityIndexExisting() == false) {
if (securityLifecycleService.securityIndex().indexExists() == false) {
// TODO remove this short circuiting and fix tests that fail without this!
listener.onResponse(Collections.emptyList());
} else if (userNames.length == 1) { // optimization for single user lookup
@ -123,7 +123,7 @@ public class NativeUsersStore extends AbstractComponent {
(uap) -> listener.onResponse(uap == null ? Collections.emptyList() : Collections.singletonList(uap.user())),
handleException));
} else {
securityLifecycleService.prepareIndexIfNeededThenExecute(listener::onFailure, () -> {
securityLifecycleService.securityIndex().prepareIndexIfNeededThenExecute(listener::onFailure, () -> {
final QueryBuilder query;
if (userNames == null || userNames.length == 0) {
query = QueryBuilders.termQuery(Fields.TYPE.getPreferredName(), USER_DOC_TYPE);
@ -154,11 +154,11 @@ public class NativeUsersStore extends AbstractComponent {
* Async method to retrieve a user and their password
*/
private void getUserAndPassword(final String user, final ActionListener<UserAndPassword> listener) {
if (securityLifecycleService.isSecurityIndexExisting() == false) {
if (securityLifecycleService.securityIndex().indexExists() == false) {
// TODO remove this short circuiting and fix tests that fail without this!
listener.onResponse(null);
} else {
securityLifecycleService.prepareIndexIfNeededThenExecute(listener::onFailure, () ->
securityLifecycleService.securityIndex().prepareIndexIfNeededThenExecute(listener::onFailure, () ->
executeAsyncWithOrigin(client.threadPool().getThreadContext(), SECURITY_ORIGIN,
client.prepareGet(SECURITY_INDEX_NAME,
INDEX_TYPE, getIdForUser(USER_DOC_TYPE, user)).request(),
@ -199,7 +199,7 @@ public class NativeUsersStore extends AbstractComponent {
docType = USER_DOC_TYPE;
}
securityLifecycleService.prepareIndexIfNeededThenExecute(listener::onFailure, () -> {
securityLifecycleService.securityIndex().prepareIndexIfNeededThenExecute(listener::onFailure, () -> {
executeAsyncWithOrigin(client.threadPool().getThreadContext(), SECURITY_ORIGIN,
client.prepareUpdate(SECURITY_INDEX_NAME, INDEX_TYPE, getIdForUser(docType, username))
.setDoc(Requests.INDEX_CONTENT_TYPE, Fields.PASSWORD.getPreferredName(),
@ -237,7 +237,7 @@ public class NativeUsersStore extends AbstractComponent {
* has been indexed
*/
private void createReservedUser(String username, char[] passwordHash, RefreshPolicy refresh, ActionListener<Void> listener) {
securityLifecycleService.prepareIndexIfNeededThenExecute(listener::onFailure, () -> {
securityLifecycleService.securityIndex().prepareIndexIfNeededThenExecute(listener::onFailure, () -> {
executeAsyncWithOrigin(client.threadPool().getThreadContext(), SECURITY_ORIGIN,
client.prepareIndex(SECURITY_INDEX_NAME, INDEX_TYPE,
getIdForUser(RESERVED_USER_TYPE, username))
@ -279,7 +279,7 @@ public class NativeUsersStore extends AbstractComponent {
private void updateUserWithoutPassword(final PutUserRequest putUserRequest, final ActionListener<Boolean> listener) {
assert putUserRequest.passwordHash() == null;
// We must have an existing document
securityLifecycleService.prepareIndexIfNeededThenExecute(listener::onFailure, () -> {
securityLifecycleService.securityIndex().prepareIndexIfNeededThenExecute(listener::onFailure, () -> {
executeAsyncWithOrigin(client.threadPool().getThreadContext(), SECURITY_ORIGIN,
client.prepareUpdate(SECURITY_INDEX_NAME, INDEX_TYPE,
getIdForUser(USER_DOC_TYPE, putUserRequest.username()))
@ -322,7 +322,7 @@ public class NativeUsersStore extends AbstractComponent {
private void indexUser(final PutUserRequest putUserRequest, final ActionListener<Boolean> listener) {
assert putUserRequest.passwordHash() != null;
securityLifecycleService.prepareIndexIfNeededThenExecute(listener::onFailure, () -> {
securityLifecycleService.securityIndex().prepareIndexIfNeededThenExecute(listener::onFailure, () -> {
executeAsyncWithOrigin(client.threadPool().getThreadContext(), SECURITY_ORIGIN,
client.prepareIndex(SECURITY_INDEX_NAME, INDEX_TYPE,
getIdForUser(USER_DOC_TYPE, putUserRequest.username()))
@ -366,7 +366,7 @@ public class NativeUsersStore extends AbstractComponent {
private void setRegularUserEnabled(final String username, final boolean enabled, final RefreshPolicy refreshPolicy,
final ActionListener<Void> listener) {
securityLifecycleService.prepareIndexIfNeededThenExecute(listener::onFailure, () -> {
securityLifecycleService.securityIndex().prepareIndexIfNeededThenExecute(listener::onFailure, () -> {
executeAsyncWithOrigin(client.threadPool().getThreadContext(), SECURITY_ORIGIN,
client.prepareUpdate(SECURITY_INDEX_NAME, INDEX_TYPE,
getIdForUser(USER_DOC_TYPE, username))
@ -401,7 +401,7 @@ public class NativeUsersStore extends AbstractComponent {
private void setReservedUserEnabled(final String username, final boolean enabled, final RefreshPolicy refreshPolicy,
boolean clearCache, final ActionListener<Void> listener) {
securityLifecycleService.prepareIndexIfNeededThenExecute(listener::onFailure, () -> {
securityLifecycleService.securityIndex().prepareIndexIfNeededThenExecute(listener::onFailure, () -> {
executeAsyncWithOrigin(client.threadPool().getThreadContext(), SECURITY_ORIGIN,
client.prepareUpdate(SECURITY_INDEX_NAME, INDEX_TYPE,
getIdForUser(RESERVED_USER_TYPE, username))
@ -431,7 +431,7 @@ public class NativeUsersStore extends AbstractComponent {
}
public void deleteUser(final DeleteUserRequest deleteUserRequest, final ActionListener<Boolean> listener) {
securityLifecycleService.prepareIndexIfNeededThenExecute(listener::onFailure, () -> {
securityLifecycleService.securityIndex().prepareIndexIfNeededThenExecute(listener::onFailure, () -> {
DeleteRequest request = client.prepareDelete(SECURITY_INDEX_NAME,
INDEX_TYPE, getIdForUser(USER_DOC_TYPE, deleteUserRequest.username())).request();
request.setRefreshPolicy(deleteUserRequest.getRefreshPolicy());
@ -470,11 +470,11 @@ public class NativeUsersStore extends AbstractComponent {
}
void getReservedUserInfo(String username, ActionListener<ReservedUserInfo> listener) {
if (securityLifecycleService.isSecurityIndexExisting() == false) {
if (securityLifecycleService.securityIndex().indexExists() == false) {
// TODO remove this short circuiting and fix tests that fail without this!
listener.onResponse(null);
} else {
securityLifecycleService.prepareIndexIfNeededThenExecute(listener::onFailure, () ->
securityLifecycleService.securityIndex().prepareIndexIfNeededThenExecute(listener::onFailure, () ->
executeAsyncWithOrigin(client.threadPool().getThreadContext(), SECURITY_ORIGIN,
client.prepareGet(SECURITY_INDEX_NAME, INDEX_TYPE,
getIdForUser(RESERVED_USER_TYPE, username)).request(),
@ -514,7 +514,7 @@ public class NativeUsersStore extends AbstractComponent {
}
void getAllReservedUserInfo(ActionListener<Map<String, ReservedUserInfo>> listener) {
securityLifecycleService.prepareIndexIfNeededThenExecute(listener::onFailure, () ->
securityLifecycleService.securityIndex().prepareIndexIfNeededThenExecute(listener::onFailure, () ->
executeAsyncWithOrigin(client.threadPool().getThreadContext(), SECURITY_ORIGIN,
client.prepareSearch(SECURITY_INDEX_NAME)
.setQuery(QueryBuilders.termQuery(Fields.TYPE.getPreferredName(), RESERVED_USER_TYPE))

View File

@ -191,7 +191,7 @@ public class ReservedRealm extends CachingUsernamePasswordRealm {
if (userIsDefinedForCurrentSecurityMapping(username) == false) {
logger.debug("Marking user [{}] as disabled because the security mapping is not at the required version", username);
listener.onResponse(DISABLED_DEFAULT_USER_INFO.deepClone());
} else if (securityLifecycleService.isSecurityIndexExisting() == false) {
} else if (securityLifecycleService.securityIndex().indexExists() == false) {
listener.onResponse(getDefaultUserInfo(username));
} else {
nativeUsersStore.getReservedUserInfo(username, ActionListener.wrap((userInfo) -> {
@ -218,7 +218,7 @@ public class ReservedRealm extends CachingUsernamePasswordRealm {
private boolean userIsDefinedForCurrentSecurityMapping(String username) {
final Version requiredVersion = getDefinedVersion(username);
return securityLifecycleService.checkSecurityMappingVersion(requiredVersion::onOrBefore);
return securityLifecycleService.securityIndex().checkMappingVersion(requiredVersion::onOrBefore);
}
private Version getDefinedVersion(String username) {

View File

@ -120,7 +120,7 @@ public class NativeRoleMappingStore extends AbstractComponent implements UserRol
* <em>package private</em> for unit testing
*/
void loadMappings(ActionListener<List<ExpressionRoleMapping>> listener) {
if (securityLifecycleService.isSecurityIndexOutOfDate()) {
if (securityLifecycleService.securityIndex().isIndexUpToDate() == false) {
listener.onFailure(new IllegalStateException(
"Security index is not on the current version - the native realm will not be operational until " +
"the upgrade API is run on the security index"));
@ -176,7 +176,7 @@ public class NativeRoleMappingStore extends AbstractComponent implements UserRol
private <Request, Result> void modifyMapping(String name, CheckedBiConsumer<Request, ActionListener<Result>, Exception> inner,
Request request, ActionListener<Result> listener) {
if (securityLifecycleService.isSecurityIndexOutOfDate()) {
if (securityLifecycleService.securityIndex().isIndexUpToDate() == false) {
listener.onFailure(new IllegalStateException(
"Security index is not on the current version - the native realm will not be operational until " +
"the upgrade API is run on the security index"));
@ -192,7 +192,7 @@ public class NativeRoleMappingStore extends AbstractComponent implements UserRol
private void innerPutMapping(PutRoleMappingRequest request, ActionListener<Boolean> listener) {
final ExpressionRoleMapping mapping = request.getMapping();
securityLifecycleService.prepareIndexIfNeededThenExecute(listener::onFailure, () -> {
securityLifecycleService.securityIndex().prepareIndexIfNeededThenExecute(listener::onFailure, () -> {
final XContentBuilder xContentBuilder;
try {
xContentBuilder = mapping.toXContent(jsonBuilder(), ToXContent.EMPTY_PARAMS, true);
@ -222,7 +222,7 @@ public class NativeRoleMappingStore extends AbstractComponent implements UserRol
}
private void innerDeleteMapping(DeleteRoleMappingRequest request, ActionListener<Boolean> listener) throws IOException {
if (securityLifecycleService.isSecurityIndexOutOfDate()) {
if (securityLifecycleService.securityIndex().isIndexUpToDate() == false) {
listener.onFailure(new IllegalStateException(
"Security index is not on the current version - the native realm will not be operational until " +
"the upgrade API is run on the security index"));
@ -276,16 +276,16 @@ public class NativeRoleMappingStore extends AbstractComponent implements UserRol
}
private void getMappings(ActionListener<List<ExpressionRoleMapping>> listener) {
if (securityLifecycleService.isSecurityIndexAvailable()) {
if (securityLifecycleService.securityIndex().isAvailable()) {
loadMappings(listener);
} else {
logger.info("The security index is not yet available - no role mappings can be loaded");
if (logger.isDebugEnabled()) {
logger.debug("Security Index [{}] [exists: {}] [available: {}] [mapping up to date: {}]",
SECURITY_INDEX_NAME,
securityLifecycleService.isSecurityIndexExisting(),
securityLifecycleService.isSecurityIndexAvailable(),
securityLifecycleService.isSecurityIndexMappingUpToDate()
securityLifecycleService.securityIndex().indexExists(),
securityLifecycleService.securityIndex().isAvailable(),
securityLifecycleService.securityIndex().isMappingUpToDate()
);
}
listener.onResponse(Collections.emptyList());
@ -302,7 +302,7 @@ public class NativeRoleMappingStore extends AbstractComponent implements UserRol
* </ul>
*/
public void usageStats(ActionListener<Map<String, Object>> listener) {
if (securityLifecycleService.isSecurityIndexExisting() == false) {
if (securityLifecycleService.securityIndex().indexExists() == false) {
reportStats(listener, Collections.emptyList());
} else {
getMappings(ActionListener.wrap(mappings -> reportStats(listener, mappings), listener::onFailure));

View File

@ -100,7 +100,7 @@ public class NativeRolesStore extends AbstractComponent {
* Retrieve a list of roles, if rolesToGet is null or empty, fetch all roles
*/
public void getRoleDescriptors(String[] names, final ActionListener<Collection<RoleDescriptor>> listener) {
if (securityLifecycleService.isSecurityIndexExisting() == false) {
if (securityLifecycleService.securityIndex().indexExists() == false) {
// TODO remove this short circuiting and fix tests that fail without this!
listener.onResponse(Collections.emptyList());
} else if (names != null && names.length == 1) {
@ -108,7 +108,7 @@ public class NativeRolesStore extends AbstractComponent {
listener.onResponse(roleDescriptor == null ? Collections.emptyList() : Collections.singletonList(roleDescriptor)),
listener::onFailure));
} else {
securityLifecycleService.prepareIndexIfNeededThenExecute(listener::onFailure, () -> {
securityLifecycleService.securityIndex().prepareIndexIfNeededThenExecute(listener::onFailure, () -> {
QueryBuilder query;
if (names == null || names.length == 0) {
query = QueryBuilders.termQuery(RoleDescriptor.Fields.TYPE.getPreferredName(), ROLE_TYPE);
@ -133,7 +133,7 @@ public class NativeRolesStore extends AbstractComponent {
}
public void deleteRole(final DeleteRoleRequest deleteRoleRequest, final ActionListener<Boolean> listener) {
securityLifecycleService.prepareIndexIfNeededThenExecute(listener::onFailure, () -> {
securityLifecycleService.securityIndex().prepareIndexIfNeededThenExecute(listener::onFailure, () -> {
DeleteRequest request = client.prepareDelete(SecurityLifecycleService.SECURITY_INDEX_NAME,
ROLE_DOC_TYPE, getIdForUser(deleteRoleRequest.name())).request();
request.setRefreshPolicy(deleteRoleRequest.getRefreshPolicy());
@ -166,7 +166,7 @@ public class NativeRolesStore extends AbstractComponent {
// pkg-private for testing
void innerPutRole(final PutRoleRequest request, final RoleDescriptor role, final ActionListener<Boolean> listener) {
securityLifecycleService.prepareIndexIfNeededThenExecute(listener::onFailure, () -> {
securityLifecycleService.securityIndex().prepareIndexIfNeededThenExecute(listener::onFailure, () -> {
final XContentBuilder xContentBuilder;
try {
xContentBuilder = role.toXContent(jsonBuilder(), ToXContent.EMPTY_PARAMS, true);
@ -197,13 +197,13 @@ public class NativeRolesStore extends AbstractComponent {
public void usageStats(ActionListener<Map<String, Object>> listener) {
Map<String, Object> usageStats = new HashMap<>();
if (securityLifecycleService.isSecurityIndexExisting() == false) {
if (securityLifecycleService.securityIndex().indexExists() == false) {
usageStats.put("size", 0L);
usageStats.put("fls", false);
usageStats.put("dls", false);
listener.onResponse(usageStats);
} else {
securityLifecycleService.prepareIndexIfNeededThenExecute(listener::onFailure, () ->
securityLifecycleService.securityIndex().prepareIndexIfNeededThenExecute(listener::onFailure, () ->
executeAsyncWithOrigin(client.threadPool().getThreadContext(), SECURITY_ORIGIN,
client.prepareMultiSearch()
.add(client.prepareSearch(SecurityLifecycleService.SECURITY_INDEX_NAME)
@ -259,11 +259,11 @@ public class NativeRolesStore extends AbstractComponent {
}
private void getRoleDescriptor(final String roleId, ActionListener<RoleDescriptor> roleActionListener) {
if (securityLifecycleService.isSecurityIndexExisting() == false) {
if (securityLifecycleService.securityIndex().indexExists() == false) {
// TODO remove this short circuiting and fix tests that fail without this!
roleActionListener.onResponse(null);
} else {
securityLifecycleService.prepareIndexIfNeededThenExecute(roleActionListener::onFailure, () ->
securityLifecycleService.securityIndex().prepareIndexIfNeededThenExecute(roleActionListener::onFailure, () ->
executeGetRoleRequest(roleId, new ActionListener<GetResponse>() {
@Override
public void onResponse(GetResponse response) {
@ -288,7 +288,7 @@ public class NativeRolesStore extends AbstractComponent {
}
private void executeGetRoleRequest(String role, ActionListener<GetResponse> listener) {
securityLifecycleService.prepareIndexIfNeededThenExecute(listener::onFailure, () ->
securityLifecycleService.securityIndex().prepareIndexIfNeededThenExecute(listener::onFailure, () ->
executeAsyncWithOrigin(client.threadPool().getThreadContext(), SECURITY_ORIGIN,
client.prepareGet(SecurityLifecycleService.SECURITY_INDEX_NAME,
ROLE_DOC_TYPE, getIdForUser(role)).request(),

View File

@ -58,7 +58,7 @@ import static org.elasticsearch.xpack.core.security.SecurityLifecycleServiceFiel
/**
* Manages the lifecycle of a single index, its template, mapping and and data upgrades/migrations.
*/
public class IndexLifecycleManager extends AbstractComponent {
public class SecurityIndexManager extends AbstractComponent {
public static final String INTERNAL_SECURITY_INDEX = ".security-" + IndexUpgradeCheckVersion.UPRADE_VERSION;
public static final int INTERNAL_INDEX_FORMAT = 6;
@ -74,7 +74,7 @@ public class IndexLifecycleManager extends AbstractComponent {
private volatile State indexState = new State(false, false, false, false, null);
public IndexLifecycleManager(Settings settings, Client client, String indexName) {
public SecurityIndexManager(Settings settings, Client client, String indexName) {
super(settings);
this.client = client;
this.indexName = indexName;
@ -347,7 +347,7 @@ public class IndexLifecycleManager extends AbstractComponent {
private Tuple<String, Settings> loadMappingAndSettingsSourceFromTemplate() {
final byte[] template = TemplateUtils.loadTemplate("/" + SECURITY_TEMPLATE_NAME + ".json",
Version.CURRENT.toString(), IndexLifecycleManager.TEMPLATE_VERSION_PATTERN).getBytes(StandardCharsets.UTF_8);
Version.CURRENT.toString(), SecurityIndexManager.TEMPLATE_VERSION_PATTERN).getBytes(StandardCharsets.UTF_8);
PutIndexTemplateRequest request = new PutIndexTemplateRequest(SECURITY_TEMPLATE_NAME).source(template, XContentType.JSON);
return new Tuple<>(request.mappings().get("doc"), request.settings());
}

View File

@ -37,7 +37,7 @@ import org.elasticsearch.threadpool.ThreadPool;
import org.elasticsearch.transport.MockTransportClient;
import org.elasticsearch.xpack.core.security.SecurityLifecycleServiceField;
import org.elasticsearch.xpack.security.audit.index.IndexAuditTrail;
import org.elasticsearch.xpack.security.support.IndexLifecycleManager;
import org.elasticsearch.xpack.security.support.SecurityIndexManager;
import org.elasticsearch.xpack.security.test.SecurityTestUtils;
import org.elasticsearch.xpack.core.template.TemplateUtils;
import org.junit.After;
@ -105,10 +105,10 @@ public class SecurityLifecycleServiceTests extends ESTestCase {
ClusterState.Builder clusterStateBuilder = createClusterStateWithTemplate(templateString);
final ClusterState clusterState = clusterStateBuilder.build();
assertTrue(IndexLifecycleManager.checkTemplateExistsAndVersionMatches(
assertTrue(SecurityIndexManager.checkTemplateExistsAndVersionMatches(
SecurityLifecycleServiceField.SECURITY_TEMPLATE_NAME, clusterState, logger,
Version.V_5_0_0::before));
assertFalse(IndexLifecycleManager.checkTemplateExistsAndVersionMatches(
assertFalse(SecurityIndexManager.checkTemplateExistsAndVersionMatches(
SecurityLifecycleServiceField.SECURITY_TEMPLATE_NAME, clusterState, logger,
Version.V_5_0_0::after));
}
@ -126,7 +126,7 @@ public class SecurityLifecycleServiceTests extends ESTestCase {
ClusterState.Builder clusterStateBuilder = createClusterStateWithMappingAndTemplate(templateString);
securityLifecycleService.clusterChanged(new ClusterChangedEvent("test-event",
clusterStateBuilder.build(), EMPTY_CLUSTER_STATE));
final IndexLifecycleManager securityIndex = securityLifecycleService.securityIndex();
final SecurityIndexManager securityIndex = securityLifecycleService.securityIndex();
assertTrue(securityIndex.checkMappingVersion(Version.V_5_0_0::before));
assertFalse(securityIndex.checkMappingVersion(Version.V_5_0_0::after));
}
@ -172,7 +172,7 @@ public class SecurityLifecycleServiceTests extends ESTestCase {
private static IndexMetaData.Builder createIndexMetadata(String indexName, String templateString) throws IOException {
String template = TemplateUtils.loadTemplate(templateString, Version.CURRENT.toString(),
IndexLifecycleManager.TEMPLATE_VERSION_PATTERN);
SecurityIndexManager.TEMPLATE_VERSION_PATTERN);
PutIndexTemplateRequest request = new PutIndexTemplateRequest();
request.source(template, XContentType.JSON);
IndexMetaData.Builder indexMetaData = IndexMetaData.builder(indexName);
@ -219,7 +219,7 @@ public class SecurityLifecycleServiceTests extends ESTestCase {
String templateName, String templateString) throws IOException {
String template = TemplateUtils.loadTemplate(templateString, Version.CURRENT.toString(),
IndexLifecycleManager.TEMPLATE_VERSION_PATTERN);
SecurityIndexManager.TEMPLATE_VERSION_PATTERN);
PutIndexTemplateRequest request = new PutIndexTemplateRequest();
request.source(template, XContentType.JSON);
IndexTemplateMetaData.Builder templateBuilder = IndexTemplateMetaData.builder(templateName)

View File

@ -63,7 +63,7 @@ import java.util.function.Predicate;
import static org.elasticsearch.cluster.metadata.IndexMetaData.INDEX_FORMAT_SETTING;
import static org.elasticsearch.xpack.security.SecurityLifecycleService.SECURITY_INDEX_NAME;
import static org.elasticsearch.xpack.security.support.IndexLifecycleManager.INTERNAL_INDEX_FORMAT;
import static org.elasticsearch.xpack.security.support.SecurityIndexManager.INTERNAL_INDEX_FORMAT;
import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.hasItem;

View File

@ -67,6 +67,7 @@ import org.elasticsearch.xpack.security.authc.saml.SamlRealm;
import org.elasticsearch.xpack.security.authc.saml.SamlRealmTestHelper;
import org.elasticsearch.xpack.security.authc.saml.SamlRealmTests;
import org.elasticsearch.xpack.security.authc.saml.SamlTestCase;
import org.elasticsearch.xpack.security.support.SecurityIndexManager;
import org.junit.After;
import org.junit.Before;
import org.opensaml.saml.saml2.core.NameID;
@ -161,10 +162,12 @@ public class TransportSamlInvalidateSessionActionTests extends SamlTestCase {
};
final SecurityLifecycleService lifecycleService = mock(SecurityLifecycleService.class);
final SecurityIndexManager securityIndex = mock(SecurityIndexManager.class);
when(lifecycleService.securityIndex()).thenReturn(securityIndex);
doAnswer(inv -> {
((Runnable) inv.getArguments()[1]).run();
return null;
}).when(lifecycleService).prepareIndexIfNeededThenExecute(any(Consumer.class), any(Runnable.class));
}).when(securityIndex).prepareIndexIfNeededThenExecute(any(Consumer.class), any(Runnable.class));
final ClusterService clusterService = ClusterServiceUtils.createClusterService(threadPool);
tokenService = new TokenService(settings, Clock.systemUTC(), client, lifecycleService, clusterService);

View File

@ -56,6 +56,7 @@ import org.elasticsearch.xpack.security.authc.saml.SamlRealm;
import org.elasticsearch.xpack.security.authc.saml.SamlRealmTests;
import org.elasticsearch.xpack.security.authc.saml.SamlTestCase;
import org.elasticsearch.xpack.security.authc.support.UserRoleMapper;
import org.elasticsearch.xpack.security.support.SecurityIndexManager;
import org.junit.After;
import org.junit.Before;
import org.opensaml.saml.saml2.core.NameID;
@ -173,10 +174,12 @@ public class TransportSamlLogoutActionTests extends SamlTestCase {
}).when(client).execute(eq(IndexAction.INSTANCE), any(IndexRequest.class), any(ActionListener.class));
final SecurityLifecycleService lifecycleService = mock(SecurityLifecycleService.class);
final SecurityIndexManager securityIndex = mock(SecurityIndexManager.class);
when(lifecycleService.securityIndex()).thenReturn(securityIndex);
doAnswer(inv -> {
((Runnable) inv.getArguments()[1]).run();
return null;
}).when(lifecycleService).prepareIndexIfNeededThenExecute(any(Consumer.class), any(Runnable.class));
}).when(securityIndex).prepareIndexIfNeededThenExecute(any(Consumer.class), any(Runnable.class));
final ClusterService clusterService = ClusterServiceUtils.createClusterService(threadPool);
tokenService = new TokenService(settings, Clock.systemUTC(), client, lifecycleService, clusterService);

View File

@ -28,6 +28,7 @@ import org.elasticsearch.xpack.security.SecurityLifecycleService;
import org.elasticsearch.xpack.security.authc.esnative.NativeUsersStore;
import org.elasticsearch.xpack.security.authc.esnative.ReservedRealm;
import org.elasticsearch.xpack.security.authc.esnative.ReservedRealmTests;
import org.elasticsearch.xpack.security.support.SecurityIndexManager;
import org.junit.Before;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;
@ -76,7 +77,9 @@ public class TransportGetUsersActionTests extends ESTestCase {
public void testAnonymousUser() {
NativeUsersStore usersStore = mock(NativeUsersStore.class);
SecurityLifecycleService securityLifecycleService = mock(SecurityLifecycleService.class);
when(securityLifecycleService.isSecurityIndexAvailable()).thenReturn(true);
SecurityIndexManager securityIndex = mock(SecurityIndexManager.class);
when(securityLifecycleService.securityIndex()).thenReturn(securityIndex);
when(securityIndex.isAvailable()).thenReturn(true);
AnonymousUser anonymousUser = new AnonymousUser(settings);
ReservedRealm reservedRealm =
new ReservedRealm(mock(Environment.class), settings, usersStore, anonymousUser, securityLifecycleService, new ThreadContext(Settings.EMPTY));
@ -146,8 +149,10 @@ public class TransportGetUsersActionTests extends ESTestCase {
public void testReservedUsersOnly() {
NativeUsersStore usersStore = mock(NativeUsersStore.class);
SecurityLifecycleService securityLifecycleService = mock(SecurityLifecycleService.class);
when(securityLifecycleService.isSecurityIndexAvailable()).thenReturn(true);
when(securityLifecycleService.checkSecurityMappingVersion(any())).thenReturn(true);
SecurityIndexManager securityIndex = mock(SecurityIndexManager.class);
when(securityLifecycleService.securityIndex()).thenReturn(securityIndex);
when(securityIndex.isAvailable()).thenReturn(true);
when(securityIndex.checkMappingVersion(any())).thenReturn(true);
ReservedRealmTests.mockGetAllReservedUserInfo(usersStore, Collections.emptyMap());
ReservedRealm reservedRealm =
@ -194,7 +199,9 @@ public class TransportGetUsersActionTests extends ESTestCase {
Arrays.asList(new User("jane"), new User("fred")), randomUsers());
NativeUsersStore usersStore = mock(NativeUsersStore.class);
SecurityLifecycleService securityLifecycleService = mock(SecurityLifecycleService.class);
when(securityLifecycleService.isSecurityIndexAvailable()).thenReturn(true);
SecurityIndexManager securityIndex = mock(SecurityIndexManager.class);
when(securityLifecycleService.securityIndex()).thenReturn(securityIndex);
when(securityIndex.isAvailable()).thenReturn(true);
ReservedRealmTests.mockGetAllReservedUserInfo(usersStore, Collections.emptyMap());
ReservedRealm reservedRealm = new ReservedRealm(mock(Environment.class), settings, usersStore, new AnonymousUser(settings),
securityLifecycleService, new ThreadContext(Settings.EMPTY));

View File

@ -29,6 +29,7 @@ import org.elasticsearch.xpack.security.SecurityLifecycleService;
import org.elasticsearch.xpack.security.authc.esnative.NativeUsersStore;
import org.elasticsearch.xpack.security.authc.esnative.ReservedRealm;
import org.elasticsearch.xpack.security.authc.esnative.ReservedRealmTests;
import org.elasticsearch.xpack.security.support.SecurityIndexManager;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;
@ -118,7 +119,9 @@ public class TransportPutUserActionTests extends ESTestCase {
public void testReservedUser() {
NativeUsersStore usersStore = mock(NativeUsersStore.class);
SecurityLifecycleService securityLifecycleService = mock(SecurityLifecycleService.class);
when(securityLifecycleService.isSecurityIndexAvailable()).thenReturn(true);
SecurityIndexManager securityIndex = mock(SecurityIndexManager.class);
when(securityLifecycleService.securityIndex()).thenReturn(securityIndex);
when(securityIndex.isAvailable()).thenReturn(true);
ReservedRealmTests.mockGetAllReservedUserInfo(usersStore, Collections.emptyMap());
Settings settings = Settings.builder().put("path.home", createTempDir()).build();
ReservedRealm reservedRealm = new ReservedRealm(TestEnvironment.newEnvironment(settings), settings, usersStore,

View File

@ -68,6 +68,7 @@ import org.elasticsearch.xpack.security.SecurityLifecycleService;
import org.elasticsearch.xpack.security.audit.AuditTrailService;
import org.elasticsearch.xpack.security.authc.AuthenticationService.Authenticator;
import org.elasticsearch.xpack.security.authc.esnative.ReservedRealm;
import org.elasticsearch.xpack.security.support.SecurityIndexManager;
import org.junit.After;
import org.junit.Before;
@ -125,6 +126,7 @@ public class AuthenticationServiceTests extends ESTestCase {
private ThreadContext threadContext;
private TokenService tokenService;
private SecurityLifecycleService lifecycleService;
private SecurityIndexManager securityIndex;
private Client client;
private InetSocketAddress remoteAddress;
@ -181,11 +183,13 @@ public class AuthenticationServiceTests extends ESTestCase {
return builder;
}).when(client).prepareGet(anyString(), anyString(), anyString());
lifecycleService = mock(SecurityLifecycleService.class);
securityIndex = mock(SecurityIndexManager.class);
when(lifecycleService.securityIndex()).thenReturn(securityIndex);
doAnswer(invocationOnMock -> {
Runnable runnable = (Runnable) invocationOnMock.getArguments()[1];
runnable.run();
return null;
}).when(lifecycleService).prepareIndexIfNeededThenExecute(any(Consumer.class), any(Runnable.class));
}).when(securityIndex).prepareIndexIfNeededThenExecute(any(Consumer.class), any(Runnable.class));
ClusterService clusterService = ClusterServiceUtils.createClusterService(threadPool);
tokenService = new TokenService(settings, Clock.systemUTC(), client, lifecycleService, clusterService);
service = new AuthenticationService(settings, realms, auditTrail,
@ -924,8 +928,8 @@ public class AuthenticationServiceTests extends ESTestCase {
}
public void testExpiredToken() throws Exception {
when(lifecycleService.isSecurityIndexAvailable()).thenReturn(true);
when(lifecycleService.isSecurityIndexExisting()).thenReturn(true);
when(securityIndex.isAvailable()).thenReturn(true);
when(lifecycleService.securityIndex().indexExists()).thenReturn(true);
User user = new User("_username", "r1");
final Authentication expected = new Authentication(user, new RealmRef("realm", "custom", "node"), null);
PlainActionFuture<Tuple<UserToken, String>> tokenFuture = new PlainActionFuture<>();
@ -963,7 +967,7 @@ public class AuthenticationServiceTests extends ESTestCase {
doAnswer(invocationOnMock -> {
((Runnable) invocationOnMock.getArguments()[1]).run();
return null;
}).when(lifecycleService).prepareIndexIfNeededThenExecute(any(Consumer.class), any(Runnable.class));
}).when(securityIndex).prepareIndexIfNeededThenExecute(any(Consumer.class), any(Runnable.class));
try (ThreadContext.StoredContext ignore = threadContext.stashContext()) {
threadContext.putHeader("Authorization", "Bearer " + token);

View File

@ -18,6 +18,7 @@ import org.elasticsearch.xpack.core.ssl.SSLService;
import org.elasticsearch.xpack.security.SecurityLifecycleService;
import org.elasticsearch.xpack.security.authc.esnative.NativeUsersStore;
import org.elasticsearch.xpack.security.authc.support.mapper.NativeRoleMappingStore;
import org.elasticsearch.xpack.security.support.SecurityIndexManager;
import java.util.Map;
import java.util.function.BiConsumer;
@ -30,11 +31,14 @@ import static org.mockito.Matchers.isA;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyZeroInteractions;
import static org.mockito.Mockito.when;
public class InternalRealmsTests extends ESTestCase {
public void testNativeRealmRegistersIndexHealthChangeListener() throws Exception {
SecurityLifecycleService lifecycleService = mock(SecurityLifecycleService.class);
SecurityIndexManager securityIndex = mock(SecurityIndexManager.class);
when(lifecycleService.securityIndex()).thenReturn(securityIndex);
Map<String, Realm.Factory> factories = InternalRealms.getFactories(mock(ThreadPool.class), mock(ResourceWatcherService.class),
mock(SSLService.class), mock(NativeUsersStore.class), mock(NativeRoleMappingStore.class), lifecycleService);
assertThat(factories, hasEntry(is(NativeRealmSettings.TYPE), any(Realm.Factory.class)));
@ -43,10 +47,10 @@ public class InternalRealmsTests extends ESTestCase {
Settings settings = Settings.builder().put("path.home", createTempDir()).build();
factories.get(NativeRealmSettings.TYPE).create(new RealmConfig("test", Settings.EMPTY, settings,
TestEnvironment.newEnvironment(settings), new ThreadContext(settings)));
verify(lifecycleService).addSecurityIndexHealthChangeListener(isA(BiConsumer.class));
verify(securityIndex).addIndexHealthChangeListener(isA(BiConsumer.class));
factories.get(NativeRealmSettings.TYPE).create(new RealmConfig("test", Settings.EMPTY, settings,
TestEnvironment.newEnvironment(settings), new ThreadContext(settings)));
verify(lifecycleService, times(2)).addSecurityIndexHealthChangeListener(isA(BiConsumer.class));
verify(securityIndex, times(2)).addIndexHealthChangeListener(isA(BiConsumer.class));
}
}

View File

@ -51,6 +51,7 @@ import org.elasticsearch.xpack.core.security.authc.TokenMetaData;
import org.elasticsearch.xpack.core.security.user.User;
import org.elasticsearch.xpack.core.watcher.watch.ClockMock;
import org.elasticsearch.xpack.security.SecurityLifecycleService;
import org.elasticsearch.xpack.security.support.SecurityIndexManager;
import org.junit.AfterClass;
import org.junit.Before;
import org.junit.BeforeClass;
@ -86,6 +87,7 @@ public class TokenServiceTests extends ESTestCase {
private Client client;
private SecurityLifecycleService lifecycleService;
private SecurityIndexManager securityIndex;
private ClusterService clusterService;
private Settings tokenServiceEnabledSettings = Settings.builder()
.put(XPackSettings.TOKEN_SERVICE_ENABLED_SETTING.getKey(), true).build();
@ -131,11 +133,13 @@ public class TokenServiceTests extends ESTestCase {
// setup lifecycle service
lifecycleService = mock(SecurityLifecycleService.class);
securityIndex = mock(SecurityIndexManager.class);
when(lifecycleService.securityIndex()).thenReturn(securityIndex);
doAnswer(invocationOnMock -> {
Runnable runnable = (Runnable) invocationOnMock.getArguments()[1];
runnable.run();
return null;
}).when(lifecycleService).prepareIndexIfNeededThenExecute(any(Consumer.class), any(Runnable.class));
}).when(securityIndex).prepareIndexIfNeededThenExecute(any(Consumer.class), any(Runnable.class));
this.clusterService = ClusterServiceUtils.createClusterService(threadPool);
}
@ -376,7 +380,7 @@ public class TokenServiceTests extends ESTestCase {
}
public void testInvalidatedToken() throws Exception {
when(lifecycleService.isSecurityIndexExisting()).thenReturn(true);
when(securityIndex.indexExists()).thenReturn(true);
TokenService tokenService =
new TokenService(tokenServiceEnabledSettings, systemUTC(), client, lifecycleService, clusterService);
Authentication authentication = new Authentication(new User("joe", "admin"), new RealmRef("native_realm", "native", "node1"), null);
@ -563,8 +567,8 @@ public class TokenServiceTests extends ESTestCase {
UserToken serialized = future.get();
assertEquals(authentication, serialized.getAuthentication());
when(lifecycleService.isSecurityIndexAvailable()).thenReturn(false);
when(lifecycleService.isSecurityIndexExisting()).thenReturn(true);
when(securityIndex.isAvailable()).thenReturn(false);
when(securityIndex.indexExists()).thenReturn(true);
future = new PlainActionFuture<>();
tokenService.getAndValidateToken(requestContext, future);
assertNull(future.get());

View File

@ -55,7 +55,7 @@ import static org.elasticsearch.action.support.WriteRequest.RefreshPolicy.IMMEDI
import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertNoTimeout;
import static org.elasticsearch.xpack.core.security.authc.support.UsernamePasswordToken.basicAuthHeaderValue;
import static org.elasticsearch.xpack.security.SecurityLifecycleService.SECURITY_INDEX_NAME;
import static org.elasticsearch.xpack.security.support.IndexLifecycleManager.INTERNAL_SECURITY_INDEX;
import static org.elasticsearch.xpack.security.support.SecurityIndexManager.INTERNAL_SECURITY_INDEX;
import static org.hamcrest.Matchers.arrayContaining;
import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.is;

View File

@ -33,6 +33,7 @@ import org.elasticsearch.xpack.core.security.user.KibanaUser;
import org.elasticsearch.xpack.core.security.user.LogstashSystemUser;
import org.elasticsearch.xpack.core.security.user.User;
import org.elasticsearch.xpack.security.SecurityLifecycleService;
import org.elasticsearch.xpack.security.support.SecurityIndexManager;
import org.junit.Before;
import java.io.IOException;
@ -236,16 +237,17 @@ public class NativeUsersStoreTests extends ESTestCase {
private NativeUsersStore startNativeUsersStore() {
SecurityLifecycleService securityLifecycleService = mock(SecurityLifecycleService.class);
when(securityLifecycleService.isSecurityIndexAvailable()).thenReturn(true);
when(securityLifecycleService.isSecurityIndexExisting()).thenReturn(true);
when(securityLifecycleService.isSecurityIndexMappingUpToDate()).thenReturn(true);
when(securityLifecycleService.isSecurityIndexOutOfDate()).thenReturn(false);
when(securityLifecycleService.isSecurityIndexUpToDate()).thenReturn(true);
SecurityIndexManager securityIndex = mock(SecurityIndexManager.class);
when(securityLifecycleService.securityIndex()).thenReturn(securityIndex);
when(securityIndex.isAvailable()).thenReturn(true);
when(securityIndex.indexExists()).thenReturn(true);
when(securityIndex.isMappingUpToDate()).thenReturn(true);
when(securityIndex.isIndexUpToDate()).thenReturn(true);
doAnswer((i) -> {
Runnable action = (Runnable) i.getArguments()[1];
action.run();
return null;
}).when(securityLifecycleService).prepareIndexIfNeededThenExecute(any(Consumer.class), any(Runnable.class));
}).when(securityIndex).prepareIndexIfNeededThenExecute(any(Consumer.class), any(Runnable.class));
return new NativeUsersStore(Settings.EMPTY, client, securityLifecycleService);
}

View File

@ -29,6 +29,7 @@ import org.elasticsearch.xpack.core.security.user.User;
import org.elasticsearch.xpack.core.security.user.UsernamesField;
import org.elasticsearch.xpack.security.SecurityLifecycleService;
import org.elasticsearch.xpack.security.authc.esnative.NativeUsersStore.ReservedUserInfo;
import org.elasticsearch.xpack.security.support.SecurityIndexManager;
import org.junit.Before;
import org.mockito.ArgumentCaptor;
@ -63,13 +64,16 @@ public class ReservedRealmTests extends ESTestCase {
private static final SecureString EMPTY_PASSWORD = new SecureString("".toCharArray());
private NativeUsersStore usersStore;
private SecurityLifecycleService securityLifecycleService;
private SecurityIndexManager securityIndex;
@Before
public void setupMocks() throws Exception {
usersStore = mock(NativeUsersStore.class);
securityLifecycleService = mock(SecurityLifecycleService.class);
when(securityLifecycleService.isSecurityIndexAvailable()).thenReturn(true);
when(securityLifecycleService.checkSecurityMappingVersion(any())).thenReturn(true);
securityIndex = mock(SecurityIndexManager.class);
when(securityLifecycleService.securityIndex()).thenReturn(securityIndex);
when(securityIndex.isAvailable()).thenReturn(true);
when(securityIndex.checkMappingVersion(any())).thenReturn(true);
mockGetAllReservedUserInfo(usersStore, Collections.emptyMap());
}
@ -90,7 +94,7 @@ public class ReservedRealmTests extends ESTestCase {
Settings settings = Settings.builder().put(XPackSettings.RESERVED_REALM_ENABLED_SETTING.getKey(), false).build();
final boolean securityIndexExists = randomBoolean();
if (securityIndexExists) {
when(securityLifecycleService.isSecurityIndexExisting()).thenReturn(true);
when(securityIndex.indexExists()).thenReturn(true);
}
final ReservedRealm reservedRealm =
new ReservedRealm(mock(Environment.class), settings, usersStore,
@ -120,7 +124,7 @@ public class ReservedRealmTests extends ESTestCase {
final User expectedUser = randomReservedUser(enabled);
final String principal = expectedUser.principal();
final SecureString newPassword = new SecureString("foobar".toCharArray());
when(securityLifecycleService.isSecurityIndexExisting()).thenReturn(true);
when(securityIndex.indexExists()).thenReturn(true);
doAnswer((i) -> {
ActionListener callback = (ActionListener) i.getArguments()[1];
callback.onResponse(new ReservedUserInfo(Hasher.BCRYPT.hash(newPassword), enabled, false));
@ -146,10 +150,10 @@ public class ReservedRealmTests extends ESTestCase {
assertEquals(expectedUser, authenticated);
assertThat(expectedUser.enabled(), is(enabled));
verify(securityLifecycleService, times(2)).isSecurityIndexExisting();
verify(securityIndex, times(2)).indexExists();
verify(usersStore, times(2)).getReservedUserInfo(eq(principal), any(ActionListener.class));
final ArgumentCaptor<Predicate> predicateCaptor = ArgumentCaptor.forClass(Predicate.class);
verify(securityLifecycleService, times(2)).checkSecurityMappingVersion(predicateCaptor.capture());
verify(securityIndex, times(2)).checkMappingVersion(predicateCaptor.capture());
verifyVersionPredicate(principal, predicateCaptor.getValue());
verifyNoMoreInteractions(usersStore);
}
@ -165,10 +169,10 @@ public class ReservedRealmTests extends ESTestCase {
reservedRealm.doLookupUser(principal, listener);
final User user = listener.actionGet();
assertEquals(expectedUser, user);
verify(securityLifecycleService).isSecurityIndexExisting();
verify(securityIndex).indexExists();
final ArgumentCaptor<Predicate> predicateCaptor = ArgumentCaptor.forClass(Predicate.class);
verify(securityLifecycleService).checkSecurityMappingVersion(predicateCaptor.capture());
verify(securityIndex).checkMappingVersion(predicateCaptor.capture());
verifyVersionPredicate(principal, predicateCaptor.getValue());
PlainActionFuture<User> future = new PlainActionFuture<>();
@ -199,7 +203,7 @@ public class ReservedRealmTests extends ESTestCase {
new AnonymousUser(Settings.EMPTY), securityLifecycleService, new ThreadContext(Settings.EMPTY));
final User expectedUser = randomReservedUser(true);
final String principal = expectedUser.principal();
when(securityLifecycleService.isSecurityIndexExisting()).thenReturn(true);
when(securityIndex.indexExists()).thenReturn(true);
final RuntimeException e = new RuntimeException("store threw");
doAnswer((i) -> {
ActionListener callback = (ActionListener) i.getArguments()[1];
@ -212,11 +216,11 @@ public class ReservedRealmTests extends ESTestCase {
ElasticsearchSecurityException securityException = expectThrows(ElasticsearchSecurityException.class, future::actionGet);
assertThat(securityException.getMessage(), containsString("failed to lookup"));
verify(securityLifecycleService).isSecurityIndexExisting();
verify(securityIndex).indexExists();
verify(usersStore).getReservedUserInfo(eq(principal), any(ActionListener.class));
final ArgumentCaptor<Predicate> predicateCaptor = ArgumentCaptor.forClass(Predicate.class);
verify(securityLifecycleService).checkSecurityMappingVersion(predicateCaptor.capture());
verify(securityIndex).checkMappingVersion(predicateCaptor.capture());
verifyVersionPredicate(principal, predicateCaptor.getValue());
verifyNoMoreInteractions(usersStore);
@ -269,7 +273,7 @@ public class ReservedRealmTests extends ESTestCase {
}
public void testFailedAuthentication() throws Exception {
when(securityLifecycleService.isSecurityIndexExisting()).thenReturn(true);
when(securityIndex.indexExists()).thenReturn(true);
SecureString password = new SecureString("password".toCharArray());
char[] hash = Hasher.BCRYPT.hash(password);
ReservedUserInfo userInfo = new ReservedUserInfo(hash, true, false);
@ -302,7 +306,7 @@ public class ReservedRealmTests extends ESTestCase {
MockSecureSettings mockSecureSettings = new MockSecureSettings();
mockSecureSettings.setString("bootstrap.password", "foobar");
Settings settings = Settings.builder().setSecureSettings(mockSecureSettings).build();
when(securityLifecycleService.isSecurityIndexExisting()).thenReturn(true);
when(securityIndex.indexExists()).thenReturn(true);
final ReservedRealm reservedRealm = new ReservedRealm(mock(Environment.class), settings, usersStore,
new AnonymousUser(Settings.EMPTY), securityLifecycleService, new ThreadContext(Settings.EMPTY));
@ -324,7 +328,7 @@ public class ReservedRealmTests extends ESTestCase {
MockSecureSettings mockSecureSettings = new MockSecureSettings();
mockSecureSettings.setString("bootstrap.password", "foobar");
Settings settings = Settings.builder().setSecureSettings(mockSecureSettings).build();
when(securityLifecycleService.isSecurityIndexExisting()).thenReturn(true);
when(securityIndex.indexExists()).thenReturn(true);
final ReservedRealm reservedRealm = new ReservedRealm(mock(Environment.class), settings, usersStore,
new AnonymousUser(Settings.EMPTY), securityLifecycleService, new ThreadContext(Settings.EMPTY));
@ -351,7 +355,7 @@ public class ReservedRealmTests extends ESTestCase {
MockSecureSettings mockSecureSettings = new MockSecureSettings();
mockSecureSettings.setString("bootstrap.password", "foobar");
Settings settings = Settings.builder().setSecureSettings(mockSecureSettings).build();
when(securityLifecycleService.isSecurityIndexExisting()).thenReturn(false);
when(securityIndex.indexExists()).thenReturn(false);
final ReservedRealm reservedRealm = new ReservedRealm(mock(Environment.class), settings, usersStore,
new AnonymousUser(Settings.EMPTY), securityLifecycleService, new ThreadContext(Settings.EMPTY));
@ -369,7 +373,7 @@ public class ReservedRealmTests extends ESTestCase {
final String password = randomAlphaOfLengthBetween(8, 24);
mockSecureSettings.setString("bootstrap.password", password);
Settings settings = Settings.builder().setSecureSettings(mockSecureSettings).build();
when(securityLifecycleService.isSecurityIndexExisting()).thenReturn(true);
when(securityIndex.indexExists()).thenReturn(true);
final ReservedRealm reservedRealm = new ReservedRealm(mock(Environment.class), settings, usersStore,
new AnonymousUser(Settings.EMPTY), securityLifecycleService, new ThreadContext(Settings.EMPTY));
@ -391,7 +395,7 @@ public class ReservedRealmTests extends ESTestCase {
final String password = randomAlphaOfLengthBetween(8, 24);
mockSecureSettings.setString("bootstrap.password", password);
Settings settings = Settings.builder().setSecureSettings(mockSecureSettings).build();
when(securityLifecycleService.isSecurityIndexExisting()).thenReturn(false);
when(securityIndex.indexExists()).thenReturn(false);
final ReservedRealm reservedRealm = new ReservedRealm(mock(Environment.class), settings, usersStore,
new AnonymousUser(Settings.EMPTY), securityLifecycleService, new ThreadContext(Settings.EMPTY));

View File

@ -30,6 +30,7 @@ import org.elasticsearch.xpack.core.security.user.User;
import org.elasticsearch.xpack.security.SecurityLifecycleService;
import org.elasticsearch.xpack.security.authc.support.CachingUsernamePasswordRealm;
import org.elasticsearch.xpack.security.authc.support.UserRoleMapper;
import org.elasticsearch.xpack.security.support.SecurityIndexManager;
import org.hamcrest.Matchers;
import java.util.Arrays;
@ -75,7 +76,9 @@ public class NativeRoleMappingStoreTests extends ESTestCase {
final Client client = mock(Client.class);
final SecurityLifecycleService lifecycleService = mock(SecurityLifecycleService.class);
when(lifecycleService.isSecurityIndexAvailable()).thenReturn(true);
SecurityIndexManager securityIndex = mock(SecurityIndexManager.class);
when(lifecycleService.securityIndex()).thenReturn(securityIndex);
when(securityIndex.isAvailable()).thenReturn(true);
final NativeRoleMappingStore store = new NativeRoleMappingStore(Settings.EMPTY, client, lifecycleService) {
@Override

View File

@ -21,7 +21,7 @@ import java.util.concurrent.CyclicBarrier;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
public class IndexLifecycleManagerIntegTests extends SecurityIntegTestCase {
public class SecurityIndexManagerIntegTests extends SecurityIntegTestCase {
public void testConcurrentOperationsTryingToCreateSecurityIndexAndAlias() throws Exception {
assertSecurityIndexActive();

View File

@ -52,17 +52,17 @@ import org.hamcrest.Matchers;
import org.junit.Before;
import static org.elasticsearch.cluster.routing.RecoverySource.StoreRecoverySource.EXISTING_STORE_INSTANCE;
import static org.elasticsearch.xpack.security.support.IndexLifecycleManager.TEMPLATE_VERSION_PATTERN;
import static org.elasticsearch.xpack.security.support.SecurityIndexManager.TEMPLATE_VERSION_PATTERN;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
public class IndexLifecycleManagerTests extends ESTestCase {
public class SecurityIndexManagerTests extends ESTestCase {
private static final ClusterName CLUSTER_NAME = new ClusterName("index-lifecycle-manager-tests");
private static final ClusterState EMPTY_CLUSTER_STATE = new ClusterState.Builder(CLUSTER_NAME).build();
public static final String INDEX_NAME = "IndexLifecycleManagerTests";
private static final String TEMPLATE_NAME = "IndexLifecycleManagerTests-template";
private IndexLifecycleManager manager;
public static final String INDEX_NAME = "SecurityIndexManagerTests";
private static final String TEMPLATE_NAME = "SecurityIndexManagerTests-template";
private SecurityIndexManager manager;
private Map<Action<?, ?, ?>, Map<ActionRequest, ActionListener<?>>> actions;
@Before
@ -86,7 +86,7 @@ public class IndexLifecycleManagerTests extends ESTestCase {
actions.put(action, map);
}
};
manager = new IndexLifecycleManager(Settings.EMPTY, client, INDEX_NAME);
manager = new SecurityIndexManager(Settings.EMPTY, client, INDEX_NAME);
}
public void testIndexWithUpToDateMappingAndTemplate() throws IOException {
@ -221,7 +221,7 @@ public class IndexLifecycleManagerTests extends ESTestCase {
// index doesn't exist and now exists with wrong format
ClusterState.Builder clusterStateBuilder = createClusterState(INDEX_NAME, TEMPLATE_NAME,
IndexLifecycleManager.INTERNAL_INDEX_FORMAT - 1);
SecurityIndexManager.INTERNAL_INDEX_FORMAT - 1);
markShardsAvailable(clusterStateBuilder);
manager.clusterChanged(event(clusterStateBuilder));
assertTrue(listenerCalled.get());
@ -235,7 +235,7 @@ public class IndexLifecycleManagerTests extends ESTestCase {
listenerCalled.set(false);
// index doesn't exist and now exists with correct format
clusterStateBuilder = createClusterState(INDEX_NAME, TEMPLATE_NAME, IndexLifecycleManager.INTERNAL_INDEX_FORMAT);
clusterStateBuilder = createClusterState(INDEX_NAME, TEMPLATE_NAME, SecurityIndexManager.INTERNAL_INDEX_FORMAT);
markShardsAvailable(clusterStateBuilder);
manager.clusterChanged(event(clusterStateBuilder));
assertFalse(listenerCalled.get());
@ -255,7 +255,7 @@ public class IndexLifecycleManagerTests extends ESTestCase {
}
public static ClusterState.Builder createClusterState(String indexName, String templateName) throws IOException {
return createClusterState(indexName, templateName, templateName, IndexLifecycleManager.INTERNAL_INDEX_FORMAT);
return createClusterState(indexName, templateName, templateName, SecurityIndexManager.INTERNAL_INDEX_FORMAT);
}
public static ClusterState.Builder createClusterState(String indexName, String templateName, int format) throws IOException {

View File

@ -30,6 +30,7 @@ import org.elasticsearch.common.settings.SettingsFilter;
import org.elasticsearch.common.unit.TimeValue;
import org.elasticsearch.common.util.concurrent.EsExecutors;
import org.elasticsearch.common.xcontent.NamedXContentRegistry;
import org.elasticsearch.core.internal.io.IOUtils;
import org.elasticsearch.env.Environment;
import org.elasticsearch.env.NodeEnvironment;
import org.elasticsearch.index.IndexModule;
@ -216,6 +217,7 @@ public class Watcher extends Plugin implements ActionPlugin, ScriptPlugin {
private static final Logger logger = Loggers.getLogger(Watcher.class);
private WatcherIndexingListener listener;
private HttpClient httpClient;
protected final Settings settings;
protected final boolean transportClient;
@ -266,7 +268,7 @@ public class Watcher extends Plugin implements ActionPlugin, ScriptPlugin {
// TODO: add more auth types, or remove this indirection
HttpAuthRegistry httpAuthRegistry = new HttpAuthRegistry(httpAuthFactories);
HttpRequestTemplate.Parser httpTemplateParser = new HttpRequestTemplate.Parser(httpAuthRegistry);
final HttpClient httpClient = new HttpClient(settings, httpAuthRegistry, getSslService());
httpClient = new HttpClient(settings, httpAuthRegistry, getSslService());
// notification
EmailService emailService = new EmailService(settings, cryptoService, clusterService.getClusterSettings());
@ -608,4 +610,9 @@ public class Watcher extends Plugin implements ActionPlugin, ScriptPlugin {
public List<ScriptContext> getContexts() {
return Arrays.asList(Watcher.SCRIPT_SEARCH_CONTEXT, Watcher.SCRIPT_EXECUTABLE_CONTEXT, Watcher.SCRIPT_TEMPLATE_CONTEXT);
}
@Override
public void close() throws IOException {
IOUtils.closeWhileHandlingException(httpClient);
}
}

View File

@ -46,6 +46,7 @@ import org.elasticsearch.xpack.watcher.common.http.auth.HttpAuthRegistry;
import javax.net.ssl.HostnameVerifier;
import java.io.ByteArrayOutputStream;
import java.io.Closeable;
import java.io.IOException;
import java.io.InputStream;
import java.net.URI;
@ -56,9 +57,13 @@ import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class HttpClient extends AbstractComponent {
public class HttpClient extends AbstractComponent implements Closeable {
private static final String SETTINGS_SSL_PREFIX = "xpack.http.ssl.";
// picking a reasonable high value here to allow for setups with lots of watch executions or many http inputs/actions
// this is also used as the value per route, if you are connecting to the same endpoint a lot, which is likely, when
// you are querying a remote Elasticsearch cluster
private static final int MAX_CONNECTIONS = 500;
private final HttpAuthRegistry httpAuthRegistry;
private final CloseableHttpClient client;
@ -84,6 +89,10 @@ public class HttpClient extends AbstractComponent {
SSLConnectionSocketFactory factory = new SSLConnectionSocketFactory(sslService.sslSocketFactory(sslSettings), verifier);
clientBuilder.setSSLSocketFactory(factory);
clientBuilder.evictExpiredConnections();
clientBuilder.setMaxConnPerRoute(MAX_CONNECTIONS);
clientBuilder.setMaxConnTotal(MAX_CONNECTIONS);
client = clientBuilder.build();
}
@ -251,6 +260,11 @@ public class HttpClient extends AbstractComponent {
}
}
@Override
public void close() throws IOException {
client.close();
}
/**
* Helper class to have all HTTP methods except HEAD allow for an body, including GET
*/

View File

@ -44,7 +44,6 @@ import org.joda.time.DateTime;
import org.junit.Before;
import javax.mail.internet.AddressException;
import java.io.IOException;
import java.util.Map;
@ -219,10 +218,9 @@ public class WebhookActionTests extends ESTestCase {
public void testThatSelectingProxyWorks() throws Exception {
Environment environment = TestEnvironment.newEnvironment(Settings.builder().put("path.home", createTempDir()).build());
HttpClient httpClient = new HttpClient(Settings.EMPTY, authRegistry,
new SSLService(environment.settings(), environment));
try (MockWebServer proxyServer = new MockWebServer()) {
try (HttpClient httpClient = new HttpClient(Settings.EMPTY, authRegistry,
new SSLService(environment.settings(), environment)); MockWebServer proxyServer = new MockWebServer()) {
proxyServer.start();
proxyServer.enqueue(new MockResponse().setResponseCode(200).setBody("fullProxiedContent"));

View File

@ -77,6 +77,7 @@ public class HttpClientTests extends ESTestCase {
@After
public void shutdown() throws Exception {
webServer.close();
httpClient.close();
}
public void testBasics() throws Exception {
@ -184,17 +185,18 @@ public class HttpClientTests extends ESTestCase {
.setSecureSettings(secureSettings)
.build();
}
httpClient = new HttpClient(settings, authRegistry, new SSLService(settings, environment));
secureSettings = new MockSecureSettings();
// We can't use the client created above for the server since it is only a truststore
secureSettings.setString("xpack.ssl.keystore.secure_password", "testnode");
Settings settings2 = Settings.builder()
.put("xpack.ssl.keystore.path", getDataPath("/org/elasticsearch/xpack/security/keystore/testnode.jks"))
.setSecureSettings(secureSettings)
.build();
try (HttpClient client = new HttpClient(settings, authRegistry, new SSLService(settings, environment))) {
secureSettings = new MockSecureSettings();
// We can't use the client created above for the server since it is only a truststore
secureSettings.setString("xpack.ssl.keystore.secure_password", "testnode");
Settings settings2 = Settings.builder()
.put("xpack.ssl.keystore.path", getDataPath("/org/elasticsearch/xpack/security/keystore/testnode.jks"))
.setSecureSettings(secureSettings)
.build();
TestsSSLService sslService = new TestsSSLService(settings2, environment);
testSslMockWebserver(sslService.sslContext(), false);
TestsSSLService sslService = new TestsSSLService(settings2, environment);
testSslMockWebserver(client, sslService.sslContext(), false);
}
}
public void testHttpsDisableHostnameVerification() throws Exception {
@ -217,18 +219,19 @@ public class HttpClientTests extends ESTestCase {
.setSecureSettings(secureSettings)
.build();
}
httpClient = new HttpClient(settings, authRegistry, new SSLService(settings, environment));
MockSecureSettings secureSettings = new MockSecureSettings();
// We can't use the client created above for the server since it only defines a truststore
secureSettings.setString("xpack.ssl.keystore.secure_password", "testnode-no-subjaltname");
Settings settings2 = Settings.builder()
.put("xpack.ssl.keystore.path",
getDataPath("/org/elasticsearch/xpack/security/transport/ssl/certs/simple/testnode-no-subjaltname.jks"))
.setSecureSettings(secureSettings)
.build();
try (HttpClient client = new HttpClient(settings, authRegistry, new SSLService(settings, environment))) {
MockSecureSettings secureSettings = new MockSecureSettings();
// We can't use the client created above for the server since it only defines a truststore
secureSettings.setString("xpack.ssl.keystore.secure_password", "testnode-no-subjaltname");
Settings settings2 = Settings.builder()
.put("xpack.ssl.keystore.path",
getDataPath("/org/elasticsearch/xpack/security/transport/ssl/certs/simple/testnode-no-subjaltname.jks"))
.setSecureSettings(secureSettings)
.build();
TestsSSLService sslService = new TestsSSLService(settings2, environment);
testSslMockWebserver(sslService.sslContext(), false);
TestsSSLService sslService = new TestsSSLService(settings2, environment);
testSslMockWebserver(client, sslService.sslContext(), false);
}
}
public void testHttpsClientAuth() throws Exception {
@ -241,11 +244,12 @@ public class HttpClientTests extends ESTestCase {
.build();
TestsSSLService sslService = new TestsSSLService(settings, environment);
httpClient = new HttpClient(settings, authRegistry, sslService);
testSslMockWebserver(sslService.sslContext(), true);
try (HttpClient client = new HttpClient(settings, authRegistry, sslService)) {
testSslMockWebserver(client, sslService.sslContext(), true);
}
}
private void testSslMockWebserver(SSLContext sslContext, boolean needClientAuth) throws IOException {
private void testSslMockWebserver(HttpClient client, SSLContext sslContext, boolean needClientAuth) throws IOException {
try (MockWebServer mockWebServer = new MockWebServer(sslContext, needClientAuth)) {
mockWebServer.enqueue(new MockResponse().setResponseCode(200).setBody("body"));
mockWebServer.start();
@ -253,7 +257,7 @@ public class HttpClientTests extends ESTestCase {
HttpRequest.Builder request = HttpRequest.builder("localhost", mockWebServer.getPort())
.scheme(Scheme.HTTPS)
.path("/test");
HttpResponse response = httpClient.execute(request.build());
HttpResponse response = client.execute(request.build());
assertThat(response.status(), equalTo(200));
assertThat(response.body().utf8ToString(), equalTo("body"));
@ -288,14 +292,14 @@ public class HttpClientTests extends ESTestCase {
@Network
public void testHttpsWithoutTruststore() throws Exception {
HttpClient httpClient = new HttpClient(Settings.EMPTY, authRegistry, new SSLService(Settings.EMPTY, environment));
// Known server with a valid cert from a commercial CA
HttpRequest.Builder request = HttpRequest.builder("www.elastic.co", 443).scheme(Scheme.HTTPS);
HttpResponse response = httpClient.execute(request.build());
assertThat(response.status(), equalTo(200));
assertThat(response.hasContent(), is(true));
assertThat(response.body(), notNullValue());
try (HttpClient client = new HttpClient(Settings.EMPTY, authRegistry, new SSLService(Settings.EMPTY, environment))) {
// Known server with a valid cert from a commercial CA
HttpRequest.Builder request = HttpRequest.builder("www.elastic.co", 443).scheme(Scheme.HTTPS);
HttpResponse response = client.execute(request.build());
assertThat(response.status(), equalTo(200));
assertThat(response.hasContent(), is(true));
assertThat(response.body(), notNullValue());
}
}
public void testThatProxyCanBeConfigured() throws Exception {
@ -307,15 +311,16 @@ public class HttpClientTests extends ESTestCase {
.put(HttpSettings.PROXY_HOST.getKey(), "localhost")
.put(HttpSettings.PROXY_PORT.getKey(), proxyServer.getPort())
.build();
HttpClient httpClient = new HttpClient(settings, authRegistry, new SSLService(settings, environment));
HttpRequest.Builder requestBuilder = HttpRequest.builder("localhost", webServer.getPort())
.method(HttpMethod.GET)
.path("/");
HttpResponse response = httpClient.execute(requestBuilder.build());
assertThat(response.status(), equalTo(200));
assertThat(response.body().utf8ToString(), equalTo("fullProxiedContent"));
try (HttpClient client = new HttpClient(settings, authRegistry, new SSLService(settings, environment))) {
HttpResponse response = client.execute(requestBuilder.build());
assertThat(response.status(), equalTo(200));
assertThat(response.body().utf8ToString(), equalTo("fullProxiedContent"));
}
// ensure we hit the proxyServer and not the webserver
assertThat(webServer.requests(), hasSize(0));
@ -386,16 +391,16 @@ public class HttpClientTests extends ESTestCase {
.setSecureSettings(secureSettings)
.build();
HttpClient httpClient = new HttpClient(settings, authRegistry, new SSLService(settings, environment));
HttpRequest.Builder requestBuilder = HttpRequest.builder("localhost", webServer.getPort())
.method(HttpMethod.GET)
.scheme(Scheme.HTTP)
.path("/");
HttpResponse response = httpClient.execute(requestBuilder.build());
assertThat(response.status(), equalTo(200));
assertThat(response.body().utf8ToString(), equalTo("fullProxiedContent"));
try (HttpClient client = new HttpClient(settings, authRegistry, new SSLService(settings, environment))) {
HttpResponse response = client.execute(requestBuilder.build());
assertThat(response.status(), equalTo(200));
assertThat(response.body().utf8ToString(), equalTo("fullProxiedContent"));
}
// ensure we hit the proxyServer and not the webserver
assertThat(webServer.requests(), hasSize(0));
@ -413,16 +418,17 @@ public class HttpClientTests extends ESTestCase {
.put(HttpSettings.PROXY_PORT.getKey(), proxyServer.getPort() + 1)
.put(HttpSettings.PROXY_HOST.getKey(), "https")
.build();
HttpClient httpClient = new HttpClient(settings, authRegistry, new SSLService(settings, environment));
HttpRequest.Builder requestBuilder = HttpRequest.builder("localhost", webServer.getPort())
.method(HttpMethod.GET)
.proxy(new HttpProxy("localhost", proxyServer.getPort(), Scheme.HTTP))
.path("/");
HttpResponse response = httpClient.execute(requestBuilder.build());
assertThat(response.status(), equalTo(200));
assertThat(response.body().utf8ToString(), equalTo("fullProxiedContent"));
try (HttpClient client = new HttpClient(settings, authRegistry, new SSLService(settings, environment))) {
HttpResponse response = client.execute(requestBuilder.build());
assertThat(response.status(), equalTo(200));
assertThat(response.body().utf8ToString(), equalTo("fullProxiedContent"));
}
// ensure we hit the proxyServer and not the webserver
assertThat(webServer.requests(), hasSize(0));
@ -535,12 +541,13 @@ public class HttpClientTests extends ESTestCase {
Settings settings = Settings.builder()
.put(HttpSettings.MAX_HTTP_RESPONSE_SIZE.getKey(), new ByteSizeValue(randomBytesLength - 1, ByteSizeUnit.BYTES))
.build();
HttpClient httpClient = new HttpClient(settings, authRegistry, new SSLService(environment.settings(), environment));
HttpRequest.Builder requestBuilder = HttpRequest.builder("localhost", webServer.getPort()).method(HttpMethod.GET).path("/");
IOException e = expectThrows(IOException.class, () -> httpClient.execute(requestBuilder.build()));
assertThat(e.getMessage(), startsWith("Maximum limit of"));
try (HttpClient client = new HttpClient(settings, authRegistry, new SSLService(environment.settings(), environment))) {
IOException e = expectThrows(IOException.class, () -> client.execute(requestBuilder.build()));
assertThat(e.getMessage(), startsWith("Maximum limit of"));
}
}
public void testThatGetRedirectIsFollowed() throws Exception {

View File

@ -40,66 +40,69 @@ public class HttpReadTimeoutTests extends ESTestCase {
public void testDefaultTimeout() throws Exception {
Environment environment = TestEnvironment.newEnvironment(Settings.builder().put("path.home", createTempDir()).build());
HttpClient httpClient = new HttpClient(Settings.EMPTY, mock(HttpAuthRegistry.class),
new SSLService(environment.settings(), environment));
HttpRequest request = HttpRequest.builder("localhost", webServer.getPort())
.method(HttpMethod.POST)
.path("/")
.build();
long start = System.nanoTime();
expectThrows(SocketTimeoutException.class, () -> httpClient.execute(request));
TimeValue timeout = TimeValue.timeValueNanos(System.nanoTime() - start);
logger.info("http connection timed out after {}", timeout);
try (HttpClient httpClient = new HttpClient(Settings.EMPTY, mock(HttpAuthRegistry.class),
new SSLService(environment.settings(), environment))) {
long start = System.nanoTime();
// it's supposed to be 10, but we'll give it an error margin of 2 seconds
assertThat(timeout.seconds(), greaterThan(8L));
assertThat(timeout.seconds(), lessThan(12L));
expectThrows(SocketTimeoutException.class, () -> httpClient.execute(request));
TimeValue timeout = TimeValue.timeValueNanos(System.nanoTime() - start);
logger.info("http connection timed out after {}", timeout);
// it's supposed to be 10, but we'll give it an error margin of 2 seconds
assertThat(timeout.seconds(), greaterThan(8L));
assertThat(timeout.seconds(), lessThan(12L));
}
}
public void testDefaultTimeoutCustom() throws Exception {
Environment environment = TestEnvironment.newEnvironment(Settings.builder().put("path.home", createTempDir()).build());
HttpClient httpClient = new HttpClient(Settings.builder()
.put("xpack.http.default_read_timeout", "3s").build()
, mock(HttpAuthRegistry.class), new SSLService(environment.settings(), environment));
HttpRequest request = HttpRequest.builder("localhost", webServer.getPort())
.method(HttpMethod.POST)
.path("/")
.build();
long start = System.nanoTime();
expectThrows(SocketTimeoutException.class, () -> httpClient.execute(request));
TimeValue timeout = TimeValue.timeValueNanos(System.nanoTime() - start);
logger.info("http connection timed out after {}", timeout);
try (HttpClient httpClient = new HttpClient(Settings.builder()
.put("xpack.http.default_read_timeout", "3s").build()
, mock(HttpAuthRegistry.class), new SSLService(environment.settings(), environment))) {
// it's supposed to be 3, but we'll give it an error margin of 2 seconds
assertThat(timeout.seconds(), greaterThan(1L));
assertThat(timeout.seconds(), lessThan(5L));
long start = System.nanoTime();
expectThrows(SocketTimeoutException.class, () -> httpClient.execute(request));
TimeValue timeout = TimeValue.timeValueNanos(System.nanoTime() - start);
logger.info("http connection timed out after {}", timeout);
// it's supposed to be 3, but we'll give it an error margin of 2 seconds
assertThat(timeout.seconds(), greaterThan(1L));
assertThat(timeout.seconds(), lessThan(5L));
}
}
public void testTimeoutCustomPerRequest() throws Exception {
Environment environment = TestEnvironment.newEnvironment(Settings.builder().put("path.home", createTempDir()).build());
HttpClient httpClient = new HttpClient(Settings.builder()
.put("xpack.http.default_read_timeout", "10s").build()
, mock(HttpAuthRegistry.class), new SSLService(environment.settings(), environment));
HttpRequest request = HttpRequest.builder("localhost", webServer.getPort())
.readTimeout(TimeValue.timeValueSeconds(3))
.method(HttpMethod.POST)
.path("/")
.build();
long start = System.nanoTime();
expectThrows(SocketTimeoutException.class, () -> httpClient.execute(request));
TimeValue timeout = TimeValue.timeValueNanos(System.nanoTime() - start);
logger.info("http connection timed out after {}", timeout);
try (HttpClient httpClient = new HttpClient(Settings.builder()
.put("xpack.http.default_read_timeout", "10s").build()
, mock(HttpAuthRegistry.class), new SSLService(environment.settings(), environment))) {
// it's supposed to be 3, but we'll give it an error margin of 2 seconds
assertThat(timeout.seconds(), greaterThan(1L));
assertThat(timeout.seconds(), lessThan(5L));
long start = System.nanoTime();
expectThrows(SocketTimeoutException.class, () -> httpClient.execute(request));
TimeValue timeout = TimeValue.timeValueNanos(System.nanoTime() - start);
logger.info("http connection timed out after {}", timeout);
// it's supposed to be 3, but we'll give it an error margin of 2 seconds
assertThat(timeout.seconds(), greaterThan(1L));
assertThat(timeout.seconds(), lessThan(5L));
}
}
}

View File

@ -23,7 +23,7 @@ import org.elasticsearch.test.rest.ESRestTestCase;
import org.elasticsearch.xpack.core.monitoring.exporter.MonitoringTemplateUtils;
import org.elasticsearch.xpack.core.watcher.client.WatchSourceBuilder;
import org.elasticsearch.xpack.core.watcher.support.xcontent.ObjectPath;
import org.elasticsearch.xpack.security.support.IndexLifecycleManager;
import org.elasticsearch.xpack.security.support.SecurityIndexManager;
import org.elasticsearch.xpack.test.rest.XPackRestTestHelper;
import org.elasticsearch.xpack.watcher.actions.logging.LoggingAction;
import org.elasticsearch.xpack.watcher.common.text.TextTemplate;
@ -138,7 +138,7 @@ public class FullClusterRestartIT extends ESRestTestCase {
logger.info("settings map {}", settingsMap);
if (settingsMap.containsKey("index")) {
int format = Integer.parseInt(String.valueOf(((Map<String, Object>)settingsMap.get("index")).get("format")));
needsUpgrade = format == IndexLifecycleManager.INTERNAL_INDEX_FORMAT ? false : true;
needsUpgrade = format == SecurityIndexManager.INTERNAL_INDEX_FORMAT ? false : true;
} else {
needsUpgrade = true;
}

View File

@ -8,6 +8,7 @@ package org.elasticsearch.upgrades;
import com.carrotsearch.randomizedtesting.annotations.ParametersFactory;
import com.carrotsearch.randomizedtesting.annotations.TimeoutSuite;
import org.apache.lucene.util.LuceneTestCase;
import org.apache.lucene.util.TimeUnits;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.util.concurrent.ThreadContext;
@ -29,6 +30,7 @@ import static java.util.Collections.singletonMap;
import static org.hamcrest.Matchers.is;
@TimeoutSuite(millis = 5 * TimeUnits.MINUTE) // to account for slow as hell VMs
@LuceneTestCase.AwaitsFix(bugUrl = "https://github.com/elastic/elasticsearch/issues/30456")
public class UpgradeClusterClientYamlTestSuiteIT extends ESClientYamlSuiteTestCase {
/**