Adds upgrade API functionality for security (elastic/x-pack-elasticsearch#2012)

This commit adds the upgrade API functionality and script for security.
It also enables previously muted tests that would fail due to the lack
of security upgrade features in testing cluster restarts and old
security index backward compatibility.

Original commit: elastic/x-pack-elasticsearch@4abe9f1263
This commit is contained in:
Ali Beyad 2017-07-18 11:44:28 -04:00 committed by GitHub
parent 8b608ef23b
commit 37cc602aef
22 changed files with 281 additions and 661 deletions

View File

@ -509,7 +509,11 @@ public class XPackPlugin extends Plugin implements ScriptPlugin, ActionPlugin, I
@Override
public UnaryOperator<Map<String, IndexTemplateMetaData>> getIndexTemplateMetaDataUpgrader() {
return watcher.getIndexTemplateMetaDataUpgrader();
return templates -> {
templates = watcher.getIndexTemplateMetaDataUpgrader().apply(templates);
templates = security.getIndexTemplateMetaDataUpgrader().apply(templates);
return templates;
};
}
public void onIndexModule(IndexModule module) {

View File

@ -6,6 +6,7 @@
package org.elasticsearch.xpack.security;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.security.GeneralSecurityException;
import java.time.Clock;
import java.util.ArrayList;
@ -26,6 +27,7 @@ import java.util.stream.Collectors;
import org.apache.logging.log4j.Logger;
import org.apache.lucene.util.SetOnce;
import org.elasticsearch.Version;
import org.elasticsearch.action.ActionListener;
import org.elasticsearch.action.ActionRequest;
import org.elasticsearch.action.ActionResponse;
@ -33,6 +35,7 @@ import org.elasticsearch.action.support.ActionFilter;
import org.elasticsearch.action.support.DestructiveOperations;
import org.elasticsearch.bootstrap.BootstrapCheck;
import org.elasticsearch.cluster.metadata.IndexNameExpressionResolver;
import org.elasticsearch.cluster.metadata.IndexTemplateMetaData;
import org.elasticsearch.cluster.node.DiscoveryNodes;
import org.elasticsearch.cluster.service.ClusterService;
import org.elasticsearch.common.Booleans;
@ -57,6 +60,10 @@ import org.elasticsearch.common.util.BigArrays;
import org.elasticsearch.common.util.CollectionUtils;
import org.elasticsearch.common.util.concurrent.ThreadContext;
import org.elasticsearch.common.xcontent.NamedXContentRegistry;
import org.elasticsearch.common.xcontent.XContent;
import org.elasticsearch.common.xcontent.XContentFactory;
import org.elasticsearch.common.xcontent.XContentParser;
import org.elasticsearch.common.xcontent.XContentType;
import org.elasticsearch.env.Environment;
import org.elasticsearch.http.HttpServerTransport;
import org.elasticsearch.index.IndexModule;
@ -168,6 +175,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.transport.SecurityServerTransportInterceptor;
import org.elasticsearch.xpack.security.transport.filter.IPFilter;
import org.elasticsearch.xpack.security.transport.netty4.SecurityNetty4HttpServerTransport;
@ -176,12 +184,14 @@ import org.elasticsearch.xpack.security.user.AnonymousUser;
import org.elasticsearch.xpack.ssl.SSLBootstrapCheck;
import org.elasticsearch.xpack.ssl.SSLConfigurationSettings;
import org.elasticsearch.xpack.ssl.SSLService;
import org.elasticsearch.xpack.template.TemplateUtils;
import org.joda.time.DateTime;
import org.joda.time.DateTimeZone;
import static java.util.Collections.emptyList;
import static java.util.Collections.singletonList;
import static org.elasticsearch.xpack.XPackSettings.HTTP_SSL_ENABLED;
import static org.elasticsearch.xpack.security.SecurityLifecycleService.SECURITY_TEMPLATE_NAME;
public class Security implements ActionPlugin, IngestPlugin, NetworkPlugin {
@ -857,4 +867,21 @@ public class Security implements ActionPlugin, IngestPlugin, NetworkPlugin {
}
return Collections.emptyList();
}
public UnaryOperator<Map<String, IndexTemplateMetaData>> getIndexTemplateMetaDataUpgrader() {
return templates -> {
final byte[] securityTemplate = TemplateUtils.loadTemplate("/" + SECURITY_TEMPLATE_NAME + ".json",
Version.CURRENT.toString(), IndexLifecycleManager.TEMPLATE_VERSION_PATTERN).getBytes(StandardCharsets.UTF_8);
final XContent xContent = XContentFactory.xContent(XContentType.JSON);
try (XContentParser parser = xContent.createParser(NamedXContentRegistry.EMPTY, securityTemplate)) {
templates.put(SECURITY_TEMPLATE_NAME, IndexTemplateMetaData.Builder.fromXContent(parser, SECURITY_TEMPLATE_NAME));
} catch (IOException e) {
// TODO: should we handle this with a thrown exception?
logger.error("Error loading security template [{}] as part of metadata upgrading", SECURITY_TEMPLATE_NAME);
}
return templates;
};
}
}

View File

@ -25,6 +25,7 @@ import org.elasticsearch.xpack.security.audit.index.IndexAuditTrail;
import org.elasticsearch.xpack.security.authc.esnative.NativeRealmMigrator;
import org.elasticsearch.xpack.security.support.IndexLifecycleManager;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.function.BiConsumer;
@ -47,6 +48,7 @@ public class SecurityLifecycleService extends AbstractComponent implements Clust
public static final String SECURITY_INDEX_NAME = ".security";
public static final String SECURITY_TEMPLATE_NAME = "security-index-template";
public static final String NEW_SECURITY_INDEX_NAME = SECURITY_INDEX_NAME + "-" + IndexLifecycleManager.NEW_INDEX_VERSION;
private static final Version MIN_READ_VERSION = Version.V_5_0_0;
@ -188,7 +190,7 @@ public class SecurityLifecycleService extends AbstractComponent implements Clust
}
public static List<String> indexNames() {
return Collections.singletonList(SECURITY_INDEX_NAME);
return Collections.unmodifiableList(Arrays.asList(SECURITY_INDEX_NAME, NEW_SECURITY_INDEX_NAME));
}
/**

View File

@ -199,7 +199,7 @@ public class NativeRealmMigrator implements IndexLifecycleManager.IndexDataMigra
/**
* Determines whether the supplied source as a {@link Map} has its password explicitly set to be the default password
*/
private boolean hasOldStyleDefaultPassword(Map<String, Object> userSource) {
public static boolean hasOldStyleDefaultPassword(Map<String, Object> userSource) {
// TODO we should store the hash as something other than a string... bytes?
final String passwordHash = (String) userSource.get(User.Fields.PASSWORD.getPreferredName());
if (passwordHash == null) {

View File

@ -69,9 +69,9 @@ import java.util.function.Consumer;
*/
public class NativeUsersStore extends AbstractComponent {
static final String INDEX_TYPE = "doc";
public static final String INDEX_TYPE = "doc";
private static final String USER_DOC_TYPE = "user";
static final String RESERVED_USER_TYPE = "reserved-user";
public static final String RESERVED_USER_TYPE = "reserved-user";
private final Hasher hasher = Hasher.BCRYPT;
private final InternalClient client;

View File

@ -27,6 +27,7 @@ import org.elasticsearch.ElasticsearchParseException;
import org.elasticsearch.ResourceAlreadyExistsException;
import org.elasticsearch.Version;
import org.elasticsearch.action.ActionListener;
import org.elasticsearch.action.admin.indices.alias.IndicesAliasesResponse;
import org.elasticsearch.action.admin.indices.create.CreateIndexRequest;
import org.elasticsearch.action.admin.indices.create.CreateIndexResponse;
import org.elasticsearch.action.admin.indices.mapping.put.PutMappingRequest;
@ -54,9 +55,11 @@ import org.elasticsearch.index.mapper.MapperService;
import org.elasticsearch.threadpool.ThreadPool;
import org.elasticsearch.xpack.security.InternalClient;
import org.elasticsearch.xpack.template.TemplateUtils;
import org.elasticsearch.xpack.upgrade.IndexUpgradeCheck;
import static org.elasticsearch.cluster.metadata.IndexMetaData.INDEX_FORMAT_SETTING;
import static org.elasticsearch.common.xcontent.XContentHelper.convertToMap;
import static org.elasticsearch.xpack.security.SecurityLifecycleService.SECURITY_INDEX_NAME;
/**
* Manages the lifecycle of a single index, its template, mapping and and data upgrades/migrations.
@ -68,6 +71,7 @@ public class IndexLifecycleManager extends AbstractComponent {
private static final String SECURITY_VERSION_STRING = "security-version";
public static final String TEMPLATE_VERSION_PATTERN =
Pattern.quote("${security.template.version}");
public static int NEW_INDEX_VERSION = IndexUpgradeCheck.UPRADE_VERSION;
private static final int MAX_MIGRATE_ATTEMPTS = 10;
@ -102,7 +106,7 @@ public class IndexLifecycleManager extends AbstractComponent {
void performUpgrade(@Nullable Version previousVersion, ActionListener<Boolean> listener);
}
public static final IndexDataMigrator NULL_MIGRATOR = (version, listener) -> listener.onResponse(false);
public static final IndexDataMigrator NULL_MIGRATOR = (version, listener) -> listener.onResponse(false);
public IndexLifecycleManager(Settings settings, InternalClient client, ClusterService clusterService, ThreadPool threadPool,
String indexName, String templateName, IndexDataMigrator migrator) {
@ -185,15 +189,6 @@ public class IndexLifecycleManager extends AbstractComponent {
this.mappingIsUpToDate = checkIndexMappingUpToDate(state);
this.canWriteToIndex = templateIsUpToDate && mappingIsUpToDate;
this.mappingVersion = oldestIndexMappingVersion(state);
if (state.nodes().isLocalNodeElectedMaster()) {
if (templateIsUpToDate == false) {
updateTemplate();
}
if (indexAvailable && mappingIsUpToDate == false) {
migrateData(state, this::updateMapping);
}
}
}
private void checkIndexHealthChange(ClusterChangedEvent event) {
@ -514,7 +509,7 @@ public class IndexLifecycleManager extends AbstractComponent {
@Override
public void onResponse(CreateIndexResponse createIndexResponse) {
if (createIndexResponse.isAcknowledged()) {
andThen.run();
setSecurityIndexAlias(listener, andThen);
} else {
listener.onFailure(new ElasticsearchException("Failed to create security index"));
}
@ -533,4 +528,31 @@ public class IndexLifecycleManager extends AbstractComponent {
});
}
}
/**
* Sets the security index alias to .security after it has been created. This is required
* because we cannot add the alias as part of the security index template, as the security
* template is also used when the new security index is created during the upgrade API, at
* which point the old .security index already exists and is being reindexed from, so the
* alias cannot be added as part of the template, hence the alias creation has to happen
* manually here after index creation.
*/
private <T> void setSecurityIndexAlias(final ActionListener<T> listener, final Runnable andThen) {
client.admin().indices().prepareAliases().addAlias(INTERNAL_SECURITY_INDEX, SECURITY_INDEX_NAME)
.execute(new ActionListener<IndicesAliasesResponse>() {
@Override
public void onResponse(IndicesAliasesResponse response) {
if (response.isAcknowledged()) {
andThen.run();
} else {
listener.onFailure(new ElasticsearchException("Failed to set security index alias"));
}
}
@Override
public void onFailure(Exception e) {
listener.onFailure(e);
}
});
}
}

View File

@ -33,10 +33,10 @@ import java.util.function.Consumer;
* A component that performs the following upgrade procedure:
* <p>
* - Check that all data and master nodes are running running the same version
* - Create a new index .{name}-v6
* - Create a new index .{name}-6
* - Make index .{name} read only
* - Reindex from .{name} to .{name}-v6 with transform
* - Delete index .{name} and add alias .{name} to .{name}-v6
* - Reindex from .{name} to .{name}-6 with transform
* - Delete index .{name} and add alias .{name} to .{name}-6
*/
public class InternalIndexReindexer<T> {
@ -75,7 +75,7 @@ public class InternalIndexReindexer<T> {
private void innerUpgrade(ParentTaskAssigningClient parentAwareClient, String index, ClusterState clusterState,
ActionListener<BulkByScrollResponse> listener) {
String newIndex = index + "_v" + version;
String newIndex = index + "-" + version;
try {
checkMasterAndDataNodeVersion(clusterState);
parentAwareClient.admin().indices().prepareCreate(newIndex).execute(ActionListener.wrap(createIndexResponse ->

View File

@ -12,6 +12,10 @@ import org.elasticsearch.action.ActionRequest;
import org.elasticsearch.action.ActionResponse;
import org.elasticsearch.action.admin.indices.delete.DeleteIndexResponse;
import org.elasticsearch.action.admin.indices.template.delete.DeleteIndexTemplateResponse;
import org.elasticsearch.action.bulk.BulkRequestBuilder;
import org.elasticsearch.action.bulk.BulkResponse;
import org.elasticsearch.action.support.WriteRequest;
import org.elasticsearch.action.update.UpdateRequest;
import org.elasticsearch.client.Client;
import org.elasticsearch.cluster.metadata.IndexMetaData;
import org.elasticsearch.cluster.metadata.IndexNameExpressionResolver;
@ -23,16 +27,20 @@ import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.settings.SettingsFilter;
import org.elasticsearch.common.xcontent.NamedXContentRegistry;
import org.elasticsearch.index.IndexNotFoundException;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.plugins.ActionPlugin;
import org.elasticsearch.rest.RestController;
import org.elasticsearch.rest.RestHandler;
import org.elasticsearch.script.Script;
import org.elasticsearch.script.ScriptService;
import org.elasticsearch.script.ScriptType;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.threadpool.ThreadPool;
import org.elasticsearch.transport.TransportResponse;
import org.elasticsearch.watcher.ResourceWatcherService;
import org.elasticsearch.xpack.security.InternalClient;
import org.elasticsearch.xpack.security.authc.esnative.NativeRealmMigrator;
import org.elasticsearch.xpack.security.user.User;
import org.elasticsearch.xpack.upgrade.actions.IndexUpgradeAction;
import org.elasticsearch.xpack.upgrade.actions.IndexUpgradeInfoAction;
import org.elasticsearch.xpack.upgrade.rest.RestIndexUpgradeAction;
@ -46,10 +54,17 @@ import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.function.BiFunction;
import java.util.function.Supplier;
import static org.elasticsearch.xpack.security.SecurityLifecycleService.SECURITY_INDEX_NAME;
import static org.elasticsearch.xpack.security.authc.esnative.NativeUsersStore.INDEX_TYPE;
import static org.elasticsearch.xpack.security.authc.esnative.NativeUsersStore.RESERVED_USER_TYPE;
public class Upgrade implements ActionPlugin {
public static final Version UPGRADE_INTRODUCED = Version.V_5_6_0;
@ -65,6 +80,7 @@ public class Upgrade implements ActionPlugin {
this.settings = settings;
this.upgradeCheckFactories = new ArrayList<>();
upgradeCheckFactories.add(getWatcherUpgradeCheckFactory(settings));
upgradeCheckFactories.add(getSecurityUpgradeCheckFactory(settings));
}
public Collection<Object> createComponents(InternalClient internalClient, ClusterService clusterService, ThreadPool threadPool,
@ -103,6 +119,86 @@ public class Upgrade implements ActionPlugin {
return indexMetaData.getSettings().getAsInt(IndexMetaData.INDEX_FORMAT_SETTING.getKey(), 0) == EXPECTED_INDEX_FORMAT_VERSION;
}
static BiFunction<InternalClient, ClusterService, IndexUpgradeCheck> getSecurityUpgradeCheckFactory(Settings settings) {
return (internalClient, clusterService) ->
new IndexUpgradeCheck<Void>("security",
settings,
indexMetaData -> {
if (".security".equals(indexMetaData.getIndex().getName())
|| indexMetaData.getAliases().containsKey(".security")) {
if (checkInternalIndexFormat(indexMetaData)) {
return UpgradeActionRequired.UP_TO_DATE;
} else {
return UpgradeActionRequired.UPGRADE;
}
} else {
return UpgradeActionRequired.NOT_APPLICABLE;
}
},
internalClient,
clusterService,
new String[] { "user", "reserved-user", "role", "doc" },
new Script(ScriptType.INLINE, "painless",
"ctx._source.type = ctx._type;\n" +
"if (!ctx._type.equals(\"doc\")) {\n" +
" ctx._id = ctx._type + \"-\" + ctx._id;\n" +
" ctx._type = \"doc\";" +
"}\n",
new HashMap<>()),
listener -> listener.onResponse(null),
(success, listener) -> postSecurityUpgrade(internalClient, listener));
}
private static void postSecurityUpgrade(Client client, ActionListener<TransportResponse.Empty> listener) {
// update passwords to the new style, if they are in the old default password mechanism
client.prepareSearch(SECURITY_INDEX_NAME)
.setQuery(QueryBuilders.termQuery(User.Fields.TYPE.getPreferredName(), RESERVED_USER_TYPE))
.setFetchSource(true)
.execute(ActionListener.wrap(searchResponse -> {
assert searchResponse.getHits().getTotalHits() <= 10 :
"there are more than 10 reserved users we need to change this to retrieve them all!";
Set<String> toConvert = new HashSet<>();
for (SearchHit searchHit : searchResponse.getHits()) {
Map<String, Object> sourceMap = searchHit.getSourceAsMap();
if (NativeRealmMigrator.hasOldStyleDefaultPassword(sourceMap)) {
toConvert.add(searchHit.getId());
}
}
if (toConvert.isEmpty()) {
listener.onResponse(TransportResponse.Empty.INSTANCE);
} else {
final BulkRequestBuilder bulkRequestBuilder = client.prepareBulk();
for (final String id : toConvert) {
final UpdateRequest updateRequest = new UpdateRequest(SECURITY_INDEX_NAME,
INDEX_TYPE, RESERVED_USER_TYPE + "-" + id);
updateRequest.setRefreshPolicy(WriteRequest.RefreshPolicy.IMMEDIATE)
.doc(User.Fields.PASSWORD.getPreferredName(), "",
User.Fields.TYPE.getPreferredName(), RESERVED_USER_TYPE);
bulkRequestBuilder.add(updateRequest);
}
bulkRequestBuilder.execute(new ActionListener<BulkResponse>() {
@Override
public void onResponse(BulkResponse bulkItemResponses) {
if (bulkItemResponses.hasFailures()) {
final String msg = "failed to update old style reserved user passwords: " +
bulkItemResponses.buildFailureMessage();
listener.onFailure(new ElasticsearchException(msg));
} else {
listener.onResponse(TransportResponse.Empty.INSTANCE);
}
}
@Override
public void onFailure(Exception e) {
listener.onFailure(e);
}
});
}
}, listener::onFailure));
}
static BiFunction<InternalClient, ClusterService, IndexUpgradeCheck> getWatcherUpgradeCheckFactory(Settings settings) {
return (internalClient, clusterService) ->
new IndexUpgradeCheck<Boolean>("watcher",

View File

@ -1,5 +1,5 @@
{
"index_patterns" : ".security-*",
"index_patterns" : [ ".security-*" ],
"order" : 1000,
"settings" : {
"number_of_shards" : 1,
@ -113,8 +113,5 @@
}
}
}
},
"aliases" : {
".security": {}
}
}

View File

@ -1,188 +0,0 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
package org.elasticsearch;
import org.apache.lucene.util.LuceneTestCase;
import org.apache.lucene.util.LuceneTestCase.AwaitsFix;
import org.elasticsearch.action.admin.indices.alias.get.GetAliasesResponse;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.action.support.IndicesOptions;
import org.elasticsearch.client.Client;
import org.elasticsearch.cluster.metadata.AliasMetaData;
import org.elasticsearch.common.bytes.BytesArray;
import org.elasticsearch.common.xcontent.ToXContent;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.xpack.XPackFeatureSet;
import org.elasticsearch.xpack.action.XPackUsageRequestBuilder;
import org.elasticsearch.xpack.action.XPackUsageResponse;
import org.elasticsearch.xpack.security.SecurityFeatureSet;
import org.elasticsearch.xpack.security.action.role.ClearRolesCacheRequestBuilder;
import org.elasticsearch.xpack.security.action.role.ClearRolesCacheResponse;
import org.elasticsearch.xpack.security.action.role.GetRolesResponse;
import org.elasticsearch.xpack.security.action.role.PutRoleResponse;
import org.elasticsearch.xpack.security.action.user.GetUsersResponse;
import org.elasticsearch.xpack.security.action.user.PutUserResponse;
import org.elasticsearch.xpack.security.authc.support.UsernamePasswordToken;
import org.elasticsearch.xpack.security.authz.RoleDescriptor;
import org.elasticsearch.xpack.security.authz.permission.FieldPermissions;
import org.elasticsearch.xpack.security.authz.permission.FieldPermissionsDefinition;
import org.elasticsearch.xpack.security.client.SecurityClient;
import org.elasticsearch.xpack.security.user.User;
import java.util.Collections;
import java.util.List;
import static java.util.Collections.singletonMap;
import static org.elasticsearch.common.xcontent.XContentFactory.jsonBuilder;
import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertHitCount;
import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertNoSearchHits;
import static org.elasticsearch.xpack.security.authc.support.UsernamePasswordTokenTests.basicAuthHeaderValue;
import static org.hamcrest.Matchers.anyOf;
import static org.hamcrest.Matchers.arrayWithSize;
import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.hasSize;
/**
* Backwards compatibility test that loads some data from a pre-Version.CURRENT cluster and attempts to do some basic security stuff with
* it. It contains:
* <ul>
* <li>This user: {@code {"username": "bwc_test_user", "roles" : [ "bwc_test_role" ], "password" : "9876543210"}}</li>
* <li>This role: {@code {"name": "bwc_test_role", "cluster": ["all"]}, "run_as": [ "other_user" ], "indices": [{
* "names": [ "index1", "index2" ],
* "privileges": ["all"],
* "fields": [ "title", "body" ],
* "query": "{\"match\": {\"title\": \"foo\"}}"
* }]}</li>
* <li>This document in {@code index1}: {@code {
* "title": "foo",
* "body": "bwc_test_user should be able to see this field",
* "secured_body": "bwc_test_user should not be able to see this field"}}</li>
* <li>This document in {@code index1}: {@code {"title": "bwc_test_user should not be able to see this document"}}</li>
* <li>This document in {@code index2}: {@code {
* "title": "foo",
* "body": "bwc_test_user should be able to see this field",
* "secured_body": "bwc_test_user should not be able to see this field"}}</li>
* <li>This document in {@code index2}: {@code {"title": "bwc_test_user should not be able to see this document"}}</li>
* <li>This document in {@code index3}: {@code {"title": "bwc_test_user should not see this index"}}</li>
* </ul>
**/
// This will only work when the upgrade API is in place!
@AwaitsFix(bugUrl = "https://github.com/elastic/dev/issues/741")
public class OldSecurityIndexBackwardsCompatibilityTests extends AbstractOldXPackIndicesBackwardsCompatibilityTestCase {
protected void checkVersion(Version version) throws Exception {
// wait for service to start
SecurityClient securityClient = new SecurityClient(client());
assertSecurityIndexActive();
// make sure usage stats are still working even with old fls format
ClearRolesCacheResponse clearResponse = new ClearRolesCacheRequestBuilder(client()).get();
assertThat(clearResponse.failures().size(), equalTo(0));
XPackUsageResponse usageResponse = new XPackUsageRequestBuilder(client()).get();
List<XPackFeatureSet.Usage> usagesList = usageResponse.getUsages();
for (XPackFeatureSet.Usage usage : usagesList) {
if (usage instanceof SecurityFeatureSet.Usage) {
XContentBuilder builder = jsonBuilder();
usage.toXContent(builder, ToXContent.EMPTY_PARAMS);
assertThat(builder.string(),
anyOf(containsString("\"roles\":{\"native\":{\"size\":1,\"fls\":true,\"dls\":true}"),
containsString("\"roles\":{\"native\":{\"size\":1,\"dls\":true,\"fls\":true}")));
}
}
// test that user and roles are there
logger.info("Getting roles...");
GetRolesResponse getRolesResponse = securityClient.prepareGetRoles("bwc_test_role").get();
assertThat(getRolesResponse.roles(), arrayWithSize(1));
RoleDescriptor role = getRolesResponse.roles()[0];
assertEquals("bwc_test_role", role.getName());
assertThat(role.getIndicesPrivileges(), arrayWithSize(1));
RoleDescriptor.IndicesPrivileges indicesPrivileges = role.getIndicesPrivileges()[0];
assertThat(indicesPrivileges.getIndices(), arrayWithSize(2));
assertArrayEquals(new String[] { "index1", "index2" }, indicesPrivileges.getIndices());
final FieldPermissions fieldPermissions = new FieldPermissions(
new FieldPermissionsDefinition(indicesPrivileges.getGrantedFields(), indicesPrivileges.getDeniedFields()));
assertTrue(fieldPermissions.grantsAccessTo("title"));
assertTrue(fieldPermissions.grantsAccessTo("body"));
assertArrayEquals(new String[] { "all" }, indicesPrivileges.getPrivileges());
assertEquals("{\"match\": {\"title\": \"foo\"}}", indicesPrivileges.getQuery().iterator().next().utf8ToString());
assertArrayEquals(new String[] { "all" }, role.getClusterPrivileges());
assertArrayEquals(new String[] { "other_user" }, role.getRunAs());
assertEquals("bwc_test_role", role.getName());
// check x-content is rendered in new format although it comes from an old index
XContentBuilder builder = jsonBuilder();
indicesPrivileges.toXContent(builder, null);
assertThat(builder.string(), containsString("\"field_security\":{\"grant\":[\"title\",\"body\"]}"));
logger.info("Getting users...");
assertSecurityIndexActive();
GetUsersResponse getUsersResponse = securityClient.prepareGetUsers("bwc_test_user").get();
assertThat(getUsersResponse.users(), arrayWithSize(1));
User user = getUsersResponse.users()[0];
assertArrayEquals(new String[] { "bwc_test_role" }, user.roles());
assertEquals("bwc_test_user", user.principal());
// check that documents are there
assertHitCount(client().prepareSearch("index1", "index2", "index3").get(), 5);
/* check that a search that misses all documents doesn't hit any alias starting with `-`. We have one in the backwards compatibility
* indices for versions before 5.1.0 because we can't create them any more. */
if (version.before(Version.V_5_1_1)) {
GetAliasesResponse aliasesResponse = client().admin().indices().prepareGetAliases().get();
List<AliasMetaData> aliases = aliasesResponse.getAliases().get("index3");
assertThat("alias doesn't exist", aliases, hasSize(1));
assertEquals("-index3", aliases.get(0).getAlias());
SearchResponse searchResponse = client().prepareSearch("does_not_exist_*")
.setIndicesOptions(IndicesOptions.fromOptions(randomBoolean(), true, true, randomBoolean())).get();
assertNoSearchHits(searchResponse);
}
Client bwcTestUserClient = client().filterWithHeader(
singletonMap(UsernamePasswordToken.BASIC_AUTH_HEADER, basicAuthHeaderValue("bwc_test_user", "9876543210")));
// check that index permissions work as expected
SearchResponse searchResponse = bwcTestUserClient.prepareSearch("index1", "index2").get();
assertEquals(2, searchResponse.getHits().getTotalHits());
assertEquals("foo", searchResponse.getHits().getHits()[0].getSourceAsMap().get("title"));
assertEquals("bwc_test_user should be able to see this field", searchResponse.getHits().getHits()[0].getSourceAsMap().get("body"));
assertNull(searchResponse.getHits().getHits()[0].getSourceAsMap().get("secured_body"));
assertEquals("foo", searchResponse.getHits().getHits()[1].getSourceAsMap().get("title"));
assertEquals("bwc_test_user should be able to see this field", searchResponse.getHits().getHits()[1].getSourceAsMap().get("body"));
assertNull(searchResponse.getHits().getHits()[1].getSourceAsMap().get("secured_body"));
Exception e = expectThrows(ElasticsearchSecurityException.class, () -> bwcTestUserClient.prepareSearch("index3").get());
assertEquals("action [indices:data/read/search] is unauthorized for user [bwc_test_user]", e.getMessage());
// try adding a user
PutRoleResponse roleResponse = securityClient.preparePutRole("test_role").addIndices(
new String[] { "index3" },
new String[] { "all" },
new String[] { "title", "body" },
null,
new BytesArray("{\"term\": {\"title\":\"not\"}}")).cluster("all")
.get();
assertTrue(roleResponse.isCreated());
PutUserResponse userResponse = securityClient.preparePutUser("another_bwc_test_user", "123123".toCharArray(), "test_role")
.email("a@b.c").get();
assertTrue(userResponse.created());
searchResponse = client().filterWithHeader(
Collections.singletonMap(UsernamePasswordToken.BASIC_AUTH_HEADER,
basicAuthHeaderValue("another_bwc_test_user", "123123")
)).prepareSearch("index3").get();
assertEquals(1, searchResponse.getHits().getTotalHits());
assertEquals("bwc_test_user should not see this index", searchResponse.getHits().getHits()[0].getSourceAsMap().get("title"));
userResponse = securityClient.preparePutUser("meta_bwc_test_user", "123123".toCharArray(), "test_role").email("a@b.c")
.metadata(singletonMap("test", 1)).get();
assertTrue(userResponse.created());
getUsersResponse = securityClient.prepareGetUsers("meta_bwc_test_user").get();
assertThat(getUsersResponse.users(), arrayWithSize(1));
user = getUsersResponse.users()[0];
assertArrayEquals(new String[] { "test_role" }, user.roles());
assertEquals("meta_bwc_test_user", user.principal());
}
}

View File

@ -6,41 +6,24 @@
package org.elasticsearch.license;
import org.apache.http.message.BasicHeader;
import org.elasticsearch.ElasticsearchParseException;
import org.elasticsearch.ElasticsearchSecurityException;
import org.elasticsearch.Version;
import org.elasticsearch.action.DocWriteResponse;
import org.elasticsearch.action.admin.cluster.health.ClusterHealthResponse;
import org.elasticsearch.action.admin.cluster.node.stats.NodesStatsResponse;
import org.elasticsearch.action.admin.cluster.stats.ClusterStatsIndices;
import org.elasticsearch.action.admin.cluster.stats.ClusterStatsResponse;
import org.elasticsearch.action.admin.indices.create.CreateIndexRequest;
import org.elasticsearch.action.admin.indices.create.CreateIndexResponse;
import org.elasticsearch.action.admin.indices.mapping.get.GetMappingsRequest;
import org.elasticsearch.action.admin.indices.mapping.get.GetMappingsResponse;
import org.elasticsearch.action.admin.indices.mapping.put.PutMappingRequest;
import org.elasticsearch.action.admin.indices.mapping.put.PutMappingResponse;
import org.elasticsearch.action.admin.indices.stats.IndicesStatsResponse;
import org.elasticsearch.action.admin.indices.template.put.PutIndexTemplateRequest;
import org.elasticsearch.action.admin.indices.template.put.PutIndexTemplateResponse;
import org.elasticsearch.action.get.GetRequest;
import org.elasticsearch.action.get.GetResponse;
import org.elasticsearch.action.index.IndexResponse;
import org.elasticsearch.client.Client;
import org.elasticsearch.client.Response;
import org.elasticsearch.client.ResponseException;
import org.elasticsearch.client.transport.NoNodeAvailableException;
import org.elasticsearch.client.transport.TransportClient;
import org.elasticsearch.cluster.metadata.MappingMetaData;
import org.elasticsearch.cluster.routing.ShardRoutingState;
import org.elasticsearch.common.bytes.BytesArray;
import org.elasticsearch.common.network.NetworkModule;
import org.elasticsearch.common.settings.SecureString;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.util.concurrent.ThreadContext;
import org.elasticsearch.common.xcontent.XContentHelper;
import org.elasticsearch.common.xcontent.XContentType;
import org.elasticsearch.common.xcontent.json.JsonXContent;
import org.elasticsearch.plugins.Plugin;
import org.elasticsearch.rest.RestStatus;
import org.elasticsearch.test.SecurityIntegTestCase;
@ -51,26 +34,17 @@ import org.elasticsearch.transport.Transport;
import org.elasticsearch.xpack.TestXPackTransportClient;
import org.elasticsearch.xpack.XPackPlugin;
import org.elasticsearch.xpack.security.Security;
import org.elasticsearch.xpack.security.SecurityLifecycleService;
import org.elasticsearch.xpack.security.action.user.GetUsersResponse;
import org.elasticsearch.xpack.security.authc.support.UsernamePasswordToken;
import org.elasticsearch.xpack.security.client.SecurityClient;
import org.elasticsearch.xpack.security.support.IndexLifecycleManager;
import org.elasticsearch.xpack.template.TemplateUtils;
import org.junit.Before;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.regex.Pattern;
import static org.elasticsearch.common.xcontent.XContentFactory.jsonBuilder;
import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertNoFailures;
import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.greaterThanOrEqualTo;
import static org.hamcrest.Matchers.hasItem;
import static org.hamcrest.Matchers.is;
@ -268,62 +242,6 @@ public class LicensingTests extends SecurityIntegTestCase {
}
}
public void testNativeRealmMigratorWorksUnderBasicLicense() throws Exception {
final String internalSecurityIndex = IndexLifecycleManager.INTERNAL_SECURITY_INDEX;
final String aliasedIndex = SecurityLifecycleService.SECURITY_INDEX_NAME;
final String reservedUserType = "doc";
final String securityVersionField = "security-version";
final String oldVersionThatRequiresMigration = Version.V_5_0_2.toString();
final String expectedVersionAfterMigration = Version.CURRENT.toString();
final Client client = internalCluster().transportClient();
final String template = TemplateUtils.loadTemplate("/" + SecurityLifecycleService.SECURITY_TEMPLATE_NAME + ".json",
oldVersionThatRequiresMigration, Pattern.quote("${security.template.version}"));
PutIndexTemplateRequest putTemplateRequest = client.admin().indices()
.preparePutTemplate(SecurityLifecycleService.SECURITY_TEMPLATE_NAME)
.setSource(new BytesArray(template.getBytes(StandardCharsets.UTF_8)), XContentType.JSON)
.request();
final PutIndexTemplateResponse putTemplateResponse = client.admin().indices().putTemplate(putTemplateRequest).actionGet();
assertThat(putTemplateResponse.isAcknowledged(), equalTo(true));
final CreateIndexRequest createIndexRequest = client.admin().indices().prepareCreate(internalSecurityIndex).request();
final CreateIndexResponse createIndexResponse = client.admin().indices().create(createIndexRequest).actionGet();
assertThat(createIndexResponse.isAcknowledged(), equalTo(true));
final Map<String, Object> templateMap = XContentHelper.convertToMap(JsonXContent.jsonXContent, template, false);
final Map<String, Object> mappings = (Map<String, Object>) templateMap.get("mappings");
final Map<String, Object> reservedUserMapping = (Map<String, Object>) mappings.get(reservedUserType);
final PutMappingRequest putMappingRequest = client.admin().indices()
.preparePutMapping(internalSecurityIndex).setSource(reservedUserMapping).setType(reservedUserType).request();
final PutMappingResponse putMappingResponse = client.admin().indices().putMapping(putMappingRequest).actionGet();
assertThat(putMappingResponse.isAcknowledged(), equalTo(true));
final GetMappingsRequest getMappingsRequest = client.admin().indices().prepareGetMappings(internalSecurityIndex).request();
logger.info("Waiting for '{}' in mapping meta-data of index '{}' to equal '{}'",
securityVersionField, aliasedIndex, expectedVersionAfterMigration);
final boolean upgradeOk = awaitBusy(() -> {
final GetMappingsResponse getMappingsResponse = client.admin().indices().getMappings(getMappingsRequest).actionGet();
final MappingMetaData metaData = getMappingsResponse.mappings().get(internalSecurityIndex).get(reservedUserType);
try {
Map<String, Object> meta = (Map<String, Object>) metaData.sourceAsMap().get("_meta");
return meta != null && expectedVersionAfterMigration.equals(meta.get(securityVersionField));
} catch (ElasticsearchParseException e) {
return false;
}
}, 3, TimeUnit.SECONDS);
assertThat("Update of " + securityVersionField + " did not happen within allowed time limit", upgradeOk, equalTo(true));
logger.info("Update of {}/{} complete, checking that logstash_system user exists", aliasedIndex, securityVersionField);
final String logstashUser = "reserved-user-logstash_system";
final GetRequest getRequest = client.prepareGet(aliasedIndex, reservedUserType, logstashUser).setRefresh(true).request();
final GetResponse getResponse = client.get(getRequest).actionGet();
assertThat(getResponse.isExists(), equalTo(true));
assertThat(getResponse.getFields(), equalTo(Collections.emptyMap()));
}
private static void assertElasticsearchSecurityException(ThrowingRunnable runnable) {
ElasticsearchSecurityException ee = expectThrows(ElasticsearchSecurityException.class, runnable);
assertThat(ee.getMetadata(LicenseUtils.EXPIRED_FEATURE_METADATA), hasItem(XPackPlugin.SECURITY));

View File

@ -7,6 +7,7 @@ package org.elasticsearch.xpack.security;
import org.apache.http.HttpEntity;
import org.apache.http.util.EntityUtils;
import org.elasticsearch.Version;
import org.elasticsearch.client.Response;
import org.elasticsearch.test.rest.yaml.ClientYamlTestCandidate;
import org.elasticsearch.test.rest.yaml.ESClientYamlSuiteTestCase;
@ -74,7 +75,9 @@ public abstract class SecurityClusterClientYamlTestCase extends ESClientYamlSuit
for (String key : mappings.keySet()) {
String templatePath = mappingsPath + "." + key + "._meta.security-version";
String templateVersion = objectPath.evaluate(templatePath);
assertEquals(masterTemplateVersion, templateVersion);
final Version mVersion = Version.fromString(masterTemplateVersion);
final Version tVersion = Version.fromString(templateVersion);
assertTrue(mVersion.onOrBefore(tVersion));
}
} catch (Exception e) {
throw new AssertionError("failed to get cluster state", e);

View File

@ -6,22 +6,16 @@
package org.elasticsearch.xpack.security;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.atomic.AtomicReference;
import org.elasticsearch.ElasticsearchException;
import org.elasticsearch.Version;
import org.elasticsearch.action.Action;
import org.elasticsearch.action.ActionListener;
import org.elasticsearch.action.ActionRequest;
import org.elasticsearch.action.ActionRequestBuilder;
import org.elasticsearch.action.ActionResponse;
import org.elasticsearch.action.admin.indices.mapping.put.PutMappingResponse;
import org.elasticsearch.action.admin.indices.template.put.PutIndexTemplateRequest;
import org.elasticsearch.action.admin.indices.template.put.PutIndexTemplateResponse;
import org.elasticsearch.client.Client;
import org.elasticsearch.client.transport.TransportClient;
import org.elasticsearch.cluster.ClusterChangedEvent;
@ -42,7 +36,6 @@ import org.elasticsearch.transport.MockTransportClient;
import org.elasticsearch.xpack.security.audit.index.IndexAuditTrail;
import org.elasticsearch.xpack.security.authc.esnative.NativeRealmMigrator;
import org.elasticsearch.xpack.security.support.IndexLifecycleManager;
import org.elasticsearch.xpack.security.support.IndexLifecycleManager.UpgradeState;
import org.elasticsearch.xpack.security.test.SecurityTestUtils;
import org.elasticsearch.xpack.template.TemplateUtils;
import org.junit.After;
@ -51,11 +44,8 @@ import org.mockito.Mockito;
import static org.elasticsearch.xpack.security.SecurityLifecycleService.SECURITY_INDEX_NAME;
import static org.elasticsearch.xpack.security.SecurityLifecycleService.SECURITY_TEMPLATE_NAME;
import static org.elasticsearch.xpack.security.SecurityLifecycleService.securityIndexMappingAndTemplateSufficientToRead;
import static org.elasticsearch.xpack.security.SecurityLifecycleService.securityIndexMappingAndTemplateUpToDate;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.iterableWithSize;
import static org.hamcrest.Matchers.notNullValue;
import static org.hamcrest.Matchers.nullValue;
import static org.mockito.Matchers.any;
import static org.mockito.Mockito.mock;
@ -64,7 +54,6 @@ import static org.mockito.Mockito.when;
public class SecurityLifecycleServiceTests extends ESTestCase {
private TransportClient transportClient;
private ThreadPool threadPool;
private NativeRealmMigrator nativeRealmMigrator;
private SecurityLifecycleService securityLifecycleService;
private static final ClusterState EMPTY_CLUSTER_STATE =
new ClusterState.Builder(new ClusterName("test-cluster")).build();
@ -94,7 +83,7 @@ public class SecurityLifecycleServiceTests extends ESTestCase {
}
}
nativeRealmMigrator = mock(NativeRealmMigrator.class);
NativeRealmMigrator nativeRealmMigrator = mock(NativeRealmMigrator.class);
Mockito.doAnswer(invocation -> {
ActionListener<Boolean> listener = (ActionListener) invocation.getArguments()[1];
listener.onResponse(false);
@ -126,12 +115,6 @@ public class SecurityLifecycleServiceTests extends ESTestCase {
assertThat(listeners.size(), equalTo(0));
}
public void testFaultyIndexTemplateIsIdentifiedAsNotUpToDate() throws IOException {
String templateString = "/wrong-version-" + SECURITY_TEMPLATE_NAME + ".json";
ClusterState.Builder clusterStateBuilder = createClusterStateWithTemplate(templateString);
checkTemplateUpdateWorkCorrectly(clusterStateBuilder);
}
public void testIndexTemplateVersionMatching() throws Exception {
String templateString = "/" + SECURITY_TEMPLATE_NAME + ".json";
ClusterState.Builder clusterStateBuilder = createClusterStateWithTemplate(templateString);
@ -145,155 +128,6 @@ public class SecurityLifecycleServiceTests extends ESTestCase {
Version.V_5_0_0::after));
}
private void checkTemplateUpdateWorkCorrectly(ClusterState.Builder clusterStateBuilder)
throws IOException {
final int numberOfSecurityIndices = 1; // .security
final ClusterState clusterState = clusterStateBuilder.build();
securityLifecycleService.clusterChanged(new ClusterChangedEvent("test-event",
clusterState, EMPTY_CLUSTER_STATE));
assertThat(securityLifecycleService.securityIndex().isTemplateUpToDate(), equalTo(false));
assertThat(listeners.size(), equalTo(numberOfSecurityIndices));
assertTrue(securityLifecycleService.securityIndex().isTemplateCreationPending());
// if we do it again this should not send an update
ActionListener listener = listeners.get(0);
listeners.clear();
securityLifecycleService.clusterChanged(new ClusterChangedEvent("test-event",
clusterState, EMPTY_CLUSTER_STATE));
assertThat(securityLifecycleService.securityIndex().isTemplateUpToDate(), equalTo(false));
assertThat(listeners.size(), equalTo(0));
assertTrue(securityLifecycleService.securityIndex().isTemplateCreationPending());
// if we now simulate an error...
listener.onFailure(new Exception());
assertThat(securityLifecycleService.securityIndex().isTemplateUpToDate(), equalTo(false));
assertFalse(securityLifecycleService.securityIndex().isTemplateCreationPending());
// ... we should be able to send a new update
securityLifecycleService.clusterChanged(new ClusterChangedEvent("test-event",
clusterState, EMPTY_CLUSTER_STATE));
assertThat(securityLifecycleService.securityIndex().isTemplateUpToDate(), equalTo(false));
assertThat(listeners.size(), equalTo(1));
assertTrue(securityLifecycleService.securityIndex().isTemplateCreationPending());
// now check what happens if we get back an unacknowledged response
expectThrows(ElasticsearchException.class,
() -> listeners.get(0).onResponse(new TestPutIndexTemplateResponse())
);
assertThat(securityLifecycleService.securityIndex().isTemplateUpToDate(), equalTo(false));
assertFalse(securityLifecycleService.securityIndex().isTemplateCreationPending());
// and now let's see what happens if we get back a response
listeners.clear();
securityLifecycleService.clusterChanged(new ClusterChangedEvent("test-event",
clusterState, EMPTY_CLUSTER_STATE));
assertThat(securityLifecycleService.securityIndex().isTemplateUpToDate(), equalTo(false));
assertTrue(securityLifecycleService.securityIndex().isTemplateCreationPending());
assertThat(listeners.size(), equalTo(1));
listeners.get(0).onResponse(new TestPutIndexTemplateResponse(true));
assertThat(securityLifecycleService.securityIndex().isTemplateUpToDate(), equalTo(true));
assertFalse(securityLifecycleService.securityIndex().isTemplateCreationPending());
}
public void testMissingIndexTemplateIsIdentifiedAsMissing() throws IOException {
// add the correct mapping
String mappingString = "/" + SECURITY_TEMPLATE_NAME + ".json";
ClusterState.Builder clusterStateBuilder = createClusterStateWithMapping(mappingString);
checkTemplateUpdateWorkCorrectly(clusterStateBuilder);
}
public void testMissingVersionIndexTemplateIsIdentifiedAsNotUpToDate() throws IOException {
String templateString = "/missing-version-" + SECURITY_TEMPLATE_NAME + ".json";
ClusterState.Builder clusterStateBuilder = createClusterStateWithTemplate(templateString);
checkTemplateUpdateWorkCorrectly(clusterStateBuilder);
}
public void testOutdatedMappingIsIdentifiedAsNotUpToDate() throws IOException {
String templateString = "/wrong-version-" + SECURITY_TEMPLATE_NAME + ".json";
final Version wrongVersion = Version.fromString("4.0.0");
ClusterState.Builder clusterStateBuilder = createClusterStateWithMappingAndTemplate(templateString);
final ClusterState clusterState = clusterStateBuilder.build();
assertFalse(securityIndexMappingAndTemplateUpToDate(clusterState, logger));
assertFalse(securityIndexMappingAndTemplateSufficientToRead(clusterState, logger));
checkMappingUpdateWorkCorrectly(clusterStateBuilder, wrongVersion);
}
private void checkMappingUpdateWorkCorrectly(ClusterState.Builder clusterStateBuilder, Version expectedOldVersion) {
final int totalNumberOfTypes = 1;
AtomicReference<Version> migratorVersionRef = new AtomicReference<>(null);
AtomicReference<ActionListener<Boolean>> migratorListenerRef = new AtomicReference<>(null);
Mockito.doAnswer(invocation -> {
migratorVersionRef.set((Version) invocation.getArguments()[0]);
migratorListenerRef.set((ActionListener<Boolean>) invocation.getArguments()[1]);
return null;
}).when(nativeRealmMigrator).performUpgrade(any(Version.class), any(ActionListener.class));
final IndexLifecycleManager securityIndex = securityLifecycleService.securityIndex();
assertThat(securityIndex.getMigrationState(), equalTo(UpgradeState.NOT_STARTED));
securityLifecycleService.clusterChanged(new ClusterChangedEvent("test-event",
clusterStateBuilder.build(), EMPTY_CLUSTER_STATE));
assertThat(migratorVersionRef.get(), equalTo(expectedOldVersion));
assertThat(migratorListenerRef.get(), notNullValue());
// security migrator has not responded yet
assertThat(this.listeners.size(), equalTo(0));
assertThat(securityIndex.isMappingUpdatePending(), equalTo(false));
assertThat(securityIndex.getMigrationState(), equalTo(UpgradeState.IN_PROGRESS));
migratorListenerRef.get().onResponse(true);
assertThat(this.listeners, iterableWithSize(totalNumberOfTypes));
assertThat(securityIndex.isMappingUpdatePending(), equalTo(true));
assertThat(securityIndex.getMigrationState(), equalTo(UpgradeState.COMPLETE));
// if we do it again this should not send an update
List<ActionListener> cloneListeners = new ArrayList<>(this.listeners);
this.listeners.clear();
securityLifecycleService.clusterChanged(new ClusterChangedEvent("test-event",
clusterStateBuilder.build(), EMPTY_CLUSTER_STATE));
assertThat(this.listeners.size(), equalTo(0));
assertThat(securityIndex.isMappingUpdatePending(), equalTo(true));
// if we now simulate an error...
cloneListeners.forEach(l -> l.onFailure(new Exception("Testing failure handling")));
assertThat(securityIndex.isMappingUpdatePending(), equalTo(false));
// ... we should be able to send a new update
securityLifecycleService.clusterChanged(new ClusterChangedEvent("test-event",
clusterStateBuilder.build(), EMPTY_CLUSTER_STATE));
assertThat(this.listeners.size(), equalTo(totalNumberOfTypes));
assertThat(securityIndex.isMappingUpdatePending(), equalTo(true));
// now check what happens if we get back an unacknowledged response
try {
this.listeners.get(0).onResponse(new TestPutMappingResponse());
fail("this should have failed because request was not acknowledged");
} catch (ElasticsearchException e) {
}
assertThat(securityIndex.isMappingUpdatePending(), equalTo(false));
// and now check what happens if we get back an acknowledged response
this.listeners.clear();
securityLifecycleService.clusterChanged(new ClusterChangedEvent("test-event",
clusterStateBuilder.build(), EMPTY_CLUSTER_STATE));
assertThat(this.listeners.size(), equalTo(totalNumberOfTypes));
int counter = 0;
for (ActionListener actionListener : this.listeners) {
actionListener.onResponse(new TestPutMappingResponse(true));
if (++counter < totalNumberOfTypes) {
assertThat(securityIndex.isMappingUpdatePending(), equalTo(true));
} else {
assertThat(securityIndex.isMappingUpdatePending(), equalTo(false));
}
}
}
public void testUpToDateMappingsAreIdentifiedAsUpToDate() throws IOException {
String securityTemplateString = "/" + SECURITY_TEMPLATE_NAME + ".json";
ClusterState.Builder clusterStateBuilder = createClusterStateWithMappingAndTemplate(securityTemplateString);
@ -424,24 +258,4 @@ public class SecurityLifecycleServiceTests extends ESTestCase {
state.metaData(MetaData.builder().generateClusterUuidIfNeeded());
return state.build();
}
private static class TestPutMappingResponse extends PutMappingResponse {
TestPutMappingResponse(boolean acknowledged) {
super(acknowledged);
}
TestPutMappingResponse() {
super();
}
}
private static class TestPutIndexTemplateResponse extends PutIndexTemplateResponse {
TestPutIndexTemplateResponse(boolean acknowledged) {
super(acknowledged);
}
TestPutIndexTemplateResponse() {
super();
}
}
}

View File

@ -149,7 +149,7 @@ public class SecuritySettingsTests extends ESTestCase {
assertThat(e.getMessage(), not(containsString(IndexAuditTrail.INDEX_NAME_PREFIX)));
}
Security.validateAutoCreateIndex(Settings.builder().put("action.auto_create_index", ".security").build());
Security.validateAutoCreateIndex(Settings.builder().put("action.auto_create_index", ".security,.security-6").build());
Security.validateAutoCreateIndex(Settings.builder().put("action.auto_create_index", ".security*").build());
Security.validateAutoCreateIndex(Settings.builder().put("action.auto_create_index", "*s*").build());
Security.validateAutoCreateIndex(Settings.builder().put("action.auto_create_index", ".s*").build());
@ -170,13 +170,13 @@ public class SecuritySettingsTests extends ESTestCase {
}
Security.validateAutoCreateIndex(Settings.builder()
.put("action.auto_create_index", ".security")
.put("action.auto_create_index", ".security,.security-6")
.put(XPackSettings.AUDIT_ENABLED.getKey(), true)
.build());
try {
Security.validateAutoCreateIndex(Settings.builder()
.put("action.auto_create_index", ".security")
.put("action.auto_create_index", ".security,.security-6")
.put(XPackSettings.AUDIT_ENABLED.getKey(), true)
.put(Security.AUDIT_OUTPUTS_SETTING.getKey(), randomFrom("index", "logfile,index"))
.build());
@ -187,7 +187,7 @@ public class SecuritySettingsTests extends ESTestCase {
}
Security.validateAutoCreateIndex(Settings.builder()
.put("action.auto_create_index", ".security_audit_log*,.security")
.put("action.auto_create_index", ".security_audit_log*,.security,.security-6")
.put(XPackSettings.AUDIT_ENABLED.getKey(), true)
.put(Security.AUDIT_OUTPUTS_SETTING.getKey(), randomFrom("index", "logfile,index"))
.build());

View File

@ -6,7 +6,6 @@
package org.elasticsearch.xpack.security.support;
import java.io.IOException;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;
@ -23,10 +22,8 @@ import org.elasticsearch.action.ActionRequestBuilder;
import org.elasticsearch.action.ActionResponse;
import org.elasticsearch.action.admin.indices.mapping.put.PutMappingAction;
import org.elasticsearch.action.admin.indices.mapping.put.PutMappingRequest;
import org.elasticsearch.action.admin.indices.mapping.put.PutMappingResponse;
import org.elasticsearch.action.admin.indices.template.put.PutIndexTemplateAction;
import org.elasticsearch.action.admin.indices.template.put.PutIndexTemplateRequest;
import org.elasticsearch.action.admin.indices.template.put.PutIndexTemplateResponse;
import org.elasticsearch.client.Client;
import org.elasticsearch.cluster.ClusterChangedEvent;
import org.elasticsearch.cluster.ClusterName;
@ -45,7 +42,6 @@ import org.elasticsearch.cluster.routing.UnassignedInfo;
import org.elasticsearch.cluster.service.ClusterService;
import org.elasticsearch.common.UUIDs;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.unit.TimeValue;
import org.elasticsearch.common.util.concurrent.ThreadContext;
import org.elasticsearch.common.xcontent.XContentType;
import org.elasticsearch.index.Index;
@ -57,13 +53,11 @@ import org.elasticsearch.xpack.security.test.SecurityTestUtils;
import org.elasticsearch.xpack.template.TemplateUtils;
import org.hamcrest.Matchers;
import org.junit.Before;
import org.mockito.Mockito;
import static org.elasticsearch.cluster.routing.RecoverySource.StoreRecoverySource.EXISTING_STORE_INSTANCE;
import static org.elasticsearch.xpack.security.support.IndexLifecycleManager.NULL_MIGRATOR;
import static org.elasticsearch.xpack.security.support.IndexLifecycleManager.TEMPLATE_VERSION_PATTERN;
import static org.hamcrest.Matchers.notNullValue;
import static org.mockito.Matchers.any;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
@ -141,109 +135,6 @@ public class IndexLifecycleManagerTests extends ESTestCase {
return new ClusterChangedEvent("test-event", clusterStateBuilder.build(), EMPTY_CLUSTER_STATE);
}
public void testIndexLifecycleWithOldMappingVersion() throws IOException {
assertInitialState();
AtomicReference<ActionListener<Boolean>> migrationListenerRef = new AtomicReference<>(null);
migrator = (version, listener) -> migrationListenerRef.set(listener);
ClusterState.Builder clusterStateBuilder = createClusterState(INDEX_NAME, TEMPLATE_NAME + "-v512");
markShardsAvailable(clusterStateBuilder);
manager.clusterChanged(event(clusterStateBuilder));
assertTemplateAndMappingOutOfDate(true, false, IndexLifecycleManager.UpgradeState.IN_PROGRESS);
actions.get(PutIndexTemplateAction.INSTANCE).values().forEach(
l -> ((ActionListener<PutIndexTemplateResponse>) l).onResponse(new PutIndexTemplateResponse(true) {
})
);
assertTemplateAndMappingOutOfDate(false, false, IndexLifecycleManager.UpgradeState.IN_PROGRESS);
migrationListenerRef.get().onResponse(true);
assertTemplateAndMappingOutOfDate(false, true, IndexLifecycleManager.UpgradeState.COMPLETE);
actions.get(PutMappingAction.INSTANCE).values().forEach(
l -> ((ActionListener<PutMappingResponse>) l).onResponse(new PutMappingResponse(true) {
})
);
assertTemplateAndMappingOutOfDate(false, false, IndexLifecycleManager.UpgradeState.COMPLETE);
clusterStateBuilder = createClusterState(INDEX_NAME, TEMPLATE_NAME);
markShardsAvailable(clusterStateBuilder);
manager.clusterChanged(event(clusterStateBuilder));
assertCompleteState(true);
}
public void testRetryDataMigration() throws IOException {
assertInitialState();
AtomicReference<ActionListener<Boolean>> migrationListenerRef = new AtomicReference<>(null);
migrator = (version, listener) -> migrationListenerRef.set(listener);
ClusterState.Builder clusterStateBuilder = createClusterState(INDEX_NAME, TEMPLATE_NAME + "-v512");
markShardsAvailable(clusterStateBuilder);
manager.clusterChanged(event(clusterStateBuilder));
assertTemplateAndMappingOutOfDate(true, false, IndexLifecycleManager.UpgradeState.IN_PROGRESS);
actions.get(PutIndexTemplateAction.INSTANCE).values().forEach(
l -> ((ActionListener<PutIndexTemplateResponse>) l).onResponse(new PutIndexTemplateResponse(true) {
})
);
actions.get(PutIndexTemplateAction.INSTANCE).clear();
clusterStateBuilder = createClusterState(INDEX_NAME, TEMPLATE_NAME, TEMPLATE_NAME + "-v512");
markShardsAvailable(clusterStateBuilder);
when(clusterService.state()).thenReturn(clusterStateBuilder.build());
assertTemplateAndMappingOutOfDate(false, false, IndexLifecycleManager.UpgradeState.IN_PROGRESS);
AtomicReference<Runnable> scheduled = new AtomicReference<>(null);
when(threadPool.schedule(any(TimeValue.class), Mockito.eq(ThreadPool.Names.SAME), any(Runnable.class))).thenAnswer(invocation -> {
final Runnable runnable = (Runnable) invocation.getArguments()[2];
scheduled.set(runnable);
return null;
});
migrationListenerRef.get().onFailure(new RuntimeException("Migration Failed #1"));
assertTemplateAndMappingOutOfDate(false, false, IndexLifecycleManager.UpgradeState.FAILED);
assertThat(scheduled.get(), notNullValue());
scheduled.get().run();
assertTemplateAndMappingOutOfDate(false, false, IndexLifecycleManager.UpgradeState.IN_PROGRESS);
scheduled.set(null);
migrationListenerRef.get().onFailure(new RuntimeException("Migration Failed #2"));
assertTemplateAndMappingOutOfDate(false, false, IndexLifecycleManager.UpgradeState.FAILED);
assertThat(scheduled.get(), notNullValue());
actions.getOrDefault(PutIndexTemplateAction.INSTANCE, Collections.emptyMap()).clear();
scheduled.get().run();
assertTemplateAndMappingOutOfDate(false, false, IndexLifecycleManager.UpgradeState.IN_PROGRESS);
scheduled.set(null);
migrationListenerRef.get().onResponse(false);
assertTemplateAndMappingOutOfDate(false, true, IndexLifecycleManager.UpgradeState.COMPLETE);
actions.get(PutMappingAction.INSTANCE).values().forEach(
l -> ((ActionListener<PutMappingResponse>) l).onResponse(new PutMappingResponse(true) {
})
);
assertTemplateAndMappingOutOfDate(false, false, IndexLifecycleManager.UpgradeState.COMPLETE);
clusterStateBuilder = createClusterState(INDEX_NAME, TEMPLATE_NAME);
markShardsAvailable(clusterStateBuilder);
manager.clusterChanged(event(clusterStateBuilder));
assertCompleteState(true);
}
public void testIndexHealthChangeListeners() throws Exception {
final AtomicBoolean listenerCalled = new AtomicBoolean(false);
final AtomicReference<ClusterIndexHealth> previousHealth = new AtomicReference<>();

View File

@ -36,6 +36,14 @@ public class IndexUpgradeCheckTests extends ESTestCase {
assertThat(check.actionRequired(watcherIndexWithAliasUpgraded), equalTo(UpgradeActionRequired.UP_TO_DATE));
}
public void testSecurityIndexUpgradeCheck() throws Exception{
IndexUpgradeCheck check = Upgrade.getSecurityUpgradeCheckFactory(Settings.EMPTY).apply(null, null);
assertThat(check.getName(), equalTo("security"));
IndexMetaData securityIndex = newTestIndexMeta(".security", Settings.EMPTY);
assertThat(check.actionRequired(securityIndex), equalTo(UpgradeActionRequired.UPGRADE));
}
public static IndexMetaData newTestIndexMeta(String name, String alias, Settings indexSettings) throws IOException {
Settings build = Settings.builder().put(IndexMetaData.SETTING_VERSION_CREATED, Version.CURRENT)
.put(IndexMetaData.SETTING_NUMBER_OF_REPLICAS, 1)

View File

@ -81,7 +81,7 @@ public class InternalIndexReindexerIT extends IndexUpgradeIntegTestCase {
BulkByScrollResponse response = future.actionGet();
assertThat(response.getCreated(), equalTo(2L));
SearchResponse searchResponse = client().prepareSearch("test_v123").get();
SearchResponse searchResponse = client().prepareSearch("test-123").get();
assertThat(searchResponse.getHits().getTotalHits(), equalTo(2L));
assertThat(searchResponse.getHits().getHits().length, equalTo(2));
for (SearchHit hit : searchResponse.getHits().getHits()) {
@ -92,7 +92,7 @@ public class InternalIndexReindexerIT extends IndexUpgradeIntegTestCase {
GetAliasesResponse aliasesResponse = client().admin().indices().prepareGetAliases("test").get();
assertThat(aliasesResponse.getAliases().size(), equalTo(1));
List<AliasMetaData> testAlias = aliasesResponse.getAliases().get("test_v123");
List<AliasMetaData> testAlias = aliasesResponse.getAliases().get("test-123");
assertNotNull(testAlias);
assertThat(testAlias.size(), equalTo(1));
assertThat(testAlias.get(0).alias(), equalTo("test"));
@ -100,7 +100,7 @@ public class InternalIndexReindexerIT extends IndexUpgradeIntegTestCase {
public void testTargetIndexExists() throws Exception {
createTestIndex("test");
createTestIndex("test_v123");
createTestIndex("test-123");
InternalIndexReindexer reindexer = createIndexReindexer(123, script("add_bar"), Strings.EMPTY_ARRAY);
PlainActionFuture<BulkByScrollResponse> future = PlainActionFuture.newFuture();
reindexer.upgrade(new TaskId("abc", 123), "test", clusterState(), future);
@ -113,14 +113,14 @@ public class InternalIndexReindexerIT extends IndexUpgradeIntegTestCase {
public void testTargetIndexExistsAsAlias() throws Exception {
createTestIndex("test");
createTestIndex("test-foo");
client().admin().indices().prepareAliases().addAlias("test-foo", "test_v123").get();
client().admin().indices().prepareAliases().addAlias("test-foo", "test-123").get();
InternalIndexReindexer reindexer = createIndexReindexer(123, script("add_bar"), Strings.EMPTY_ARRAY);
PlainActionFuture<BulkByScrollResponse> future = PlainActionFuture.newFuture();
reindexer.upgrade(new TaskId("abc", 123), "test", clusterState(), future);
assertThrows(future, InvalidIndexNameException.class);
// Make sure that the index is not marked as read-only
client().prepareIndex("test_v123", "doc").setSource("foo", "bar").get();
client().prepareIndex("test-123", "doc").setSource("foo", "bar").get();
}
public void testSourceIndexIsReadonly() throws Exception {

View File

@ -13,11 +13,9 @@ import org.elasticsearch.client.Response;
import org.elasticsearch.common.Booleans;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.util.concurrent.ThreadContext;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.common.xcontent.XContentHelper;
import org.elasticsearch.common.xcontent.XContentType;
import org.elasticsearch.common.xcontent.json.JsonXContent;
import org.elasticsearch.test.NotEqualMessageBuilder;
import org.elasticsearch.test.StreamsUtils;
import org.elasticsearch.test.rest.ESRestTestCase;
import org.elasticsearch.xpack.common.text.TextTemplate;
@ -45,14 +43,12 @@ import static java.util.Collections.emptyMap;
import static java.util.Collections.singletonMap;
import static org.elasticsearch.common.unit.TimeValue.timeValueSeconds;
import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.empty;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.everyItem;
import static org.hamcrest.Matchers.greaterThanOrEqualTo;
import static org.hamcrest.Matchers.hasEntry;
import static org.hamcrest.Matchers.hasItems;
import static org.hamcrest.Matchers.hasKey;
import static org.hamcrest.Matchers.hasSize;
import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.not;
import static org.hamcrest.Matchers.notNullValue;
@ -110,35 +106,24 @@ public class FullClusterRestartIT extends ESRestTestCase {
assertThat(toStr(client().performRequest("GET", docLocation)), containsString(doc));
}
// This will only work when the upgrade API is in place!
@AwaitsFix(bugUrl = "https://github.com/elastic/dev/issues/741")
public void testSecurityNativeRealm() throws IOException {
XContentBuilder userBuilder = JsonXContent.contentBuilder().startObject();
userBuilder.field("password", "j@rV1s");
userBuilder.array("roles", "admin", "other_role1");
userBuilder.field("full_name", "Jack Nicholson");
userBuilder.field("email", "jacknich@example.com");
userBuilder.startObject("metadata"); {
userBuilder.field("intelligence", 7);
}
userBuilder.endObject();
userBuilder.field("enabled", true);
String user = userBuilder.endObject().string();
public void testSecurityNativeRealm() throws Exception {
if (runningAgainstOldCluster) {
client().performRequest("PUT", "/_xpack/security/user/jacknich", emptyMap(),
new StringEntity(user, ContentType.APPLICATION_JSON));
createUser("preupgrade_user");
createRole("preupgrade_role");
} else {
// run upgrade API first
waitForYellow(".security");
client().performRequest("POST", "_xpack/migration/upgrade/.security");
// create additional user and role
createUser("postupgrade_user");
createRole("postupgrade_role");
}
Map<String, Object> response = toMap(client().performRequest("GET", "/_xpack/security/user/jacknich"));
Map<String, Object> expected = toMap(user);
expected.put("username", "jacknich");
expected.remove("password");
expected = singletonMap("jacknich", expected);
if (false == response.equals(expected)) {
NotEqualMessageBuilder message = new NotEqualMessageBuilder();
message.compareMaps(response, expected);
fail("User doesn't match.\n" + message.toString());
assertUserInfo("preupgrade_user");
assertRoleInfo("preupgrade_role");
if (!runningAgainstOldCluster) {
assertUserInfo("postupgrade_user");
assertRoleInfo("postupgrade_role");
}
}
@ -170,7 +155,7 @@ public class FullClusterRestartIT extends ESRestTestCase {
logger.info(response);
@SuppressWarnings("unchecked") Map<String, Object> indices = (Map<String, Object>) response.get("indices");
assertThat(indices.entrySet(), hasSize(1));
assertThat(indices.entrySet().size(), greaterThanOrEqualTo(1));
assertThat(indices.get(".watches"), notNullValue());
@SuppressWarnings("unchecked") Map<String, Object> index = (Map<String, Object>) indices.get(".watches");
assertThat(index.get("action_required"), equalTo("upgrade"));
@ -183,10 +168,10 @@ public class FullClusterRestartIT extends ESRestTestCase {
// we posted 3 watches, but monitoring can post a few more
assertThat((int)upgradeResponse.get("total"), greaterThanOrEqualTo(3));
logger.info("checking that upgrade procedure on the new cluster is required again");
logger.info("checking that upgrade procedure on the new cluster is no longer required");
Map<String, Object> responseAfter = toMap(client().performRequest("GET", "/_xpack/migration/assistance"));
@SuppressWarnings("unchecked") Map<String, Object> indicesAfter = (Map<String, Object>) responseAfter.get("indices");
assertThat(indicesAfter.entrySet(), empty());
assertNull(indicesAfter.get(".watches"));
// Wait for watcher to actually start....
Map<String, Object> startWatchResponse = toMap(client().performRequest("POST", "_xpack/watcher/_start"));
@ -337,4 +322,55 @@ public class FullClusterRestartIT extends ESRestTestCase {
static String toStr(Response response) throws IOException {
return EntityUtils.toString(response.getEntity());
}
private void createUser(final String id) throws Exception {
final String userJson =
"{\n" +
" \"password\" : \"j@rV1s\",\n" +
" \"roles\" : [ \"admin\", \"other_role1\" ],\n" +
" \"full_name\" : \"" + randomAlphaOfLength(5) + "\",\n" +
" \"email\" : \"" + id + "@example.com\",\n" +
" \"enabled\": true\n" +
"}";
client().performRequest("PUT", "/_xpack/security/user/" + id, emptyMap(),
new StringEntity(userJson, ContentType.APPLICATION_JSON));
}
private void createRole(final String id) throws Exception {
final String roleJson =
"{\n" +
" \"run_as\": [ \"abc\" ],\n" +
" \"cluster\": [ \"monitor\" ],\n" +
" \"indices\": [\n" +
" {\n" +
" \"names\": [ \"events-*\" ],\n" +
" \"privileges\": [ \"read\" ],\n" +
" \"field_security\" : {\n" +
" \"grant\" : [ \"category\", \"@timestamp\", \"message\" ]\n" +
" },\n" +
" \"query\": \"{\\\"match\\\": {\\\"category\\\": \\\"click\\\"}}\"\n" +
" }\n" +
" ]\n" +
"}";
client().performRequest("PUT", "/_xpack/security/role/" + id, emptyMap(),
new StringEntity(roleJson, ContentType.APPLICATION_JSON));
}
private void assertUserInfo(final String user) throws Exception {
Map<String, Object> response = toMap(client().performRequest("GET", "/_xpack/security/user/" + user));
@SuppressWarnings("unchecked") Map<String, Object> userInfo = (Map<String, Object>) response.get(user);
assertEquals(user + "@example.com", userInfo.get("email"));
assertNotNull(userInfo.get("full_name"));
assertNotNull(userInfo.get("roles"));
}
private void assertRoleInfo(final String role) throws Exception {
@SuppressWarnings("unchecked") Map<String, Object> response = (Map<String, Object>)
toMap(client().performRequest("GET", "/_xpack/security/role/" + role)).get(role);
assertNotNull(response.get("run_as"));
assertNotNull(response.get("cluster"));
assertNotNull(response.get("indices"));
}
}

View File

@ -8,7 +8,6 @@ package org.elasticsearch.upgrades;
import com.carrotsearch.randomizedtesting.annotations.ParametersFactory;
import com.carrotsearch.randomizedtesting.annotations.TimeoutSuite;
import org.apache.lucene.util.LuceneTestCase.AwaitsFix;
import org.apache.lucene.util.TimeUnits;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.util.concurrent.ThreadContext;
@ -22,8 +21,6 @@ import org.junit.Before;
import java.nio.charset.StandardCharsets;
import java.util.Base64;
// This will only work, when the upgrade API is in place!
@AwaitsFix(bugUrl = "https://github.com/elastic/dev/issues/741")
@TimeoutSuite(millis = 5 * TimeUnits.MINUTE) // to account for slow as hell VMs
public class UpgradeClusterClientYamlTestSuiteIT extends SecurityClusterClientYamlTestCase {

View File

@ -100,7 +100,9 @@ public class WatchBackwardsCompatibilityIT extends ESRestTestCase {
for (String key : mappings.keySet()) {
String templateVersion = objectPath.evaluate(mappingsPath + "." + key + "" +
"._meta.security-version");
assertEquals(masterTemplateVersion, templateVersion);
final Version mVersion = Version.fromString(masterTemplateVersion);
final Version tVersion = Version.fromString(templateVersion);
assertTrue(mVersion.onOrBefore(tVersion));
}
} catch (Exception e) {
throw new AssertionError("failed to get cluster state", e);

View File

@ -1,5 +1,6 @@
---
"Create user and role":
"Verify native store security actions":
# create native user and role
- do:
xpack.security.put_user:
username: "native_user"
@ -45,11 +46,6 @@
index: ".security"
wait_for_active_shards: 2 # 1 primary and 1 replica since we have two nodes
---
"default password migration":
- skip:
version: " - 5.1.1"
reason: "the rest enabled action trips an assertion. see https://github.com/elastic/x-pack/pull/4443"
# Check that enabling a user in old cluster will not prevent the user from having a "default password" in the new cluster.
# See: org.elasticsearch.xpack.security.authc.esnative.NativeRealmMigrator.doConvertDefaultPasswords
- do:
@ -78,3 +74,10 @@
username: "logstash_system"
- match: { logstash_system.enabled: true }
# run the upgrade API once all the old cluster actions are done,
# prior to moving to the mixed cluster
- do:
headers:
Authorization: "Basic ZWxhc3RpYzpjaGFuZ2VtZQ==" # corresponds to elastic:changeme
xpack.migration.upgrade:
index: ".security"

View File

@ -7,16 +7,8 @@ package org.elasticsearch.test;
import com.carrotsearch.hppc.cursors.ObjectCursor;
import org.apache.http.HttpEntity;
import org.apache.http.HttpHost;
import org.apache.http.entity.ContentType;
import org.apache.http.message.BasicHeader;
import org.apache.http.nio.entity.NStringEntity;
import org.apache.lucene.util.LuceneTestCase;
import org.elasticsearch.action.admin.cluster.health.ClusterHealthResponse;
import org.elasticsearch.client.Response;
import org.elasticsearch.client.RestClient;
import org.elasticsearch.client.RestClientBuilder;
import org.elasticsearch.cluster.ClusterState;
import org.elasticsearch.cluster.metadata.IndexMetaData;
import org.elasticsearch.common.io.stream.NotSerializableExceptionWrapper;
@ -29,10 +21,8 @@ import org.elasticsearch.xpack.security.action.role.GetRolesResponse;
import org.elasticsearch.xpack.security.action.role.PutRoleResponse;
import org.elasticsearch.xpack.security.authc.support.UsernamePasswordToken;
import org.elasticsearch.xpack.security.client.SecurityClient;
import org.elasticsearch.xpack.security.user.ElasticUser;
import org.junit.After;
import org.junit.AfterClass;
import org.junit.Before;
import java.io.IOException;
import java.net.InetAddress;
@ -87,18 +77,16 @@ public class TribeWithSecurityIT extends SecurityIntegTestCase {
@After
public void removeSecurityIndex() {
client().admin().indices().prepareDelete(INTERNAL_SECURITY_INDEX).get();
cluster2.client().admin().indices().prepareDelete(INTERNAL_SECURITY_INDEX).get();
if (client().admin().indices().prepareExists(INTERNAL_SECURITY_INDEX).get().isExists()) {
client().admin().indices().prepareDelete(INTERNAL_SECURITY_INDEX).get();
}
if (cluster2.client().admin().indices().prepareExists(INTERNAL_SECURITY_INDEX).get().isExists()) {
cluster2.client().admin().indices().prepareDelete(INTERNAL_SECURITY_INDEX).get();
}
securityClient(client()).prepareClearRealmCache().get();
securityClient(cluster2.client()).prepareClearRealmCache().get();
}
@Before
public void addSecurityIndex() throws IOException {
client().admin().indices().prepareCreate(INTERNAL_SECURITY_INDEX).get();
cluster2.client().admin().indices().prepareCreate(INTERNAL_SECURITY_INDEX).get();
}
@Override
protected Settings externalClusterClientSettings() {
Settings.Builder builder = Settings.builder().put(super.externalClusterClientSettings());