test: Add more Shield related tests for testing the Watcher roles.

Original commit: elastic/x-pack-elasticsearch@482d8fe65c
This commit is contained in:
Martijn van Groningen 2015-05-19 17:50:53 +02:00
parent 284a60e16d
commit 2861f8ce21
2 changed files with 208 additions and 13 deletions

View File

@ -35,6 +35,7 @@ import org.elasticsearch.test.ElasticsearchIntegrationTest;
import org.elasticsearch.test.ElasticsearchIntegrationTest.ClusterScope; import org.elasticsearch.test.ElasticsearchIntegrationTest.ClusterScope;
import org.elasticsearch.test.InternalTestCluster; import org.elasticsearch.test.InternalTestCluster;
import org.elasticsearch.test.TestCluster; import org.elasticsearch.test.TestCluster;
import org.elasticsearch.watcher.WatcherLifeCycleService;
import org.elasticsearch.watcher.WatcherPlugin; import org.elasticsearch.watcher.WatcherPlugin;
import org.elasticsearch.watcher.WatcherService; import org.elasticsearch.watcher.WatcherService;
import org.elasticsearch.watcher.WatcherState; import org.elasticsearch.watcher.WatcherState;
@ -155,7 +156,7 @@ public abstract class AbstractWatcherIntegrationTests extends ElasticsearchInteg
if (checkWatcherRunningOnlyOnce()) { if (checkWatcherRunningOnlyOnce()) {
ensureWatcherOnlyRunningOnce(); ensureWatcherOnlyRunningOnce();
} }
stopWatcher(); stopWatcher(false);
} }
@AfterClass @AfterClass
@ -189,15 +190,15 @@ public abstract class AbstractWatcherIntegrationTests extends ElasticsearchInteg
private void startWatcherIfNodesExist() throws Exception { private void startWatcherIfNodesExist() throws Exception {
if (internalTestCluster().size() > 0) { if (internalTestCluster().size() > 0) {
ensureLicenseEnabled(); ensureLicenseEnabled();
WatcherStatsResponse response = watcherClient().prepareWatcherStats().get(); WatcherState state = getInstanceFromMaster(WatcherService.class).state();
if (response.getWatcherState() == WatcherState.STOPPED) { if (state == WatcherState.STOPPED) {
logger.info("[{}#{}]: starting watcher", getTestClass().getSimpleName(), getTestName()); logger.info("[{}#{}]: starting watcher", getTestClass().getSimpleName(), getTestName());
startWatcher(); startWatcher(false);
} else if (response.getWatcherState() == WatcherState.STARTING) { } else if (state == WatcherState.STARTING) {
logger.info("[{}#{}]: watcher is starting, waiting for it to get in a started state", getTestClass().getSimpleName(), getTestName()); logger.info("[{}#{}]: watcher is starting, waiting for it to get in a started state", getTestClass().getSimpleName(), getTestName());
ensureWatcherStarted(false); ensureWatcherStarted(false);
} else { } else {
logger.info("[{}#{}]: not starting watcher, because watcher is in state [{}]", getTestClass().getSimpleName(), getTestName(), response.getWatcherState()); logger.info("[{}#{}]: not starting watcher, because watcher is in state [{}]", getTestClass().getSimpleName(), getTestName(), state);
} }
} else { } else {
logger.info("[{}#{}]: not starting watcher, because test cluster has no nodes", getTestClass().getSimpleName(), getTestName()); logger.info("[{}#{}]: not starting watcher, because test cluster has no nodes", getTestClass().getSimpleName(), getTestName());
@ -427,13 +428,29 @@ public abstract class AbstractWatcherIntegrationTests extends ElasticsearchInteg
} }
protected void startWatcher() throws Exception { protected void startWatcher() throws Exception {
watcherClient().prepareWatchService().start().get(); startWatcher(true);
ensureWatcherStarted(false);
} }
protected void stopWatcher() throws Exception { protected void stopWatcher() throws Exception {
stopWatcher(true);
}
protected void startWatcher(boolean useClient) throws Exception {
if (useClient) {
watcherClient().prepareWatchService().start().get();
} else {
getInstanceFromMaster(WatcherLifeCycleService.class).start();
}
ensureWatcherStarted(useClient);
}
protected void stopWatcher(boolean useClient) throws Exception {
if (useClient) {
watcherClient().prepareWatchService().stop().get(); watcherClient().prepareWatchService().stop().get();
ensureWatcherStopped(false); } else {
getInstanceFromMaster(WatcherLifeCycleService.class).stop();
}
ensureWatcherStopped(useClient);
} }
protected void ensureWatcherOnlyRunningOnce() { protected void ensureWatcherOnlyRunningOnce() {
@ -587,8 +604,6 @@ public abstract class AbstractWatcherIntegrationTests extends ElasticsearchInteg
public static final String TEST_USERNAME = "test"; public static final String TEST_USERNAME = "test";
public static final String TEST_PASSWORD = "changeme"; public static final String TEST_PASSWORD = "changeme";
public static final String TEST_BASIC_AUTH_TOKEN = UsernamePasswordToken.basicAuthHeaderValue(TEST_USERNAME, new SecuredString(TEST_PASSWORD.toCharArray()));
public static final String ADMIN_BASIC_AUTH_TOKEN = UsernamePasswordToken.basicAuthHeaderValue("admin", new SecuredString("changeme".toCharArray()));
static boolean auditLogsEnabled = SystemPropertyUtil.getBoolean("tests.audit_logs", true); static boolean auditLogsEnabled = SystemPropertyUtil.getBoolean("tests.audit_logs", true);
static byte[] systemKey = generateKey(); // must be the same for all nodes static byte[] systemKey = generateKey(); // must be the same for all nodes
@ -596,23 +611,27 @@ public abstract class AbstractWatcherIntegrationTests extends ElasticsearchInteg
public static final String IP_FILTER = "allow: all\n"; public static final String IP_FILTER = "allow: all\n";
public static final String USERS = public static final String USERS =
"transport_client:{plain}changeme\n" +
TEST_USERNAME + ":{plain}" + TEST_PASSWORD + "\n" + TEST_USERNAME + ":{plain}" + TEST_PASSWORD + "\n" +
"admin:{plain}changeme\n" + "admin:{plain}changeme\n" +
"monitor:{plain}changeme"; "monitor:{plain}changeme";
public static final String USER_ROLES = public static final String USER_ROLES =
"transport_client:transport_client\n" +
"test:test\n" + "test:test\n" +
"admin:admin\n" + "admin:admin\n" +
"monitor:monitor"; "monitor:monitor";
public static final String ROLES = public static final String ROLES =
"test:\n" + // a user for the test infra. "test:\n" + // a user for the test infra.
" cluster: all\n" + " cluster: cluster:monitor/state, cluster:monitor/health, indices:admin/template/delete, cluster:admin/repository/delete, indices:admin/template/put, cluster:monitor/stats\n" +
" indices:\n" + " indices:\n" +
" '*': all\n" + " '*': all\n" +
"\n" + "\n" +
"admin:\n" + "admin:\n" +
" cluster: manage_watcher, cluster:monitor/nodes/info\n" + " cluster: manage_watcher, cluster:monitor/nodes/info\n" +
"transport_client:\n" +
" cluster: cluster:monitor/nodes/info\n" +
"\n" + "\n" +
"monitor:\n" + "monitor:\n" +
" cluster: monitor_watcher, cluster:monitor/nodes/info\n" " cluster: monitor_watcher, cluster:monitor/nodes/info\n"

View File

@ -0,0 +1,176 @@
/*
* 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.watcher.test.integration;
import org.elasticsearch.ElasticsearchException;
import org.elasticsearch.common.joda.time.DateTime;
import org.elasticsearch.common.settings.ImmutableSettings;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.shield.ShieldPlugin;
import org.elasticsearch.shield.authc.AuthenticationException;
import org.elasticsearch.shield.authc.support.SecuredString;
import org.elasticsearch.shield.authz.AuthorizationException;
import org.elasticsearch.watcher.WatcherPlugin;
import org.elasticsearch.watcher.WatcherState;
import org.elasticsearch.watcher.client.WatchSourceBuilders;
import org.elasticsearch.watcher.condition.ConditionBuilders;
import org.elasticsearch.watcher.test.AbstractWatcherIntegrationTests;
import org.elasticsearch.watcher.transport.actions.delete.DeleteWatchResponse;
import org.elasticsearch.watcher.transport.actions.execute.ExecuteWatchResponse;
import org.elasticsearch.watcher.transport.actions.get.GetWatchResponse;
import org.elasticsearch.watcher.transport.actions.put.PutWatchResponse;
import org.elasticsearch.watcher.transport.actions.stats.WatcherStatsResponse;
import org.elasticsearch.watcher.trigger.TriggerBuilders;
import org.elasticsearch.watcher.trigger.TriggerEvent;
import org.elasticsearch.watcher.trigger.schedule.IntervalSchedule;
import org.elasticsearch.watcher.trigger.schedule.ScheduleTriggerEvent;
import org.junit.Test;
import static org.elasticsearch.common.joda.time.DateTimeZone.UTC;
import static org.elasticsearch.shield.authc.support.UsernamePasswordToken.basicAuthHeaderValue;
import static org.elasticsearch.watcher.client.WatchSourceBuilders.watchBuilder;
import static org.elasticsearch.watcher.trigger.TriggerBuilders.schedule;
import static org.elasticsearch.watcher.trigger.schedule.Schedules.interval;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.core.Is.is;
public class BasicShieldTests extends AbstractWatcherIntegrationTests {
@Override
protected boolean enableShield() {
return true;
}
@Override
protected Settings transportClientSettings() {
return ImmutableSettings.builder()
.put("client.transport.sniff", false)
.put("plugin.types", ShieldPlugin.class.getName() + "," + WatcherPlugin.class.getName())
// Use just the transport user here, so we can test Watcher roles specifically
.put("shield.user", "transport_client:changeme")
.build();
}
@Test
public void testNoAuthorization() throws Exception {
try {
watcherClient().prepareWatcherStats().get();
fail("authentication failure should have occurred");
} catch (AuthorizationException e) {
// transport_client is the default user
assertThat(e.getMessage(), equalTo("action [cluster:monitor/watcher/stats] is unauthorized for user [transport_client]"));
}
}
@Test
public void testWatcherMonitorRole() throws Exception {
// stats and get watch apis require at least monitor role:
String token = basicAuthHeaderValue("test", new SecuredString("changeme".toCharArray()));
try {
watcherClient().prepareWatcherStats()
.putHeader("Authorization", token)
.get();
fail("authentication failure should have occurred");
} catch (AuthorizationException e) {
assertThat(e.getMessage(), equalTo("action [cluster:monitor/watcher/stats] is unauthorized for user [test]"));
}
try {
watcherClient().prepareGetWatch("_id")
.putHeader("Authorization", token)
.get();
fail("authentication failure should have occurred");
} catch (AuthorizationException e) {
assertThat(e.getMessage(), equalTo("action [cluster:monitor/watcher/watch/get] is unauthorized for user [test]"));
}
// stats and get watch are allowed by role monitor:
token = basicAuthHeaderValue("monitor", new SecuredString("changeme".toCharArray()));
WatcherStatsResponse statsResponse = watcherClient().prepareWatcherStats()
.putHeader("Authorization", token)
.get();
assertThat(statsResponse.getWatcherState(), equalTo(WatcherState.STARTED));
GetWatchResponse getWatchResponse = watcherClient().prepareGetWatch("_id")
.putHeader("Authorization", token)
.get();
assertThat(getWatchResponse.isFound(), is(false));
// but put watch isn't allowed by monitor:
try {
watcherClient().preparePutWatch("_id")
.setSource(watchBuilder().trigger(schedule(interval(5, IntervalSchedule.Interval.Unit.SECONDS))))
.putHeader("Authorization", token)
.get();
fail("authentication failure should have occurred");
} catch (AuthorizationException e) {
assertThat(e.getMessage(), equalTo("action [cluster:admin/watcher/watch/put] is unauthorized for user [monitor]"));
}
}
@Test
public void testWatcherAdminRole() throws Exception {
// put, execute and delete watch apis requires watcher admin role:
String token = basicAuthHeaderValue("test", new SecuredString("changeme".toCharArray()));
try {
watcherClient().preparePutWatch("_id")
.setSource(watchBuilder().trigger(schedule(interval(5, IntervalSchedule.Interval.Unit.SECONDS))))
.putHeader("Authorization", token)
.get();
fail("authentication failure should have occurred");
} catch (AuthorizationException e) {
assertThat(e.getMessage(), equalTo("action [cluster:admin/watcher/watch/put] is unauthorized for user [test]"));
}
TriggerEvent triggerEvent = new ScheduleTriggerEvent(new DateTime(UTC), new DateTime(UTC));
try {
watcherClient().prepareExecuteWatch("_id")
.setTriggerEvent(triggerEvent)
.putHeader("Authorization", token)
.get();
fail("authentication failure should have occurred");
} catch (AuthorizationException e) {
assertThat(e.getMessage(), equalTo("action [cluster:admin/watcher/watch/execute] is unauthorized for user [test]"));
}
try {
watcherClient().prepareDeleteWatch("_id")
.putHeader("Authorization", token)
.get();
fail("authentication failure should have occurred");
} catch (AuthorizationException e) {
assertThat(e.getMessage(), equalTo("action [cluster:admin/watcher/watch/delete] is unauthorized for user [test]"));
}
// put, execute and delete watch apis are allowed by role admin:
token = basicAuthHeaderValue("admin", new SecuredString("changeme".toCharArray()));
PutWatchResponse putWatchResponse = watcherClient().preparePutWatch("_id")
.setSource(watchBuilder().trigger(schedule(interval(5, IntervalSchedule.Interval.Unit.SECONDS))))
.putHeader("Authorization", token)
.get();
assertThat(putWatchResponse.getVersion(), equalTo(1l));
ExecuteWatchResponse executeWatchResponse = watcherClient().prepareExecuteWatch("_id")
.setTriggerEvent(triggerEvent)
.putHeader("Authorization", token)
.get();
DeleteWatchResponse deleteWatchResponse = watcherClient().prepareDeleteWatch("_id")
.putHeader("Authorization", token)
.get();
assertThat(deleteWatchResponse.getVersion(), equalTo(2l));
assertThat(deleteWatchResponse.isFound(), is(true));
// stats and get watch are also allowed by role monitor:
token = basicAuthHeaderValue("monitor", new SecuredString("changeme".toCharArray()));
WatcherStatsResponse statsResponse = watcherClient().prepareWatcherStats()
.putHeader("Authorization", token)
.get();
assertThat(statsResponse.getWatcherState(), equalTo(WatcherState.STARTED));
GetWatchResponse getWatchResponse = watcherClient().prepareGetWatch("_id")
.putHeader("Authorization", token)
.get();
assertThat(getWatchResponse.isFound(), is(false));
}
}