Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Do not refresh realm cache unless required #42169

Merged
merged 1 commit into from
May 20, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
import org.elasticsearch.action.support.ContextPreservingActionListener;
import org.elasticsearch.client.Client;
import org.elasticsearch.common.CheckedBiConsumer;
import org.elasticsearch.common.Strings;
import org.elasticsearch.common.bytes.BytesReference;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.util.concurrent.ThreadContext;
Expand Down Expand Up @@ -329,7 +330,12 @@ public void onSecurityIndexStateChange(SecurityIndexManager.State previousState,
}

private <Result> void refreshRealms(ActionListener<Result> listener, Result result) {
String[] realmNames = this.realmsToRefresh.toArray(new String[realmsToRefresh.size()]);
if (realmsToRefresh.isEmpty()) {
listener.onResponse(result);
return;
}

final String[] realmNames = this.realmsToRefresh.toArray(Strings.EMPTY_ARRAY);
final SecurityClient securityClient = new SecurityClient(client);
executeAsyncWithOrigin(client.threadPool().getThreadContext(), SECURITY_ORIGIN,
securityClient.prepareClearRealmCache().realms(realmNames).request(),
Expand All @@ -340,7 +346,7 @@ private <Result> void refreshRealms(ActionListener<Result> listener, Result resu
listener.onResponse(result);
},
ex -> {
logger.warn("Failed to clear cache for realms [{}]", Arrays.toString(realmNames));
logger.warn(new ParameterizedMessage("Failed to clear cache for realms [{}]", Arrays.toString(realmNames)), ex);
listener.onFailure(ex);
}),
securityClient::clearRealmCache);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,7 @@ private SecurityIndexManager.State dummyState(ClusterHealthStatus indexStatus) {

public void testCacheClearOnIndexHealthChange() {
final AtomicInteger numInvalidation = new AtomicInteger(0);
final NativeRoleMappingStore store = buildRoleMappingStoreForInvalidationTesting(numInvalidation);
final NativeRoleMappingStore store = buildRoleMappingStoreForInvalidationTesting(numInvalidation, true);

int expectedInvalidation = 0;
// existing to no longer present
Expand Down Expand Up @@ -180,7 +180,7 @@ public void testCacheClearOnIndexHealthChange() {

public void testCacheClearOnIndexOutOfDateChange() {
final AtomicInteger numInvalidation = new AtomicInteger(0);
final NativeRoleMappingStore store = buildRoleMappingStoreForInvalidationTesting(numInvalidation);
final NativeRoleMappingStore store = buildRoleMappingStoreForInvalidationTesting(numInvalidation, true);

store.onSecurityIndexStateChange(
new SecurityIndexManager.State(Instant.now(), false, true, true, null, concreteSecurityIndexName, null),
Expand All @@ -193,40 +193,59 @@ public void testCacheClearOnIndexOutOfDateChange() {
assertEquals(2, numInvalidation.get());
}

private NativeRoleMappingStore buildRoleMappingStoreForInvalidationTesting(AtomicInteger invalidationCounter) {
public void testCacheIsNotClearedIfNoRealmsAreAttached() {
final AtomicInteger numInvalidation = new AtomicInteger(0);
final NativeRoleMappingStore store = buildRoleMappingStoreForInvalidationTesting(numInvalidation, false);

final SecurityIndexManager.State noIndexState = dummyState(null);
final SecurityIndexManager.State greenIndexState = dummyState(ClusterHealthStatus.GREEN);
store.onSecurityIndexStateChange(noIndexState, greenIndexState);
assertEquals(0, numInvalidation.get());
}

private NativeRoleMappingStore buildRoleMappingStoreForInvalidationTesting(AtomicInteger invalidationCounter, boolean attachRealm) {
final Settings settings = Settings.builder().put("path.home", createTempDir()).build();

final ThreadPool threadPool = mock(ThreadPool.class);
final ThreadContext threadContext = new ThreadContext(settings);
when(threadPool.getThreadContext()).thenReturn(threadContext);

final String realmName = randomAlphaOfLengthBetween(4, 8);

final Client client = mock(Client.class);
when(client.threadPool()).thenReturn(threadPool);
when(client.settings()).thenReturn(settings);
doAnswer(invocationOnMock -> {
assertThat(invocationOnMock.getArguments(), Matchers.arrayWithSize(3));
final ClearRealmCacheRequest request = (ClearRealmCacheRequest) invocationOnMock.getArguments()[1];
assertThat(request.realms(), Matchers.arrayContaining(realmName));

ActionListener<ClearRealmCacheResponse> listener = (ActionListener<ClearRealmCacheResponse>) invocationOnMock.getArguments()[2];
invalidationCounter.incrementAndGet();
listener.onResponse(new ClearRealmCacheResponse(new ClusterName("cluster"), Collections.emptyList(), Collections.emptyList()));
return null;
}).when(client).execute(eq(ClearRealmCacheAction.INSTANCE), any(ClearRealmCacheRequest.class), any(ActionListener.class));

final Environment env = TestEnvironment.newEnvironment(settings);
final RealmConfig realmConfig = new RealmConfig(new RealmConfig.RealmIdentifier("ldap", getTestName()),
settings, env, threadContext);
final CachingUsernamePasswordRealm mockRealm = new CachingUsernamePasswordRealm(realmConfig, threadPool) {
@Override
protected void doAuthenticate(UsernamePasswordToken token, ActionListener<AuthenticationResult> listener) {
listener.onResponse(AuthenticationResult.notHandled());
}

@Override
protected void doLookupUser(String username, ActionListener<User> listener) {
listener.onResponse(null);
}
};
final NativeRoleMappingStore store = new NativeRoleMappingStore(Settings.EMPTY, client, mock(SecurityIndexManager.class),
mock(ScriptService.class));
store.refreshRealmOnChange(mockRealm);

if (attachRealm) {
final Environment env = TestEnvironment.newEnvironment(settings);
final RealmConfig.RealmIdentifier identifier = new RealmConfig.RealmIdentifier("ldap", realmName);
final RealmConfig realmConfig = new RealmConfig(identifier, settings, env, threadContext);
final CachingUsernamePasswordRealm mockRealm = new CachingUsernamePasswordRealm(realmConfig, threadPool) {
@Override
protected void doAuthenticate(UsernamePasswordToken token, ActionListener<AuthenticationResult> listener) {
listener.onResponse(AuthenticationResult.notHandled());
}

@Override
protected void doLookupUser(String username, ActionListener<User> listener) {
listener.onResponse(null);
}
};
store.refreshRealmOnChange(mockRealm);
}
return store;
}
}