fix the rest response for the clear cache action and add tests

Today the XContent building of the response for the ClearRealmsCacheResponse is broken and causes
an exception to be thrown. This fixes the building of the response and adds tests that call the HTTP
endpoint and do a basic check on the response.

Closes elastic/elasticsearch#390

Original commit: elastic/x-pack-elasticsearch@8ad9dae4ea
This commit is contained in:
jaymode 2015-08-05 09:25:30 -04:00
parent b433ee390c
commit 1b72f13458
3 changed files with 126 additions and 36 deletions

View File

@ -230,7 +230,7 @@ describes the different hash algorithm that can be set:
[float]
[[cache-eviction-api]]
==== Cache Eviction API
Shield exposes an API to force cached user eviction. The following example, evicts all users from the `ldap1`
Shield exposes an API to force cached user eviction. The following example, evicts all users from the `esusers`
realm:
[source, java]
@ -238,6 +238,9 @@ realm:
$ curl -XPOST 'http://localhost:9200/_shield/realm/esusers/_cache/clear'
------------------------------------------------------------
NOTE: if no realm is defined, the default realm name, `default_esusers` can be used to clear the cache
of the default esusers realm.
It is also possible to evict specific users:
[source, java]

View File

@ -49,6 +49,7 @@ public class ClearRealmCacheResponse extends BaseNodesResponse<ClearRealmCacheRe
@Override
public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
builder.startObject();
builder.field("cluster_name", getClusterName().value());
builder.startObject("nodes");
for (ClearRealmCacheResponse.Node node: getNodes()) {
@ -56,6 +57,7 @@ public class ClearRealmCacheResponse extends BaseNodesResponse<ClearRealmCacheRe
builder.field("name", node.getNode().name());
builder.endObject();
}
builder.endObject();
return builder.endObject();
}

View File

@ -5,8 +5,14 @@
*/
package org.elasticsearch.integration;
import com.google.common.base.Joiner;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.elasticsearch.action.ActionListener;
import org.elasticsearch.common.Strings;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.http.HttpServerTransport;
import org.elasticsearch.node.Node;
import org.elasticsearch.shield.User;
import org.elasticsearch.shield.action.authc.cache.ClearRealmCacheRequest;
import org.elasticsearch.shield.action.authc.cache.ClearRealmCacheResponse;
@ -19,6 +25,8 @@ import org.elasticsearch.shield.authc.support.UsernamePasswordToken;
import org.elasticsearch.shield.client.ShieldClient;
import org.elasticsearch.test.ShieldIntegTestCase;
import org.elasticsearch.test.ShieldSettingsSource;
import org.elasticsearch.test.rest.client.http.HttpRequestBuilder;
import org.elasticsearch.test.rest.client.http.HttpResponse;
import org.junit.BeforeClass;
import org.junit.Test;
@ -51,13 +59,13 @@ public class ClearRealmsCacheTests extends ShieldIntegTestCase {
EVICT_ALL() {
@Override
public ClearRealmCacheRequest createRequest() {
return new ClearRealmCacheRequest();
public void assertEviction(User prevUser, User newUser) {
assertThat(prevUser, not(sameInstance(newUser)));
}
@Override
public void assertEviction(User prevUser, User newUser) {
assertThat(prevUser, not(sameInstance(newUser)));
public void executeRequest() throws Exception {
executeTransportRequest(new ClearRealmCacheRequest());
}
},
@ -69,8 +77,38 @@ public class ClearRealmsCacheTests extends ShieldIntegTestCase {
}
@Override
public ClearRealmCacheRequest createRequest() {
return new ClearRealmCacheRequest().usernames(evicted_usernames);
public void assertEviction(User prevUser, User newUser) {
if (Arrays.binarySearch(evicted_usernames, prevUser.principal()) >= 0) {
assertThat(prevUser, not(sameInstance(newUser)));
} else {
assertThat(prevUser, sameInstance(newUser));
}
}
@Override
public void executeRequest() throws Exception {
executeTransportRequest(new ClearRealmCacheRequest().usernames(evicted_usernames));
}
},
EVICT_ALL_HTTP() {
@Override
public void assertEviction(User prevUser, User newUser) {
assertThat(prevUser, not(sameInstance(newUser)));
}
@Override
public void executeRequest() throws Exception {
executeHttpRequest("/_shield/realm/" + (randomBoolean() ? "*" : "_all") + "/_cache/clear", Collections.<String, String>emptyMap());
}
},
EVICT_SOME_HTTP() {
private final String[] evicted_usernames = randomSelection(usernames);
{
Arrays.sort(evicted_usernames);
}
@Override
@ -81,13 +119,76 @@ public class ClearRealmsCacheTests extends ShieldIntegTestCase {
assertThat(prevUser, sameInstance(newUser));
}
}
@Override
public void executeRequest() throws Exception {
String path = "/_shield/realm/" + (randomBoolean() ? "*" : "_all") + "/_cache/clear";
Map<String, String> params = Collections.singletonMap("usernames", Joiner.on(',').join(evicted_usernames));
executeHttpRequest(path, params);
}
};
public abstract ClearRealmCacheRequest createRequest();
public abstract void assertEviction(User prevUser, User newUser);
public abstract void executeRequest() throws Exception;
static void executeTransportRequest(ClearRealmCacheRequest request) throws Exception {
ShieldClient client = new ShieldClient(client());
final CountDownLatch latch = new CountDownLatch(1);
final AtomicReference<Throwable> error = new AtomicReference<>();
client.authc().clearRealmCache(request, new ActionListener<ClearRealmCacheResponse>() {
@Override
public void onResponse(ClearRealmCacheResponse response) {
assertThat(response.getNodes().length, equalTo(internalTestCluster().getNodeNames().length));
latch.countDown();
}
@Override
public void onFailure(Throwable e) {
error.set(e);
latch.countDown();
}
});
if (!latch.await(5, TimeUnit.SECONDS)) {
fail("waiting for clear realms cache request too long");
}
if (error.get() != null) {
fail("failed to clear realm caches" + error.get().getMessage());
}
}
static void executeHttpRequest(String path, Map<String, String> params) throws Exception {
try (CloseableHttpClient client = HttpClients.createDefault()) {
HttpRequestBuilder requestBuilder = new HttpRequestBuilder(client)
.httpTransport(internalTestCluster().getDataNodeInstance(HttpServerTransport.class))
.method("POST")
.path(path);
for (Map.Entry<String, String> entry : params.entrySet()) {
requestBuilder.addParam(entry.getKey(), entry.getValue());
}
requestBuilder.addHeader(UsernamePasswordToken.BASIC_AUTH_HEADER, UsernamePasswordToken.basicAuthHeaderValue(ShieldSettingsSource.DEFAULT_USER_NAME, new SecuredString(ShieldSettingsSource.DEFAULT_PASSWORD.toCharArray())));
HttpResponse response = requestBuilder.execute();
assertThat(response.hasBody(), is(true));
String body = response.getBody();
assertThat(body.contains("cluster_name"), is(true));
}
}
}
@Override
public Settings nodeSettings(int nodeOrdinal) {
return Settings.builder()
.put(super.nodeSettings(nodeOrdinal))
.put(Node.HTTP_ENABLED, true)
.build();
}
@Override
public boolean sslTransportEnabled() {
return false;
}
@Override
protected String configRoles() {
@ -121,6 +222,16 @@ public class ClearRealmsCacheTests extends ShieldIntegTestCase {
testScenario(Scenario.EVICT_SOME);
}
@Test
public void testEvictAllHttp() throws Exception {
testScenario(Scenario.EVICT_ALL_HTTP);
}
@Test
public void testEvictSomeHttp() throws Exception {
testScenario(Scenario.EVICT_SOME_HTTP);
}
private void testScenario(Scenario scenario) throws Exception {
Map<String, UsernamePasswordToken> tokens = new HashMap<>();
@ -158,33 +269,7 @@ public class ClearRealmsCacheTests extends ShieldIntegTestCase {
}
// now, lets run the scenario
ShieldClient client = new ShieldClient(client());
final CountDownLatch latch = new CountDownLatch(1);
final AtomicReference<Throwable> error = new AtomicReference<>();
client.authc().clearRealmCache(scenario.createRequest(), new ActionListener<ClearRealmCacheResponse>() {
@Override
public void onResponse(ClearRealmCacheResponse response) {
assertThat(response.getNodes().length, equalTo(internalTestCluster().getNodeNames().length));
latch.countDown();
}
@Override
public void onFailure(Throwable e) {
error.set(e);
latch.countDown();
}
});
if (!latch.await(5, TimeUnit.SECONDS)) {
fail("waiting for clear realms cache request too long");
}
if (error.get() != null) {
logger.error("failed to clear realm caches", error.get());
fail("failed to clear realm caches");
}
scenario.executeRequest();
// now, user_a should have been evicted, but user_b should still be cached
for (String username : usernames) {