IndexLifecycleManager checks for index existense and up to date mappings (elastic/x-pack-elasticsearch#3515)
This change makes the IndexLifecycleManager check for both index existence and up to date mappings on the index prior to executing the provided runnable. Doing this provides a mechanism to make non-breaking mapping updates to the security index in minor versions. relates elastic/x-pack-elasticsearch#3462 Original commit: elastic/x-pack-elasticsearch@80f05d83b4
This commit is contained in:
parent
1369a49b9f
commit
cec90f452a
|
@ -207,7 +207,6 @@ import java.util.function.Function;
|
||||||
import java.util.function.Predicate;
|
import java.util.function.Predicate;
|
||||||
import java.util.function.Supplier;
|
import java.util.function.Supplier;
|
||||||
import java.util.function.UnaryOperator;
|
import java.util.function.UnaryOperator;
|
||||||
import java.util.stream.Collectors;
|
|
||||||
|
|
||||||
import static java.util.Collections.emptyList;
|
import static java.util.Collections.emptyList;
|
||||||
import static java.util.Collections.singletonList;
|
import static java.util.Collections.singletonList;
|
||||||
|
@ -755,29 +754,22 @@ public class Security implements ActionPlugin, IngestPlugin, NetworkPlugin, Clus
|
||||||
}
|
}
|
||||||
|
|
||||||
final boolean indexAuditingEnabled = Security.indexAuditLoggingEnabled(settings);
|
final boolean indexAuditingEnabled = Security.indexAuditLoggingEnabled(settings);
|
||||||
final String auditIndex;
|
|
||||||
if (indexAuditingEnabled) {
|
if (indexAuditingEnabled) {
|
||||||
auditIndex = "," + IndexAuditTrail.INDEX_NAME_PREFIX + "*";
|
String auditIndex = IndexAuditTrail.INDEX_NAME_PREFIX + "*";
|
||||||
} else {
|
String errorMessage = LoggerMessageFormat.format(
|
||||||
auditIndex = "";
|
"the [action.auto_create_index] setting value [{}] is too" +
|
||||||
}
|
" restrictive. disable [action.auto_create_index] or set it to include " +
|
||||||
String securityIndices = SecurityLifecycleService.indexNames().stream()
|
"[{}]", (Object) value, auditIndex);
|
||||||
.collect(Collectors.joining(","));
|
if (Booleans.isFalse(value)) {
|
||||||
String errorMessage = LoggerMessageFormat.format(
|
throw new IllegalArgumentException(errorMessage);
|
||||||
"the [action.auto_create_index] setting value [{}] is too" +
|
}
|
||||||
" restrictive. disable [action.auto_create_index] or set it to " +
|
|
||||||
"[{}{}]", (Object) value, securityIndices, auditIndex);
|
|
||||||
if (Booleans.isFalse(value)) {
|
|
||||||
throw new IllegalArgumentException(errorMessage);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Booleans.isTrue(value)) {
|
if (Booleans.isTrue(value)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
String[] matches = Strings.commaDelimitedListToStringArray(value);
|
String[] matches = Strings.commaDelimitedListToStringArray(value);
|
||||||
List<String> indices = new ArrayList<>(SecurityLifecycleService.indexNames());
|
List<String> indices = new ArrayList<>();
|
||||||
if (indexAuditingEnabled) {
|
|
||||||
DateTime now = new DateTime(DateTimeZone.UTC);
|
DateTime now = new DateTime(DateTimeZone.UTC);
|
||||||
// just use daily rollover
|
// just use daily rollover
|
||||||
indices.add(IndexNameResolver.resolve(IndexAuditTrail.INDEX_NAME_PREFIX, now, IndexNameResolver.Rollover.DAILY));
|
indices.add(IndexNameResolver.resolve(IndexAuditTrail.INDEX_NAME_PREFIX, now, IndexNameResolver.Rollover.DAILY));
|
||||||
|
@ -788,34 +780,32 @@ public class Security implements ActionPlugin, IngestPlugin, NetworkPlugin, Clus
|
||||||
indices.add(IndexNameResolver.resolve(IndexAuditTrail.INDEX_NAME_PREFIX, now.plusMonths(4), IndexNameResolver.Rollover.DAILY));
|
indices.add(IndexNameResolver.resolve(IndexAuditTrail.INDEX_NAME_PREFIX, now.plusMonths(4), IndexNameResolver.Rollover.DAILY));
|
||||||
indices.add(IndexNameResolver.resolve(IndexAuditTrail.INDEX_NAME_PREFIX, now.plusMonths(5), IndexNameResolver.Rollover.DAILY));
|
indices.add(IndexNameResolver.resolve(IndexAuditTrail.INDEX_NAME_PREFIX, now.plusMonths(5), IndexNameResolver.Rollover.DAILY));
|
||||||
indices.add(IndexNameResolver.resolve(IndexAuditTrail.INDEX_NAME_PREFIX, now.plusMonths(6), IndexNameResolver.Rollover.DAILY));
|
indices.add(IndexNameResolver.resolve(IndexAuditTrail.INDEX_NAME_PREFIX, now.plusMonths(6), IndexNameResolver.Rollover.DAILY));
|
||||||
}
|
|
||||||
|
|
||||||
for (String index : indices) {
|
for (String index : indices) {
|
||||||
boolean matched = false;
|
boolean matched = false;
|
||||||
for (String match : matches) {
|
for (String match : matches) {
|
||||||
char c = match.charAt(0);
|
char c = match.charAt(0);
|
||||||
if (c == '-') {
|
if (c == '-') {
|
||||||
if (Regex.simpleMatch(match.substring(1), index)) {
|
if (Regex.simpleMatch(match.substring(1), index)) {
|
||||||
throw new IllegalArgumentException(errorMessage);
|
throw new IllegalArgumentException(errorMessage);
|
||||||
}
|
}
|
||||||
} else if (c == '+') {
|
} else if (c == '+') {
|
||||||
if (Regex.simpleMatch(match.substring(1), index)) {
|
if (Regex.simpleMatch(match.substring(1), index)) {
|
||||||
matched = true;
|
matched = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (Regex.simpleMatch(match, index)) {
|
if (Regex.simpleMatch(match, index)) {
|
||||||
matched = true;
|
matched = true;
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (!matched) {
|
||||||
|
throw new IllegalArgumentException(errorMessage);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (!matched) {
|
|
||||||
throw new IllegalArgumentException(errorMessage);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (indexAuditingEnabled) {
|
|
||||||
logger.warn("the [action.auto_create_index] setting is configured to be restrictive [{}]. " +
|
logger.warn("the [action.auto_create_index] setting is configured to be restrictive [{}]. " +
|
||||||
" for the next 6 months audit indices are allowed to be created, but please make sure" +
|
" for the next 6 months audit indices are allowed to be created, but please make sure" +
|
||||||
" that any future history indices after 6 months with the pattern " +
|
" that any future history indices after 6 months with the pattern " +
|
||||||
|
@ -904,17 +894,8 @@ public class Security implements ActionPlugin, IngestPlugin, NetworkPlugin, Clus
|
||||||
|
|
||||||
public UnaryOperator<Map<String, IndexTemplateMetaData>> getIndexTemplateMetaDataUpgrader() {
|
public UnaryOperator<Map<String, IndexTemplateMetaData>> getIndexTemplateMetaDataUpgrader() {
|
||||||
return templates -> {
|
return templates -> {
|
||||||
final byte[] securityTemplate = TemplateUtils.loadTemplate("/" + SECURITY_TEMPLATE_NAME + ".json",
|
templates.remove(SECURITY_TEMPLATE_NAME);
|
||||||
Version.CURRENT.toString(), IndexLifecycleManager.TEMPLATE_VERSION_PATTERN).getBytes(StandardCharsets.UTF_8);
|
|
||||||
final XContent xContent = XContentFactory.xContent(XContentType.JSON);
|
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 template [{}] as part of metadata upgrading", SECURITY_TEMPLATE_NAME);
|
|
||||||
}
|
|
||||||
|
|
||||||
final byte[] auditTemplate = TemplateUtils.loadTemplate("/" + IndexAuditTrail.INDEX_TEMPLATE_NAME + ".json",
|
final byte[] auditTemplate = TemplateUtils.loadTemplate("/" + IndexAuditTrail.INDEX_TEMPLATE_NAME + ".json",
|
||||||
Version.CURRENT.toString(), IndexLifecycleManager.TEMPLATE_VERSION_PATTERN).getBytes(StandardCharsets.UTF_8);
|
Version.CURRENT.toString(), IndexLifecycleManager.TEMPLATE_VERSION_PATTERN).getBytes(StandardCharsets.UTF_8);
|
||||||
|
|
||||||
|
|
|
@ -7,7 +7,6 @@ package org.elasticsearch.xpack.security;
|
||||||
|
|
||||||
import org.apache.logging.log4j.Logger;
|
import org.apache.logging.log4j.Logger;
|
||||||
import org.elasticsearch.Version;
|
import org.elasticsearch.Version;
|
||||||
import org.elasticsearch.action.ActionListener;
|
|
||||||
import org.elasticsearch.client.Client;
|
import org.elasticsearch.client.Client;
|
||||||
import org.elasticsearch.cluster.ClusterChangedEvent;
|
import org.elasticsearch.cluster.ClusterChangedEvent;
|
||||||
import org.elasticsearch.cluster.ClusterState;
|
import org.elasticsearch.cluster.ClusterState;
|
||||||
|
@ -28,6 +27,7 @@ import java.util.Arrays;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.function.BiConsumer;
|
import java.util.function.BiConsumer;
|
||||||
|
import java.util.function.Consumer;
|
||||||
import java.util.function.Predicate;
|
import java.util.function.Predicate;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -64,7 +64,7 @@ public class SecurityLifecycleService extends AbstractComponent implements Clust
|
||||||
this.settings = settings;
|
this.settings = settings;
|
||||||
this.threadPool = threadPool;
|
this.threadPool = threadPool;
|
||||||
this.indexAuditTrail = indexAuditTrail;
|
this.indexAuditTrail = indexAuditTrail;
|
||||||
this.securityIndex = new IndexLifecycleManager(settings, client, SECURITY_INDEX_NAME, SECURITY_TEMPLATE_NAME);
|
this.securityIndex = new IndexLifecycleManager(settings, client, SECURITY_INDEX_NAME);
|
||||||
clusterService.addListener(this);
|
clusterService.addListener(this);
|
||||||
clusterService.addLifecycleListener(new LifecycleListener() {
|
clusterService.addLifecycleListener(new LifecycleListener() {
|
||||||
@Override
|
@Override
|
||||||
|
@ -114,20 +114,34 @@ public class SecurityLifecycleService extends AbstractComponent implements Clust
|
||||||
return securityIndex;
|
return securityIndex;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns {@code true} if the security index exists
|
||||||
|
*/
|
||||||
public boolean isSecurityIndexExisting() {
|
public boolean isSecurityIndexExisting() {
|
||||||
return securityIndex.indexExists();
|
return securityIndex.indexExists();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns <code>true</code> if the security index does not exist or it exists and has the current
|
||||||
|
* value for the <code>index.format</code> index setting
|
||||||
|
*/
|
||||||
public boolean isSecurityIndexUpToDate() {
|
public boolean isSecurityIndexUpToDate() {
|
||||||
return securityIndex.isIndexUpToDate();
|
return securityIndex.isIndexUpToDate();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns <code>true</code> if the security index exists and all primary shards are active
|
||||||
|
*/
|
||||||
public boolean isSecurityIndexAvailable() {
|
public boolean isSecurityIndexAvailable() {
|
||||||
return securityIndex.isAvailable();
|
return securityIndex.isAvailable();
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isSecurityIndexWriteable() {
|
/**
|
||||||
return securityIndex.isWritable();
|
* Returns <code>true</code> if the security index does not exist or the mappings are up to date
|
||||||
|
* based on the version in the <code>_meta</code> field
|
||||||
|
*/
|
||||||
|
public boolean isSecurityIndexMappingUpToDate() {
|
||||||
|
return securityIndex().isMappingUpToDate();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -170,22 +184,16 @@ public class SecurityLifecycleService extends AbstractComponent implements Clust
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static boolean securityIndexMappingAndTemplateSufficientToRead(ClusterState clusterState,
|
public static boolean securityIndexMappingSufficientToRead(ClusterState clusterState, Logger logger) {
|
||||||
Logger logger) {
|
return checkMappingVersions(clusterState, logger, MIN_READ_VERSION::onOrBefore);
|
||||||
return checkTemplateAndMappingVersions(clusterState, logger, MIN_READ_VERSION::onOrBefore);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static boolean securityIndexMappingAndTemplateUpToDate(ClusterState clusterState,
|
static boolean securityIndexMappingUpToDate(ClusterState clusterState, Logger logger) {
|
||||||
Logger logger) {
|
return checkMappingVersions(clusterState, logger, Version.CURRENT::equals);
|
||||||
return checkTemplateAndMappingVersions(clusterState, logger, Version.CURRENT::equals);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static boolean checkTemplateAndMappingVersions(ClusterState clusterState, Logger logger,
|
private static boolean checkMappingVersions(ClusterState clusterState, Logger logger, Predicate<Version> versionPredicate) {
|
||||||
Predicate<Version> versionPredicate) {
|
return IndexLifecycleManager.checkIndexMappingVersionMatches(SECURITY_INDEX_NAME, clusterState, logger, versionPredicate);
|
||||||
return IndexLifecycleManager.checkTemplateExistsAndVersionMatches(SECURITY_TEMPLATE_NAME,
|
|
||||||
clusterState, logger, versionPredicate) &&
|
|
||||||
IndexLifecycleManager.checkIndexMappingVersionMatches(SECURITY_INDEX_NAME,
|
|
||||||
clusterState, logger, versionPredicate);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static List<String> indexNames() {
|
public static List<String> indexNames() {
|
||||||
|
@ -193,17 +201,11 @@ public class SecurityLifecycleService extends AbstractComponent implements Clust
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates the security index, if it does not already exist, then runs the given
|
* Prepares the security index by creating it if it doesn't exist or updating the mappings if the mappings are
|
||||||
* action on the security index.
|
* out of date. After any tasks have been executed, the runnable is then executed.
|
||||||
*/
|
*/
|
||||||
public <T> void createIndexIfNeededThenExecute(final ActionListener<T> listener, final Runnable andThen) {
|
public void prepareIndexIfNeededThenExecute(final Consumer<Exception> consumer, final Runnable andThen) {
|
||||||
if (!isSecurityIndexExisting() || isSecurityIndexUpToDate()) {
|
securityIndex.prepareIndexIfNeededThenExecute(consumer, andThen);
|
||||||
securityIndex.createIndexIfNeededThenExecute(listener, andThen);
|
|
||||||
} else {
|
|
||||||
listener.onFailure(new IllegalStateException(
|
|
||||||
"Security index is not on the current version - the native realm will not be operational until " +
|
|
||||||
"the upgrade API is run on the security index"));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -271,14 +271,7 @@ public final class TokenService extends AbstractComponent {
|
||||||
*/
|
*/
|
||||||
public void invalidateToken(String tokenString, ActionListener<Boolean> listener) {
|
public void invalidateToken(String tokenString, ActionListener<Boolean> listener) {
|
||||||
ensureEnabled();
|
ensureEnabled();
|
||||||
if (lifecycleService.isSecurityIndexOutOfDate()) {
|
if (Strings.isNullOrEmpty(tokenString)) {
|
||||||
listener.onFailure(new IllegalStateException(
|
|
||||||
"Security index is not on the current version - the native realm will not be operational until " +
|
|
||||||
"the upgrade API is run on the security index"));
|
|
||||||
return;
|
|
||||||
} else if (lifecycleService.isSecurityIndexWriteable() == false) {
|
|
||||||
listener.onFailure(new IllegalStateException("cannot write to the tokens index"));
|
|
||||||
} else if (Strings.isNullOrEmpty(tokenString)) {
|
|
||||||
listener.onFailure(new IllegalArgumentException("token must be provided"));
|
listener.onFailure(new IllegalArgumentException("token must be provided"));
|
||||||
} else {
|
} else {
|
||||||
maybeStartTokenRemover();
|
maybeStartTokenRemover();
|
||||||
|
@ -291,7 +284,7 @@ public final class TokenService extends AbstractComponent {
|
||||||
listener.onResponse(false);
|
listener.onResponse(false);
|
||||||
} else {
|
} else {
|
||||||
final String id = getDocumentId(userToken);
|
final String id = getDocumentId(userToken);
|
||||||
lifecycleService.createIndexIfNeededThenExecute(listener, () -> {
|
lifecycleService.prepareIndexIfNeededThenExecute(listener::onFailure, () -> {
|
||||||
executeAsyncWithOrigin(client.threadPool().getThreadContext(), SECURITY_ORIGIN,
|
executeAsyncWithOrigin(client.threadPool().getThreadContext(), SECURITY_ORIGIN,
|
||||||
client.prepareIndex(SecurityLifecycleService.SECURITY_INDEX_NAME, TYPE, id)
|
client.prepareIndex(SecurityLifecycleService.SECURITY_INDEX_NAME, TYPE, id)
|
||||||
.setOpType(OpType.CREATE)
|
.setOpType(OpType.CREATE)
|
||||||
|
@ -338,47 +331,38 @@ public final class TokenService extends AbstractComponent {
|
||||||
* have been explicitly cleared.
|
* have been explicitly cleared.
|
||||||
*/
|
*/
|
||||||
private void checkIfTokenIsRevoked(UserToken userToken, ActionListener<UserToken> listener) {
|
private void checkIfTokenIsRevoked(UserToken userToken, ActionListener<UserToken> listener) {
|
||||||
if (lifecycleService.isSecurityIndexAvailable()) {
|
if (lifecycleService.isSecurityIndexExisting() == false) {
|
||||||
if (lifecycleService.isSecurityIndexOutOfDate()) {
|
|
||||||
listener.onFailure(new IllegalStateException(
|
|
||||||
"Security index is not on the current version - the native realm will not be operational until " +
|
|
||||||
"the upgrade API is run on the security index"));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
executeAsyncWithOrigin(client.threadPool().getThreadContext(), SECURITY_ORIGIN,
|
|
||||||
client.prepareGet(SecurityLifecycleService.SECURITY_INDEX_NAME, TYPE, getDocumentId(userToken)).request(),
|
|
||||||
new ActionListener<GetResponse>() {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onResponse(GetResponse response) {
|
|
||||||
if (response.isExists()) {
|
|
||||||
// this token is explicitly expired!
|
|
||||||
listener.onFailure(expiredTokenException());
|
|
||||||
} else {
|
|
||||||
listener.onResponse(userToken);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onFailure(Exception e) {
|
|
||||||
// if the index or the shard is not there / available we assume that
|
|
||||||
// the token is not valid
|
|
||||||
if (TransportActions.isShardNotAvailableException(e)) {
|
|
||||||
logger.warn("failed to get token [{}] since index is not available", userToken.getId());
|
|
||||||
listener.onResponse(null);
|
|
||||||
} else {
|
|
||||||
logger.error(new ParameterizedMessage("failed to get token [{}]", userToken.getId()), e);
|
|
||||||
listener.onFailure(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}, client::get);
|
|
||||||
} else if (lifecycleService.isSecurityIndexExisting()) {
|
|
||||||
// index exists but the index isn't available, do not trust the token
|
|
||||||
logger.warn("could not validate token as the security index is not available");
|
|
||||||
listener.onResponse(null);
|
|
||||||
} else {
|
|
||||||
// index doesn't exist so the token is considered valid.
|
// index doesn't exist so the token is considered valid.
|
||||||
listener.onResponse(userToken);
|
listener.onResponse(userToken);
|
||||||
|
} else {
|
||||||
|
lifecycleService.prepareIndexIfNeededThenExecute(listener::onFailure, () ->
|
||||||
|
executeAsyncWithOrigin(client.threadPool().getThreadContext(), SECURITY_ORIGIN,
|
||||||
|
client.prepareGet(SecurityLifecycleService.SECURITY_INDEX_NAME, TYPE, getDocumentId(userToken)).request(),
|
||||||
|
new ActionListener<GetResponse>() {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onResponse(GetResponse response) {
|
||||||
|
if (response.isExists()) {
|
||||||
|
// this token is explicitly expired!
|
||||||
|
listener.onFailure(expiredTokenException());
|
||||||
|
} else {
|
||||||
|
listener.onResponse(userToken);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onFailure(Exception e) {
|
||||||
|
// if the index or the shard is not there / available we assume that
|
||||||
|
// the token is not valid
|
||||||
|
if (TransportActions.isShardNotAvailableException(e)) {
|
||||||
|
logger.warn("failed to get token [{}] since index is not available", userToken.getId());
|
||||||
|
listener.onResponse(null);
|
||||||
|
} else {
|
||||||
|
logger.error(new ParameterizedMessage("failed to get token [{}]", userToken.getId()), e);
|
||||||
|
listener.onFailure(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, client::get));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -17,6 +17,7 @@ import org.elasticsearch.action.index.IndexResponse;
|
||||||
import org.elasticsearch.action.search.SearchRequest;
|
import org.elasticsearch.action.search.SearchRequest;
|
||||||
import org.elasticsearch.action.search.SearchResponse;
|
import org.elasticsearch.action.search.SearchResponse;
|
||||||
import org.elasticsearch.action.support.ContextPreservingActionListener;
|
import org.elasticsearch.action.support.ContextPreservingActionListener;
|
||||||
|
import org.elasticsearch.action.support.TransportActions;
|
||||||
import org.elasticsearch.action.support.WriteRequest.RefreshPolicy;
|
import org.elasticsearch.action.support.WriteRequest.RefreshPolicy;
|
||||||
import org.elasticsearch.action.update.UpdateResponse;
|
import org.elasticsearch.action.update.UpdateResponse;
|
||||||
import org.elasticsearch.client.Client;
|
import org.elasticsearch.client.Client;
|
||||||
|
@ -113,25 +114,23 @@ public class NativeUsersStore extends AbstractComponent {
|
||||||
listener.onFailure(t);
|
listener.onFailure(t);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
if (userNames.length == 1) { // optimization for single user lookup
|
|
||||||
|
if (securityLifecycleService.isSecurityIndexExisting() == false) {
|
||||||
|
// TODO remove this short circuiting and fix tests that fail without this!
|
||||||
|
listener.onResponse(Collections.emptyList());
|
||||||
|
} else if (userNames.length == 1) { // optimization for single user lookup
|
||||||
final String username = userNames[0];
|
final String username = userNames[0];
|
||||||
getUserAndPassword(username, ActionListener.wrap(
|
getUserAndPassword(username, ActionListener.wrap(
|
||||||
(uap) -> listener.onResponse(uap == null ? Collections.emptyList() : Collections.singletonList(uap.user())),
|
(uap) -> listener.onResponse(uap == null ? Collections.emptyList() : Collections.singletonList(uap.user())),
|
||||||
handleException::accept));
|
handleException));
|
||||||
} else {
|
} else {
|
||||||
if (securityLifecycleService.isSecurityIndexOutOfDate()) {
|
securityLifecycleService.prepareIndexIfNeededThenExecute(listener::onFailure, () -> {
|
||||||
listener.onFailure(new IllegalStateException(
|
|
||||||
"Security index is not on the current version - the native realm will not be operational " +
|
|
||||||
"until the upgrade API is run on the security index"));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
final QueryBuilder query;
|
final QueryBuilder query;
|
||||||
if (userNames == null || userNames.length == 0) {
|
if (userNames == null || userNames.length == 0) {
|
||||||
query = QueryBuilders.termQuery(Fields.TYPE.getPreferredName(), USER_DOC_TYPE);
|
query = QueryBuilders.termQuery(Fields.TYPE.getPreferredName(), USER_DOC_TYPE);
|
||||||
} else {
|
} else {
|
||||||
final String[] users = Arrays.asList(userNames).stream()
|
final String[] users = Arrays.asList(userNames).stream()
|
||||||
.map(s -> getIdForUser(USER_DOC_TYPE, s)).toArray(String[]::new);
|
.map(s -> getIdForUser(USER_DOC_TYPE, s)).toArray(String[]::new);
|
||||||
query = QueryBuilders.boolQuery().filter(QueryBuilders.idsQuery(INDEX_TYPE).addIds(users));
|
query = QueryBuilders.boolQuery().filter(QueryBuilders.idsQuery(INDEX_TYPE).addIds(users));
|
||||||
}
|
}
|
||||||
final Supplier<ThreadContext.StoredContext> supplier = client.threadPool().getThreadContext().newRestorableContext(false);
|
final Supplier<ThreadContext.StoredContext> supplier = client.threadPool().getThreadContext().newRestorableContext(false);
|
||||||
|
@ -148,10 +147,7 @@ public class NativeUsersStore extends AbstractComponent {
|
||||||
return u != null ? u.user() : null;
|
return u != null ? u.user() : null;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
} catch (Exception e) {
|
});
|
||||||
logger.error(new ParameterizedMessage("unable to retrieve users {}", Arrays.toString(userNames)), e);
|
|
||||||
listener.onFailure(e);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -159,43 +155,34 @@ public class NativeUsersStore extends AbstractComponent {
|
||||||
* Async method to retrieve a user and their password
|
* Async method to retrieve a user and their password
|
||||||
*/
|
*/
|
||||||
private void getUserAndPassword(final String user, final ActionListener<UserAndPassword> listener) {
|
private void getUserAndPassword(final String user, final ActionListener<UserAndPassword> listener) {
|
||||||
if (securityLifecycleService.isSecurityIndexOutOfDate()) {
|
if (securityLifecycleService.isSecurityIndexExisting() == false) {
|
||||||
listener.onFailure(new IllegalStateException(
|
// TODO remove this short circuiting and fix tests that fail without this!
|
||||||
"Security index is not on the current version - the native realm will not be operational until " +
|
|
||||||
"the upgrade API is run on the security index"));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
executeAsyncWithOrigin(client.threadPool().getThreadContext(), SECURITY_ORIGIN,
|
|
||||||
client.prepareGet(SecurityLifecycleService.SECURITY_INDEX_NAME,
|
|
||||||
INDEX_TYPE, getIdForUser(USER_DOC_TYPE, user)).request(),
|
|
||||||
new ActionListener<GetResponse>() {
|
|
||||||
@Override
|
|
||||||
public void onResponse(GetResponse response) {
|
|
||||||
listener.onResponse(transformUser(response.getId(), response.getSource()));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onFailure(Exception t) {
|
|
||||||
if (t instanceof IndexNotFoundException) {
|
|
||||||
logger.trace(
|
|
||||||
(org.apache.logging.log4j.util.Supplier<?>) () -> new ParameterizedMessage(
|
|
||||||
"could not retrieve user [{}] because security index does not exist", user), t);
|
|
||||||
} else {
|
|
||||||
logger.error(new ParameterizedMessage("failed to retrieve user [{}]", user), t);
|
|
||||||
}
|
|
||||||
// We don't invoke the onFailure listener here, instead
|
|
||||||
// we call the response with a null user
|
|
||||||
listener.onResponse(null);
|
|
||||||
}
|
|
||||||
}, client::get);
|
|
||||||
} catch (IndexNotFoundException infe) {
|
|
||||||
logger.trace((org.apache.logging.log4j.util.Supplier<?>)
|
|
||||||
() -> new ParameterizedMessage("could not retrieve user [{}] because security index does not exist", user));
|
|
||||||
listener.onResponse(null);
|
listener.onResponse(null);
|
||||||
} catch (Exception e) {
|
} else {
|
||||||
logger.error(new ParameterizedMessage("unable to retrieve user [{}]", user), e);
|
securityLifecycleService.prepareIndexIfNeededThenExecute(listener::onFailure, () ->
|
||||||
listener.onFailure(e);
|
executeAsyncWithOrigin(client.threadPool().getThreadContext(), SECURITY_ORIGIN,
|
||||||
|
client.prepareGet(SecurityLifecycleService.SECURITY_INDEX_NAME,
|
||||||
|
INDEX_TYPE, getIdForUser(USER_DOC_TYPE, user)).request(),
|
||||||
|
new ActionListener<GetResponse>() {
|
||||||
|
@Override
|
||||||
|
public void onResponse(GetResponse response) {
|
||||||
|
listener.onResponse(transformUser(response.getId(), response.getSource()));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onFailure(Exception t) {
|
||||||
|
if (t instanceof IndexNotFoundException) {
|
||||||
|
logger.trace(
|
||||||
|
(org.apache.logging.log4j.util.Supplier<?>) () -> new ParameterizedMessage(
|
||||||
|
"could not retrieve user [{}] because security index does not exist", user), t);
|
||||||
|
} else {
|
||||||
|
logger.error(new ParameterizedMessage("failed to retrieve user [{}]", user), t);
|
||||||
|
}
|
||||||
|
// We don't invoke the onFailure listener here, instead
|
||||||
|
// we call the response with a null user
|
||||||
|
listener.onResponse(null);
|
||||||
|
}
|
||||||
|
}, client::get));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -208,55 +195,46 @@ public class NativeUsersStore extends AbstractComponent {
|
||||||
assert SystemUser.NAME.equals(username) == false && XPackUser.NAME.equals(username) == false : username + "is internal!";
|
assert SystemUser.NAME.equals(username) == false && XPackUser.NAME.equals(username) == false : username + "is internal!";
|
||||||
if (isTribeNode) {
|
if (isTribeNode) {
|
||||||
listener.onFailure(new UnsupportedOperationException("users may not be created or modified using a tribe node"));
|
listener.onFailure(new UnsupportedOperationException("users may not be created or modified using a tribe node"));
|
||||||
return;
|
|
||||||
} else if (securityLifecycleService.isSecurityIndexOutOfDate()) {
|
|
||||||
listener.onFailure(new IllegalStateException(
|
|
||||||
"Security index is not on the current version - the native realm will not be operational until " +
|
|
||||||
"the upgrade API is run on the security index"));
|
|
||||||
return;
|
|
||||||
} else if (securityLifecycleService.isSecurityIndexWriteable() == false) {
|
|
||||||
listener.onFailure(new IllegalStateException("password cannot be changed as user service cannot write until template and " +
|
|
||||||
"mappings are up to date"));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
final String docType;
|
|
||||||
if (ClientReservedRealm.isReserved(username, settings)) {
|
|
||||||
docType = RESERVED_USER_TYPE;
|
|
||||||
} else {
|
} else {
|
||||||
docType = USER_DOC_TYPE;
|
final String docType;
|
||||||
}
|
if (ClientReservedRealm.isReserved(username, settings)) {
|
||||||
|
docType = RESERVED_USER_TYPE;
|
||||||
|
} else {
|
||||||
|
docType = USER_DOC_TYPE;
|
||||||
|
}
|
||||||
|
|
||||||
securityLifecycleService.createIndexIfNeededThenExecute(listener, () -> {
|
securityLifecycleService.prepareIndexIfNeededThenExecute(listener::onFailure, () -> {
|
||||||
executeAsyncWithOrigin(client.threadPool().getThreadContext(), SECURITY_ORIGIN,
|
executeAsyncWithOrigin(client.threadPool().getThreadContext(), SECURITY_ORIGIN,
|
||||||
client.prepareUpdate(SecurityLifecycleService.SECURITY_INDEX_NAME, INDEX_TYPE, getIdForUser(docType, username))
|
client.prepareUpdate(SecurityLifecycleService.SECURITY_INDEX_NAME, INDEX_TYPE, getIdForUser(docType, username))
|
||||||
.setDoc(Requests.INDEX_CONTENT_TYPE, Fields.PASSWORD.getPreferredName(), String.valueOf(request.passwordHash()))
|
.setDoc(Requests.INDEX_CONTENT_TYPE, Fields.PASSWORD.getPreferredName(),
|
||||||
.setRefreshPolicy(request.getRefreshPolicy()).request(),
|
String.valueOf(request.passwordHash()))
|
||||||
new ActionListener<UpdateResponse>() {
|
.setRefreshPolicy(request.getRefreshPolicy()).request(),
|
||||||
@Override
|
new ActionListener<UpdateResponse>() {
|
||||||
public void onResponse(UpdateResponse updateResponse) {
|
@Override
|
||||||
assert updateResponse.getResult() == DocWriteResponse.Result.UPDATED;
|
public void onResponse(UpdateResponse updateResponse) {
|
||||||
clearRealmCache(request.username(), listener, null);
|
assert updateResponse.getResult() == DocWriteResponse.Result.UPDATED;
|
||||||
}
|
clearRealmCache(request.username(), listener, null);
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onFailure(Exception e) {
|
|
||||||
if (isIndexNotFoundOrDocumentMissing(e)) {
|
|
||||||
if (docType.equals(RESERVED_USER_TYPE)) {
|
|
||||||
createReservedUser(username, request.passwordHash(), request.getRefreshPolicy(), listener);
|
|
||||||
} else {
|
|
||||||
logger.debug((org.apache.logging.log4j.util.Supplier<?>) () ->
|
|
||||||
new ParameterizedMessage("failed to change password for user [{}]", request.username()), e);
|
|
||||||
ValidationException validationException = new ValidationException();
|
|
||||||
validationException.addValidationError("user must exist in order to change password");
|
|
||||||
listener.onFailure(validationException);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
listener.onFailure(e);
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}, client::update);
|
@Override
|
||||||
});
|
public void onFailure(Exception e) {
|
||||||
|
if (isIndexNotFoundOrDocumentMissing(e)) {
|
||||||
|
if (docType.equals(RESERVED_USER_TYPE)) {
|
||||||
|
createReservedUser(username, request.passwordHash(), request.getRefreshPolicy(), listener);
|
||||||
|
} else {
|
||||||
|
logger.debug((org.apache.logging.log4j.util.Supplier<?>) () ->
|
||||||
|
new ParameterizedMessage("failed to change password for user [{}]", request.username()), e);
|
||||||
|
ValidationException validationException = new ValidationException();
|
||||||
|
validationException.addValidationError("user must exist in order to change password");
|
||||||
|
listener.onFailure(validationException);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
listener.onFailure(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, client::update);
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -264,13 +242,7 @@ public class NativeUsersStore extends AbstractComponent {
|
||||||
* has been indexed
|
* has been indexed
|
||||||
*/
|
*/
|
||||||
private void createReservedUser(String username, char[] passwordHash, RefreshPolicy refresh, ActionListener<Void> listener) {
|
private void createReservedUser(String username, char[] passwordHash, RefreshPolicy refresh, ActionListener<Void> listener) {
|
||||||
if (securityLifecycleService.isSecurityIndexOutOfDate()) {
|
securityLifecycleService.prepareIndexIfNeededThenExecute(listener::onFailure, () -> {
|
||||||
listener.onFailure(new IllegalStateException(
|
|
||||||
"Security index is not on the current version - the native realm will not be operational until " +
|
|
||||||
"the upgrade API is run on the security index"));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
securityLifecycleService.createIndexIfNeededThenExecute(listener, () -> {
|
|
||||||
executeAsyncWithOrigin(client.threadPool().getThreadContext(), SECURITY_ORIGIN,
|
executeAsyncWithOrigin(client.threadPool().getThreadContext(), SECURITY_ORIGIN,
|
||||||
client.prepareIndex(SecurityLifecycleService.SECURITY_INDEX_NAME, INDEX_TYPE,
|
client.prepareIndex(SecurityLifecycleService.SECURITY_INDEX_NAME, INDEX_TYPE,
|
||||||
getIdForUser(RESERVED_USER_TYPE, username))
|
getIdForUser(RESERVED_USER_TYPE, username))
|
||||||
|
@ -301,27 +273,10 @@ public class NativeUsersStore extends AbstractComponent {
|
||||||
public void putUser(final PutUserRequest request, final ActionListener<Boolean> listener) {
|
public void putUser(final PutUserRequest request, final ActionListener<Boolean> listener) {
|
||||||
if (isTribeNode) {
|
if (isTribeNode) {
|
||||||
listener.onFailure(new UnsupportedOperationException("users may not be created or modified using a tribe node"));
|
listener.onFailure(new UnsupportedOperationException("users may not be created or modified using a tribe node"));
|
||||||
return;
|
} else if (request.passwordHash() == null) {
|
||||||
} else if (securityLifecycleService.isSecurityIndexOutOfDate()) {
|
updateUserWithoutPassword(request, listener);
|
||||||
listener.onFailure(new IllegalStateException(
|
} else {
|
||||||
"Security index is not on the current version - the native realm will not be operational until " +
|
indexUser(request, listener);
|
||||||
"the upgrade API is run on the security index"));
|
|
||||||
return;
|
|
||||||
} else if (securityLifecycleService.isSecurityIndexWriteable() == false) {
|
|
||||||
listener.onFailure(new IllegalStateException("user cannot be created or changed as the user service cannot write until " +
|
|
||||||
"template and mappings are up to date"));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
if (request.passwordHash() == null) {
|
|
||||||
updateUserWithoutPassword(request, listener);
|
|
||||||
} else {
|
|
||||||
indexUser(request, listener);
|
|
||||||
}
|
|
||||||
} catch (Exception e) {
|
|
||||||
logger.error(new ParameterizedMessage("unable to put user [{}]", request.username()), e);
|
|
||||||
listener.onFailure(e);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -330,9 +285,8 @@ public class NativeUsersStore extends AbstractComponent {
|
||||||
*/
|
*/
|
||||||
private void updateUserWithoutPassword(final PutUserRequest putUserRequest, final ActionListener<Boolean> listener) {
|
private void updateUserWithoutPassword(final PutUserRequest putUserRequest, final ActionListener<Boolean> listener) {
|
||||||
assert putUserRequest.passwordHash() == null;
|
assert putUserRequest.passwordHash() == null;
|
||||||
assert !securityLifecycleService.isSecurityIndexOutOfDate() : "security index should be up to date";
|
|
||||||
// We must have an existing document
|
// We must have an existing document
|
||||||
securityLifecycleService.createIndexIfNeededThenExecute(listener, () -> {
|
securityLifecycleService.prepareIndexIfNeededThenExecute(listener::onFailure, () -> {
|
||||||
executeAsyncWithOrigin(client.threadPool().getThreadContext(), SECURITY_ORIGIN,
|
executeAsyncWithOrigin(client.threadPool().getThreadContext(), SECURITY_ORIGIN,
|
||||||
client.prepareUpdate(SecurityLifecycleService.SECURITY_INDEX_NAME, INDEX_TYPE,
|
client.prepareUpdate(SecurityLifecycleService.SECURITY_INDEX_NAME, INDEX_TYPE,
|
||||||
getIdForUser(USER_DOC_TYPE, putUserRequest.username()))
|
getIdForUser(USER_DOC_TYPE, putUserRequest.username()))
|
||||||
|
@ -375,8 +329,7 @@ public class NativeUsersStore extends AbstractComponent {
|
||||||
|
|
||||||
private void indexUser(final PutUserRequest putUserRequest, final ActionListener<Boolean> listener) {
|
private void indexUser(final PutUserRequest putUserRequest, final ActionListener<Boolean> listener) {
|
||||||
assert putUserRequest.passwordHash() != null;
|
assert putUserRequest.passwordHash() != null;
|
||||||
assert !securityLifecycleService.isSecurityIndexOutOfDate() : "security index should be up to date";
|
securityLifecycleService.prepareIndexIfNeededThenExecute(listener::onFailure, () -> {
|
||||||
securityLifecycleService.createIndexIfNeededThenExecute(listener, () -> {
|
|
||||||
executeAsyncWithOrigin(client.threadPool().getThreadContext(), SECURITY_ORIGIN,
|
executeAsyncWithOrigin(client.threadPool().getThreadContext(), SECURITY_ORIGIN,
|
||||||
client.prepareIndex(SecurityLifecycleService.SECURITY_INDEX_NAME, INDEX_TYPE,
|
client.prepareIndex(SecurityLifecycleService.SECURITY_INDEX_NAME, INDEX_TYPE,
|
||||||
getIdForUser(USER_DOC_TYPE, putUserRequest.username()))
|
getIdForUser(USER_DOC_TYPE, putUserRequest.username()))
|
||||||
|
@ -413,19 +366,7 @@ public class NativeUsersStore extends AbstractComponent {
|
||||||
final ActionListener<Void> listener) {
|
final ActionListener<Void> listener) {
|
||||||
if (isTribeNode) {
|
if (isTribeNode) {
|
||||||
listener.onFailure(new UnsupportedOperationException("users may not be created or modified using a tribe node"));
|
listener.onFailure(new UnsupportedOperationException("users may not be created or modified using a tribe node"));
|
||||||
return;
|
} else if (ClientReservedRealm.isReserved(username, settings)) {
|
||||||
} else if (securityLifecycleService.isSecurityIndexOutOfDate()) {
|
|
||||||
listener.onFailure(new IllegalStateException(
|
|
||||||
"Security index is not on the current version - the native realm will not be operational until " +
|
|
||||||
"the upgrade API is run on the security index"));
|
|
||||||
return;
|
|
||||||
} else if (securityLifecycleService.isSecurityIndexWriteable() == false) {
|
|
||||||
listener.onFailure(new IllegalStateException("enabled status cannot be changed as user service cannot write until template " +
|
|
||||||
"and mappings are up to date"));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ClientReservedRealm.isReserved(username, settings)) {
|
|
||||||
setReservedUserEnabled(username, enabled, refreshPolicy, true, listener);
|
setReservedUserEnabled(username, enabled, refreshPolicy, true, listener);
|
||||||
} else {
|
} else {
|
||||||
setRegularUserEnabled(username, enabled, refreshPolicy, listener);
|
setRegularUserEnabled(username, enabled, refreshPolicy, listener);
|
||||||
|
@ -434,115 +375,92 @@ public class NativeUsersStore extends AbstractComponent {
|
||||||
|
|
||||||
private void setRegularUserEnabled(final String username, final boolean enabled, final RefreshPolicy refreshPolicy,
|
private void setRegularUserEnabled(final String username, final boolean enabled, final RefreshPolicy refreshPolicy,
|
||||||
final ActionListener<Void> listener) {
|
final ActionListener<Void> listener) {
|
||||||
assert !securityLifecycleService.isSecurityIndexOutOfDate() : "security index should be up to date";
|
securityLifecycleService.prepareIndexIfNeededThenExecute(listener::onFailure, () -> {
|
||||||
try {
|
executeAsyncWithOrigin(client.threadPool().getThreadContext(), SECURITY_ORIGIN,
|
||||||
securityLifecycleService.createIndexIfNeededThenExecute(listener, () -> {
|
client.prepareUpdate(SecurityLifecycleService.SECURITY_INDEX_NAME, INDEX_TYPE,
|
||||||
executeAsyncWithOrigin(client.threadPool().getThreadContext(), SECURITY_ORIGIN,
|
getIdForUser(USER_DOC_TYPE, username))
|
||||||
client.prepareUpdate(SecurityLifecycleService.SECURITY_INDEX_NAME, INDEX_TYPE,
|
.setDoc(Requests.INDEX_CONTENT_TYPE, Fields.ENABLED.getPreferredName(), enabled)
|
||||||
getIdForUser(USER_DOC_TYPE, username))
|
.setRefreshPolicy(refreshPolicy)
|
||||||
.setDoc(Requests.INDEX_CONTENT_TYPE, Fields.ENABLED.getPreferredName(), enabled)
|
.request(),
|
||||||
.setRefreshPolicy(refreshPolicy)
|
new ActionListener<UpdateResponse>() {
|
||||||
.request(),
|
@Override
|
||||||
new ActionListener<UpdateResponse>() {
|
public void onResponse(UpdateResponse updateResponse) {
|
||||||
@Override
|
clearRealmCache(username, listener, null);
|
||||||
public void onResponse(UpdateResponse updateResponse) {
|
}
|
||||||
clearRealmCache(username, listener, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onFailure(Exception e) {
|
public void onFailure(Exception e) {
|
||||||
Exception failure = e;
|
Exception failure = e;
|
||||||
if (isIndexNotFoundOrDocumentMissing(e)) {
|
if (isIndexNotFoundOrDocumentMissing(e)) {
|
||||||
// if the index doesn't exist we can never update a user
|
// if the index doesn't exist we can never update a user
|
||||||
// if the document doesn't exist, then this update is not valid
|
// if the document doesn't exist, then this update is not valid
|
||||||
logger.debug((org.apache.logging.log4j.util.Supplier<?>)
|
logger.debug((org.apache.logging.log4j.util.Supplier<?>)
|
||||||
() -> new ParameterizedMessage("failed to {} user [{}]",
|
() -> new ParameterizedMessage("failed to {} user [{}]",
|
||||||
enabled ? "enable" : "disable", username), e);
|
enabled ? "enable" : "disable", username), e);
|
||||||
ValidationException validationException = new ValidationException();
|
ValidationException validationException = new ValidationException();
|
||||||
validationException.addValidationError("only existing users can be " +
|
validationException.addValidationError("only existing users can be " +
|
||||||
(enabled ? "enabled" : "disabled"));
|
(enabled ? "enabled" : "disabled"));
|
||||||
failure = validationException;
|
failure = validationException;
|
||||||
}
|
|
||||||
listener.onFailure(failure);
|
|
||||||
}
|
}
|
||||||
}, client::update);
|
listener.onFailure(failure);
|
||||||
});
|
}
|
||||||
} catch (Exception e) {
|
}, client::update);
|
||||||
listener.onFailure(e);
|
});
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setReservedUserEnabled(final String username, final boolean enabled, final RefreshPolicy refreshPolicy,
|
private void setReservedUserEnabled(final String username, final boolean enabled, final RefreshPolicy refreshPolicy,
|
||||||
boolean clearCache, final ActionListener<Void> listener) {
|
boolean clearCache, final ActionListener<Void> listener) {
|
||||||
assert !securityLifecycleService.isSecurityIndexOutOfDate() : "security index should be up to date";
|
securityLifecycleService.prepareIndexIfNeededThenExecute(listener::onFailure, () -> {
|
||||||
try {
|
executeAsyncWithOrigin(client.threadPool().getThreadContext(), SECURITY_ORIGIN,
|
||||||
securityLifecycleService.createIndexIfNeededThenExecute(listener, () -> {
|
client.prepareUpdate(SecurityLifecycleService.SECURITY_INDEX_NAME, INDEX_TYPE,
|
||||||
executeAsyncWithOrigin(client.threadPool().getThreadContext(), SECURITY_ORIGIN,
|
getIdForUser(RESERVED_USER_TYPE, username))
|
||||||
client.prepareUpdate(SecurityLifecycleService.SECURITY_INDEX_NAME, INDEX_TYPE,
|
.setDoc(Requests.INDEX_CONTENT_TYPE, Fields.ENABLED.getPreferredName(), enabled)
|
||||||
getIdForUser(RESERVED_USER_TYPE, username))
|
.setUpsert(XContentType.JSON,
|
||||||
.setDoc(Requests.INDEX_CONTENT_TYPE, Fields.ENABLED.getPreferredName(), enabled)
|
Fields.PASSWORD.getPreferredName(), "",
|
||||||
.setUpsert(XContentType.JSON,
|
Fields.ENABLED.getPreferredName(), enabled,
|
||||||
Fields.PASSWORD.getPreferredName(), "",
|
Fields.TYPE.getPreferredName(), RESERVED_USER_TYPE)
|
||||||
Fields.ENABLED.getPreferredName(), enabled,
|
.setRefreshPolicy(refreshPolicy)
|
||||||
Fields.TYPE.getPreferredName(), RESERVED_USER_TYPE)
|
.request(),
|
||||||
.setRefreshPolicy(refreshPolicy)
|
new ActionListener<UpdateResponse>() {
|
||||||
.request(),
|
@Override
|
||||||
new ActionListener<UpdateResponse>() {
|
public void onResponse(UpdateResponse updateResponse) {
|
||||||
|
if (clearCache) {
|
||||||
|
clearRealmCache(username, listener, null);
|
||||||
|
} else {
|
||||||
|
listener.onResponse(null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onFailure(Exception e) {
|
||||||
|
listener.onFailure(e);
|
||||||
|
}
|
||||||
|
}, client::update);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public void deleteUser(final DeleteUserRequest deleteUserRequest, final ActionListener<Boolean> listener) {
|
||||||
|
if (isTribeNode) {
|
||||||
|
listener.onFailure(new UnsupportedOperationException("users may not be deleted using a tribe node"));
|
||||||
|
} else {
|
||||||
|
securityLifecycleService.prepareIndexIfNeededThenExecute(listener::onFailure, () -> {
|
||||||
|
DeleteRequest request = client.prepareDelete(SecurityLifecycleService.SECURITY_INDEX_NAME,
|
||||||
|
INDEX_TYPE, getIdForUser(USER_DOC_TYPE, deleteUserRequest.username())).request();
|
||||||
|
request.setRefreshPolicy(deleteUserRequest.getRefreshPolicy());
|
||||||
|
executeAsyncWithOrigin(client.threadPool().getThreadContext(), SECURITY_ORIGIN, request,
|
||||||
|
new ActionListener<DeleteResponse>() {
|
||||||
@Override
|
@Override
|
||||||
public void onResponse(UpdateResponse updateResponse) {
|
public void onResponse(DeleteResponse deleteResponse) {
|
||||||
if (clearCache) {
|
clearRealmCache(deleteUserRequest.username(), listener,
|
||||||
clearRealmCache(username, listener, null);
|
deleteResponse.getResult() == DocWriteResponse.Result.DELETED);
|
||||||
} else {
|
|
||||||
listener.onResponse(null);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onFailure(Exception e) {
|
public void onFailure(Exception e) {
|
||||||
listener.onFailure(e);
|
listener.onFailure(e);
|
||||||
}
|
}
|
||||||
}, client::update);
|
}, client::delete);
|
||||||
});
|
});
|
||||||
} catch (Exception e) {
|
|
||||||
listener.onFailure(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void deleteUser(final DeleteUserRequest deleteUserRequest, final ActionListener<Boolean> listener) {
|
|
||||||
if (isTribeNode) {
|
|
||||||
listener.onFailure(new UnsupportedOperationException("users may not be deleted using a tribe node"));
|
|
||||||
return;
|
|
||||||
} else if (securityLifecycleService.isSecurityIndexOutOfDate()) {
|
|
||||||
listener.onFailure(new IllegalStateException(
|
|
||||||
"Security index is not on the current version - the native realm will not be operational until " +
|
|
||||||
"the upgrade API is run on the security index"));
|
|
||||||
return;
|
|
||||||
} else if (securityLifecycleService.isSecurityIndexWriteable() == false) {
|
|
||||||
listener.onFailure(new IllegalStateException("user cannot be deleted as user service cannot write until template and " +
|
|
||||||
"mappings are up to date"));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
DeleteRequest request = client.prepareDelete(SecurityLifecycleService.SECURITY_INDEX_NAME,
|
|
||||||
INDEX_TYPE, getIdForUser(USER_DOC_TYPE, deleteUserRequest.username())).request();
|
|
||||||
request.indicesOptions().ignoreUnavailable();
|
|
||||||
request.setRefreshPolicy(deleteUserRequest.getRefreshPolicy());
|
|
||||||
executeAsyncWithOrigin(client.threadPool().getThreadContext(), SECURITY_ORIGIN, request, new ActionListener<DeleteResponse>() {
|
|
||||||
@Override
|
|
||||||
public void onResponse(DeleteResponse deleteResponse) {
|
|
||||||
clearRealmCache(deleteUserRequest.username(), listener,
|
|
||||||
deleteResponse.getResult() == DocWriteResponse.Result.DELETED);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onFailure(Exception e) {
|
|
||||||
listener.onFailure(e);
|
|
||||||
}
|
|
||||||
}, client::delete);
|
|
||||||
} catch (Exception e) {
|
|
||||||
logger.error("unable to remove user", e);
|
|
||||||
listener.onFailure(e);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -565,62 +483,52 @@ public class NativeUsersStore extends AbstractComponent {
|
||||||
}
|
}
|
||||||
|
|
||||||
void getReservedUserInfo(String username, ActionListener<ReservedUserInfo> listener) {
|
void getReservedUserInfo(String username, ActionListener<ReservedUserInfo> listener) {
|
||||||
if (!securityLifecycleService.isSecurityIndexExisting()) {
|
if (securityLifecycleService.isSecurityIndexExisting() == false) {
|
||||||
listener.onFailure(new IllegalStateException("Attempt to get reserved user info but the security index does not exist"));
|
// TODO remove this short circuiting and fix tests that fail without this!
|
||||||
return;
|
listener.onResponse(null);
|
||||||
} else if (securityLifecycleService.isSecurityIndexOutOfDate()) {
|
} else {
|
||||||
listener.onFailure(new IllegalStateException(
|
securityLifecycleService.prepareIndexIfNeededThenExecute(listener::onFailure, () ->
|
||||||
"Security index is not on the current version - the native realm not be operational until " +
|
executeAsyncWithOrigin(client.threadPool().getThreadContext(), SECURITY_ORIGIN,
|
||||||
"the upgrade API is run on the security index"));
|
client.prepareGet(SecurityLifecycleService.SECURITY_INDEX_NAME, INDEX_TYPE,
|
||||||
return;
|
getIdForUser(RESERVED_USER_TYPE, username)).request(),
|
||||||
}
|
new ActionListener<GetResponse>() {
|
||||||
executeAsyncWithOrigin(client.threadPool().getThreadContext(), SECURITY_ORIGIN,
|
@Override
|
||||||
client.prepareGet(SecurityLifecycleService.SECURITY_INDEX_NAME, INDEX_TYPE, getIdForUser(RESERVED_USER_TYPE, username))
|
public void onResponse(GetResponse getResponse) {
|
||||||
.request(),
|
if (getResponse.isExists()) {
|
||||||
new ActionListener<GetResponse>() {
|
Map<String, Object> sourceMap = getResponse.getSourceAsMap();
|
||||||
@Override
|
String password = (String) sourceMap.get(Fields.PASSWORD.getPreferredName());
|
||||||
public void onResponse(GetResponse getResponse) {
|
Boolean enabled = (Boolean) sourceMap.get(Fields.ENABLED.getPreferredName());
|
||||||
if (getResponse.isExists()) {
|
if (password == null) {
|
||||||
Map<String, Object> sourceMap = getResponse.getSourceAsMap();
|
listener.onFailure(new IllegalStateException("password hash must not be null!"));
|
||||||
String password = (String) sourceMap.get(Fields.PASSWORD.getPreferredName());
|
} else if (enabled == null) {
|
||||||
Boolean enabled = (Boolean) sourceMap.get(Fields.ENABLED.getPreferredName());
|
listener.onFailure(new IllegalStateException("enabled must not be null!"));
|
||||||
if (password == null) {
|
} else if (password.isEmpty()) {
|
||||||
listener.onFailure(new IllegalStateException("password hash must not be null!"));
|
listener.onResponse((enabled ? ReservedRealm.ENABLED_DEFAULT_USER_INFO : ReservedRealm
|
||||||
} else if (enabled == null) {
|
.DISABLED_DEFAULT_USER_INFO).deepClone());
|
||||||
listener.onFailure(new IllegalStateException("enabled must not be null!"));
|
} else {
|
||||||
} else if (password.isEmpty()) {
|
listener.onResponse(new ReservedUserInfo(password.toCharArray(), enabled, false));
|
||||||
listener.onResponse((enabled ? ReservedRealm.ENABLED_DEFAULT_USER_INFO : ReservedRealm
|
}
|
||||||
.DISABLED_DEFAULT_USER_INFO).deepClone());
|
} else {
|
||||||
} else {
|
listener.onResponse(null);
|
||||||
listener.onResponse(new ReservedUserInfo(password.toCharArray(), enabled, false));
|
}
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
listener.onResponse(null);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onFailure(Exception e) {
|
public void onFailure(Exception e) {
|
||||||
if (e instanceof IndexNotFoundException) {
|
if (TransportActions.isShardNotAvailableException(e)) {
|
||||||
logger.trace((org.apache.logging.log4j.util.Supplier<?>) () -> new ParameterizedMessage(
|
logger.trace((org.apache.logging.log4j.util.Supplier<?>) () -> new ParameterizedMessage(
|
||||||
"could not retrieve built in user [{}] info since security index does not exist", username), e);
|
"could not retrieve built in user [{}] info since security index unavailable", username),
|
||||||
listener.onResponse(null);
|
e);
|
||||||
} else {
|
}
|
||||||
logger.error(new ParameterizedMessage("failed to retrieve built in user [{}] info", username), e);
|
listener.onFailure(e);
|
||||||
listener.onFailure(null);
|
}
|
||||||
}
|
}, client::get));
|
||||||
}
|
}
|
||||||
}, client::get);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void getAllReservedUserInfo(ActionListener<Map<String, ReservedUserInfo>> listener) {
|
void getAllReservedUserInfo(ActionListener<Map<String, ReservedUserInfo>> listener) {
|
||||||
if (securityLifecycleService.isSecurityIndexOutOfDate()) {
|
securityLifecycleService.prepareIndexIfNeededThenExecute(listener::onFailure, () ->
|
||||||
listener.onFailure(new IllegalStateException(
|
executeAsyncWithOrigin(client.threadPool().getThreadContext(), SECURITY_ORIGIN,
|
||||||
"Security index is not on the current version - the native realm will not be operational until " +
|
|
||||||
"the upgrade API is run on the security index"));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
executeAsyncWithOrigin(client.threadPool().getThreadContext(), SECURITY_ORIGIN,
|
|
||||||
client.prepareSearch(SecurityLifecycleService.SECURITY_INDEX_NAME)
|
client.prepareSearch(SecurityLifecycleService.SECURITY_INDEX_NAME)
|
||||||
.setQuery(QueryBuilders.termQuery(Fields.TYPE.getPreferredName(), RESERVED_USER_TYPE))
|
.setQuery(QueryBuilders.termQuery(Fields.TYPE.getPreferredName(), RESERVED_USER_TYPE))
|
||||||
.setFetchSource(true).request(),
|
.setFetchSource(true).request(),
|
||||||
|
@ -661,7 +569,7 @@ public class NativeUsersStore extends AbstractComponent {
|
||||||
listener.onFailure(e);
|
listener.onFailure(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}, client::search);
|
}, client::search));
|
||||||
}
|
}
|
||||||
|
|
||||||
private <Response> void clearRealmCache(String username, ActionListener<Response> listener, Response response) {
|
private <Response> void clearRealmCache(String username, ActionListener<Response> listener, Response response) {
|
||||||
|
|
|
@ -167,8 +167,6 @@ public class NativeRoleMappingStore extends AbstractComponent implements UserRol
|
||||||
listener.onFailure(new IllegalStateException(
|
listener.onFailure(new IllegalStateException(
|
||||||
"Security index is not on the current version - the native realm will not be operational until " +
|
"Security index is not on the current version - the native realm will not be operational until " +
|
||||||
"the upgrade API is run on the security index"));
|
"the upgrade API is run on the security index"));
|
||||||
} else if (securityLifecycleService.isSecurityIndexWriteable() == false) {
|
|
||||||
listener.onFailure(new IllegalStateException("role-mappings cannot be modified until template and mappings are up to date"));
|
|
||||||
} else {
|
} else {
|
||||||
try {
|
try {
|
||||||
inner.accept(request, ActionListener.wrap(r -> refreshRealms(listener, r), listener::onFailure));
|
inner.accept(request, ActionListener.wrap(r -> refreshRealms(listener, r), listener::onFailure));
|
||||||
|
@ -181,7 +179,7 @@ public class NativeRoleMappingStore extends AbstractComponent implements UserRol
|
||||||
|
|
||||||
private void innerPutMapping(PutRoleMappingRequest request, ActionListener<Boolean> listener) {
|
private void innerPutMapping(PutRoleMappingRequest request, ActionListener<Boolean> listener) {
|
||||||
final ExpressionRoleMapping mapping = request.getMapping();
|
final ExpressionRoleMapping mapping = request.getMapping();
|
||||||
securityLifecycleService.createIndexIfNeededThenExecute(listener, () -> {
|
securityLifecycleService.prepareIndexIfNeededThenExecute(listener::onFailure, () -> {
|
||||||
final XContentBuilder xContentBuilder;
|
final XContentBuilder xContentBuilder;
|
||||||
try {
|
try {
|
||||||
xContentBuilder = mapping.toXContent(jsonBuilder(), ToXContent.EMPTY_PARAMS, true);
|
xContentBuilder = mapping.toXContent(jsonBuilder(), ToXContent.EMPTY_PARAMS, true);
|
||||||
|
@ -270,11 +268,11 @@ public class NativeRoleMappingStore extends AbstractComponent implements UserRol
|
||||||
} else {
|
} else {
|
||||||
logger.info("The security index is not yet available - no role mappings can be loaded");
|
logger.info("The security index is not yet available - no role mappings can be loaded");
|
||||||
if (logger.isDebugEnabled()) {
|
if (logger.isDebugEnabled()) {
|
||||||
logger.debug("Security Index [{}] [exists: {}] [available: {}] [writable: {}]",
|
logger.debug("Security Index [{}] [exists: {}] [available: {}] [mapping up to date: {}]",
|
||||||
SECURITY_INDEX_NAME,
|
SECURITY_INDEX_NAME,
|
||||||
securityLifecycleService.isSecurityIndexExisting(),
|
securityLifecycleService.isSecurityIndexExisting(),
|
||||||
securityLifecycleService.isSecurityIndexAvailable(),
|
securityLifecycleService.isSecurityIndexAvailable(),
|
||||||
securityLifecycleService.isSecurityIndexWriteable()
|
securityLifecycleService.isSecurityIndexMappingUpToDate()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
listener.onResponse(Collections.emptyList());
|
listener.onResponse(Collections.emptyList());
|
||||||
|
|
|
@ -31,8 +31,6 @@ import org.elasticsearch.common.util.concurrent.ThreadContext;
|
||||||
import org.elasticsearch.common.xcontent.ToXContent;
|
import org.elasticsearch.common.xcontent.ToXContent;
|
||||||
import org.elasticsearch.common.xcontent.XContentBuilder;
|
import org.elasticsearch.common.xcontent.XContentBuilder;
|
||||||
import org.elasticsearch.common.xcontent.XContentType;
|
import org.elasticsearch.common.xcontent.XContentType;
|
||||||
import org.elasticsearch.index.IndexNotFoundException;
|
|
||||||
import org.elasticsearch.index.get.GetResult;
|
|
||||||
import org.elasticsearch.index.query.QueryBuilder;
|
import org.elasticsearch.index.query.QueryBuilder;
|
||||||
import org.elasticsearch.index.query.QueryBuilders;
|
import org.elasticsearch.index.query.QueryBuilders;
|
||||||
import org.elasticsearch.license.LicenseUtils;
|
import org.elasticsearch.license.LicenseUtils;
|
||||||
|
@ -105,16 +103,15 @@ public class NativeRolesStore extends AbstractComponent {
|
||||||
* Retrieve a list of roles, if rolesToGet is null or empty, fetch all roles
|
* Retrieve a list of roles, if rolesToGet is null or empty, fetch all roles
|
||||||
*/
|
*/
|
||||||
public void getRoleDescriptors(String[] names, final ActionListener<Collection<RoleDescriptor>> listener) {
|
public void getRoleDescriptors(String[] names, final ActionListener<Collection<RoleDescriptor>> listener) {
|
||||||
if (names != null && names.length == 1) {
|
if (securityLifecycleService.isSecurityIndexExisting() == false) {
|
||||||
|
// TODO remove this short circuiting and fix tests that fail without this!
|
||||||
|
listener.onResponse(Collections.emptyList());
|
||||||
|
} else if (names != null && names.length == 1) {
|
||||||
getRoleDescriptor(Objects.requireNonNull(names[0]), ActionListener.wrap(roleDescriptor ->
|
getRoleDescriptor(Objects.requireNonNull(names[0]), ActionListener.wrap(roleDescriptor ->
|
||||||
listener.onResponse(roleDescriptor == null ? Collections.emptyList() : Collections.singletonList(roleDescriptor)),
|
listener.onResponse(roleDescriptor == null ? Collections.emptyList() : Collections.singletonList(roleDescriptor)),
|
||||||
listener::onFailure));
|
listener::onFailure));
|
||||||
} else if (securityLifecycleService.isSecurityIndexOutOfDate()) {
|
|
||||||
listener.onFailure(new IllegalStateException(
|
|
||||||
"Security index is not on the current version - the native realm will not be operational until " +
|
|
||||||
"the upgrade API is run on the security index"));
|
|
||||||
} else {
|
} else {
|
||||||
try {
|
securityLifecycleService.prepareIndexIfNeededThenExecute(listener::onFailure, () -> {
|
||||||
QueryBuilder query;
|
QueryBuilder query;
|
||||||
if (names == null || names.length == 0) {
|
if (names == null || names.length == 0) {
|
||||||
query = QueryBuilders.termQuery(RoleDescriptor.Fields.TYPE.getPreferredName(), ROLE_TYPE);
|
query = QueryBuilders.termQuery(RoleDescriptor.Fields.TYPE.getPreferredName(), ROLE_TYPE);
|
||||||
|
@ -134,61 +131,39 @@ public class NativeRolesStore extends AbstractComponent {
|
||||||
ScrollHelper.fetchAllByEntity(client, request, new ContextPreservingActionListener<>(supplier, listener),
|
ScrollHelper.fetchAllByEntity(client, request, new ContextPreservingActionListener<>(supplier, listener),
|
||||||
(hit) -> transformRole(hit.getId(), hit.getSourceRef(), logger, licenseState));
|
(hit) -> transformRole(hit.getId(), hit.getSourceRef(), logger, licenseState));
|
||||||
}
|
}
|
||||||
} catch (Exception e) {
|
});
|
||||||
logger.error(new ParameterizedMessage("unable to retrieve roles {}", Arrays.toString(names)), e);
|
|
||||||
listener.onFailure(e);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void deleteRole(final DeleteRoleRequest deleteRoleRequest, final ActionListener<Boolean> listener) {
|
public void deleteRole(final DeleteRoleRequest deleteRoleRequest, final ActionListener<Boolean> listener) {
|
||||||
if (isTribeNode) {
|
if (isTribeNode) {
|
||||||
listener.onFailure(new UnsupportedOperationException("roles may not be deleted using a tribe node"));
|
listener.onFailure(new UnsupportedOperationException("roles may not be deleted using a tribe node"));
|
||||||
return;
|
} else {
|
||||||
} else if (securityLifecycleService.isSecurityIndexOutOfDate()) {
|
securityLifecycleService.prepareIndexIfNeededThenExecute(listener::onFailure, () -> {
|
||||||
listener.onFailure(new IllegalStateException(
|
DeleteRequest request = client.prepareDelete(SecurityLifecycleService.SECURITY_INDEX_NAME,
|
||||||
"Security index is not on the current version - the native realm will not be operational until " +
|
ROLE_DOC_TYPE, getIdForUser(deleteRoleRequest.name())).request();
|
||||||
"the upgrade API is run on the security index"));
|
request.setRefreshPolicy(deleteRoleRequest.getRefreshPolicy());
|
||||||
return;
|
executeAsyncWithOrigin(client.threadPool().getThreadContext(), SECURITY_ORIGIN, request,
|
||||||
} else if (securityLifecycleService.isSecurityIndexWriteable() == false) {
|
new ActionListener<DeleteResponse>() {
|
||||||
listener.onFailure(new IllegalStateException("role cannot be deleted as service cannot write until template and " +
|
@Override
|
||||||
"mappings are up to date"));
|
public void onResponse(DeleteResponse deleteResponse) {
|
||||||
return;
|
clearRoleCache(deleteRoleRequest.name(), listener,
|
||||||
}
|
deleteResponse.getResult() == DocWriteResponse.Result.DELETED);
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
@Override
|
||||||
DeleteRequest request = client.prepareDelete(SecurityLifecycleService.SECURITY_INDEX_NAME,
|
public void onFailure(Exception e) {
|
||||||
ROLE_DOC_TYPE, getIdForUser(deleteRoleRequest.name())).request();
|
logger.error("failed to delete role from the index", e);
|
||||||
request.setRefreshPolicy(deleteRoleRequest.getRefreshPolicy());
|
listener.onFailure(e);
|
||||||
executeAsyncWithOrigin(client.threadPool().getThreadContext(), SECURITY_ORIGIN, request,
|
}
|
||||||
new ActionListener<DeleteResponse>() {
|
}, client::delete);
|
||||||
@Override
|
});
|
||||||
public void onResponse(DeleteResponse deleteResponse) {
|
|
||||||
clearRoleCache(deleteRoleRequest.name(), listener,
|
|
||||||
deleteResponse.getResult() == DocWriteResponse.Result.DELETED);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onFailure(Exception e) {
|
|
||||||
logger.error("failed to delete role from the index", e);
|
|
||||||
listener.onFailure(e);
|
|
||||||
}
|
|
||||||
}, client::delete);
|
|
||||||
} catch (IndexNotFoundException e) {
|
|
||||||
logger.trace("security index does not exist", e);
|
|
||||||
listener.onResponse(false);
|
|
||||||
} catch (Exception e) {
|
|
||||||
logger.error("unable to remove role", e);
|
|
||||||
listener.onFailure(e);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void putRole(final PutRoleRequest request, final RoleDescriptor role, final ActionListener<Boolean> listener) {
|
public void putRole(final PutRoleRequest request, final RoleDescriptor role, final ActionListener<Boolean> listener) {
|
||||||
if (isTribeNode) {
|
if (isTribeNode) {
|
||||||
listener.onFailure(new UnsupportedOperationException("roles may not be created or modified using a tribe node"));
|
listener.onFailure(new UnsupportedOperationException("roles may not be created or modified using a tribe node"));
|
||||||
} else if (securityLifecycleService.isSecurityIndexWriteable() == false) {
|
|
||||||
listener.onFailure(new IllegalStateException("role cannot be created or modified as service cannot write until template and " +
|
|
||||||
"mappings are up to date"));
|
|
||||||
} else if (licenseState.isDocumentAndFieldLevelSecurityAllowed()) {
|
} else if (licenseState.isDocumentAndFieldLevelSecurityAllowed()) {
|
||||||
innerPutRole(request, role, listener);
|
innerPutRole(request, role, listener);
|
||||||
} else if (role.isUsingDocumentOrFieldLevelSecurity()) {
|
} else if (role.isUsingDocumentOrFieldLevelSecurity()) {
|
||||||
|
@ -200,44 +175,33 @@ public class NativeRolesStore extends AbstractComponent {
|
||||||
|
|
||||||
// pkg-private for testing
|
// pkg-private for testing
|
||||||
void innerPutRole(final PutRoleRequest request, final RoleDescriptor role, final ActionListener<Boolean> listener) {
|
void innerPutRole(final PutRoleRequest request, final RoleDescriptor role, final ActionListener<Boolean> listener) {
|
||||||
if (securityLifecycleService.isSecurityIndexOutOfDate()) {
|
securityLifecycleService.prepareIndexIfNeededThenExecute(listener::onFailure, () -> {
|
||||||
listener.onFailure(new IllegalStateException(
|
final XContentBuilder xContentBuilder;
|
||||||
"Security index is not on the current version - the native realm will not be operational until " +
|
try {
|
||||||
"the upgrade API is run on the security index"));
|
xContentBuilder = role.toXContent(jsonBuilder(), ToXContent.EMPTY_PARAMS, true);
|
||||||
return;
|
} catch (IOException e) {
|
||||||
}
|
listener.onFailure(e);
|
||||||
try {
|
return;
|
||||||
securityLifecycleService.createIndexIfNeededThenExecute(listener, () -> {
|
}
|
||||||
final XContentBuilder xContentBuilder;
|
executeAsyncWithOrigin(client.threadPool().getThreadContext(), SECURITY_ORIGIN,
|
||||||
try {
|
client.prepareIndex(SecurityLifecycleService.SECURITY_INDEX_NAME, ROLE_DOC_TYPE, getIdForUser(role.getName()))
|
||||||
xContentBuilder = role.toXContent(jsonBuilder(), ToXContent.EMPTY_PARAMS, true);
|
.setSource(xContentBuilder)
|
||||||
} catch (IOException e) {
|
.setRefreshPolicy(request.getRefreshPolicy())
|
||||||
listener.onFailure(e);
|
.request(),
|
||||||
return;
|
new ActionListener<IndexResponse>() {
|
||||||
}
|
@Override
|
||||||
executeAsyncWithOrigin(client.threadPool().getThreadContext(), SECURITY_ORIGIN,
|
public void onResponse(IndexResponse indexResponse) {
|
||||||
client.prepareIndex(SecurityLifecycleService.SECURITY_INDEX_NAME, ROLE_DOC_TYPE, getIdForUser(role.getName()))
|
final boolean created = indexResponse.getResult() == DocWriteResponse.Result.CREATED;
|
||||||
.setSource(xContentBuilder)
|
clearRoleCache(role.getName(), listener, created);
|
||||||
.setRefreshPolicy(request.getRefreshPolicy())
|
}
|
||||||
.request(),
|
|
||||||
new ActionListener<IndexResponse>() {
|
|
||||||
@Override
|
|
||||||
public void onResponse(IndexResponse indexResponse) {
|
|
||||||
final boolean created = indexResponse.getResult() == DocWriteResponse.Result.CREATED;
|
|
||||||
clearRoleCache(role.getName(), listener, created);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onFailure(Exception e) {
|
public void onFailure(Exception e) {
|
||||||
logger.error(new ParameterizedMessage("failed to put role [{}]", request.name()), e);
|
logger.error(new ParameterizedMessage("failed to put role [{}]", request.name()), e);
|
||||||
listener.onFailure(e);
|
listener.onFailure(e);
|
||||||
}
|
}
|
||||||
}, client::index);
|
}, client::index);
|
||||||
});
|
});
|
||||||
} catch (Exception e) {
|
|
||||||
logger.error(new ParameterizedMessage("unable to put role [{}]", request.name()), e);
|
|
||||||
listener.onFailure(e);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void usageStats(ActionListener<Map<String, Object>> listener) {
|
public void usageStats(ActionListener<Map<String, Object>> listener) {
|
||||||
|
@ -248,118 +212,97 @@ public class NativeRolesStore extends AbstractComponent {
|
||||||
usageStats.put("dls", false);
|
usageStats.put("dls", false);
|
||||||
listener.onResponse(usageStats);
|
listener.onResponse(usageStats);
|
||||||
} else {
|
} else {
|
||||||
if (securityLifecycleService.isSecurityIndexOutOfDate()) {
|
securityLifecycleService.prepareIndexIfNeededThenExecute(listener::onFailure, () ->
|
||||||
listener.onFailure(new IllegalStateException(
|
executeAsyncWithOrigin(client.threadPool().getThreadContext(), SECURITY_ORIGIN,
|
||||||
"Security index is not on the current version - the native realm will not be operational until " +
|
client.prepareMultiSearch()
|
||||||
"the upgrade API is run on the security index"));
|
.add(client.prepareSearch(SecurityLifecycleService.SECURITY_INDEX_NAME)
|
||||||
return;
|
.setQuery(QueryBuilders.termQuery(RoleDescriptor.Fields.TYPE.getPreferredName(), ROLE_TYPE))
|
||||||
}
|
.setSize(0))
|
||||||
executeAsyncWithOrigin(client.threadPool().getThreadContext(), SECURITY_ORIGIN,
|
.add(client.prepareSearch(SecurityLifecycleService.SECURITY_INDEX_NAME)
|
||||||
client.prepareMultiSearch()
|
.setQuery(QueryBuilders.boolQuery()
|
||||||
.add(client.prepareSearch(SecurityLifecycleService.SECURITY_INDEX_NAME)
|
.must(QueryBuilders.termQuery(RoleDescriptor.Fields.TYPE.getPreferredName(), ROLE_TYPE))
|
||||||
.setQuery(QueryBuilders.termQuery(RoleDescriptor.Fields.TYPE.getPreferredName(), ROLE_TYPE))
|
.must(QueryBuilders.boolQuery()
|
||||||
.setSize(0))
|
.should(existsQuery("indices.field_security.grant"))
|
||||||
.add(client.prepareSearch(SecurityLifecycleService.SECURITY_INDEX_NAME)
|
.should(existsQuery("indices.field_security.except"))
|
||||||
.setQuery(QueryBuilders.boolQuery()
|
// for backwardscompat with 2.x
|
||||||
.must(QueryBuilders.termQuery(RoleDescriptor.Fields.TYPE.getPreferredName(), ROLE_TYPE))
|
.should(existsQuery("indices.fields"))))
|
||||||
.must(QueryBuilders.boolQuery()
|
.setSize(0)
|
||||||
.should(existsQuery("indices.field_security.grant"))
|
.setTerminateAfter(1))
|
||||||
.should(existsQuery("indices.field_security.except"))
|
.add(client.prepareSearch(SecurityLifecycleService.SECURITY_INDEX_NAME)
|
||||||
// for backwardscompat with 2.x
|
.setQuery(QueryBuilders.boolQuery()
|
||||||
.should(existsQuery("indices.fields"))))
|
.must(QueryBuilders.termQuery(RoleDescriptor.Fields.TYPE.getPreferredName(), ROLE_TYPE))
|
||||||
.setSize(0)
|
.filter(existsQuery("indices.query")))
|
||||||
.setTerminateAfter(1))
|
.setSize(0)
|
||||||
.add(client.prepareSearch(SecurityLifecycleService.SECURITY_INDEX_NAME)
|
.setTerminateAfter(1))
|
||||||
.setQuery(QueryBuilders.boolQuery()
|
.request(),
|
||||||
.must(QueryBuilders.termQuery(RoleDescriptor.Fields.TYPE.getPreferredName(), ROLE_TYPE))
|
new ActionListener<MultiSearchResponse>() {
|
||||||
.filter(existsQuery("indices.query")))
|
@Override
|
||||||
.setSize(0)
|
public void onResponse(MultiSearchResponse items) {
|
||||||
.setTerminateAfter(1))
|
Item[] responses = items.getResponses();
|
||||||
.request(),
|
if (responses[0].isFailure()) {
|
||||||
new ActionListener<MultiSearchResponse>() {
|
usageStats.put("size", 0);
|
||||||
@Override
|
} else {
|
||||||
public void onResponse(MultiSearchResponse items) {
|
usageStats.put("size", responses[0].getResponse().getHits().getTotalHits());
|
||||||
Item[] responses = items.getResponses();
|
}
|
||||||
if (responses[0].isFailure()) {
|
|
||||||
usageStats.put("size", 0);
|
if (responses[1].isFailure()) {
|
||||||
} else {
|
usageStats.put("fls", false);
|
||||||
usageStats.put("size", responses[0].getResponse().getHits().getTotalHits());
|
} else {
|
||||||
|
usageStats.put("fls", responses[1].getResponse().getHits().getTotalHits() > 0L);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (responses[2].isFailure()) {
|
||||||
|
usageStats.put("dls", false);
|
||||||
|
} else {
|
||||||
|
usageStats.put("dls", responses[2].getResponse().getHits().getTotalHits() > 0L);
|
||||||
|
}
|
||||||
|
listener.onResponse(usageStats);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (responses[1].isFailure()) {
|
@Override
|
||||||
usageStats.put("fls", false);
|
public void onFailure(Exception e) {
|
||||||
} else {
|
listener.onFailure(e);
|
||||||
usageStats.put("fls", responses[1].getResponse().getHits().getTotalHits() > 0L);
|
|
||||||
}
|
}
|
||||||
|
}, client::multiSearch));
|
||||||
if (responses[2].isFailure()) {
|
|
||||||
usageStats.put("dls", false);
|
|
||||||
} else {
|
|
||||||
usageStats.put("dls", responses[2].getResponse().getHits().getTotalHits() > 0L);
|
|
||||||
}
|
|
||||||
listener.onResponse(usageStats);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onFailure(Exception e) {
|
|
||||||
listener.onFailure(e);
|
|
||||||
}
|
|
||||||
}, client::multiSearch);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void getRoleDescriptor(final String roleId, ActionListener<RoleDescriptor> roleActionListener) {
|
private void getRoleDescriptor(final String roleId, ActionListener<RoleDescriptor> roleActionListener) {
|
||||||
if (securityLifecycleService.isSecurityIndexExisting() == false) {
|
if (securityLifecycleService.isSecurityIndexExisting() == false) {
|
||||||
|
// TODO remove this short circuiting and fix tests that fail without this!
|
||||||
roleActionListener.onResponse(null);
|
roleActionListener.onResponse(null);
|
||||||
} else {
|
} else {
|
||||||
executeGetRoleRequest(roleId, new ActionListener<GetResponse>() {
|
securityLifecycleService.prepareIndexIfNeededThenExecute(roleActionListener::onFailure, () ->
|
||||||
@Override
|
executeGetRoleRequest(roleId, new ActionListener<GetResponse>() {
|
||||||
public void onResponse(GetResponse response) {
|
@Override
|
||||||
final RoleDescriptor descriptor = transformRole(response);
|
public void onResponse(GetResponse response) {
|
||||||
roleActionListener.onResponse(descriptor);
|
final RoleDescriptor descriptor = transformRole(response);
|
||||||
}
|
roleActionListener.onResponse(descriptor);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onFailure(Exception e) {
|
public void onFailure(Exception e) {
|
||||||
// if the index or the shard is not there / available we just claim the role is not there
|
// if the index or the shard is not there / available we just claim the role is not there
|
||||||
if (TransportActions.isShardNotAvailableException(e)) {
|
if (TransportActions.isShardNotAvailableException(e)) {
|
||||||
logger.warn((org.apache.logging.log4j.util.Supplier<?>) () ->
|
logger.warn((org.apache.logging.log4j.util.Supplier<?>) () ->
|
||||||
new ParameterizedMessage("failed to load role [{}] index not available", roleId), e);
|
new ParameterizedMessage("failed to load role [{}] index not available", roleId), e);
|
||||||
roleActionListener.onResponse(null);
|
roleActionListener.onResponse(null);
|
||||||
} else {
|
} else {
|
||||||
logger.error(new ParameterizedMessage("failed to load role [{}]", roleId), e);
|
logger.error(new ParameterizedMessage("failed to load role [{}]", roleId), e);
|
||||||
roleActionListener.onFailure(e);
|
roleActionListener.onFailure(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
}));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void executeGetRoleRequest(String role, ActionListener<GetResponse> listener) {
|
private void executeGetRoleRequest(String role, ActionListener<GetResponse> listener) {
|
||||||
if (securityLifecycleService.isSecurityIndexOutOfDate()) {
|
securityLifecycleService.prepareIndexIfNeededThenExecute(listener::onFailure, () ->
|
||||||
listener.onFailure(new IllegalStateException(
|
|
||||||
"Security index is not on the current version - the native realm will not be operational until " +
|
|
||||||
"the upgrade API is run on the security index"));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
executeAsyncWithOrigin(client.threadPool().getThreadContext(), SECURITY_ORIGIN,
|
executeAsyncWithOrigin(client.threadPool().getThreadContext(), SECURITY_ORIGIN,
|
||||||
client.prepareGet(SecurityLifecycleService.SECURITY_INDEX_NAME,
|
client.prepareGet(SecurityLifecycleService.SECURITY_INDEX_NAME,
|
||||||
ROLE_DOC_TYPE, getIdForUser(role)).request(),
|
ROLE_DOC_TYPE, getIdForUser(role)).request(),
|
||||||
listener,
|
listener,
|
||||||
client::get);
|
client::get));
|
||||||
} catch (IndexNotFoundException e) {
|
|
||||||
logger.trace(
|
|
||||||
(org.apache.logging.log4j.util.Supplier<?>) () -> new ParameterizedMessage(
|
|
||||||
"unable to retrieve role [{}] since security index does not exist", role), e);
|
|
||||||
listener.onResponse(new GetResponse(
|
|
||||||
new GetResult(SecurityLifecycleService.SECURITY_INDEX_NAME, ROLE_DOC_TYPE,
|
|
||||||
getIdForUser(role), -1, false, null, null)));
|
|
||||||
} catch (Exception e) {
|
|
||||||
logger.error("unable to retrieve role", e);
|
|
||||||
listener.onFailure(e);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private <Response> void clearRoleCache(final String role, ActionListener<Response> listener, Response response) {
|
private <Response> void clearRoleCache(final String role, ActionListener<Response> listener, Response response) {
|
||||||
|
|
|
@ -16,6 +16,10 @@ import org.elasticsearch.action.ActionListener;
|
||||||
import org.elasticsearch.action.admin.indices.alias.Alias;
|
import org.elasticsearch.action.admin.indices.alias.Alias;
|
||||||
import org.elasticsearch.action.admin.indices.create.CreateIndexRequest;
|
import org.elasticsearch.action.admin.indices.create.CreateIndexRequest;
|
||||||
import org.elasticsearch.action.admin.indices.create.CreateIndexResponse;
|
import org.elasticsearch.action.admin.indices.create.CreateIndexResponse;
|
||||||
|
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.PutIndexTemplateRequest;
|
||||||
|
import org.elasticsearch.action.support.ActiveShardCount;
|
||||||
import org.elasticsearch.client.Client;
|
import org.elasticsearch.client.Client;
|
||||||
import org.elasticsearch.cluster.ClusterChangedEvent;
|
import org.elasticsearch.cluster.ClusterChangedEvent;
|
||||||
import org.elasticsearch.cluster.ClusterState;
|
import org.elasticsearch.cluster.ClusterState;
|
||||||
|
@ -25,18 +29,22 @@ import org.elasticsearch.cluster.metadata.IndexMetaData;
|
||||||
import org.elasticsearch.cluster.metadata.MappingMetaData;
|
import org.elasticsearch.cluster.metadata.MappingMetaData;
|
||||||
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.common.collect.Tuple;
|
||||||
import org.elasticsearch.common.component.AbstractComponent;
|
import org.elasticsearch.common.component.AbstractComponent;
|
||||||
import org.elasticsearch.common.settings.Settings;
|
import org.elasticsearch.common.settings.Settings;
|
||||||
|
import org.elasticsearch.common.xcontent.XContentType;
|
||||||
import org.elasticsearch.index.mapper.MapperService;
|
import org.elasticsearch.index.mapper.MapperService;
|
||||||
import org.elasticsearch.xpack.template.TemplateUtils;
|
import org.elasticsearch.xpack.template.TemplateUtils;
|
||||||
import org.elasticsearch.xpack.upgrade.IndexUpgradeCheck;
|
import org.elasticsearch.xpack.upgrade.IndexUpgradeCheck;
|
||||||
|
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.concurrent.CopyOnWriteArrayList;
|
import java.util.concurrent.CopyOnWriteArrayList;
|
||||||
import java.util.function.BiConsumer;
|
import java.util.function.BiConsumer;
|
||||||
|
import java.util.function.Consumer;
|
||||||
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 java.util.stream.Collectors;
|
||||||
|
@ -45,6 +53,7 @@ import static org.elasticsearch.cluster.metadata.IndexMetaData.INDEX_FORMAT_SETT
|
||||||
import static org.elasticsearch.xpack.ClientHelper.SECURITY_ORIGIN;
|
import static org.elasticsearch.xpack.ClientHelper.SECURITY_ORIGIN;
|
||||||
import static org.elasticsearch.xpack.ClientHelper.executeAsyncWithOrigin;
|
import static org.elasticsearch.xpack.ClientHelper.executeAsyncWithOrigin;
|
||||||
import static org.elasticsearch.xpack.security.SecurityLifecycleService.SECURITY_INDEX_NAME;
|
import static org.elasticsearch.xpack.security.SecurityLifecycleService.SECURITY_INDEX_NAME;
|
||||||
|
import static org.elasticsearch.xpack.security.SecurityLifecycleService.SECURITY_TEMPLATE_NAME;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Manages the lifecycle of a single index, its template, mapping and and data upgrades/migrations.
|
* Manages the lifecycle of a single index, its template, mapping and and data upgrades/migrations.
|
||||||
|
@ -58,7 +67,6 @@ public class IndexLifecycleManager extends AbstractComponent {
|
||||||
Pattern.quote("${security.template.version}");
|
Pattern.quote("${security.template.version}");
|
||||||
|
|
||||||
private final String indexName;
|
private final String indexName;
|
||||||
private final String templateName;
|
|
||||||
private final Client client;
|
private final Client client;
|
||||||
|
|
||||||
private final List<BiConsumer<ClusterIndexHealth, ClusterIndexHealth>> indexHealthChangeListeners = new CopyOnWriteArrayList<>();
|
private final List<BiConsumer<ClusterIndexHealth, ClusterIndexHealth>> indexHealthChangeListeners = new CopyOnWriteArrayList<>();
|
||||||
|
@ -66,11 +74,10 @@ public class IndexLifecycleManager extends AbstractComponent {
|
||||||
|
|
||||||
private volatile State indexState = new State(false, false, false, false, null);
|
private volatile State indexState = new State(false, false, false, false, null);
|
||||||
|
|
||||||
public IndexLifecycleManager(Settings settings, Client client, String indexName, String templateName) {
|
public IndexLifecycleManager(Settings settings, Client client, String indexName) {
|
||||||
super(settings);
|
super(settings);
|
||||||
this.client = client;
|
this.client = client;
|
||||||
this.indexName = indexName;
|
this.indexName = indexName;
|
||||||
this.templateName = templateName;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean checkMappingVersion(Predicate<Version> requiredVersion) {
|
public boolean checkMappingVersion(Predicate<Version> requiredVersion) {
|
||||||
|
@ -95,8 +102,8 @@ public class IndexLifecycleManager extends AbstractComponent {
|
||||||
return this.indexState.indexAvailable;
|
return this.indexState.indexAvailable;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isWritable() {
|
public boolean isMappingUpToDate() {
|
||||||
return this.indexState.canWriteToIndex;
|
return this.indexState.mappingUpToDate;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -133,12 +140,9 @@ public class IndexLifecycleManager extends AbstractComponent {
|
||||||
final boolean isIndexUpToDate = indexExists == false ||
|
final boolean isIndexUpToDate = indexExists == false ||
|
||||||
INDEX_FORMAT_SETTING.get(securityIndex.getSettings()).intValue() == INTERNAL_INDEX_FORMAT;
|
INDEX_FORMAT_SETTING.get(securityIndex.getSettings()).intValue() == INTERNAL_INDEX_FORMAT;
|
||||||
final boolean indexAvailable = checkIndexAvailable(clusterState);
|
final boolean indexAvailable = checkIndexAvailable(clusterState);
|
||||||
final boolean templateIsUpToDate = TemplateUtils.checkTemplateExistsAndIsUpToDate(templateName,
|
final boolean mappingIsUpToDate = indexExists == false || checkIndexMappingUpToDate(clusterState);
|
||||||
SECURITY_VERSION_STRING, clusterState, logger);
|
|
||||||
final boolean mappingIsUpToDate = checkIndexMappingUpToDate(clusterState);
|
|
||||||
final boolean canWriteToIndex = templateIsUpToDate && (mappingIsUpToDate || isIndexUpToDate);
|
|
||||||
final Version mappingVersion = oldestIndexMappingVersion(clusterState);
|
final Version mappingVersion = oldestIndexMappingVersion(clusterState);
|
||||||
this.indexState = new State(indexExists, isIndexUpToDate, indexAvailable, canWriteToIndex, mappingVersion);
|
this.indexState = new State(indexExists, isIndexUpToDate, indexAvailable, mappingIsUpToDate, mappingVersion);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void checkIndexHealthChange(ClusterChangedEvent event) {
|
private void checkIndexHealthChange(ClusterChangedEvent event) {
|
||||||
|
@ -284,15 +288,23 @@ public class IndexLifecycleManager extends AbstractComponent {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates the security index, if it does not already exist, then runs the given
|
* Prepares the index by creating it if it doesn't exist or updating the mappings if the mappings are
|
||||||
* action on the security index.
|
* out of date. After any tasks have been executed, the runnable is then executed.
|
||||||
*/
|
*/
|
||||||
public <T> void createIndexIfNeededThenExecute(final ActionListener<T> listener, final Runnable andThen) {
|
public void prepareIndexIfNeededThenExecute(final Consumer<Exception> consumer, final Runnable andThen) {
|
||||||
if (this.indexState.indexExists) {
|
final State indexState = this.indexState; // use a local copy so all checks execute against the same state!
|
||||||
andThen.run();
|
// TODO we should improve this so we don't fire off a bunch of requests to do the same thing (create or update mappings)
|
||||||
} else {
|
if (indexState.indexExists && indexState.isIndexUpToDate == false) {
|
||||||
CreateIndexRequest request = new CreateIndexRequest(INTERNAL_SECURITY_INDEX);
|
consumer.accept(new IllegalStateException(
|
||||||
request.alias(new Alias(SECURITY_INDEX_NAME));
|
"Security index is not on the current version. Security features relying on the index will not be available until " +
|
||||||
|
"the upgrade API is run on the security index"));
|
||||||
|
} else if (indexState.indexExists == false) {
|
||||||
|
Tuple<String, Settings> mappingAndSettings = loadMappingAndSettingsSourceFromTemplate();
|
||||||
|
CreateIndexRequest request = new CreateIndexRequest(INTERNAL_SECURITY_INDEX)
|
||||||
|
.alias(new Alias(SECURITY_INDEX_NAME))
|
||||||
|
.mapping("doc", mappingAndSettings.v1(), XContentType.JSON)
|
||||||
|
.waitForActiveShards(ActiveShardCount.ALL)
|
||||||
|
.settings(mappingAndSettings.v2());
|
||||||
executeAsyncWithOrigin(client.threadPool().getThreadContext(), SECURITY_ORIGIN, request,
|
executeAsyncWithOrigin(client.threadPool().getThreadContext(), SECURITY_ORIGIN, request,
|
||||||
new ActionListener<CreateIndexResponse>() {
|
new ActionListener<CreateIndexResponse>() {
|
||||||
@Override
|
@Override
|
||||||
|
@ -300,7 +312,7 @@ public class IndexLifecycleManager extends AbstractComponent {
|
||||||
if (createIndexResponse.isAcknowledged()) {
|
if (createIndexResponse.isAcknowledged()) {
|
||||||
andThen.run();
|
andThen.run();
|
||||||
} else {
|
} else {
|
||||||
listener.onFailure(new ElasticsearchException("Failed to create security index"));
|
consumer.accept(new ElasticsearchException("Failed to create security index"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -312,13 +324,33 @@ public class IndexLifecycleManager extends AbstractComponent {
|
||||||
// node hasn't yet received the cluster state update with the index
|
// node hasn't yet received the cluster state update with the index
|
||||||
andThen.run();
|
andThen.run();
|
||||||
} else {
|
} else {
|
||||||
listener.onFailure(e);
|
consumer.accept(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}, client.admin().indices()::create);
|
}, client.admin().indices()::create);
|
||||||
|
} else if (indexState.mappingUpToDate == false) {
|
||||||
|
PutMappingRequest request = new PutMappingRequest(INTERNAL_SECURITY_INDEX)
|
||||||
|
.source(loadMappingAndSettingsSourceFromTemplate().v1(), XContentType.JSON)
|
||||||
|
.type("doc");
|
||||||
|
executeAsyncWithOrigin(client.threadPool().getThreadContext(), SECURITY_ORIGIN, request,
|
||||||
|
ActionListener.<PutMappingResponse>wrap(putMappingResponse -> {
|
||||||
|
if (putMappingResponse.isAcknowledged()) {
|
||||||
|
andThen.run();
|
||||||
|
} else {
|
||||||
|
consumer.accept(new IllegalStateException("put mapping request was not acknowledged"));
|
||||||
|
}
|
||||||
|
}, consumer), client.admin().indices()::putMapping);
|
||||||
|
} else {
|
||||||
|
andThen.run();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private Tuple<String, Settings> loadMappingAndSettingsSourceFromTemplate() {
|
||||||
|
final byte[] template = TemplateUtils.loadTemplate("/" + SECURITY_TEMPLATE_NAME + ".json",
|
||||||
|
Version.CURRENT.toString(), IndexLifecycleManager.TEMPLATE_VERSION_PATTERN).getBytes(StandardCharsets.UTF_8);
|
||||||
|
PutIndexTemplateRequest request = new PutIndexTemplateRequest(SECURITY_TEMPLATE_NAME).source(template, XContentType.JSON);
|
||||||
|
return new Tuple<>(request.mappings().get("doc"), request.settings());
|
||||||
|
}
|
||||||
/**
|
/**
|
||||||
* Holder class so we can update all values at once
|
* Holder class so we can update all values at once
|
||||||
*/
|
*/
|
||||||
|
@ -326,15 +358,15 @@ public class IndexLifecycleManager extends AbstractComponent {
|
||||||
private final boolean indexExists;
|
private final boolean indexExists;
|
||||||
private final boolean isIndexUpToDate;
|
private final boolean isIndexUpToDate;
|
||||||
private final boolean indexAvailable;
|
private final boolean indexAvailable;
|
||||||
private final boolean canWriteToIndex;
|
private final boolean mappingUpToDate;
|
||||||
private final Version mappingVersion;
|
private final Version mappingVersion;
|
||||||
|
|
||||||
private State(boolean indexExists, boolean isIndexUpToDate, boolean indexAvailable,
|
private State(boolean indexExists, boolean isIndexUpToDate, boolean indexAvailable,
|
||||||
boolean canWriteToIndex, Version mappingVersion) {
|
boolean mappingUpToDate, Version mappingVersion) {
|
||||||
this.indexExists = indexExists;
|
this.indexExists = indexExists;
|
||||||
this.isIndexUpToDate = isIndexUpToDate;
|
this.isIndexUpToDate = isIndexUpToDate;
|
||||||
this.indexAvailable = indexAvailable;
|
this.indexAvailable = indexAvailable;
|
||||||
this.canWriteToIndex = canWriteToIndex;
|
this.mappingUpToDate = mappingUpToDate;
|
||||||
this.mappingVersion = mappingVersion;
|
this.mappingVersion = mappingVersion;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -37,6 +37,7 @@ import org.elasticsearch.xpack.security.Security;
|
||||||
import org.elasticsearch.xpack.security.action.user.GetUsersResponse;
|
import org.elasticsearch.xpack.security.action.user.GetUsersResponse;
|
||||||
import org.elasticsearch.xpack.security.authc.support.UsernamePasswordToken;
|
import org.elasticsearch.xpack.security.authc.support.UsernamePasswordToken;
|
||||||
import org.elasticsearch.xpack.security.client.SecurityClient;
|
import org.elasticsearch.xpack.security.client.SecurityClient;
|
||||||
|
import org.junit.After;
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
@ -119,6 +120,11 @@ public class LicensingTests extends SecurityIntegTestCase {
|
||||||
enableLicensing();
|
enableLicensing();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@After
|
||||||
|
public void cleanupSecurityIndex() {
|
||||||
|
deleteSecurityIndex();
|
||||||
|
}
|
||||||
|
|
||||||
public void testEnableDisableBehaviour() throws Exception {
|
public void testEnableDisableBehaviour() throws Exception {
|
||||||
IndexResponse indexResponse = index("test", "type", jsonBuilder()
|
IndexResponse indexResponse = index("test", "type", jsonBuilder()
|
||||||
.startObject()
|
.startObject()
|
||||||
|
|
|
@ -58,8 +58,7 @@ import static org.elasticsearch.test.SecuritySettingsSource.TEST_PASSWORD_SECURE
|
||||||
import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertAcked;
|
import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertAcked;
|
||||||
import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertNoTimeout;
|
import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertNoTimeout;
|
||||||
import static org.elasticsearch.xpack.security.SecurityLifecycleService.SECURITY_INDEX_NAME;
|
import static org.elasticsearch.xpack.security.SecurityLifecycleService.SECURITY_INDEX_NAME;
|
||||||
import static org.elasticsearch.xpack.security.SecurityLifecycleService.securityIndexMappingAndTemplateSufficientToRead;
|
import static org.elasticsearch.xpack.security.SecurityLifecycleService.securityIndexMappingSufficientToRead;
|
||||||
import static org.elasticsearch.xpack.security.SecurityLifecycleService.securityIndexMappingAndTemplateUpToDate;
|
|
||||||
import static org.elasticsearch.xpack.security.authc.support.UsernamePasswordToken.basicAuthHeaderValue;
|
import static org.elasticsearch.xpack.security.authc.support.UsernamePasswordToken.basicAuthHeaderValue;
|
||||||
import static org.hamcrest.Matchers.is;
|
import static org.hamcrest.Matchers.is;
|
||||||
import static org.hamcrest.core.IsCollectionContaining.hasItem;
|
import static org.hamcrest.core.IsCollectionContaining.hasItem;
|
||||||
|
@ -450,33 +449,9 @@ public abstract class SecurityIntegTestCase extends ESIntegTestCase {
|
||||||
ClusterState clusterState = client.admin().cluster().prepareState().setLocal(true).get().getState();
|
ClusterState clusterState = client.admin().cluster().prepareState().setLocal(true).get().getState();
|
||||||
assertFalse(clusterState.blocks().hasGlobalBlock(GatewayService.STATE_NOT_RECOVERED_BLOCK));
|
assertFalse(clusterState.blocks().hasGlobalBlock(GatewayService.STATE_NOT_RECOVERED_BLOCK));
|
||||||
XContentBuilder builder = JsonXContent.contentBuilder().prettyPrint().startObject();
|
XContentBuilder builder = JsonXContent.contentBuilder().prettyPrint().startObject();
|
||||||
assertTrue("security index mapping and template not sufficient to read:\n" +
|
assertTrue("security index mapping not sufficient to read:\n" +
|
||||||
clusterState.toXContent(builder, ToXContent.EMPTY_PARAMS).endObject().string(),
|
clusterState.toXContent(builder, ToXContent.EMPTY_PARAMS).endObject().string(),
|
||||||
securityIndexMappingAndTemplateSufficientToRead(clusterState, logger));
|
securityIndexMappingSufficientToRead(clusterState, logger));
|
||||||
Index securityIndex = resolveSecurityIndex(clusterState.metaData());
|
|
||||||
if (securityIndex != null) {
|
|
||||||
IndexRoutingTable indexRoutingTable = clusterState.routingTable().index(securityIndex);
|
|
||||||
if (indexRoutingTable != null) {
|
|
||||||
assertTrue(indexRoutingTable.allPrimaryShardsActive());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}, 30L, TimeUnit.SECONDS);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void assertSecurityIndexWriteable() throws Exception {
|
|
||||||
assertSecurityIndexWriteable(cluster());
|
|
||||||
}
|
|
||||||
|
|
||||||
public void assertSecurityIndexWriteable(TestCluster testCluster) throws Exception {
|
|
||||||
for (Client client : testCluster.getClients()) {
|
|
||||||
assertBusy(() -> {
|
|
||||||
ClusterState clusterState = client.admin().cluster().prepareState().setLocal(true).get().getState();
|
|
||||||
assertFalse(clusterState.blocks().hasGlobalBlock(GatewayService.STATE_NOT_RECOVERED_BLOCK));
|
|
||||||
XContentBuilder builder = JsonXContent.contentBuilder().prettyPrint().startObject();
|
|
||||||
assertTrue("security index mapping and template not up to date:\n" +
|
|
||||||
clusterState.toXContent(builder, ToXContent.EMPTY_PARAMS).endObject().string(),
|
|
||||||
securityIndexMappingAndTemplateUpToDate(clusterState, logger));
|
|
||||||
Index securityIndex = resolveSecurityIndex(clusterState.metaData());
|
Index securityIndex = resolveSecurityIndex(clusterState.metaData());
|
||||||
if (securityIndex != null) {
|
if (securityIndex != null) {
|
||||||
IndexRoutingTable indexRoutingTable = clusterState.routingTable().index(securityIndex);
|
IndexRoutingTable indexRoutingTable = clusterState.routingTable().index(securityIndex);
|
||||||
|
|
|
@ -1,84 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
|
||||||
* or more contributor license agreements. Licensed under the Elastic License;
|
|
||||||
* you may not use this file except in compliance with the Elastic License.
|
|
||||||
*/
|
|
||||||
package org.elasticsearch.xpack.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;
|
|
||||||
import org.elasticsearch.test.rest.yaml.ObjectPath;
|
|
||||||
import org.junit.Before;
|
|
||||||
|
|
||||||
import java.nio.charset.StandardCharsets;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
import static org.elasticsearch.xpack.security.SecurityLifecycleService.SECURITY_TEMPLATE_NAME;
|
|
||||||
import static org.hamcrest.Matchers.greaterThanOrEqualTo;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A base {@link ESClientYamlSuiteTestCase} test class for the security module,
|
|
||||||
* which depends on security template and mappings being up to date before any writes
|
|
||||||
* to the {@code .security} index can take place.
|
|
||||||
*/
|
|
||||||
public abstract class SecurityClusterClientYamlTestCase extends ESClientYamlSuiteTestCase {
|
|
||||||
|
|
||||||
public SecurityClusterClientYamlTestCase(ClientYamlTestCandidate testCandidate) {
|
|
||||||
super(testCandidate);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Before
|
|
||||||
public void waitForSecuritySetup() throws Exception {
|
|
||||||
waitForSecurity();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void waitForSecurity() throws Exception {
|
|
||||||
String masterNode = null;
|
|
||||||
HttpEntity entity = client().performRequest("GET", "/_cat/nodes?h=id,master").getEntity();
|
|
||||||
String catNodesResponse = EntityUtils.toString(entity, StandardCharsets.UTF_8);
|
|
||||||
for (String line : catNodesResponse.split("\n")) {
|
|
||||||
int indexOfStar = line.indexOf('*'); // * in the node's output denotes it is master
|
|
||||||
if (indexOfStar != -1) {
|
|
||||||
masterNode = line.substring(0, indexOfStar).trim();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
assertNotNull(masterNode);
|
|
||||||
final String masterNodeId = masterNode;
|
|
||||||
|
|
||||||
assertBusy(() -> {
|
|
||||||
try {
|
|
||||||
Response nodesResponse = client().performRequest("GET", "/_nodes");
|
|
||||||
ObjectPath nodesPath = ObjectPath.createFromResponse(nodesResponse);
|
|
||||||
Map<String, Object> nodes = nodesPath.evaluate("nodes");
|
|
||||||
Version masterVersion = null;
|
|
||||||
for (String nodeId : nodes.keySet()) {
|
|
||||||
// get the ES version number master is on
|
|
||||||
if (nodeId.startsWith(masterNodeId)) {
|
|
||||||
masterVersion = Version.fromString(nodesPath.evaluate("nodes." + nodeId + ".version"));
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
assertNotNull(masterVersion);
|
|
||||||
|
|
||||||
Response response = client().performRequest("GET", "/_cluster/state/metadata");
|
|
||||||
ObjectPath objectPath = ObjectPath.createFromResponse(response);
|
|
||||||
String mappingsPath = "metadata.templates." + SECURITY_TEMPLATE_NAME + ".mappings";
|
|
||||||
Map<String, Object> mappings = objectPath.evaluate(mappingsPath);
|
|
||||||
assertNotNull(mappings);
|
|
||||||
assertThat(mappings.size(), greaterThanOrEqualTo(1));
|
|
||||||
for (String key : mappings.keySet()) {
|
|
||||||
String templatePath = mappingsPath + "." + key + "._meta.security-version";
|
|
||||||
Version templateVersion = Version.fromString(objectPath.evaluate(templatePath));
|
|
||||||
assertEquals(masterVersion, templateVersion);
|
|
||||||
}
|
|
||||||
} catch (Exception e) {
|
|
||||||
throw new AssertionError("failed to get cluster state", e);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -44,7 +44,7 @@ import org.junit.Before;
|
||||||
|
|
||||||
import static org.elasticsearch.xpack.security.SecurityLifecycleService.SECURITY_INDEX_NAME;
|
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.SECURITY_TEMPLATE_NAME;
|
||||||
import static org.elasticsearch.xpack.security.SecurityLifecycleService.securityIndexMappingAndTemplateUpToDate;
|
import static org.elasticsearch.xpack.security.SecurityLifecycleService.securityIndexMappingUpToDate;
|
||||||
import static org.hamcrest.Matchers.equalTo;
|
import static org.hamcrest.Matchers.equalTo;
|
||||||
import static org.mockito.Mockito.mock;
|
import static org.mockito.Mockito.mock;
|
||||||
import static org.mockito.Mockito.when;
|
import static org.mockito.Mockito.when;
|
||||||
|
@ -135,7 +135,7 @@ public class SecurityLifecycleServiceTests extends ESTestCase {
|
||||||
ClusterState.Builder clusterStateBuilder = createClusterStateWithMappingAndTemplate(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));
|
() -> securityIndexMappingUpToDate(clusterState, logger));
|
||||||
assertEquals("Cannot read security-version string in index " + SECURITY_INDEX_NAME,
|
assertEquals("Cannot read security-version string in index " + SECURITY_INDEX_NAME,
|
||||||
exception.getMessage());
|
exception.getMessage());
|
||||||
}
|
}
|
||||||
|
|
|
@ -141,34 +141,13 @@ public class SecuritySettingsTests extends ESTestCase {
|
||||||
public void testValidAutoCreateIndex() {
|
public void testValidAutoCreateIndex() {
|
||||||
Security.validateAutoCreateIndex(Settings.EMPTY);
|
Security.validateAutoCreateIndex(Settings.EMPTY);
|
||||||
Security.validateAutoCreateIndex(Settings.builder().put("action.auto_create_index", true).build());
|
Security.validateAutoCreateIndex(Settings.builder().put("action.auto_create_index", true).build());
|
||||||
|
Security.validateAutoCreateIndex(Settings.builder().put("action.auto_create_index", false).build());
|
||||||
try {
|
|
||||||
Security.validateAutoCreateIndex(Settings.builder().put("action.auto_create_index", false).build());
|
|
||||||
fail("IllegalArgumentException expected");
|
|
||||||
} catch (IllegalArgumentException e) {
|
|
||||||
assertThat(e.getMessage(), containsString(SecurityLifecycleService.SECURITY_INDEX_NAME));
|
|
||||||
assertThat(e.getMessage(), not(containsString(IndexAuditTrail.INDEX_NAME_PREFIX)));
|
|
||||||
}
|
|
||||||
|
|
||||||
Security.validateAutoCreateIndex(Settings.builder().put("action.auto_create_index", ".security,.security-6").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", ".security*").build());
|
||||||
Security.validateAutoCreateIndex(Settings.builder().put("action.auto_create_index", "*s*").build());
|
Security.validateAutoCreateIndex(Settings.builder().put("action.auto_create_index", "*s*").build());
|
||||||
Security.validateAutoCreateIndex(Settings.builder().put("action.auto_create_index", ".s*").build());
|
Security.validateAutoCreateIndex(Settings.builder().put("action.auto_create_index", ".s*").build());
|
||||||
|
Security.validateAutoCreateIndex(Settings.builder().put("action.auto_create_index", "foo").build());
|
||||||
try {
|
Security.validateAutoCreateIndex(Settings.builder().put("action.auto_create_index", ".security_audit_log*").build());
|
||||||
Security.validateAutoCreateIndex(Settings.builder().put("action.auto_create_index", "foo").build());
|
|
||||||
fail("IllegalArgumentException expected");
|
|
||||||
} catch (IllegalArgumentException e) {
|
|
||||||
assertThat(e.getMessage(), containsString(SecurityLifecycleService.SECURITY_INDEX_NAME));
|
|
||||||
assertThat(e.getMessage(), not(containsString(IndexAuditTrail.INDEX_NAME_PREFIX)));
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
Security.validateAutoCreateIndex(Settings.builder().put("action.auto_create_index", ".security_audit_log*").build());
|
|
||||||
fail("IllegalArgumentException expected");
|
|
||||||
} catch (IllegalArgumentException e) {
|
|
||||||
assertThat(e.getMessage(), containsString(SecurityLifecycleService.SECURITY_INDEX_NAME));
|
|
||||||
}
|
|
||||||
|
|
||||||
Security.validateAutoCreateIndex(Settings.builder()
|
Security.validateAutoCreateIndex(Settings.builder()
|
||||||
.put("action.auto_create_index", ".security,.security-6")
|
.put("action.auto_create_index", ".security,.security-6")
|
||||||
|
@ -183,7 +162,6 @@ public class SecuritySettingsTests extends ESTestCase {
|
||||||
.build());
|
.build());
|
||||||
fail("IllegalArgumentException expected");
|
fail("IllegalArgumentException expected");
|
||||||
} catch (IllegalArgumentException e) {
|
} catch (IllegalArgumentException e) {
|
||||||
assertThat(e.getMessage(), containsString(SecurityLifecycleService.SECURITY_INDEX_NAME));
|
|
||||||
assertThat(e.getMessage(), containsString(IndexAuditTrail.INDEX_NAME_PREFIX));
|
assertThat(e.getMessage(), containsString(IndexAuditTrail.INDEX_NAME_PREFIX));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -62,6 +62,7 @@ import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.concurrent.CountDownLatch;
|
import java.util.concurrent.CountDownLatch;
|
||||||
import java.util.concurrent.atomic.AtomicBoolean;
|
import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
import static org.elasticsearch.test.SecurityTestsUtils.assertAuthenticationException;
|
import static org.elasticsearch.test.SecurityTestsUtils.assertAuthenticationException;
|
||||||
import static org.elasticsearch.xpack.security.support.Exceptions.authenticationError;
|
import static org.elasticsearch.xpack.security.support.Exceptions.authenticationError;
|
||||||
|
@ -865,7 +866,7 @@ public class AuthenticationServiceTests extends ESTestCase {
|
||||||
User user = new User("_username", "r1");
|
User user = new User("_username", "r1");
|
||||||
final Authentication expected = new Authentication(user, new RealmRef("realm", "custom", "node"), null);
|
final Authentication expected = new Authentication(user, new RealmRef("realm", "custom", "node"), null);
|
||||||
String token = tokenService.getUserTokenString(tokenService.createUserToken(expected));
|
String token = tokenService.getUserTokenString(tokenService.createUserToken(expected));
|
||||||
when(lifecycleService.isSecurityIndexAvailable()).thenReturn(true);
|
when(lifecycleService.isSecurityIndexExisting()).thenReturn(true);
|
||||||
GetRequestBuilder getRequestBuilder = mock(GetRequestBuilder.class);
|
GetRequestBuilder getRequestBuilder = mock(GetRequestBuilder.class);
|
||||||
when(client.prepareGet(eq(SecurityLifecycleService.SECURITY_INDEX_NAME), eq("doc"), any(String.class)))
|
when(client.prepareGet(eq(SecurityLifecycleService.SECURITY_INDEX_NAME), eq("doc"), any(String.class)))
|
||||||
.thenReturn(getRequestBuilder);
|
.thenReturn(getRequestBuilder);
|
||||||
|
@ -877,6 +878,11 @@ public class AuthenticationServiceTests extends ESTestCase {
|
||||||
return Void.TYPE;
|
return Void.TYPE;
|
||||||
}).when(client).get(any(GetRequest.class), any(ActionListener.class));
|
}).when(client).get(any(GetRequest.class), any(ActionListener.class));
|
||||||
|
|
||||||
|
doAnswer(invocationOnMock -> {
|
||||||
|
((Runnable) invocationOnMock.getArguments()[1]).run();
|
||||||
|
return null;
|
||||||
|
}).when(lifecycleService).prepareIndexIfNeededThenExecute(any(Consumer.class), any(Runnable.class));
|
||||||
|
|
||||||
try (ThreadContext.StoredContext ignore = threadContext.stashContext()) {
|
try (ThreadContext.StoredContext ignore = threadContext.stashContext()) {
|
||||||
threadContext.putHeader("Authorization", "Bearer " + token);
|
threadContext.putHeader("Authorization", "Bearer " + token);
|
||||||
ElasticsearchSecurityException e =
|
ElasticsearchSecurityException e =
|
||||||
|
|
|
@ -204,7 +204,7 @@ public class TokenAuthIntegTests extends SecurityIntegTestCase {
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
public void waitForSecurityIndexWritable() throws Exception {
|
public void waitForSecurityIndexWritable() throws Exception {
|
||||||
assertSecurityIndexWriteable();
|
assertSecurityIndexActive();
|
||||||
}
|
}
|
||||||
|
|
||||||
@After
|
@After
|
||||||
|
|
|
@ -7,6 +7,7 @@ package org.elasticsearch.xpack.security.authc;
|
||||||
|
|
||||||
import org.elasticsearch.ElasticsearchSecurityException;
|
import org.elasticsearch.ElasticsearchSecurityException;
|
||||||
import org.elasticsearch.action.ActionListener;
|
import org.elasticsearch.action.ActionListener;
|
||||||
|
import org.elasticsearch.action.NoShardAvailableActionException;
|
||||||
import org.elasticsearch.action.get.GetAction;
|
import org.elasticsearch.action.get.GetAction;
|
||||||
import org.elasticsearch.action.get.GetRequest;
|
import org.elasticsearch.action.get.GetRequest;
|
||||||
import org.elasticsearch.action.get.GetRequestBuilder;
|
import org.elasticsearch.action.get.GetRequestBuilder;
|
||||||
|
@ -18,6 +19,8 @@ import org.elasticsearch.common.settings.ClusterSettings;
|
||||||
import org.elasticsearch.common.settings.Settings;
|
import org.elasticsearch.common.settings.Settings;
|
||||||
import org.elasticsearch.common.unit.TimeValue;
|
import org.elasticsearch.common.unit.TimeValue;
|
||||||
import org.elasticsearch.common.util.concurrent.ThreadContext;
|
import org.elasticsearch.common.util.concurrent.ThreadContext;
|
||||||
|
import org.elasticsearch.index.Index;
|
||||||
|
import org.elasticsearch.index.shard.ShardId;
|
||||||
import org.elasticsearch.node.Node;
|
import org.elasticsearch.node.Node;
|
||||||
import org.elasticsearch.test.ESTestCase;
|
import org.elasticsearch.test.ESTestCase;
|
||||||
import org.elasticsearch.test.EqualsHashCodeTestUtils;
|
import org.elasticsearch.test.EqualsHashCodeTestUtils;
|
||||||
|
@ -38,10 +41,12 @@ import java.security.GeneralSecurityException;
|
||||||
import java.time.Clock;
|
import java.time.Clock;
|
||||||
import java.util.Base64;
|
import java.util.Base64;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
import static org.elasticsearch.repositories.ESBlobStoreTestCase.randomBytes;
|
import static org.elasticsearch.repositories.ESBlobStoreTestCase.randomBytes;
|
||||||
import static org.hamcrest.Matchers.containsString;
|
import static org.hamcrest.Matchers.containsString;
|
||||||
import static org.mockito.Matchers.any;
|
import static org.mockito.Matchers.any;
|
||||||
|
import static org.mockito.Matchers.anyString;
|
||||||
import static org.mockito.Matchers.eq;
|
import static org.mockito.Matchers.eq;
|
||||||
import static org.mockito.Mockito.doAnswer;
|
import static org.mockito.Mockito.doAnswer;
|
||||||
import static org.mockito.Mockito.mock;
|
import static org.mockito.Mockito.mock;
|
||||||
|
@ -60,19 +65,22 @@ public class TokenServiceTests extends ESTestCase {
|
||||||
.put(XPackSettings.TOKEN_SERVICE_ENABLED_SETTING.getKey(), true).build();
|
.put(XPackSettings.TOKEN_SERVICE_ENABLED_SETTING.getKey(), true).build();
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
public void setupClient() throws GeneralSecurityException {
|
public void setupClient() {
|
||||||
client = mock(Client.class);
|
client = mock(Client.class);
|
||||||
when(client.threadPool()).thenReturn(threadPool);
|
when(client.threadPool()).thenReturn(threadPool);
|
||||||
when(client.settings()).thenReturn(settings);
|
when(client.settings()).thenReturn(settings);
|
||||||
lifecycleService = mock(SecurityLifecycleService.class);
|
lifecycleService = mock(SecurityLifecycleService.class);
|
||||||
when(lifecycleService.isSecurityIndexWriteable()).thenReturn(true);
|
|
||||||
doAnswer(invocationOnMock -> {
|
doAnswer(invocationOnMock -> {
|
||||||
ActionListener<GetResponse> listener = (ActionListener<GetResponse>) invocationOnMock.getArguments()[2];
|
ActionListener<GetResponse> listener = (ActionListener<GetResponse>) invocationOnMock.getArguments()[2];
|
||||||
GetResponse response = mock(GetResponse.class);
|
GetResponse response = mock(GetResponse.class);
|
||||||
when(response.isExists()).thenReturn(false);
|
when(response.isExists()).thenReturn(false);
|
||||||
listener.onResponse(response);
|
listener.onResponse(response);
|
||||||
return Void.TYPE;
|
return Void.TYPE;
|
||||||
}).when(client).execute(eq(GetAction.INSTANCE), any(GetRequest.class), any(ActionListener.class));
|
}).when(client).get(any(GetRequest.class), any(ActionListener.class));
|
||||||
|
doAnswer(invocationOnMock -> {
|
||||||
|
((Runnable) invocationOnMock.getArguments()[1]).run();
|
||||||
|
return null;
|
||||||
|
}).when(lifecycleService).prepareIndexIfNeededThenExecute(any(Consumer.class), any(Runnable.class));
|
||||||
when(client.threadPool()).thenReturn(threadPool);
|
when(client.threadPool()).thenReturn(threadPool);
|
||||||
this.clusterService = new ClusterService(settings, new ClusterSettings(settings, ClusterSettings
|
this.clusterService = new ClusterService(settings, new ClusterSettings(settings, ClusterSettings
|
||||||
.BUILT_IN_CLUSTER_SETTINGS), threadPool, Collections.emptyMap());
|
.BUILT_IN_CLUSTER_SETTINGS), threadPool, Collections.emptyMap());
|
||||||
|
@ -286,7 +294,7 @@ public class TokenServiceTests extends ESTestCase {
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testInvalidatedToken() throws Exception {
|
public void testInvalidatedToken() throws Exception {
|
||||||
when(lifecycleService.isSecurityIndexAvailable()).thenReturn(true);
|
when(lifecycleService.isSecurityIndexExisting()).thenReturn(true);
|
||||||
TokenService tokenService =
|
TokenService tokenService =
|
||||||
new TokenService(tokenServiceEnabledSettings, Clock.systemUTC(), client, lifecycleService, clusterService);
|
new TokenService(tokenServiceEnabledSettings, Clock.systemUTC(), client, lifecycleService, clusterService);
|
||||||
Authentication authentication = new Authentication(new User("joe", "admin"), new RealmRef("native_realm", "native", "node1"), null);
|
Authentication authentication = new Authentication(new User("joe", "admin"), new RealmRef("native_realm", "native", "node1"), null);
|
||||||
|
@ -438,6 +446,13 @@ public class TokenServiceTests extends ESTestCase {
|
||||||
ThreadContext requestContext = new ThreadContext(Settings.EMPTY);
|
ThreadContext requestContext = new ThreadContext(Settings.EMPTY);
|
||||||
requestContext.putHeader("Authorization", "Bearer " + tokenService.getUserTokenString(token));
|
requestContext.putHeader("Authorization", "Bearer " + tokenService.getUserTokenString(token));
|
||||||
|
|
||||||
|
doAnswer(invocationOnMock -> {
|
||||||
|
ActionListener<GetResponse> listener = (ActionListener<GetResponse>) invocationOnMock.getArguments()[1];
|
||||||
|
listener.onFailure(new NoShardAvailableActionException(new ShardId(new Index("foo", "uuid"), 0), "shard oh shard"));
|
||||||
|
return Void.TYPE;
|
||||||
|
}).when(client).get(any(GetRequest.class), any(ActionListener.class));
|
||||||
|
when(client.prepareGet(anyString(), anyString(), anyString())).thenReturn(new GetRequestBuilder(client, GetAction.INSTANCE));
|
||||||
|
|
||||||
try (ThreadContext.StoredContext ignore = requestContext.newStoredContext(true)) {
|
try (ThreadContext.StoredContext ignore = requestContext.newStoredContext(true)) {
|
||||||
PlainActionFuture<UserToken> future = new PlainActionFuture<>();
|
PlainActionFuture<UserToken> future = new PlainActionFuture<>();
|
||||||
tokenService.getAndValidateToken(requestContext, future);
|
tokenService.getAndValidateToken(requestContext, future);
|
||||||
|
|
|
@ -11,6 +11,7 @@ import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.concurrent.CopyOnWriteArrayList;
|
import java.util.concurrent.CopyOnWriteArrayList;
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
import org.elasticsearch.action.Action;
|
import org.elasticsearch.action.Action;
|
||||||
import org.elasticsearch.action.ActionListener;
|
import org.elasticsearch.action.ActionListener;
|
||||||
|
@ -231,20 +232,19 @@ public class NativeUsersStoreTests extends ESTestCase {
|
||||||
|
|
||||||
actionRespond(GetRequest.class, new GetResponse(getResult));
|
actionRespond(GetRequest.class, new GetResponse(getResult));
|
||||||
}
|
}
|
||||||
|
|
||||||
private NativeUsersStore startNativeUsersStore() {
|
private NativeUsersStore startNativeUsersStore() {
|
||||||
SecurityLifecycleService securityLifecycleService = mock(SecurityLifecycleService.class);
|
SecurityLifecycleService securityLifecycleService = mock(SecurityLifecycleService.class);
|
||||||
when(securityLifecycleService.isSecurityIndexAvailable()).thenReturn(true);
|
when(securityLifecycleService.isSecurityIndexAvailable()).thenReturn(true);
|
||||||
when(securityLifecycleService.isSecurityIndexExisting()).thenReturn(true);
|
when(securityLifecycleService.isSecurityIndexExisting()).thenReturn(true);
|
||||||
when(securityLifecycleService.isSecurityIndexWriteable()).thenReturn(true);
|
when(securityLifecycleService.isSecurityIndexMappingUpToDate()).thenReturn(true);
|
||||||
when(securityLifecycleService.isSecurityIndexOutOfDate()).thenReturn(false);
|
when(securityLifecycleService.isSecurityIndexOutOfDate()).thenReturn(false);
|
||||||
when(securityLifecycleService.isSecurityIndexUpToDate()).thenReturn(true);
|
when(securityLifecycleService.isSecurityIndexUpToDate()).thenReturn(true);
|
||||||
doAnswer((i) -> {
|
doAnswer((i) -> {
|
||||||
Runnable action = (Runnable) i.getArguments()[1];
|
Runnable action = (Runnable) i.getArguments()[1];
|
||||||
action.run();
|
action.run();
|
||||||
ActionListener listener = (ActionListener) i.getArguments()[0];
|
|
||||||
listener.onResponse(null);
|
|
||||||
return null;
|
return null;
|
||||||
}).when(securityLifecycleService).createIndexIfNeededThenExecute(any(ActionListener.class), any(Runnable.class));
|
}).when(securityLifecycleService).prepareIndexIfNeededThenExecute(any(Consumer.class), any(Runnable.class));
|
||||||
return new NativeUsersStore(Settings.EMPTY, client, securityLifecycleService);
|
return new NativeUsersStore(Settings.EMPTY, client, securityLifecycleService);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -28,7 +28,7 @@ import static org.hamcrest.Matchers.instanceOf;
|
||||||
public class SecurityScrollTests extends SecurityIntegTestCase {
|
public class SecurityScrollTests extends SecurityIntegTestCase {
|
||||||
|
|
||||||
public void testScrollIsPerUser() throws Exception {
|
public void testScrollIsPerUser() throws Exception {
|
||||||
assertSecurityIndexWriteable();
|
assertSecurityIndexActive();
|
||||||
securityClient().preparePutRole("scrollable")
|
securityClient().preparePutRole("scrollable")
|
||||||
.addIndices(new String[] { randomAlphaOfLengthBetween(4, 12) }, new String[] { "read" }, null, null, null)
|
.addIndices(new String[] { randomAlphaOfLengthBetween(4, 12) }, new String[] { "read" }, null, null, null)
|
||||||
.get();
|
.get();
|
||||||
|
|
|
@ -24,7 +24,7 @@ import java.util.concurrent.atomic.AtomicInteger;
|
||||||
public class IndexLifecycleManagerIntegTests extends SecurityIntegTestCase {
|
public class IndexLifecycleManagerIntegTests extends SecurityIntegTestCase {
|
||||||
|
|
||||||
public void testConcurrentOperationsTryingToCreateSecurityIndexAndAlias() throws Exception {
|
public void testConcurrentOperationsTryingToCreateSecurityIndexAndAlias() throws Exception {
|
||||||
assertSecurityIndexWriteable();
|
assertSecurityIndexActive();
|
||||||
final int processors = Runtime.getRuntime().availableProcessors();
|
final int processors = Runtime.getRuntime().availableProcessors();
|
||||||
final int numThreads = scaledRandomIntBetween((processors + 1) / 2, 4 * processors);
|
final int numThreads = scaledRandomIntBetween((processors + 1) / 2, 4 * processors);
|
||||||
final int maxNumRequests = 100 / numThreads; // bound to a maximum of 100 requests
|
final int maxNumRequests = 100 / numThreads; // bound to a maximum of 100 requests
|
||||||
|
|
|
@ -86,7 +86,7 @@ public class IndexLifecycleManagerTests extends ESTestCase {
|
||||||
actions.put(action, map);
|
actions.put(action, map);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
manager = new IndexLifecycleManager(Settings.EMPTY, client, INDEX_NAME, TEMPLATE_NAME);
|
manager = new IndexLifecycleManager(Settings.EMPTY, client, INDEX_NAME);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testIndexWithUpToDateMappingAndTemplate() throws IOException {
|
public void testIndexWithUpToDateMappingAndTemplate() throws IOException {
|
||||||
|
@ -98,7 +98,7 @@ public class IndexLifecycleManagerTests extends ESTestCase {
|
||||||
|
|
||||||
assertThat(manager.indexExists(), Matchers.equalTo(true));
|
assertThat(manager.indexExists(), Matchers.equalTo(true));
|
||||||
assertThat(manager.isAvailable(), Matchers.equalTo(true));
|
assertThat(manager.isAvailable(), Matchers.equalTo(true));
|
||||||
assertThat(manager.isWritable(), Matchers.equalTo(true));
|
assertThat(manager.isMappingUpToDate(), Matchers.equalTo(true));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testIndexWithoutPrimaryShards() throws IOException {
|
public void testIndexWithoutPrimaryShards() throws IOException {
|
||||||
|
@ -245,13 +245,13 @@ public class IndexLifecycleManagerTests extends ESTestCase {
|
||||||
private void assertInitialState() {
|
private void assertInitialState() {
|
||||||
assertThat(manager.indexExists(), Matchers.equalTo(false));
|
assertThat(manager.indexExists(), Matchers.equalTo(false));
|
||||||
assertThat(manager.isAvailable(), Matchers.equalTo(false));
|
assertThat(manager.isAvailable(), Matchers.equalTo(false));
|
||||||
assertThat(manager.isWritable(), Matchers.equalTo(false));
|
assertThat(manager.isMappingUpToDate(), Matchers.equalTo(false));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void assertIndexUpToDateButNotAvailable() {
|
private void assertIndexUpToDateButNotAvailable() {
|
||||||
assertThat(manager.indexExists(), Matchers.equalTo(true));
|
assertThat(manager.indexExists(), Matchers.equalTo(true));
|
||||||
assertThat(manager.isAvailable(), Matchers.equalTo(false));
|
assertThat(manager.isAvailable(), Matchers.equalTo(false));
|
||||||
assertThat(manager.isWritable(), Matchers.equalTo(true));
|
assertThat(manager.isMappingUpToDate(), Matchers.equalTo(true));
|
||||||
}
|
}
|
||||||
|
|
||||||
public static ClusterState.Builder createClusterState(String indexName, String templateName) throws IOException {
|
public static ClusterState.Builder createClusterState(String indexName, String templateName) throws IOException {
|
||||||
|
|
|
@ -21,7 +21,6 @@ import org.elasticsearch.xpack.ml.MlMetaIndex;
|
||||||
import org.elasticsearch.xpack.ml.integration.MlRestTestStateCleaner;
|
import org.elasticsearch.xpack.ml.integration.MlRestTestStateCleaner;
|
||||||
import org.elasticsearch.xpack.ml.job.persistence.AnomalyDetectorsIndex;
|
import org.elasticsearch.xpack.ml.job.persistence.AnomalyDetectorsIndex;
|
||||||
import org.elasticsearch.xpack.ml.notifications.Auditor;
|
import org.elasticsearch.xpack.ml.notifications.Auditor;
|
||||||
import org.elasticsearch.xpack.security.SecurityLifecycleService;
|
|
||||||
import org.elasticsearch.xpack.watcher.support.WatcherIndexTemplateRegistry;
|
import org.elasticsearch.xpack.watcher.support.WatcherIndexTemplateRegistry;
|
||||||
import org.junit.After;
|
import org.junit.After;
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
|
@ -81,7 +80,6 @@ public class XPackRestIT extends ESClientYamlSuiteTestCase {
|
||||||
private void waitForTemplates() throws Exception {
|
private void waitForTemplates() throws Exception {
|
||||||
if (installTemplates()) {
|
if (installTemplates()) {
|
||||||
List<String> templates = new ArrayList<>();
|
List<String> templates = new ArrayList<>();
|
||||||
templates.add(SecurityLifecycleService.SECURITY_TEMPLATE_NAME);
|
|
||||||
templates.addAll(Arrays.asList(Auditor.NOTIFICATIONS_INDEX, MlMetaIndex.INDEX_NAME, AnomalyDetectorsIndex.jobStateIndexName(),
|
templates.addAll(Arrays.asList(Auditor.NOTIFICATIONS_INDEX, MlMetaIndex.INDEX_NAME, AnomalyDetectorsIndex.jobStateIndexName(),
|
||||||
AnomalyDetectorsIndex.jobResultsIndexPrefix()));
|
AnomalyDetectorsIndex.jobResultsIndexPrefix()));
|
||||||
templates.addAll(Arrays.asList(WatcherIndexTemplateRegistry.TEMPLATE_NAMES));
|
templates.addAll(Arrays.asList(WatcherIndexTemplateRegistry.TEMPLATE_NAMES));
|
||||||
|
|
|
@ -21,7 +21,6 @@ import org.elasticsearch.test.StreamsUtils;
|
||||||
import org.elasticsearch.test.rest.ESRestTestCase;
|
import org.elasticsearch.test.rest.ESRestTestCase;
|
||||||
import org.elasticsearch.xpack.watcher.common.text.TextTemplate;
|
import org.elasticsearch.xpack.watcher.common.text.TextTemplate;
|
||||||
import org.elasticsearch.xpack.monitoring.exporter.MonitoringTemplateUtils;
|
import org.elasticsearch.xpack.monitoring.exporter.MonitoringTemplateUtils;
|
||||||
import org.elasticsearch.xpack.security.SecurityClusterClientYamlTestCase;
|
|
||||||
import org.elasticsearch.xpack.security.support.IndexLifecycleManager;
|
import org.elasticsearch.xpack.security.support.IndexLifecycleManager;
|
||||||
import org.elasticsearch.xpack.test.rest.XPackRestTestHelper;
|
import org.elasticsearch.xpack.test.rest.XPackRestTestHelper;
|
||||||
import org.elasticsearch.xpack.watcher.actions.logging.LoggingAction;
|
import org.elasticsearch.xpack.watcher.actions.logging.LoggingAction;
|
||||||
|
@ -61,11 +60,6 @@ public class FullClusterRestartIT extends ESRestTestCase {
|
||||||
private final boolean runningAgainstOldCluster = Booleans.parseBoolean(System.getProperty("tests.is_old_cluster"));
|
private final boolean runningAgainstOldCluster = Booleans.parseBoolean(System.getProperty("tests.is_old_cluster"));
|
||||||
private final Version oldClusterVersion = Version.fromString(System.getProperty("tests.old_cluster_version"));
|
private final Version oldClusterVersion = Version.fromString(System.getProperty("tests.old_cluster_version"));
|
||||||
|
|
||||||
@Before
|
|
||||||
public void waitForSecuritySetup() throws Exception {
|
|
||||||
SecurityClusterClientYamlTestCase.waitForSecurity();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
public void waitForMlTemplates() throws Exception {
|
public void waitForMlTemplates() throws Exception {
|
||||||
XPackRestTestHelper.waitForMlTemplates(client());
|
XPackRestTestHelper.waitForMlTemplates(client());
|
||||||
|
|
|
@ -16,7 +16,7 @@ import org.elasticsearch.test.rest.yaml.ESClientYamlSuiteTestCase;
|
||||||
|
|
||||||
import static org.elasticsearch.xpack.security.authc.support.UsernamePasswordToken.basicAuthHeaderValue;
|
import static org.elasticsearch.xpack.security.authc.support.UsernamePasswordToken.basicAuthHeaderValue;
|
||||||
|
|
||||||
public class MultiClusterSearchWithSecurityYamlTestSuiteIT extends SecurityClusterClientYamlTestCase {
|
public class MultiClusterSearchWithSecurityYamlTestSuiteIT extends ESClientYamlSuiteTestCase {
|
||||||
|
|
||||||
private static final String USER = "test_user";
|
private static final String USER = "test_user";
|
||||||
private static final String PASS = "x-pack-test-password";
|
private static final String PASS = "x-pack-test-password";
|
||||||
|
|
|
@ -14,7 +14,7 @@ import org.elasticsearch.common.util.concurrent.ThreadContext;
|
||||||
import org.elasticsearch.test.rest.ESRestTestCase;
|
import org.elasticsearch.test.rest.ESRestTestCase;
|
||||||
import org.elasticsearch.test.rest.yaml.ClientYamlTestCandidate;
|
import org.elasticsearch.test.rest.yaml.ClientYamlTestCandidate;
|
||||||
import org.elasticsearch.test.rest.yaml.ClientYamlTestResponse;
|
import org.elasticsearch.test.rest.yaml.ClientYamlTestResponse;
|
||||||
import org.elasticsearch.xpack.security.SecurityClusterClientYamlTestCase;
|
import org.elasticsearch.test.rest.yaml.ESClientYamlSuiteTestCase;
|
||||||
import org.elasticsearch.xpack.test.rest.XPackRestTestHelper;
|
import org.elasticsearch.xpack.test.rest.XPackRestTestHelper;
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
|
|
||||||
|
@ -29,7 +29,7 @@ import static java.util.Collections.singletonMap;
|
||||||
import static org.hamcrest.Matchers.is;
|
import static org.hamcrest.Matchers.is;
|
||||||
|
|
||||||
@TimeoutSuite(millis = 5 * TimeUnits.MINUTE) // to account for slow as hell VMs
|
@TimeoutSuite(millis = 5 * TimeUnits.MINUTE) // to account for slow as hell VMs
|
||||||
public class UpgradeClusterClientYamlTestSuiteIT extends SecurityClusterClientYamlTestCase {
|
public class UpgradeClusterClientYamlTestSuiteIT extends ESClientYamlSuiteTestCase {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Waits for the Machine Learning templates to be created by {@link org.elasticsearch.plugins.MetaDataUpgrader}
|
* Waits for the Machine Learning templates to be created by {@link org.elasticsearch.plugins.MetaDataUpgrader}
|
||||||
|
|
|
@ -5,7 +5,6 @@
|
||||||
*/
|
*/
|
||||||
package org.elasticsearch.xpack.security.authc.esnative.tool;
|
package org.elasticsearch.xpack.security.authc.esnative.tool;
|
||||||
|
|
||||||
import org.apache.http.HttpHost;
|
|
||||||
import org.apache.http.message.BasicHeader;
|
import org.apache.http.message.BasicHeader;
|
||||||
import org.elasticsearch.cli.MockTerminal;
|
import org.elasticsearch.cli.MockTerminal;
|
||||||
import org.elasticsearch.client.Response;
|
import org.elasticsearch.client.Response;
|
||||||
|
@ -18,7 +17,6 @@ import org.elasticsearch.common.settings.SecureString;
|
||||||
import org.elasticsearch.common.settings.Settings;
|
import org.elasticsearch.common.settings.Settings;
|
||||||
import org.elasticsearch.common.util.concurrent.ThreadContext;
|
import org.elasticsearch.common.util.concurrent.ThreadContext;
|
||||||
import org.elasticsearch.test.rest.ESRestTestCase;
|
import org.elasticsearch.test.rest.ESRestTestCase;
|
||||||
import org.elasticsearch.xpack.security.SecurityClusterClientYamlTestCase;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.UncheckedIOException;
|
import java.io.UncheckedIOException;
|
||||||
|
@ -49,8 +47,6 @@ public class SetupPasswordToolIT extends ESRestTestCase {
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
public void testSetupPasswordToolAutoSetup() throws Exception {
|
public void testSetupPasswordToolAutoSetup() throws Exception {
|
||||||
SecurityClusterClientYamlTestCase.waitForSecurity();
|
|
||||||
|
|
||||||
final String testConfigDir = System.getProperty("tests.config.dir");
|
final String testConfigDir = System.getProperty("tests.config.dir");
|
||||||
logger.info("--> CONF: {}", testConfigDir);
|
logger.info("--> CONF: {}", testConfigDir);
|
||||||
final Path configPath = PathUtils.get(testConfigDir);
|
final Path configPath = PathUtils.get(testConfigDir);
|
||||||
|
|
|
@ -13,11 +13,10 @@ import org.elasticsearch.common.settings.Settings;
|
||||||
import org.elasticsearch.common.util.concurrent.ThreadContext;
|
import org.elasticsearch.common.util.concurrent.ThreadContext;
|
||||||
import org.elasticsearch.test.rest.yaml.ClientYamlTestCandidate;
|
import org.elasticsearch.test.rest.yaml.ClientYamlTestCandidate;
|
||||||
import org.elasticsearch.test.rest.yaml.ESClientYamlSuiteTestCase;
|
import org.elasticsearch.test.rest.yaml.ESClientYamlSuiteTestCase;
|
||||||
import org.elasticsearch.xpack.security.SecurityClusterClientYamlTestCase;
|
|
||||||
|
|
||||||
import static org.elasticsearch.xpack.security.authc.support.UsernamePasswordToken.basicAuthHeaderValue;
|
import static org.elasticsearch.xpack.security.authc.support.UsernamePasswordToken.basicAuthHeaderValue;
|
||||||
|
|
||||||
public class SmokeTestSecurityWithMustacheClientYamlTestSuiteIT extends SecurityClusterClientYamlTestCase {
|
public class SmokeTestSecurityWithMustacheClientYamlTestSuiteIT extends ESClientYamlSuiteTestCase {
|
||||||
|
|
||||||
private static final String BASIC_AUTH_VALUE = basicAuthHeaderValue("test_admin",
|
private static final String BASIC_AUTH_VALUE = basicAuthHeaderValue("test_admin",
|
||||||
new SecureString("x-pack-test-password".toCharArray()));
|
new SecureString("x-pack-test-password".toCharArray()));
|
||||||
|
|
|
@ -113,7 +113,7 @@ public class TribeWithSecurityIT extends SecurityIntegTestCase {
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testThatTribeCanAuthenticateElasticUserWithChangedPassword() throws Exception {
|
public void testThatTribeCanAuthenticateElasticUserWithChangedPassword() throws Exception {
|
||||||
assertSecurityIndexWriteable();
|
assertSecurityIndexActive();
|
||||||
securityClient(client()).prepareChangePassword("elastic", "password".toCharArray()).get();
|
securityClient(client()).prepareChangePassword("elastic", "password".toCharArray()).get();
|
||||||
|
|
||||||
assertTribeNodeHasAllIndices();
|
assertTribeNodeHasAllIndices();
|
||||||
|
@ -124,8 +124,8 @@ public class TribeWithSecurityIT extends SecurityIntegTestCase {
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testThatTribeClustersHaveDifferentPasswords() throws Exception {
|
public void testThatTribeClustersHaveDifferentPasswords() throws Exception {
|
||||||
assertSecurityIndexWriteable();
|
assertSecurityIndexActive();
|
||||||
assertSecurityIndexWriteable(cluster2);
|
assertSecurityIndexActive(cluster2);
|
||||||
securityClient().prepareChangePassword("elastic", "password".toCharArray()).get();
|
securityClient().prepareChangePassword("elastic", "password".toCharArray()).get();
|
||||||
securityClient(cluster2.client()).prepareChangePassword("elastic", "password2".toCharArray()).get();
|
securityClient(cluster2.client()).prepareChangePassword("elastic", "password2".toCharArray()).get();
|
||||||
|
|
||||||
|
@ -155,7 +155,7 @@ public class TribeWithSecurityIT extends SecurityIntegTestCase {
|
||||||
final int randomRoles = scaledRandomIntBetween(3, 8);
|
final int randomRoles = scaledRandomIntBetween(3, 8);
|
||||||
List<String> shouldBeSuccessfulRoles = new ArrayList<>();
|
List<String> shouldBeSuccessfulRoles = new ArrayList<>();
|
||||||
|
|
||||||
assertSecurityIndexWriteable();
|
assertSecurityIndexActive();
|
||||||
for (int i = 0; i < randomRoles; i++) {
|
for (int i = 0; i < randomRoles; i++) {
|
||||||
final String rolename = "preferredClusterRole" + i;
|
final String rolename = "preferredClusterRole" + i;
|
||||||
PutRoleResponse response = securityClient(client()).preparePutRole(rolename).cluster("monitor").get();
|
PutRoleResponse response = securityClient(client()).preparePutRole(rolename).cluster("monitor").get();
|
||||||
|
|
Loading…
Reference in New Issue