Skip to content

Commit 69ef441

Browse files
committed
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.
1 parent 503b494 commit 69ef441

File tree

2 files changed

+59
-5
lines changed

2 files changed

+59
-5
lines changed

x-pack/plugin/ccr/src/main/java/org/elasticsearch/xpack/ccr/CcrLicenseChecker.java

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,10 @@
77
package org.elasticsearch.xpack.ccr;
88

99
import org.elasticsearch.ElasticsearchStatusException;
10-
import org.elasticsearch.action.ActionType;
1110
import org.elasticsearch.action.ActionListener;
1211
import org.elasticsearch.action.ActionRequest;
1312
import org.elasticsearch.action.ActionResponse;
13+
import org.elasticsearch.action.ActionType;
1414
import org.elasticsearch.action.admin.cluster.state.ClusterStateRequest;
1515
import org.elasticsearch.action.admin.cluster.state.ClusterStateResponse;
1616
import org.elasticsearch.action.admin.indices.stats.IndexShardStats;
@@ -45,6 +45,7 @@
4545
import org.elasticsearch.xpack.core.security.authz.RoleDescriptor;
4646
import org.elasticsearch.xpack.core.security.authz.permission.ResourcePrivileges;
4747
import org.elasticsearch.xpack.core.security.support.Exceptions;
48+
import org.elasticsearch.xpack.core.security.user.User;
4849

4950
import java.util.Arrays;
5051
import java.util.Collections;
@@ -61,7 +62,7 @@
6162
/**
6263
* Encapsulates licensing checking for CCR.
6364
*/
64-
public final class CcrLicenseChecker {
65+
public class CcrLicenseChecker {
6566

6667
private final BooleanSupplier isCcrAllowed;
6768
private final BooleanSupplier isAuthAllowed;
@@ -307,9 +308,12 @@ public void hasPrivilegesToFollowIndices(final Client remoteClient, final String
307308
return;
308309
}
309310

310-
ThreadContext threadContext = remoteClient.threadPool().getThreadContext();
311-
SecurityContext securityContext = new SecurityContext(Settings.EMPTY, threadContext);
312-
String username = securityContext.getUser().principal();
311+
final User user = getUser(remoteClient);
312+
if (user == null) {
313+
handler.accept(new IllegalStateException("missing or unable to read authentication info on request"));
314+
return;
315+
}
316+
String username = user.principal();
313317

314318
RoleDescriptor.IndicesPrivileges privileges = RoleDescriptor.IndicesPrivileges.builder()
315319
.indices(indices)
@@ -344,6 +348,12 @@ public void hasPrivilegesToFollowIndices(final Client remoteClient, final String
344348
remoteClient.execute(HasPrivilegesAction.INSTANCE, request, ActionListener.wrap(responseHandler, handler));
345349
}
346350

351+
User getUser(final Client remoteClient) {
352+
final ThreadContext threadContext = remoteClient.threadPool().getThreadContext();
353+
final SecurityContext securityContext = new SecurityContext(Settings.EMPTY, threadContext);
354+
return securityContext.getUser();
355+
}
356+
347357
public static Client wrapClient(Client client, Map<String, String> headers) {
348358
if (headers.isEmpty()) {
349359
return client;
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
/*
2+
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
3+
* or more contributor license agreements. Licensed under the Elastic License;
4+
* you may not use this file except in compliance with the Elastic License.
5+
*/
6+
7+
package org.elasticsearch.xpack.ccr;
8+
9+
import org.elasticsearch.client.Client;
10+
import org.elasticsearch.test.ESTestCase;
11+
import org.elasticsearch.xpack.core.security.user.User;
12+
13+
import java.util.concurrent.atomic.AtomicBoolean;
14+
15+
import static org.hamcrest.Matchers.containsString;
16+
import static org.hamcrest.Matchers.hasToString;
17+
import static org.hamcrest.Matchers.instanceOf;
18+
import static org.mockito.Mockito.mock;
19+
20+
public class CcrLicenseCheckerTests extends ESTestCase {
21+
22+
public void testNoAuthenticationInfo() {
23+
final boolean isCcrAllowed = randomBoolean();
24+
final CcrLicenseChecker checker = new CcrLicenseChecker(() -> isCcrAllowed, () -> true) {
25+
26+
@Override
27+
User getUser(final Client remoteClient) {
28+
return null;
29+
}
30+
31+
};
32+
final AtomicBoolean invoked = new AtomicBoolean();
33+
checker.hasPrivilegesToFollowIndices(
34+
mock(Client.class),
35+
new String[]{randomAlphaOfLength(8)},
36+
e -> {
37+
invoked.set(true);
38+
assertThat(e, instanceOf(IllegalStateException.class));
39+
assertThat(e, hasToString(containsString("missing or unable to read authentication info on request")));
40+
});
41+
assertTrue(invoked.get());
42+
}
43+
44+
}

0 commit comments

Comments
 (0)