Avoid NPE when checking for CCR index privileges (#44397)

This commit avoids an NPE when checking for privileges to follow
indices. The problem here is that in some cases we might not be able to
read the authentication info from the thread context. In that case, a
null user would be returned and we were not guarding against this.
This commit is contained in:
Jason Tedor 2019-07-17 06:24:24 +09:00
parent 53514b0477
commit becbf450fa
No known key found for this signature in database
GPG Key ID: FA89F05560F16BC5
2 changed files with 59 additions and 5 deletions

View File

@ -7,10 +7,10 @@
package org.elasticsearch.xpack.ccr; package org.elasticsearch.xpack.ccr;
import org.elasticsearch.ElasticsearchStatusException; import org.elasticsearch.ElasticsearchStatusException;
import org.elasticsearch.action.ActionType;
import org.elasticsearch.action.ActionListener; import org.elasticsearch.action.ActionListener;
import org.elasticsearch.action.ActionRequest; import org.elasticsearch.action.ActionRequest;
import org.elasticsearch.action.ActionResponse; import org.elasticsearch.action.ActionResponse;
import org.elasticsearch.action.ActionType;
import org.elasticsearch.action.admin.cluster.state.ClusterStateRequest; import org.elasticsearch.action.admin.cluster.state.ClusterStateRequest;
import org.elasticsearch.action.admin.cluster.state.ClusterStateResponse; import org.elasticsearch.action.admin.cluster.state.ClusterStateResponse;
import org.elasticsearch.action.admin.indices.stats.IndexShardStats; import org.elasticsearch.action.admin.indices.stats.IndexShardStats;
@ -45,6 +45,7 @@ import org.elasticsearch.xpack.core.security.action.user.HasPrivilegesResponse;
import org.elasticsearch.xpack.core.security.authz.RoleDescriptor; import org.elasticsearch.xpack.core.security.authz.RoleDescriptor;
import org.elasticsearch.xpack.core.security.authz.permission.ResourcePrivileges; import org.elasticsearch.xpack.core.security.authz.permission.ResourcePrivileges;
import org.elasticsearch.xpack.core.security.support.Exceptions; import org.elasticsearch.xpack.core.security.support.Exceptions;
import org.elasticsearch.xpack.core.security.user.User;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collections; import java.util.Collections;
@ -61,7 +62,7 @@ import java.util.stream.Collectors;
/** /**
* Encapsulates licensing checking for CCR. * Encapsulates licensing checking for CCR.
*/ */
public final class CcrLicenseChecker { public class CcrLicenseChecker {
private final BooleanSupplier isCcrAllowed; private final BooleanSupplier isCcrAllowed;
private final BooleanSupplier isAuthAllowed; private final BooleanSupplier isAuthAllowed;
@ -307,9 +308,12 @@ public final class CcrLicenseChecker {
return; return;
} }
ThreadContext threadContext = remoteClient.threadPool().getThreadContext(); final User user = getUser(remoteClient);
SecurityContext securityContext = new SecurityContext(Settings.EMPTY, threadContext); if (user == null) {
String username = securityContext.getUser().principal(); handler.accept(new IllegalStateException("missing or unable to read authentication info on request"));
return;
}
String username = user.principal();
RoleDescriptor.IndicesPrivileges privileges = RoleDescriptor.IndicesPrivileges.builder() RoleDescriptor.IndicesPrivileges privileges = RoleDescriptor.IndicesPrivileges.builder()
.indices(indices) .indices(indices)
@ -344,6 +348,12 @@ public final class CcrLicenseChecker {
remoteClient.execute(HasPrivilegesAction.INSTANCE, request, ActionListener.wrap(responseHandler, handler)); remoteClient.execute(HasPrivilegesAction.INSTANCE, request, ActionListener.wrap(responseHandler, handler));
} }
User getUser(final Client remoteClient) {
final ThreadContext threadContext = remoteClient.threadPool().getThreadContext();
final SecurityContext securityContext = new SecurityContext(Settings.EMPTY, threadContext);
return securityContext.getUser();
}
public static Client wrapClient(Client client, Map<String, String> headers) { public static Client wrapClient(Client client, Map<String, String> headers) {
if (headers.isEmpty()) { if (headers.isEmpty()) {
return client; return client;

View File

@ -0,0 +1,44 @@
/*
* 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.ccr;
import org.elasticsearch.client.Client;
import org.elasticsearch.test.ESTestCase;
import org.elasticsearch.xpack.core.security.user.User;
import java.util.concurrent.atomic.AtomicBoolean;
import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.hasToString;
import static org.hamcrest.Matchers.instanceOf;
import static org.mockito.Mockito.mock;
public class CcrLicenseCheckerTests extends ESTestCase {
public void testNoAuthenticationInfo() {
final boolean isCcrAllowed = randomBoolean();
final CcrLicenseChecker checker = new CcrLicenseChecker(() -> isCcrAllowed, () -> true) {
@Override
User getUser(final Client remoteClient) {
return null;
}
};
final AtomicBoolean invoked = new AtomicBoolean();
checker.hasPrivilegesToFollowIndices(
mock(Client.class),
new String[]{randomAlphaOfLength(8)},
e -> {
invoked.set(true);
assertThat(e, instanceOf(IllegalStateException.class));
assertThat(e, hasToString(containsString("missing or unable to read authentication info on request")));
});
assertTrue(invoked.get());
}
}