Enables security to work with index aliases (elastic/x-pack-elasticsearch#1496)

This commit enables security to work with an index named .security (as
it could before) OR an alias named .security that points to a concrete
index by a different name that has the security index.  This prepares
the ability to migrate from a 5.x to 6.x security index that allows
changing and re-indexing the underlying security index while maintaining
a .security alias that points to the underlying updated index.

relates elastic/x-pack-elasticsearch#1216

Original commit: elastic/x-pack-elasticsearch@9fee12e5a0
This commit is contained in:
Ali Beyad 2017-05-23 09:10:06 -04:00 committed by GitHub
parent 521b7a1940
commit ec175debd0
7 changed files with 138 additions and 100 deletions

View File

@ -23,7 +23,6 @@ import org.elasticsearch.xpack.security.audit.index.IndexAuditTrail;
import org.elasticsearch.xpack.security.authc.esnative.NativeRealmMigrator; import org.elasticsearch.xpack.security.authc.esnative.NativeRealmMigrator;
import org.elasticsearch.xpack.security.support.IndexLifecycleManager; import org.elasticsearch.xpack.security.support.IndexLifecycleManager;
import java.util.Arrays;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.function.Predicate; import java.util.function.Predicate;

View File

@ -18,9 +18,11 @@ import org.elasticsearch.action.admin.indices.template.put.PutIndexTemplateReque
import org.elasticsearch.action.admin.indices.template.put.PutIndexTemplateResponse; import org.elasticsearch.action.admin.indices.template.put.PutIndexTemplateResponse;
import org.elasticsearch.cluster.ClusterChangedEvent; import org.elasticsearch.cluster.ClusterChangedEvent;
import org.elasticsearch.cluster.ClusterState; import org.elasticsearch.cluster.ClusterState;
import org.elasticsearch.cluster.metadata.AliasOrIndex;
import org.elasticsearch.cluster.metadata.IndexMetaData; import org.elasticsearch.cluster.metadata.IndexMetaData;
import org.elasticsearch.cluster.metadata.IndexTemplateMetaData; import org.elasticsearch.cluster.metadata.IndexTemplateMetaData;
import org.elasticsearch.cluster.metadata.MappingMetaData; import org.elasticsearch.cluster.metadata.MappingMetaData;
import org.elasticsearch.cluster.metadata.MetaData;
import org.elasticsearch.cluster.routing.IndexRoutingTable; import org.elasticsearch.cluster.routing.IndexRoutingTable;
import org.elasticsearch.common.bytes.BytesArray; import org.elasticsearch.common.bytes.BytesArray;
import org.elasticsearch.common.collect.ImmutableOpenMap; import org.elasticsearch.common.collect.ImmutableOpenMap;
@ -38,12 +40,14 @@ import org.elasticsearch.xpack.template.TemplateUtils;
import java.io.IOException; import java.io.IOException;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import java.util.HashSet; import java.util.HashSet;
import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicReference; import java.util.concurrent.atomic.AtomicReference;
import java.util.function.Predicate; import java.util.function.Predicate;
import java.util.regex.Pattern; import java.util.regex.Pattern;
import java.util.stream.Collectors;
import static org.elasticsearch.common.xcontent.XContentHelper.convertToMap; import static org.elasticsearch.common.xcontent.XContentHelper.convertToMap;
@ -64,8 +68,7 @@ public class IndexLifecycleManager extends AbstractComponent {
private final AtomicBoolean templateCreationPending = new AtomicBoolean(false); private final AtomicBoolean templateCreationPending = new AtomicBoolean(false);
private final AtomicBoolean updateMappingPending = new AtomicBoolean(false); private final AtomicBoolean updateMappingPending = new AtomicBoolean(false);
final AtomicReference<UpgradeState> migrateDataState = private final AtomicReference<UpgradeState> migrateDataState = new AtomicReference<>(UpgradeState.NOT_STARTED);
new AtomicReference<>(UpgradeState.NOT_STARTED);
private volatile boolean templateIsUpToDate; private volatile boolean templateIsUpToDate;
private volatile boolean indexExists; private volatile boolean indexExists;
@ -136,7 +139,7 @@ public class IndexLifecycleManager extends AbstractComponent {
public void clusterChanged(ClusterChangedEvent event) { public void clusterChanged(ClusterChangedEvent event) {
final ClusterState state = event.state(); final ClusterState state = event.state();
this.indexExists = event.state().metaData().indices().get(indexName) != null; this.indexExists = resolveConcreteIndex(indexName, event.state().metaData()) != null;
this.indexAvailable = checkIndexAvailable(state); this.indexAvailable = checkIndexAvailable(state);
this.templateIsUpToDate = checkTemplateExistsAndIsUpToDate(state); this.templateIsUpToDate = checkTemplateExistsAndIsUpToDate(state);
this.mappingIsUpToDate = checkIndexMappingUpToDate(state); this.mappingIsUpToDate = checkIndexMappingUpToDate(state);
@ -167,11 +170,11 @@ public class IndexLifecycleManager extends AbstractComponent {
* Returns the routing-table for this index, or <code>null</code> if the index does not exist. * Returns the routing-table for this index, or <code>null</code> if the index does not exist.
*/ */
private IndexRoutingTable getIndexRoutingTable(ClusterState clusterState) { private IndexRoutingTable getIndexRoutingTable(ClusterState clusterState) {
IndexMetaData metaData = clusterState.metaData().index(indexName); IndexMetaData metaData = resolveConcreteIndex(indexName, clusterState.metaData());
if (metaData == null) { if (metaData == null) {
return null; return null;
} else { } else {
return clusterState.routingTable().index(indexName); return clusterState.routingTable().index(metaData.getIndex());
} }
} }
@ -249,7 +252,7 @@ public class IndexLifecycleManager extends AbstractComponent {
private static Set<Version> loadIndexMappingVersions(String indexName, private static Set<Version> loadIndexMappingVersions(String indexName,
ClusterState clusterState, Logger logger) { ClusterState clusterState, Logger logger) {
Set<Version> versions = new HashSet<>(); Set<Version> versions = new HashSet<>();
IndexMetaData indexMetaData = clusterState.metaData().getIndices().get(indexName); IndexMetaData indexMetaData = resolveConcreteIndex(indexName, clusterState.metaData());
if (indexMetaData != null) { if (indexMetaData != null) {
for (Object object : indexMetaData.getMappings().values().toArray()) { for (Object object : indexMetaData.getMappings().values().toArray()) {
MappingMetaData mappingMetaData = (MappingMetaData) object; MappingMetaData mappingMetaData = (MappingMetaData) object;
@ -262,6 +265,23 @@ public class IndexLifecycleManager extends AbstractComponent {
return versions; return versions;
} }
/**
* Resolves a concrete index name or alias to a {@link IndexMetaData} instance. Requires
* that if supplied with an alias, the alias resolves to at most one concrete index.
*/
private static IndexMetaData resolveConcreteIndex(final String indexOrAliasName, final MetaData metaData) {
final AliasOrIndex aliasOrIndex = metaData.getAliasAndIndexLookup().get(indexOrAliasName);
if (aliasOrIndex != null) {
final List<IndexMetaData> indices = aliasOrIndex.getIndices();
if (aliasOrIndex.isAlias() && indices.size() > 1) {
throw new IllegalStateException("Alias [" + indexOrAliasName + "] points to more than one index: " +
indices.stream().map(imd -> imd.getIndex().getName()).collect(Collectors.toList()));
}
return indices.get(0);
}
return null;
}
private static Version readMappingVersion(String indexName, MappingMetaData mappingMetaData, private static Version readMappingVersion(String indexName, MappingMetaData mappingMetaData,
Logger logger) { Logger logger) {
try { try {

View File

@ -14,6 +14,8 @@ import org.elasticsearch.client.Client;
import org.elasticsearch.client.node.NodeClient; import org.elasticsearch.client.node.NodeClient;
import org.elasticsearch.cluster.ClusterState; import org.elasticsearch.cluster.ClusterState;
import org.elasticsearch.cluster.health.ClusterHealthStatus; import org.elasticsearch.cluster.health.ClusterHealthStatus;
import org.elasticsearch.cluster.metadata.AliasOrIndex;
import org.elasticsearch.cluster.metadata.MetaData;
import org.elasticsearch.cluster.routing.IndexRoutingTable; import org.elasticsearch.cluster.routing.IndexRoutingTable;
import org.elasticsearch.cluster.service.ClusterService; import org.elasticsearch.cluster.service.ClusterService;
import org.elasticsearch.common.network.NetworkAddress; import org.elasticsearch.common.network.NetworkAddress;
@ -21,6 +23,7 @@ import org.elasticsearch.common.settings.SecureString;
import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.transport.TransportAddress; import org.elasticsearch.common.transport.TransportAddress;
import org.elasticsearch.gateway.GatewayService; import org.elasticsearch.gateway.GatewayService;
import org.elasticsearch.index.Index;
import org.elasticsearch.index.IndexNotFoundException; import org.elasticsearch.index.IndexNotFoundException;
import org.elasticsearch.plugins.Plugin; import org.elasticsearch.plugins.Plugin;
import org.elasticsearch.xpack.XPackClient; import org.elasticsearch.xpack.XPackClient;
@ -442,9 +445,12 @@ public abstract class SecurityIntegTestCase extends ESIntegTestCase {
ClusterState clusterState = clusterService.state(); ClusterState clusterState = clusterService.state();
assertFalse(clusterState.blocks().hasGlobalBlock(GatewayService.STATE_NOT_RECOVERED_BLOCK)); assertFalse(clusterState.blocks().hasGlobalBlock(GatewayService.STATE_NOT_RECOVERED_BLOCK));
assertTrue(securityIndexMappingAndTemplateSufficientToRead(clusterState, logger)); assertTrue(securityIndexMappingAndTemplateSufficientToRead(clusterState, logger));
IndexRoutingTable indexRoutingTable = clusterState.routingTable().index(SecurityLifecycleService.SECURITY_INDEX_NAME); Index securityIndex = resolveSecurityIndex(clusterState.metaData());
if (indexRoutingTable != null) { if (securityIndex != null) {
assertTrue(indexRoutingTable.allPrimaryShardsActive()); IndexRoutingTable indexRoutingTable = clusterState.routingTable().index(securityIndex);
if (indexRoutingTable != null) {
assertTrue(indexRoutingTable.allPrimaryShardsActive());
}
} }
}); });
} }
@ -456,9 +462,12 @@ public abstract class SecurityIntegTestCase extends ESIntegTestCase {
ClusterState clusterState = clusterService.state(); ClusterState clusterState = clusterService.state();
assertFalse(clusterState.blocks().hasGlobalBlock(GatewayService.STATE_NOT_RECOVERED_BLOCK)); assertFalse(clusterState.blocks().hasGlobalBlock(GatewayService.STATE_NOT_RECOVERED_BLOCK));
assertTrue(securityIndexMappingAndTemplateUpToDate(clusterState, logger)); assertTrue(securityIndexMappingAndTemplateUpToDate(clusterState, logger));
IndexRoutingTable indexRoutingTable = clusterState.routingTable().index(SecurityLifecycleService.SECURITY_INDEX_NAME); Index securityIndex = resolveSecurityIndex(clusterState.metaData());
if (indexRoutingTable != null) { if (securityIndex != null) {
assertTrue(indexRoutingTable.allPrimaryShardsActive()); IndexRoutingTable indexRoutingTable = clusterState.routingTable().index(securityIndex);
if (indexRoutingTable != null) {
assertTrue(indexRoutingTable.allPrimaryShardsActive());
}
} }
}); });
} }
@ -472,4 +481,12 @@ public abstract class SecurityIntegTestCase extends ESIntegTestCase {
// ignore it since not all tests create this index... // ignore it since not all tests create this index...
} }
} }
private static Index resolveSecurityIndex(MetaData metaData) {
final AliasOrIndex aliasOrIndex = metaData.getAliasAndIndexLookup().get(SecurityLifecycleService.SECURITY_INDEX_NAME);
if (aliasOrIndex != null) {
return aliasOrIndex.getIndices().get(0).getIndex();
}
return null;
}
} }

View File

@ -32,9 +32,7 @@ import org.elasticsearch.cluster.metadata.IndexTemplateMetaData;
import org.elasticsearch.cluster.metadata.MetaData; import org.elasticsearch.cluster.metadata.MetaData;
import org.elasticsearch.cluster.node.DiscoveryNode; import org.elasticsearch.cluster.node.DiscoveryNode;
import org.elasticsearch.cluster.node.DiscoveryNodes; import org.elasticsearch.cluster.node.DiscoveryNodes;
import org.elasticsearch.cluster.routing.RoutingTable;
import org.elasticsearch.cluster.service.ClusterService; import org.elasticsearch.cluster.service.ClusterService;
import org.elasticsearch.common.collect.ImmutableOpenMap;
import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.xcontent.XContentType; import org.elasticsearch.common.xcontent.XContentType;
import org.elasticsearch.test.ESTestCase; import org.elasticsearch.test.ESTestCase;
@ -64,22 +62,19 @@ import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when; import static org.mockito.Mockito.when;
public class SecurityLifecycleServiceTests extends ESTestCase { public class SecurityLifecycleServiceTests extends ESTestCase {
private InternalClient client;
private TransportClient transportClient; private TransportClient transportClient;
private ThreadPool threadPool; private ThreadPool threadPool;
private ClusterService clusterService;
private NativeRealmMigrator nativeRealmMigrator; private NativeRealmMigrator nativeRealmMigrator;
private SecurityLifecycleService securityLifecycleService; private SecurityLifecycleService securityLifecycleService;
private static final ClusterState EMPTY_CLUSTER_STATE = private static final ClusterState EMPTY_CLUSTER_STATE =
new ClusterState.Builder(new ClusterName("test-cluster")).build(); new ClusterState.Builder(new ClusterName("test-cluster")).build();
private CopyOnWriteArrayList<ActionListener> listeners;
CopyOnWriteArrayList<ActionListener> listeners;
@Before @Before
public void setup() { public void setup() {
DiscoveryNode localNode = mock(DiscoveryNode.class); DiscoveryNode localNode = mock(DiscoveryNode.class);
when(localNode.getHostAddress()).thenReturn(buildNewFakeTransportAddress().toString()); when(localNode.getHostAddress()).thenReturn(buildNewFakeTransportAddress().toString());
clusterService = mock(ClusterService.class); ClusterService clusterService = mock(ClusterService.class);
when(clusterService.localNode()).thenReturn(localNode); when(clusterService.localNode()).thenReturn(localNode);
threadPool = new TestThreadPool("security template service tests"); threadPool = new TestThreadPool("security template service tests");
@ -106,7 +101,7 @@ public class SecurityLifecycleServiceTests extends ESTestCase {
return null; return null;
}).when(nativeRealmMigrator).performUpgrade(any(Version.class), any(ActionListener.class)); }).when(nativeRealmMigrator).performUpgrade(any(Version.class), any(ActionListener.class));
client = new IClient(transportClient); InternalClient client = new IClient(transportClient);
securityLifecycleService = new SecurityLifecycleService(Settings.EMPTY, clusterService, securityLifecycleService = new SecurityLifecycleService(Settings.EMPTY, clusterService,
threadPool, client, nativeRealmMigrator, mock(IndexAuditTrail.class)); threadPool, client, nativeRealmMigrator, mock(IndexAuditTrail.class));
listeners = new CopyOnWriteArrayList<>(); listeners = new CopyOnWriteArrayList<>();
@ -155,8 +150,9 @@ public class SecurityLifecycleServiceTests extends ESTestCase {
final int numberOfSecurityIndices = 1; // .security final int numberOfSecurityIndices = 1; // .security
final ClusterState clusterState = clusterStateBuilder.build();
securityLifecycleService.clusterChanged(new ClusterChangedEvent("test-event", securityLifecycleService.clusterChanged(new ClusterChangedEvent("test-event",
clusterStateBuilder.build(), EMPTY_CLUSTER_STATE)); clusterState, EMPTY_CLUSTER_STATE));
assertThat(securityLifecycleService.securityIndex().isTemplateUpToDate(), equalTo(false)); assertThat(securityLifecycleService.securityIndex().isTemplateUpToDate(), equalTo(false));
assertThat(listeners.size(), equalTo(numberOfSecurityIndices)); assertThat(listeners.size(), equalTo(numberOfSecurityIndices));
assertTrue(securityLifecycleService.securityIndex().isTemplateCreationPending()); assertTrue(securityLifecycleService.securityIndex().isTemplateCreationPending());
@ -165,7 +161,7 @@ public class SecurityLifecycleServiceTests extends ESTestCase {
ActionListener listener = listeners.get(0); ActionListener listener = listeners.get(0);
listeners.clear(); listeners.clear();
securityLifecycleService.clusterChanged(new ClusterChangedEvent("test-event", securityLifecycleService.clusterChanged(new ClusterChangedEvent("test-event",
clusterStateBuilder.build(), EMPTY_CLUSTER_STATE)); clusterState, EMPTY_CLUSTER_STATE));
assertThat(securityLifecycleService.securityIndex().isTemplateUpToDate(), equalTo(false)); assertThat(securityLifecycleService.securityIndex().isTemplateUpToDate(), equalTo(false));
assertThat(listeners.size(), equalTo(0)); assertThat(listeners.size(), equalTo(0));
assertTrue(securityLifecycleService.securityIndex().isTemplateCreationPending()); assertTrue(securityLifecycleService.securityIndex().isTemplateCreationPending());
@ -177,7 +173,7 @@ public class SecurityLifecycleServiceTests extends ESTestCase {
// ... we should be able to send a new update // ... we should be able to send a new update
securityLifecycleService.clusterChanged(new ClusterChangedEvent("test-event", securityLifecycleService.clusterChanged(new ClusterChangedEvent("test-event",
clusterStateBuilder.build(), EMPTY_CLUSTER_STATE)); clusterState, EMPTY_CLUSTER_STATE));
assertThat(securityLifecycleService.securityIndex().isTemplateUpToDate(), equalTo(false)); assertThat(securityLifecycleService.securityIndex().isTemplateUpToDate(), equalTo(false));
assertThat(listeners.size(), equalTo(1)); assertThat(listeners.size(), equalTo(1));
assertTrue(securityLifecycleService.securityIndex().isTemplateCreationPending()); assertTrue(securityLifecycleService.securityIndex().isTemplateCreationPending());
@ -192,7 +188,7 @@ public class SecurityLifecycleServiceTests extends ESTestCase {
// and now let's see what happens if we get back a response // and now let's see what happens if we get back a response
listeners.clear(); listeners.clear();
securityLifecycleService.clusterChanged(new ClusterChangedEvent("test-event", securityLifecycleService.clusterChanged(new ClusterChangedEvent("test-event",
clusterStateBuilder.build(), EMPTY_CLUSTER_STATE)); clusterState, EMPTY_CLUSTER_STATE));
assertThat(securityLifecycleService.securityIndex().isTemplateUpToDate(), equalTo(false)); assertThat(securityLifecycleService.securityIndex().isTemplateUpToDate(), equalTo(false));
assertTrue(securityLifecycleService.securityIndex().isTemplateCreationPending()); assertTrue(securityLifecycleService.securityIndex().isTemplateCreationPending());
assertThat(listeners.size(), equalTo(1)); assertThat(listeners.size(), equalTo(1));
@ -202,13 +198,9 @@ public class SecurityLifecycleServiceTests extends ESTestCase {
} }
public void testMissingIndexTemplateIsIdentifiedAsMissing() throws IOException { public void testMissingIndexTemplateIsIdentifiedAsMissing() throws IOException {
ClusterState.Builder clusterStateBuilder = new ClusterState.Builder(state());
// add the correct mapping // add the correct mapping
String mappingString = "/" + SECURITY_TEMPLATE_NAME + ".json"; String mappingString = "/" + SECURITY_TEMPLATE_NAME + ".json";
IndexMetaData.Builder indexMeta = createIndexMetadata(SECURITY_INDEX_NAME, mappingString); ClusterState.Builder clusterStateBuilder = createClusterStateWithMapping(mappingString);
MetaData.Builder builder = new MetaData.Builder(clusterStateBuilder.build().getMetaData());
builder.put(indexMeta);
clusterStateBuilder.metaData(builder);
checkTemplateUpdateWorkCorrectly(clusterStateBuilder); checkTemplateUpdateWorkCorrectly(clusterStateBuilder);
} }
@ -221,7 +213,7 @@ public class SecurityLifecycleServiceTests extends ESTestCase {
public void testOutdatedMappingIsIdentifiedAsNotUpToDate() throws IOException { public void testOutdatedMappingIsIdentifiedAsNotUpToDate() throws IOException {
String templateString = "/wrong-version-" + SECURITY_TEMPLATE_NAME + ".json"; String templateString = "/wrong-version-" + SECURITY_TEMPLATE_NAME + ".json";
final Version wrongVersion = Version.fromString("4.0.0"); final Version wrongVersion = Version.fromString("4.0.0");
ClusterState.Builder clusterStateBuilder = createClusterStateWithMapping(templateString); ClusterState.Builder clusterStateBuilder = createClusterStateWithMappingAndTemplate(templateString);
final ClusterState clusterState = clusterStateBuilder.build(); final ClusterState clusterState = clusterStateBuilder.build();
assertFalse(securityIndexMappingAndTemplateUpToDate(clusterState, logger)); assertFalse(securityIndexMappingAndTemplateUpToDate(clusterState, logger));
assertFalse(securityIndexMappingAndTemplateSufficientToRead(clusterState, logger)); assertFalse(securityIndexMappingAndTemplateSufficientToRead(clusterState, logger));
@ -305,8 +297,7 @@ public class SecurityLifecycleServiceTests extends ESTestCase {
public void testUpToDateMappingsAreIdentifiedAsUpToDate() throws IOException { public void testUpToDateMappingsAreIdentifiedAsUpToDate() throws IOException {
String securityTemplateString = "/" + SECURITY_TEMPLATE_NAME + ".json"; String securityTemplateString = "/" + SECURITY_TEMPLATE_NAME + ".json";
ClusterState.Builder clusterStateBuilder = createClusterStateWithMapping( ClusterState.Builder clusterStateBuilder = createClusterStateWithMappingAndTemplate(securityTemplateString);
securityTemplateString);
securityLifecycleService.clusterChanged(new ClusterChangedEvent("test-event", securityLifecycleService.clusterChanged(new ClusterChangedEvent("test-event",
clusterStateBuilder.build(), EMPTY_CLUSTER_STATE)); clusterStateBuilder.build(), EMPTY_CLUSTER_STATE));
assertTrue(securityLifecycleService.securityIndex().isMappingUpToDate()); assertTrue(securityLifecycleService.securityIndex().isMappingUpToDate());
@ -315,8 +306,7 @@ public class SecurityLifecycleServiceTests extends ESTestCase {
public void testMappingVersionMatching() throws IOException { public void testMappingVersionMatching() throws IOException {
String templateString = "/" + SECURITY_TEMPLATE_NAME + ".json"; String templateString = "/" + SECURITY_TEMPLATE_NAME + ".json";
ClusterState.Builder clusterStateBuilder = createClusterStateWithMapping(templateString ClusterState.Builder clusterStateBuilder = createClusterStateWithMappingAndTemplate(templateString);
);
securityLifecycleService.clusterChanged(new ClusterChangedEvent("test-event", securityLifecycleService.clusterChanged(new ClusterChangedEvent("test-event",
clusterStateBuilder.build(), EMPTY_CLUSTER_STATE)); clusterStateBuilder.build(), EMPTY_CLUSTER_STATE));
final IndexLifecycleManager securityIndex = securityLifecycleService.securityIndex(); final IndexLifecycleManager securityIndex = securityLifecycleService.securityIndex();
@ -326,12 +316,12 @@ public class SecurityLifecycleServiceTests extends ESTestCase {
public void testMissingVersionMappingThrowsError() throws IOException { public void testMissingVersionMappingThrowsError() throws IOException {
String templateString = "/missing-version-" + SECURITY_TEMPLATE_NAME + ".json"; String templateString = "/missing-version-" + SECURITY_TEMPLATE_NAME + ".json";
ClusterState.Builder clusterStateBuilder = createClusterStateWithMapping(templateString ClusterState.Builder clusterStateBuilder = createClusterStateWithMappingAndTemplate(templateString);
);
final ClusterState clusterState = clusterStateBuilder.build(); final ClusterState clusterState = clusterStateBuilder.build();
IllegalStateException exception = expectThrows(IllegalStateException.class, IllegalStateException exception = expectThrows(IllegalStateException.class,
() -> securityIndexMappingAndTemplateUpToDate(clusterState, logger)); () -> securityIndexMappingAndTemplateUpToDate(clusterState, logger));
assertEquals(exception.getMessage(), "Cannot read security-version string in index " + SECURITY_INDEX_NAME); assertEquals("Cannot read security-version string in index " + SECURITY_INDEX_NAME,
exception.getMessage());
} }
public void testMissingIndexIsIdentifiedAsUpToDate() throws IOException { public void testMissingIndexIsIdentifiedAsUpToDate() throws IOException {
@ -350,24 +340,22 @@ public class SecurityLifecycleServiceTests extends ESTestCase {
} }
private ClusterState.Builder createClusterStateWithMapping(String securityTemplateString) throws IOException { private ClusterState.Builder createClusterStateWithMapping(String securityTemplateString) throws IOException {
ImmutableOpenMap.Builder mapBuilder = ImmutableOpenMap.builder(); final ClusterState clusterState = createClusterStateWithIndex(securityTemplateString).build();
IndexMetaData securityIndex = createIndexMetadata(SECURITY_INDEX_NAME, securityTemplateString).build(); final String indexName = clusterState.metaData().getAliasAndIndexLookup()
mapBuilder.put(SECURITY_INDEX_NAME, securityIndex); .get(SECURITY_INDEX_NAME).getIndices().get(0).getIndex().getName();
MetaData.Builder metaDataBuilder = new MetaData.Builder(); return ClusterState.builder(clusterState).routingTable(SecurityTestUtils.buildIndexRoutingTable(indexName));
metaDataBuilder.indices(mapBuilder.build()); }
private ClusterState.Builder createClusterStateWithMappingAndTemplate(String securityTemplateString) throws IOException {
ClusterState.Builder clusterStateBuilder = createClusterStateWithMapping(securityTemplateString);
MetaData.Builder metaDataBuilder = new MetaData.Builder(clusterStateBuilder.build().metaData());
String securityMappingString = "/" + SECURITY_TEMPLATE_NAME + ".json"; String securityMappingString = "/" + SECURITY_TEMPLATE_NAME + ".json";
IndexTemplateMetaData.Builder securityTemplateMeta = getIndexTemplateMetaData(SECURITY_TEMPLATE_NAME, securityMappingString); IndexTemplateMetaData.Builder securityTemplateMeta = getIndexTemplateMetaData(SECURITY_TEMPLATE_NAME, securityMappingString);
metaDataBuilder.put(securityTemplateMeta); metaDataBuilder.put(securityTemplateMeta);
return clusterStateBuilder.metaData(metaDataBuilder);
ClusterState.Builder clusterStateBuilder = ClusterState.builder(state());
final RoutingTable routingTable = SecurityTestUtils.buildSecurityIndexRoutingTable();
clusterStateBuilder.metaData(metaDataBuilder.build()).routingTable(routingTable);
return clusterStateBuilder;
} }
private static IndexMetaData.Builder createIndexMetadata( private static IndexMetaData.Builder createIndexMetadata(String indexName, String templateString) throws IOException {
String indexName, String templateString) throws IOException {
String template = TemplateUtils.loadTemplate(templateString, Version.CURRENT.toString(), String template = TemplateUtils.loadTemplate(templateString, Version.CURRENT.toString(),
IndexLifecycleManager.TEMPLATE_VERSION_PATTERN); IndexLifecycleManager.TEMPLATE_VERSION_PATTERN);
PutIndexTemplateRequest request = new PutIndexTemplateRequest(); PutIndexTemplateRequest request = new PutIndexTemplateRequest();
@ -385,19 +373,30 @@ public class SecurityLifecycleServiceTests extends ESTestCase {
return indexMetaData; return indexMetaData;
} }
public static ClusterState.Builder createClusterStateWithTemplate(String securityTemplateString) throws IOException { public ClusterState.Builder createClusterStateWithTemplate(String securityTemplateString) throws IOException {
MetaData.Builder metaDataBuilder = new MetaData.Builder();
IndexTemplateMetaData.Builder securityTemplateBuilder =
getIndexTemplateMetaData(SECURITY_TEMPLATE_NAME, securityTemplateString);
metaDataBuilder.put(securityTemplateBuilder);
// add the correct mapping no matter what the template // add the correct mapping no matter what the template
String securityMappingString = "/" + SECURITY_TEMPLATE_NAME + ".json"; ClusterState clusterState = createClusterStateWithIndex("/" + SECURITY_TEMPLATE_NAME + ".json").build();
IndexMetaData.Builder securityIndexMeta = final MetaData.Builder metaDataBuilder = new MetaData.Builder(clusterState.metaData());
createIndexMetadata(SECURITY_INDEX_NAME, securityMappingString); metaDataBuilder.put(getIndexTemplateMetaData(SECURITY_TEMPLATE_NAME, securityTemplateString));
metaDataBuilder.put(securityIndexMeta); return ClusterState.builder(clusterState).metaData(metaDataBuilder);
}
return ClusterState.builder(state()).metaData(metaDataBuilder.build()); private ClusterState.Builder createClusterStateWithIndex(String securityTemplate) throws IOException {
final MetaData.Builder metaDataBuilder = new MetaData.Builder();
final boolean withAlias = randomBoolean();
final String securityIndexName = SECURITY_INDEX_NAME + (withAlias ? "-" + randomAlphaOfLength(5) : "");
metaDataBuilder.put(createIndexMetadata(securityIndexName, securityTemplate));
ClusterState.Builder clusterStateBuilder = ClusterState.builder(state());
if (withAlias) {
// try with .security index as an alias
clusterStateBuilder.metaData(SecurityTestUtils.addAliasToMetaData(metaDataBuilder.build(), securityIndexName));
} else {
// try with .security index as a concrete index
clusterStateBuilder.metaData(metaDataBuilder);
}
return clusterStateBuilder;
} }
private static IndexTemplateMetaData.Builder getIndexTemplateMetaData( private static IndexTemplateMetaData.Builder getIndexTemplateMetaData(

View File

@ -55,6 +55,7 @@ import org.elasticsearch.xpack.security.authz.permission.FieldPermissionsCache;
import org.elasticsearch.xpack.security.authz.permission.Role; import org.elasticsearch.xpack.security.authz.permission.Role;
import org.elasticsearch.xpack.security.authz.store.CompositeRolesStore; import org.elasticsearch.xpack.security.authz.store.CompositeRolesStore;
import org.elasticsearch.xpack.security.authz.store.ReservedRolesStore; import org.elasticsearch.xpack.security.authz.store.ReservedRolesStore;
import org.elasticsearch.xpack.security.test.SecurityTestUtils;
import org.elasticsearch.xpack.security.user.AnonymousUser; import org.elasticsearch.xpack.security.user.AnonymousUser;
import org.elasticsearch.xpack.security.user.User; import org.elasticsearch.xpack.security.user.User;
import org.elasticsearch.xpack.security.user.XPackUser; import org.elasticsearch.xpack.security.user.XPackUser;
@ -66,6 +67,7 @@ import java.util.HashSet;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
import static org.elasticsearch.xpack.security.SecurityLifecycleService.SECURITY_INDEX_NAME;
import static org.hamcrest.Matchers.arrayContaining; import static org.hamcrest.Matchers.arrayContaining;
import static org.hamcrest.Matchers.arrayContainingInAnyOrder; import static org.hamcrest.Matchers.arrayContainingInAnyOrder;
import static org.hamcrest.Matchers.containsInAnyOrder; import static org.hamcrest.Matchers.containsInAnyOrder;
@ -101,7 +103,10 @@ public class IndicesAndAliasesResolverTests extends ESTestCase {
.build(); .build();
indexNameExpressionResolver = new IndexNameExpressionResolver(Settings.EMPTY); indexNameExpressionResolver = new IndexNameExpressionResolver(Settings.EMPTY);
metaData = MetaData.builder()
final boolean withAlias = randomBoolean();
final String securityIndexName = SECURITY_INDEX_NAME + (withAlias ? "-" + randomAlphaOfLength(5) : "");
MetaData metaData = MetaData.builder()
.put(indexBuilder("foo").putAlias(AliasMetaData.builder("foofoobar")).settings(settings)) .put(indexBuilder("foo").putAlias(AliasMetaData.builder("foofoobar")).settings(settings))
.put(indexBuilder("foobar").putAlias(AliasMetaData.builder("foofoobar")).settings(settings)) .put(indexBuilder("foobar").putAlias(AliasMetaData.builder("foofoobar")).settings(settings))
.put(indexBuilder("closed").state(IndexMetaData.State.CLOSE) .put(indexBuilder("closed").state(IndexMetaData.State.CLOSE)
@ -117,7 +122,12 @@ public class IndicesAndAliasesResolverTests extends ESTestCase {
.put(indexBuilder("-index11").settings(settings)) .put(indexBuilder("-index11").settings(settings))
.put(indexBuilder("-index20").settings(settings)) .put(indexBuilder("-index20").settings(settings))
.put(indexBuilder("-index21").settings(settings)) .put(indexBuilder("-index21").settings(settings))
.put(indexBuilder(SecurityLifecycleService.SECURITY_INDEX_NAME).settings(settings)).build(); .put(indexBuilder(securityIndexName).settings(settings)).build();
if (withAlias) {
metaData = SecurityTestUtils.addAliasToMetaData(metaData, securityIndexName);
}
this.metaData = metaData;
user = new User("user", "role"); user = new User("user", "role");
userDashIndices = new User("dash", "dash"); userDashIndices = new User("dash", "dash");

View File

@ -42,6 +42,7 @@ import org.elasticsearch.xpack.security.action.role.PutRoleRequest;
import org.elasticsearch.xpack.security.audit.index.IndexAuditTrail; import org.elasticsearch.xpack.security.audit.index.IndexAuditTrail;
import org.elasticsearch.xpack.security.authz.RoleDescriptor; import org.elasticsearch.xpack.security.authz.RoleDescriptor;
import org.elasticsearch.xpack.security.authz.RoleDescriptor.IndicesPrivileges; import org.elasticsearch.xpack.security.authz.RoleDescriptor.IndicesPrivileges;
import org.elasticsearch.xpack.security.test.SecurityTestUtils;
import org.junit.After; import org.junit.After;
import org.junit.Before; import org.junit.Before;
@ -55,6 +56,7 @@ import java.util.UUID;
import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicBoolean;
import static org.elasticsearch.cluster.routing.RecoverySource.StoreRecoverySource.EXISTING_STORE_INSTANCE; import static org.elasticsearch.cluster.routing.RecoverySource.StoreRecoverySource.EXISTING_STORE_INSTANCE;
import static org.elasticsearch.xpack.security.SecurityLifecycleService.SECURITY_INDEX_NAME;
import static org.hamcrest.Matchers.contains; import static org.hamcrest.Matchers.contains;
import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.instanceOf; import static org.hamcrest.Matchers.instanceOf;
@ -242,18 +244,26 @@ public class NativeRolesStoreTests extends ESTestCase {
} }
private ClusterState getClusterStateWithSecurityIndex() { private ClusterState getClusterStateWithSecurityIndex() {
final boolean withAlias = randomBoolean();
final String securityIndexName = SECURITY_INDEX_NAME + (withAlias ? "-" + randomAlphaOfLength(5) : "");
Settings settings = Settings.builder() Settings settings = Settings.builder()
.put(IndexMetaData.SETTING_VERSION_CREATED, Version.CURRENT) .put(IndexMetaData.SETTING_VERSION_CREATED, Version.CURRENT)
.put(IndexMetaData.SETTING_NUMBER_OF_SHARDS, 1) .put(IndexMetaData.SETTING_NUMBER_OF_SHARDS, 1)
.put(IndexMetaData.SETTING_NUMBER_OF_REPLICAS, 0) .put(IndexMetaData.SETTING_NUMBER_OF_REPLICAS, 0)
.build(); .build();
MetaData metaData = MetaData.builder() MetaData metaData = MetaData.builder()
.put(IndexMetaData.builder(SecurityLifecycleService.SECURITY_INDEX_NAME).settings(settings)) .put(IndexMetaData.builder(securityIndexName).settings(settings))
.put(new IndexTemplateMetaData(SecurityLifecycleService.SECURITY_TEMPLATE_NAME, 0, 0, .put(new IndexTemplateMetaData(SecurityLifecycleService.SECURITY_TEMPLATE_NAME, 0, 0,
Collections.singletonList(SecurityLifecycleService.SECURITY_INDEX_NAME), Settings.EMPTY, ImmutableOpenMap.of(), Collections.singletonList(securityIndexName), Settings.EMPTY, ImmutableOpenMap.of(),
ImmutableOpenMap.of(), ImmutableOpenMap.of())) ImmutableOpenMap.of(), ImmutableOpenMap.of()))
.build(); .build();
Index index = new Index(SecurityLifecycleService.SECURITY_INDEX_NAME, UUID.randomUUID().toString());
if (withAlias) {
metaData = SecurityTestUtils.addAliasToMetaData(metaData, securityIndexName);
}
Index index = new Index(securityIndexName, UUID.randomUUID().toString());
ShardRouting shardRouting = ShardRouting.newUnassigned(new ShardId(index, 0), true, EXISTING_STORE_INSTANCE, ShardRouting shardRouting = ShardRouting.newUnassigned(new ShardId(index, 0), true, EXISTING_STORE_INSTANCE,
new UnassignedInfo(Reason.INDEX_CREATED, "")); new UnassignedInfo(Reason.INDEX_CREATED, ""));
IndexShardRoutingTable table = new IndexShardRoutingTable.Builder(new ShardId(index, 0)) IndexShardRoutingTable table = new IndexShardRoutingTable.Builder(new ShardId(index, 0))
@ -266,10 +276,12 @@ public class NativeRolesStoreTests extends ESTestCase {
.build()) .build())
.build(); .build();
return ClusterState.builder(new ClusterName(NativeRolesStoreTests.class.getName())) ClusterState clusterState = ClusterState.builder(new ClusterName(NativeRolesStoreTests.class.getName()))
.metaData(metaData) .metaData(metaData)
.routingTable(routingTable) .routingTable(routingTable)
.build(); .build();
return clusterState;
} }
private ClusterState getEmptyClusterState() { private ClusterState getEmptyClusterState() {

View File

@ -6,36 +6,29 @@
package org.elasticsearch.xpack.security.test; package org.elasticsearch.xpack.security.test;
import org.elasticsearch.ElasticsearchException; import org.elasticsearch.ElasticsearchException;
import org.elasticsearch.Version; import org.elasticsearch.cluster.metadata.AliasMetaData;
import org.elasticsearch.cluster.ClusterName;
import org.elasticsearch.cluster.ClusterState;
import org.elasticsearch.cluster.metadata.IndexMetaData; import org.elasticsearch.cluster.metadata.IndexMetaData;
import org.elasticsearch.cluster.metadata.IndexTemplateMetaData;
import org.elasticsearch.cluster.metadata.MetaData; import org.elasticsearch.cluster.metadata.MetaData;
import org.elasticsearch.cluster.routing.IndexRoutingTable; import org.elasticsearch.cluster.routing.IndexRoutingTable;
import org.elasticsearch.cluster.routing.IndexShardRoutingTable; import org.elasticsearch.cluster.routing.IndexShardRoutingTable;
import org.elasticsearch.cluster.routing.RoutingTable; import org.elasticsearch.cluster.routing.RoutingTable;
import org.elasticsearch.cluster.routing.ShardRouting; import org.elasticsearch.cluster.routing.ShardRouting;
import org.elasticsearch.cluster.routing.UnassignedInfo; import org.elasticsearch.cluster.routing.UnassignedInfo;
import org.elasticsearch.common.collect.ImmutableOpenMap;
import org.elasticsearch.common.io.FileSystemUtils; import org.elasticsearch.common.io.FileSystemUtils;
import org.elasticsearch.common.io.Streams; import org.elasticsearch.common.io.Streams;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.index.Index; import org.elasticsearch.index.Index;
import org.elasticsearch.index.shard.ShardId; import org.elasticsearch.index.shard.ShardId;
import org.elasticsearch.test.ESTestCase; import org.elasticsearch.test.ESTestCase;
import org.elasticsearch.xpack.security.SecurityLifecycleService;
import org.elasticsearch.xpack.security.authz.store.NativeRolesStoreTests;
import java.io.IOException; import java.io.IOException;
import java.io.OutputStream; import java.io.OutputStream;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import java.nio.file.Files; import java.nio.file.Files;
import java.nio.file.Path; import java.nio.file.Path;
import java.util.Collections;
import java.util.UUID; import java.util.UUID;
import static org.elasticsearch.cluster.routing.RecoverySource.StoreRecoverySource.EXISTING_STORE_INSTANCE; import static org.elasticsearch.cluster.routing.RecoverySource.StoreRecoverySource.EXISTING_STORE_INSTANCE;
import static org.elasticsearch.xpack.security.SecurityLifecycleService.SECURITY_INDEX_NAME;
public class SecurityTestUtils { public class SecurityTestUtils {
@ -72,30 +65,6 @@ public class SecurityTestUtils {
return writeFile(folder, name, content.getBytes(StandardCharsets.UTF_8)); return writeFile(folder, name, content.getBytes(StandardCharsets.UTF_8));
} }
public static ClusterState getClusterStateWithSecurityIndex() {
Settings settings = Settings.builder()
.put(IndexMetaData.SETTING_VERSION_CREATED, Version.CURRENT)
.put(IndexMetaData.SETTING_NUMBER_OF_SHARDS, 1)
.put(IndexMetaData.SETTING_NUMBER_OF_REPLICAS, 0)
.build();
MetaData metaData = MetaData.builder()
.put(IndexMetaData.builder(SecurityLifecycleService.SECURITY_INDEX_NAME).settings(settings))
.put(new IndexTemplateMetaData(SecurityLifecycleService.SECURITY_TEMPLATE_NAME, 0, 0,
Collections.singletonList(SecurityLifecycleService.SECURITY_INDEX_NAME), Settings.EMPTY, ImmutableOpenMap.of(),
ImmutableOpenMap.of(), ImmutableOpenMap.of()))
.build();
RoutingTable routingTable = buildSecurityIndexRoutingTable();
return ClusterState.builder(new ClusterName(NativeRolesStoreTests.class.getName()))
.metaData(metaData)
.routingTable(routingTable)
.build();
}
public static RoutingTable buildSecurityIndexRoutingTable() {
return buildIndexRoutingTable(SecurityLifecycleService.SECURITY_INDEX_NAME);
}
public static RoutingTable buildIndexRoutingTable(String indexName) { public static RoutingTable buildIndexRoutingTable(String indexName) {
Index index = new Index(indexName, UUID.randomUUID().toString()); Index index = new Index(indexName, UUID.randomUUID().toString());
ShardRouting shardRouting = ShardRouting.newUnassigned(new ShardId(index, 0), true, EXISTING_STORE_INSTANCE, ShardRouting shardRouting = ShardRouting.newUnassigned(new ShardId(index, 0), true, EXISTING_STORE_INSTANCE,
@ -108,4 +77,16 @@ public class SecurityTestUtils {
.add(IndexRoutingTable.builder(index).addIndexShard(table).build()) .add(IndexRoutingTable.builder(index).addIndexShard(table).build())
.build(); .build();
} }
/**
* Adds the index alias {@code .security} to the underlying concrete index.
*/
public static MetaData addAliasToMetaData(MetaData metaData, String indexName) {
AliasMetaData aliasMetaData = AliasMetaData.newAliasMetaDataBuilder(SECURITY_INDEX_NAME).build();
MetaData.Builder metaDataBuilder = new MetaData.Builder(metaData);
IndexMetaData indexMetaData = metaData.index(indexName);
metaDataBuilder.put(IndexMetaData.builder(indexMetaData).putAlias(aliasMetaData));
return metaDataBuilder.build();
}
} }