Merge branch 'master' into security/dynamic-disabling-in-es

Original commit: elastic/x-pack-elasticsearch@0bb94780d3
This commit is contained in:
Shaunak Kashyap 2016-07-25 17:27:30 -07:00
commit 7528231ef0
141 changed files with 2565 additions and 2392 deletions

View File

@ -38,11 +38,10 @@ public class IndexAuditIT extends ESIntegTestCase {
@AwaitsFix(bugUrl = "https://github.com/elastic/x-plugins/issues/2354")
public void testShieldIndexAuditTrailWorking() throws Exception {
try (Response response = getRestClient().performRequest("GET", "/",
Response response = getRestClient().performRequest("GET", "/",
new BasicHeader(UsernamePasswordToken.BASIC_AUTH_HEADER,
UsernamePasswordToken.basicAuthHeaderValue(USER, new SecuredString(PASS.toCharArray()))))) {
assertThat(response.getStatusLine().getStatusCode(), is(200));
}
UsernamePasswordToken.basicAuthHeaderValue(USER, new SecuredString(PASS.toCharArray()))));
assertThat(response.getStatusLine().getStatusCode(), is(200));
final AtomicReference<ClusterState> lastClusterState = new AtomicReference<>();
final AtomicBoolean indexExists = new AtomicBoolean(false);
boolean found = awaitBusy(() -> {

View File

@ -7,18 +7,19 @@ package org.elasticsearch.xpack.security;
import com.carrotsearch.randomizedtesting.annotations.Name;
import com.carrotsearch.randomizedtesting.annotations.ParametersFactory;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.util.concurrent.ThreadContext;
import org.elasticsearch.xpack.security.authc.support.SecuredString;
import org.elasticsearch.test.rest.ESRestTestCase;
import org.elasticsearch.test.rest.ESClientYamlSuiteTestCase;
import org.elasticsearch.test.rest.RestTestCandidate;
import org.elasticsearch.test.rest.parser.RestTestParseException;
import org.elasticsearch.xpack.security.authc.support.SecuredString;
import java.io.IOException;
import static org.elasticsearch.xpack.security.authc.support.UsernamePasswordToken.basicAuthHeaderValue;
public class RestIT extends ESRestTestCase {
public class RestIT extends ESClientYamlSuiteTestCase {
private static final String USER = "test_user";
private static final String PASS = "changeme";
@ -29,7 +30,7 @@ public class RestIT extends ESRestTestCase {
@ParametersFactory
public static Iterable<Object[]> parameters() throws IOException, RestTestParseException {
return ESRestTestCase.createParameters(0, 1);
return ESClientYamlSuiteTestCase.createParameters(0, 1);
}
@Override

View File

@ -1,215 +0,0 @@
/*
* 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.messy.tests;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.action.search.SearchType;
import org.elasticsearch.plugins.Plugin;
import org.elasticsearch.script.groovy.GroovyPlugin;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.aggregations.bucket.histogram.DateHistogramInterval;
import org.elasticsearch.search.sort.SortOrder;
import org.elasticsearch.xpack.watcher.history.HistoryStore;
import org.elasticsearch.xpack.watcher.support.WatcherDateTimeUtils;
import org.elasticsearch.xpack.watcher.test.AbstractWatcherIntegrationTestCase;
import org.elasticsearch.xpack.watcher.transport.actions.execute.ExecuteWatchResponse;
import org.elasticsearch.xpack.watcher.transport.actions.put.PutWatchResponse;
import org.elasticsearch.xpack.watcher.trigger.schedule.ScheduleTriggerEvent;
import org.joda.time.DateTime;
import org.joda.time.DateTimeZone;
import java.util.List;
import static org.elasticsearch.common.xcontent.XContentFactory.jsonBuilder;
import static org.elasticsearch.index.query.QueryBuilders.matchQuery;
import static org.elasticsearch.search.aggregations.AggregationBuilders.dateHistogram;
import static org.elasticsearch.search.builder.SearchSourceBuilder.searchSource;
import static org.elasticsearch.xpack.watcher.actions.ActionBuilders.indexAction;
import static org.elasticsearch.xpack.watcher.client.WatchSourceBuilders.watchBuilder;
import static org.elasticsearch.xpack.watcher.input.InputBuilders.searchInput;
import static org.elasticsearch.xpack.watcher.input.InputBuilders.simpleInput;
import static org.elasticsearch.xpack.watcher.transform.TransformBuilders.scriptTransform;
import static org.elasticsearch.xpack.watcher.trigger.TriggerBuilders.schedule;
import static org.elasticsearch.xpack.watcher.trigger.schedule.Schedules.cron;
import static org.hamcrest.Matchers.hasEntry;
import static org.hamcrest.Matchers.hasKey;
import static org.hamcrest.Matchers.is;
/**
*
*/
public class IndexActionIT extends AbstractWatcherIntegrationTestCase {
@Override
protected List<Class<? extends Plugin>> pluginTypes() {
List<Class<? extends Plugin>> types = super.pluginTypes();
types.add(GroovyPlugin.class);
return types;
}
public void testSimple() throws Exception {
PutWatchResponse putWatchResponse = watcherClient().preparePutWatch("_id").setSource(watchBuilder()
.trigger(schedule(cron("0/1 * * * * ? 2020")))
.input(simpleInput("foo", "bar"))
.addAction("index-buckets", indexAction("idx", "type").setExecutionTimeField("@timestamp")))
.get();
assertThat(putWatchResponse.isCreated(), is(true));
DateTime now = timeWarped() ? timeWarp().clock().now(DateTimeZone.UTC) : DateTime.now(DateTimeZone.UTC);
ExecuteWatchResponse executeWatchResponse = watcherClient().prepareExecuteWatch("_id")
.setTriggerEvent(new ScheduleTriggerEvent(now, now))
.get();
assertThat(executeWatchResponse.getRecordSource().getValue("state"), is((Object) "executed"));
flush("idx");
refresh();
SearchResponse searchResponse = client().prepareSearch("idx").setTypes("type").get();
assertThat(searchResponse.getHits().totalHits(), is(1L));
SearchHit hit = searchResponse.getHits().getAt(0);
if (timeWarped()) {
assertThat(hit.getSource(), hasEntry("@timestamp", (Object) WatcherDateTimeUtils.formatDate(now)));
} else {
assertThat(hit.getSource(), hasKey("@timestamp"));
DateTime timestamp = WatcherDateTimeUtils.parseDate((String) hit.getSource().get("@timestamp"));
assertThat(timestamp.isEqual(now) || timestamp.isAfter(now), is(true));
}
assertThat(hit.getSource(), hasEntry("foo", (Object) "bar"));
}
public void testSimpleWithDocField() throws Exception {
PutWatchResponse putWatchResponse = watcherClient().preparePutWatch("_id").setSource(watchBuilder()
.trigger(schedule(cron("0/1 * * * * ? 2020")))
.input(simpleInput("foo", "bar"))
.addAction("index-buckets",
scriptTransform("return [ '_doc' : ctx.payload ]"),
indexAction("idx", "type").setExecutionTimeField("@timestamp")))
.get();
assertThat(putWatchResponse.isCreated(), is(true));
DateTime now = timeWarped() ? timeWarp().clock().now(DateTimeZone.UTC) : DateTime.now(DateTimeZone.UTC);
ExecuteWatchResponse executeWatchResponse = watcherClient().prepareExecuteWatch("_id")
.setTriggerEvent(new ScheduleTriggerEvent(now, now))
.get();
assertThat(executeWatchResponse.getRecordSource().getValue("state"), is((Object) "executed"));
flush("idx");
refresh();
SearchResponse searchResponse = client().prepareSearch("idx").setTypes("type").get();
assertThat(searchResponse.getHits().totalHits(), is(1L));
SearchHit hit = searchResponse.getHits().getAt(0);
if (timeWarped()) {
assertThat(hit.getSource(), hasEntry("@timestamp", (Object) WatcherDateTimeUtils.formatDate(now)));
} else {
assertThat(hit.getSource(), hasKey("@timestamp"));
DateTime timestamp = WatcherDateTimeUtils.parseDate((String) hit.getSource().get("@timestamp"));
assertThat(timestamp.isEqual(now) || timestamp.isAfter(now), is(true));
}
assertThat(hit.getSource(), hasEntry("foo", (Object) "bar"));
}
public void testSimpleWithDocFieldWrongFieldType() throws Exception {
PutWatchResponse putWatchResponse = watcherClient().preparePutWatch("_id").setSource(watchBuilder()
.trigger(schedule(cron("0/1 * * * * ? 2020")))
.input(simpleInput("foo", "bar"))
.addAction("index-buckets",
scriptTransform("return [ '_doc' : 1 ]"),
indexAction("idx", "type").setExecutionTimeField("@timestamp")))
.get();
assertThat(putWatchResponse.isCreated(), is(true));
DateTime now = timeWarped() ? timeWarp().clock().now(DateTimeZone.UTC) : DateTime.now(DateTimeZone.UTC);
ExecuteWatchResponse executeWatchResponse = watcherClient().prepareExecuteWatch("_id")
.setTriggerEvent(new ScheduleTriggerEvent(now, now))
.setRecordExecution(true)
.get();
assertThat(executeWatchResponse.getRecordSource().getValue("state"), is((Object) "executed"));
flush();
refresh();
assertThat(client().admin().indices().prepareExists("idx").get().isExists(), is(false));
assertThat(docCount(HistoryStore.INDEX_PREFIX + "*", HistoryStore.DOC_TYPE, searchSource()
.query(matchQuery("result.actions.status", "failure"))), is(1L));
}
public void testIndexAggsBucketsAsDocuments() throws Exception {
DateTime now = timeWarped() ? timeWarp().clock().now(DateTimeZone.UTC) : DateTime.now(DateTimeZone.UTC);
long bucketCount = randomIntBetween(2, 5);
for (int i = 0; i < bucketCount; i++) {
index("idx", "type", jsonBuilder().startObject()
.field("timestamp", now.minusDays(i))
.endObject());
}
flush("idx");
refresh();
PutWatchResponse putWatchResponse = watcherClient().preparePutWatch("_id").setSource(watchBuilder()
.trigger(schedule(cron("0/1 * * * * ? 2020")))
.input(searchInput(new SearchRequest("idx")
.types("type")
.searchType(SearchType.QUERY_THEN_FETCH)
.source(searchSource()
.aggregation(dateHistogram("trend")
.field("timestamp")
.dateHistogramInterval(DateHistogramInterval.DAY)))))
.addAction("index-buckets",
// this transform takes the bucket list and assigns it to `_doc`
// this means each bucket will be indexed as a separate doc,
// so we expect to have the same number of documents as the number
// of buckets.
scriptTransform("return [ '_doc' : ctx.payload.aggregations.trend.buckets]"),
indexAction("idx", "bucket").setExecutionTimeField("@timestamp")))
.get();
assertThat(putWatchResponse.isCreated(), is(true));
ExecuteWatchResponse executeWatchResponse = watcherClient().prepareExecuteWatch("_id")
.setTriggerEvent(new ScheduleTriggerEvent(now, now))
.get();
assertThat(executeWatchResponse.getRecordSource().getValue("state"), is((Object) "executed"));
flush("idx");
refresh();
SearchResponse searchResponse = client().prepareSearch("idx").setTypes("bucket")
.addSort("key", SortOrder.DESC)
.get();
assertThat(searchResponse.getHits().getTotalHits(), is(bucketCount));
DateTime key = now.withMillisOfDay(0);
int i = 0;
for (SearchHit hit : searchResponse.getHits()) {
if (timeWarped()) {
assertThat(hit.getSource(), hasEntry("@timestamp", (Object) WatcherDateTimeUtils.formatDate(now)));
} else {
assertThat(hit.getSource(), hasKey("@timestamp"));
DateTime timestamp = WatcherDateTimeUtils.parseDate((String) hit.getSource().get("@timestamp"));
assertThat(timestamp.isEqual(now) || timestamp.isAfter(now), is(true));
}
assertThat(hit.getSource(), hasEntry("key", (Object) key.getMillis()));
key = key.minusDays(1);
}
}
}

View File

@ -158,7 +158,7 @@ public class WatchAckIT extends AbstractWatcherIntegrationTestCase {
assertThat(parsedWatch.status().actionStatus("_a2").ackStatus().state(),
is(ActionStatus.AckStatus.State.AWAITS_SUCCESSFUL_EXECUTION));
long throttledCount = docCount(HistoryStore.INDEX_PREFIX + "*", null,
long throttledCount = docCount(HistoryStore.INDEX_PREFIX_WITH_TEMPLATE + "*", null,
matchQuery(WatchRecord.Field.STATE.getPreferredName(), ExecutionState.THROTTLED.id()));
assertThat(throttledCount, greaterThan(0L));
}
@ -240,7 +240,7 @@ public class WatchAckIT extends AbstractWatcherIntegrationTestCase {
assertThat(parsedWatch.status().actionStatus("_a2").ackStatus().state(),
is(ActionStatus.AckStatus.State.AWAITS_SUCCESSFUL_EXECUTION));
long throttledCount = docCount(HistoryStore.INDEX_PREFIX + "*", null,
long throttledCount = docCount(HistoryStore.INDEX_PREFIX_WITH_TEMPLATE + "*", null,
matchQuery(WatchRecord.Field.STATE.getPreferredName(), ExecutionState.THROTTLED.id()));
assertThat(throttledCount, greaterThan(0L));
}

View File

@ -7,18 +7,19 @@ package org.elasticsearch.xpack.security;
import com.carrotsearch.randomizedtesting.annotations.Name;
import com.carrotsearch.randomizedtesting.annotations.ParametersFactory;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.util.concurrent.ThreadContext;
import org.elasticsearch.xpack.security.authc.support.SecuredString;
import org.elasticsearch.test.rest.ESRestTestCase;
import org.elasticsearch.test.rest.ESClientYamlSuiteTestCase;
import org.elasticsearch.test.rest.RestTestCandidate;
import org.elasticsearch.test.rest.parser.RestTestParseException;
import org.elasticsearch.xpack.security.authc.support.SecuredString;
import java.io.IOException;
import static org.elasticsearch.xpack.security.authc.support.UsernamePasswordToken.basicAuthHeaderValue;
public class RestIT extends ESRestTestCase {
public class RestIT extends ESClientYamlSuiteTestCase {
private static final String USER = "test_admin";
private static final String PASS = "changeme";
@ -28,7 +29,7 @@ public class RestIT extends ESRestTestCase {
@ParametersFactory
public static Iterable<Object[]> parameters() throws IOException, RestTestParseException {
return ESRestTestCase.createParameters(0, 1);
return ESClientYamlSuiteTestCase.createParameters(0, 1);
}
/**

View File

@ -7,7 +7,7 @@ package org.elasticsearch.example;
import org.elasticsearch.example.realm.CustomAuthenticationFailureHandler;
import org.elasticsearch.example.realm.CustomRealm;
import org.elasticsearch.xpack.security.authc.AuthenticationModule;
import org.elasticsearch.xpack.security.authc.AuthenticationFailureHandler;
import org.elasticsearch.xpack.extensions.XPackExtension;
import org.elasticsearch.xpack.security.authc.Realm;
@ -38,15 +38,16 @@ public class ExampleRealmExtension extends XPackExtension {
return "a very basic implementation of a custom realm to validate it works";
}
public void onModule(AuthenticationModule authenticationModule) {
authenticationModule.setAuthenticationFailureHandler(CustomAuthenticationFailureHandler.class);
}
@Override
public Map<String, Realm.Factory> getRealms() {
return Collections.singletonMap(CustomRealm.TYPE, CustomRealm::new);
}
@Override
public AuthenticationFailureHandler getAuthenticationFailureHandler() {
return new CustomAuthenticationFailureHandler();
}
@Override
public Collection<String> getRestHeaders() {
return Arrays.asList(CustomRealm.USER_HEADER, CustomRealm.PW_HEADER);

View File

@ -60,11 +60,10 @@ public class CustomRealmIT extends ESIntegTestCase {
}
public void testHttpAuthentication() throws Exception {
try (Response response = getRestClient().performRequest("GET", "/",
Response response = getRestClient().performRequest("GET", "/",
new BasicHeader(CustomRealm.USER_HEADER, CustomRealm.KNOWN_USER),
new BasicHeader(CustomRealm.PW_HEADER, CustomRealm.KNOWN_PW))) {
assertThat(response.getStatusLine().getStatusCode(), is(200));
}
new BasicHeader(CustomRealm.PW_HEADER, CustomRealm.KNOWN_PW));
assertThat(response.getStatusLine().getStatusCode(), is(200));
}
public void testTransportClient() throws Exception {

View File

@ -7,20 +7,20 @@ package org.elasticsearch.smoketest;
import com.carrotsearch.randomizedtesting.annotations.Name;
import com.carrotsearch.randomizedtesting.annotations.ParametersFactory;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.util.concurrent.ThreadContext;
import org.elasticsearch.xpack.security.authc.support.SecuredString;
import org.elasticsearch.test.rest.ESRestTestCase;
import org.elasticsearch.test.rest.ESClientYamlSuiteTestCase;
import org.elasticsearch.test.rest.RestTestCandidate;
import org.elasticsearch.test.rest.parser.RestTestParseException;
import org.elasticsearch.xpack.security.authc.support.SecuredString;
import java.io.IOException;
import static org.elasticsearch.xpack.security.authc.support.UsernamePasswordToken.basicAuthHeaderValue;
public class GraphWithSecurityIT extends ESRestTestCase {
public class GraphWithSecurityIT extends ESClientYamlSuiteTestCase {
private static final String TEST_ADMIN_USERNAME = "test_admin";
private static final String TEST_ADMIN_PASSWORD = "changeme";
@ -31,7 +31,7 @@ public class GraphWithSecurityIT extends ESRestTestCase {
@ParametersFactory
public static Iterable<Object[]> parameters() throws IOException, RestTestParseException {
return ESRestTestCase.createParameters(0, 1);
return ESClientYamlSuiteTestCase.createParameters(0, 1);
}
protected String[] getCredentials() {

View File

@ -21,7 +21,7 @@ public class GraphWithSecurityInsufficientRoleIT extends GraphWithSecurityIT {
public void test() throws IOException {
try {
super.test();
fail();
fail("should have failed because of missing role");
} catch(AssertionError ae) {
assertThat(ae.getMessage(), containsString("action [indices:data/read/xpack/graph/explore"));
assertThat(ae.getMessage(), containsString("returned [403 Forbidden]"));

View File

@ -7,19 +7,20 @@ package org.elasticsearch.smoketest;
import com.carrotsearch.randomizedtesting.annotations.Name;
import com.carrotsearch.randomizedtesting.annotations.ParametersFactory;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.util.concurrent.ThreadContext;
import org.elasticsearch.xpack.security.authc.support.SecuredString;
import org.elasticsearch.test.rest.ESRestTestCase;
import org.elasticsearch.test.rest.ESClientYamlSuiteTestCase;
import org.elasticsearch.test.rest.RestTestCandidate;
import org.elasticsearch.test.rest.parser.RestTestParseException;
import org.elasticsearch.xpack.security.authc.support.SecuredString;
import java.io.IOException;
import static org.elasticsearch.xpack.security.authc.support.UsernamePasswordToken.basicAuthHeaderValue;
import static org.hamcrest.Matchers.containsString;
public class MonitoringWithSecurityInsufficientRoleIT extends ESRestTestCase {
public class MonitoringWithSecurityInsufficientRoleIT extends ESClientYamlSuiteTestCase {
public MonitoringWithSecurityInsufficientRoleIT(@Name("yaml") RestTestCandidate testCandidate) {
super(testCandidate);
@ -27,7 +28,7 @@ public class MonitoringWithSecurityInsufficientRoleIT extends ESRestTestCase {
@ParametersFactory
public static Iterable<Object[]> parameters() throws IOException, RestTestParseException {
return ESRestTestCase.createParameters(0, 1);
return ESClientYamlSuiteTestCase.createParameters(0, 1);
}
@Override

View File

@ -7,18 +7,19 @@ package org.elasticsearch.smoketest;
import com.carrotsearch.randomizedtesting.annotations.Name;
import com.carrotsearch.randomizedtesting.annotations.ParametersFactory;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.util.concurrent.ThreadContext;
import org.elasticsearch.xpack.security.authc.support.SecuredString;
import org.elasticsearch.test.rest.ESRestTestCase;
import org.elasticsearch.test.rest.ESClientYamlSuiteTestCase;
import org.elasticsearch.test.rest.RestTestCandidate;
import org.elasticsearch.test.rest.parser.RestTestParseException;
import org.elasticsearch.xpack.security.authc.support.SecuredString;
import java.io.IOException;
import static org.elasticsearch.xpack.security.authc.support.UsernamePasswordToken.basicAuthHeaderValue;
public class MonitoringWithSecurityIT extends ESRestTestCase {
public class MonitoringWithSecurityIT extends ESClientYamlSuiteTestCase {
public MonitoringWithSecurityIT(@Name("yaml") RestTestCandidate testCandidate) {
super(testCandidate);
@ -26,7 +27,7 @@ public class MonitoringWithSecurityIT extends ESRestTestCase {
@ParametersFactory
public static Iterable<Object[]> parameters() throws IOException, RestTestParseException {
return ESRestTestCase.createParameters(0, 1);
return ESClientYamlSuiteTestCase.createParameters(0, 1);
}
@Override

View File

@ -7,15 +7,16 @@ package org.elasticsearch.smoketest;
import com.carrotsearch.randomizedtesting.annotations.Name;
import com.carrotsearch.randomizedtesting.annotations.ParametersFactory;
import org.elasticsearch.ElasticsearchException;
import org.elasticsearch.common.io.PathUtils;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.util.concurrent.ThreadContext;
import org.elasticsearch.xpack.security.authc.support.SecuredString;
import org.elasticsearch.test.rest.ESClientYamlSuiteTestCase;
import org.elasticsearch.test.rest.ESRestTestCase;
import org.elasticsearch.test.rest.RestTestCandidate;
import org.elasticsearch.test.rest.client.RestTestClient;
import org.elasticsearch.test.rest.parser.RestTestParseException;
import org.elasticsearch.xpack.security.authc.support.SecuredString;
import org.junit.AfterClass;
import org.junit.BeforeClass;
@ -26,7 +27,7 @@ import java.nio.file.Path;
import static org.elasticsearch.xpack.security.authc.support.UsernamePasswordToken.basicAuthHeaderValue;
public class SmokeTestPluginsSslIT extends ESRestTestCase {
public class SmokeTestPluginsSslIT extends ESClientYamlSuiteTestCase {
private static final String USER = "test_user";
private static final String PASS = "changeme";
@ -38,7 +39,7 @@ public class SmokeTestPluginsSslIT extends ESRestTestCase {
@ParametersFactory
public static Iterable<Object[]> parameters() throws IOException, RestTestParseException {
return ESRestTestCase.createParameters(0, 1);
return ESClientYamlSuiteTestCase.createParameters(0, 1);
}
static Path keyStore;
@ -65,9 +66,13 @@ public class SmokeTestPluginsSslIT extends ESRestTestCase {
String token = basicAuthHeaderValue(USER, new SecuredString(PASS.toCharArray()));
return Settings.builder()
.put(ThreadContext.PREFIX + ".Authorization", token)
.put(RestTestClient.PROTOCOL, "https")
.put(RestTestClient.TRUSTSTORE_PATH, keyStore)
.put(RestTestClient.TRUSTSTORE_PASSWORD, KEYSTORE_PASS)
.put(ESRestTestCase.TRUSTSTORE_PATH, keyStore)
.put(ESRestTestCase.TRUSTSTORE_PASSWORD, KEYSTORE_PASS)
.build();
}
@Override
protected String getProtocol() {
return "https";
}
}

View File

@ -7,18 +7,19 @@ package org.elasticsearch.smoketest;
import com.carrotsearch.randomizedtesting.annotations.Name;
import com.carrotsearch.randomizedtesting.annotations.ParametersFactory;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.util.concurrent.ThreadContext;
import org.elasticsearch.xpack.security.authc.support.SecuredString;
import org.elasticsearch.test.rest.ESRestTestCase;
import org.elasticsearch.test.rest.ESClientYamlSuiteTestCase;
import org.elasticsearch.test.rest.RestTestCandidate;
import org.elasticsearch.test.rest.parser.RestTestParseException;
import org.elasticsearch.xpack.security.authc.support.SecuredString;
import java.io.IOException;
import static org.elasticsearch.xpack.security.authc.support.UsernamePasswordToken.basicAuthHeaderValue;
public class SmokeTestPluginsIT extends ESRestTestCase {
public class SmokeTestPluginsIT extends ESClientYamlSuiteTestCase {
private static final String USER = "test_user";
private static final String PASS = "changeme";
@ -29,7 +30,7 @@ public class SmokeTestPluginsIT extends ESRestTestCase {
@ParametersFactory
public static Iterable<Object[]> parameters() throws IOException, RestTestParseException {
return ESRestTestCase.createParameters(0, 1);
return ESClientYamlSuiteTestCase.createParameters(0, 1);
}
@Override

View File

@ -0,0 +1,27 @@
apply plugin: 'elasticsearch.rest-test'
dependencies {
testCompile project(path: ':x-plugins:elasticsearch:x-pack', configuration: 'runtime')
testCompile project(path: ':modules:lang-mustache', configuration: 'runtime')
}
integTest {
cluster {
plugin ':x-plugins:elasticsearch:x-pack'
setting 'xpack.watcher.enabled', 'false'
setting 'xpack.monitoring.enabled', 'false'
setting 'path.scripts', "${project.buildDir}/resources/test/templates"
setupCommand 'setupDummyUser',
'bin/x-pack/users', 'useradd', 'test_admin', '-p', 'changeme', '-r', 'superuser'
waitCondition = { node, ant ->
File tmpFile = new File(node.cwd, 'wait.success')
ant.get(src: "http://${node.httpUri()}",
dest: tmpFile.toString(),
username: 'test_admin',
password: 'changeme',
ignoreerrors: true,
retries: 10)
return tmpFile.exists()
}
}
}

View File

@ -0,0 +1,41 @@
/*
* 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.smoketest;
import com.carrotsearch.randomizedtesting.annotations.Name;
import com.carrotsearch.randomizedtesting.annotations.ParametersFactory;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.util.concurrent.ThreadContext;
import org.elasticsearch.test.rest.ESClientYamlSuiteTestCase;
import org.elasticsearch.test.rest.RestTestCandidate;
import org.elasticsearch.test.rest.parser.RestTestParseException;
import org.elasticsearch.xpack.security.authc.support.SecuredString;
import java.io.IOException;
import static org.elasticsearch.xpack.security.authc.support.UsernamePasswordToken.basicAuthHeaderValue;
public class RestIT extends ESClientYamlSuiteTestCase {
private static final String BASIC_AUTH_VALUE = basicAuthHeaderValue("test_admin", new SecuredString("changeme".toCharArray()));
public RestIT(@Name("yaml") RestTestCandidate testCandidate) {
super(testCandidate);
}
@ParametersFactory
public static Iterable<Object[]> parameters() throws IOException, RestTestParseException {
return ESClientYamlSuiteTestCase.createParameters(0, 1);
}
@Override
protected Settings restClientSettings() {
return Settings.builder()
.put(ThreadContext.PREFIX + ".Authorization", BASIC_AUTH_VALUE)
.build();
}
}

View File

@ -0,0 +1,191 @@
---
setup:
- skip:
features: headers
- do:
cluster.health:
wait_for_status: yellow
- do:
xpack.security.put_user:
username: "inline_template_user"
body: >
{
"password": "changeme",
"roles" : [ "inline_template_role" ]
}
- do:
xpack.security.put_user:
username: "stored_template_user"
body: >
{
"password": "changeme",
"roles" : [ "stored_template_role" ]
}
- do:
xpack.security.put_user:
username: "file_template_user"
body: >
{
"password": "changeme",
"roles" : [ "file_template_role" ]
}
- do:
xpack.security.put_role:
name: "inline_template_role"
body: >
{
"indices": [
{
"names": "foobar",
"privileges": ["all"],
"query" : {
"template" : {
"inline" : {
"term" : { "username" : "{{_user.username}}" }
}
}
}
}
]
}
- do:
xpack.security.put_role:
name: "stored_template_role"
body: >
{
"indices": [
{
"names": "foobar",
"privileges": ["all"],
"query" : {
"template" : {
"id" : "1"
}
}
}
]
}
- do:
xpack.security.put_role:
name: "file_template_role"
body: >
{
"indices": [
{
"names": "foobar",
"privileges": ["all"],
"query" : {
"template" : {
"file" : "query"
}
}
}
]
}
- do:
put_template:
id: "1"
body: >
{
"term" : {
"username" : "{{_user.username}}"
}
}
- do:
index:
index: foobar
type: type
id: 1
body: >
{
"username": "inline_template_user"
}
- do:
index:
index: foobar
type: type
id: 2
body: >
{
"username": "stored_template_user"
}
- do:
index:
index: foobar
type: type
id: 3
body: >
{
"username": "file_template_user"
}
- do:
indices.refresh: {}
---
teardown:
- do:
xpack.security.delete_user:
username: "inline_template_user"
ignore: 404
- do:
xpack.security.delete_user:
username: "stored_template_user"
ignore: 404
- do:
xpack.security.delete_user:
username: "file_template_user"
ignore: 404
- do:
xpack.security.delete_role:
name: "inline_template_role"
ignore: 404
- do:
xpack.security.delete_role:
name: "stored_template_role"
ignore: 404
- do:
xpack.security.delete_role:
name: "file_template_role"
ignore: 404
---
"Test inline template":
- do:
headers:
Authorization: "Basic aW5saW5lX3RlbXBsYXRlX3VzZXI6Y2hhbmdlbWU="
search:
index: foobar
body: { "query" : { "match_all" : {} } }
- match: { hits.total: 1}
- match: { hits.hits.0._source.username: inline_template_user}
---
"Test stored template":
- do:
headers:
Authorization: "Basic c3RvcmVkX3RlbXBsYXRlX3VzZXI6Y2hhbmdlbWU="
search:
index: foobar
body: { "query" : { "match_all" : {} } }
- match: { hits.total: 1}
- match: { hits.hits.0._source.username: stored_template_user}
---
"Test file template":
- do:
headers:
Authorization: "Basic ZmlsZV90ZW1wbGF0ZV91c2VyOmNoYW5nZW1l"
search:
index: foobar
body: { "query" : { "match_all" : {} } }
- match: { hits.total: 1}
- match: { hits.hits.0._source.username: file_template_user}

View File

@ -0,0 +1,198 @@
---
setup:
- skip:
features: headers
- do:
indices.create:
index: shared_logs
- do:
cluster.health:
wait_for_status: yellow
- do:
ingest.put_pipeline:
id: "my_pipeline"
body: >
{
"processors": [
{
"set_security_user" : {
"field" : "user"
}
}
]
}
- do:
xpack.security.put_user:
username: "joe"
body: >
{
"password": "changeme",
"roles" : [ "small_companies_role" ],
"metadata" : {
"customer_id" : "1"
}
}
- do:
xpack.security.put_user:
username: "john"
body: >
{
"password": "changeme",
"roles" : [ "small_companies_role" ],
"metadata" : {
"customer_id" : "2"
}
}
---
teardown:
- do:
xpack.security.delete_user:
username: "joe"
ignore: 404
- do:
xpack.security.delete_user:
username: "john"
ignore: 404
- do:
xpack.security.delete_role:
name: "small_companies_role"
ignore: 404
---
"Test shared index seperating user by using DLS role query with user's username":
- do:
xpack.security.put_role:
name: "small_companies_role"
body: >
{
"indices": [
{
"names": "shared_logs",
"privileges": ["read", "create"],
"query" : {
"template" : {
"inline" : {
"term" : { "user.username" : "{{_user.username}}" }
}
}
}
}
]
}
- do:
headers:
Authorization: "Basic am9lOmNoYW5nZW1l"
index:
index: shared_logs
type: type
pipeline: "my_pipeline"
body: >
{
"log": "Joe's first log entry"
}
- do:
headers:
Authorization: "Basic am9objpjaGFuZ2VtZQ=="
index:
index: shared_logs
type: type
pipeline: "my_pipeline"
body: >
{
"log": "John's first log entry"
}
- do:
indices.refresh: {}
# Joe searches:
- do:
headers:
Authorization: "Basic am9lOmNoYW5nZW1l"
search:
index: shared_logs
body: { "query" : { "match_all" : {} } }
- match: { hits.total: 1}
- match: { hits.hits.0._source.user.username: joe}
# John searches:
- do:
headers:
Authorization: "Basic am9objpjaGFuZ2VtZQ=="
search:
index: shared_logs
body: { "query" : { "match_all" : {} } }
- match: { hits.total: 1}
- match: { hits.hits.0._source.user.username: john}
---
"Test shared index seperating user by using DLS role query with user's metadata":
- do:
xpack.security.put_role:
name: "small_companies_role"
body: >
{
"indices": [
{
"names": "shared_logs",
"privileges": ["read", "create"],
"query" : {
"template" : {
"inline" : {
"term" : { "user.metadata.customer_id" : "{{_user.metadata.customer_id}}" }
}
}
}
}
]
}
- do:
headers:
Authorization: "Basic am9lOmNoYW5nZW1l"
index:
index: shared_logs
type: type
pipeline: "my_pipeline"
body: >
{
"log": "Joe's first log entry"
}
- do:
headers:
Authorization: "Basic am9objpjaGFuZ2VtZQ=="
index:
index: shared_logs
type: type
pipeline: "my_pipeline"
body: >
{
"log": "John's first log entry"
}
- do:
indices.refresh: {}
# Joe searches:
- do:
headers:
Authorization: "Basic am9lOmNoYW5nZW1l"
search:
index: shared_logs
body: { "query" : { "match_all" : {} } }
- match: { hits.total: 1}
- match: { hits.hits.0._source.user.username: joe}
# John searches:
- do:
headers:
Authorization: "Basic am9objpjaGFuZ2VtZQ=="
search:
index: shared_logs
body: { "query" : { "match_all" : {} } }
- match: { hits.total: 1}
- match: { hits.hits.0._source.user.username: john}

View File

@ -0,0 +1,5 @@
{
"term" : {
"username" : "{{_user.username}}"
}
}

View File

@ -7,7 +7,8 @@ package org.elasticsearch.smoketest;
import com.carrotsearch.randomizedtesting.annotations.Name;
import com.carrotsearch.randomizedtesting.annotations.ParametersFactory;
import org.elasticsearch.test.rest.ESRestTestCase;
import org.elasticsearch.test.rest.ESClientYamlSuiteTestCase;
import org.elasticsearch.test.rest.RestTestCandidate;
import org.elasticsearch.test.rest.parser.RestTestParseException;
import org.junit.After;
@ -18,7 +19,7 @@ import java.io.IOException;
import static java.util.Collections.emptyList;
import static java.util.Collections.emptyMap;
public abstract class WatcherRestTestCase extends ESRestTestCase {
public abstract class WatcherRestTestCase extends ESClientYamlSuiteTestCase {
public WatcherRestTestCase(@Name("yaml") RestTestCandidate testCandidate) {
super(testCandidate);
@ -26,7 +27,7 @@ public abstract class WatcherRestTestCase extends ESRestTestCase {
@ParametersFactory
public static Iterable<Object[]> parameters() throws IOException, RestTestParseException {
return ESRestTestCase.createParameters(0, 1);
return ESClientYamlSuiteTestCase.createParameters(0, 1);
}
@Before

View File

@ -0,0 +1,238 @@
---
"Test simple input to index action":
- do:
xpack.watcher.put_watch:
id: my_watch
body: >
{
"trigger" : { "schedule" : { "cron" : "0/1 * * * * ? 2020" } },
"input" : { "simple" : { "foo": "bar" } },
"actions" : {
"index_action" : {
"index" : {
"index" : "idx",
"doc_type" : "type",
"execution_time_field" : "@timestamp"
}
}
}
}
- match: { _id: "my_watch" }
- do:
xpack.watcher.execute_watch:
id: "my_watch"
body: >
{
"trigger_data" : {
"triggered_time" : "2016-07-07T09:00:00Z",
"scheduled_time" : "2016-07-07T09:00:00Z"
}
}
- match: { "watch_record.state": "executed" }
- do:
indices.refresh: {}
- do:
search:
index: idx
type: type
- match: { hits.total: 1 }
- match: { hits.hits.0._source.foo: bar }
- gte: { hits.hits.0._source.@timestamp: '2016-07-08' }
---
"Test simple input with document field":
- do:
xpack.watcher.put_watch:
id: my_watch
body: >
{
"trigger" : { "schedule" : { "cron" : "0/1 * * * * ? 2020" } },
"input" : { "simple" : { "foo": "bar" } },
"actions" : {
"index_action" : {
"transform" : { "script" : { "inline": "return [ '_doc' : ctx.payload ]" } },
"index" : {
"index" : "idx",
"doc_type" : "type",
"execution_time_field" : "@timestamp"
}
}
}
}
- match: { _id: "my_watch" }
- do:
xpack.watcher.execute_watch:
id: "my_watch"
body: >
{
"trigger_data" : {
"triggered_time" : "2016-07-07T09:00:00Z",
"scheduled_time" : "2016-07-07T09:00:00Z"
}
}
- match: { "watch_record.state": "executed" }
- do:
indices.refresh: {}
- do:
search:
index: idx
type: type
- match: { hits.total: 1 }
- match: { hits.hits.0._source.foo: bar }
- gte: { hits.hits.0._source.@timestamp: '2016-07-08"' }
---
"Test simple input with wrong document results in error":
- do:
xpack.watcher.put_watch:
id: my_watch
body: >
{
"trigger" : { "schedule" : { "cron" : "0/1 * * * * ? 2020" } },
"input" : { "simple" : { "foo": "bar" } },
"actions" : {
"index_action" : {
"transform" : { "script" : { "inline": "return [ '_doc' : 1 ]" } },
"index" : {
"index" : "idx",
"doc_type" : "type",
"execution_time_field" : "@timestamp"
}
}
}
}
- match: { _id: "my_watch" }
- do:
xpack.watcher.execute_watch:
id: "my_watch"
body: >
{
"record_execution" : true,
"trigger_data" : {
"triggered_time" : "2016-07-07T09:00:00Z",
"scheduled_time" : "2016-07-07T09:00:00Z"
}
}
- match: { "watch_record.state": "executed" }
- do:
indices.refresh: {}
- do:
indices.exists:
index: idx
- is_false: ''
- do:
search:
index: .watcher-history-*
type: watch_record
body: >
{
"query" : {
"match" : {
"result.actions.status": "failure"
}
}
}
- match: { hits.total: 1 }
---
"Test search input to index action with aggs":
- do:
bulk:
refresh: true
body:
- '{"index": {"_index": "idx", "_type": "type", "_id": "1"}}'
- '{"@timestamp": "2016-07-07" }'
- '{"index": {"_index": "idx", "_type": "type", "_id": "2"}}'
- '{"@timestamp": "2016-07-08" }'
- '{"index": {"_index": "idx", "_type": "type", "_id": "3"}}'
- '{"@timestamp": "2016-07-09" }'
- do:
xpack.watcher.put_watch:
id: my_watch
body: >
{
"trigger" : { "schedule" : { "cron" : "0/1 * * * * ? 2020" } },
"input" : {
"search" : {
"request": {
"indices" : [ "idx" ],
"types" : [ "type" ],
"body" : {
"aggs" : {
"trend" : {
"date_histogram" : {
"field" : "@timestamp",
"interval" : "day"
}
}
}
}
}
}
},
"actions" : {
"index_action" : {
"transform" : { "script" : { "inline": "return [ '_doc' : ctx.payload.aggregations.trend.buckets]" } },
"index" : {
"index" : "idx",
"doc_type" : "bucket",
"execution_time_field" : "@timestamp"
}
}
}
}
- match: { _id: "my_watch" }
- do:
xpack.watcher.execute_watch:
id: "my_watch"
body: >
{
"trigger_data" : {
"triggered_time" : "2016-07-07T09:00:00Z",
"scheduled_time" : "2016-07-07T09:00:00Z"
}
}
- match: { "watch_record.state": "executed" }
- do:
indices.refresh: {}
- do:
search:
index: idx
type: bucket
- match: { hits.total: 3 }

View File

@ -7,7 +7,8 @@ package org.elasticsearch.smoketest;
import com.carrotsearch.randomizedtesting.annotations.Name;
import com.carrotsearch.randomizedtesting.annotations.ParametersFactory;
import org.elasticsearch.test.rest.ESRestTestCase;
import org.elasticsearch.test.rest.ESClientYamlSuiteTestCase;
import org.elasticsearch.test.rest.RestTestCandidate;
import org.elasticsearch.test.rest.parser.RestTestParseException;
import org.junit.After;
@ -18,7 +19,7 @@ import java.io.IOException;
import static java.util.Collections.emptyList;
import static java.util.Collections.emptyMap;
public abstract class WatcherRestTestCase extends ESRestTestCase {
public abstract class WatcherRestTestCase extends ESClientYamlSuiteTestCase {
public WatcherRestTestCase(@Name("yaml") RestTestCandidate testCandidate) {
super(testCandidate);
@ -26,7 +27,7 @@ public abstract class WatcherRestTestCase extends ESRestTestCase {
@ParametersFactory
public static Iterable<Object[]> parameters() throws IOException, RestTestParseException {
return ESRestTestCase.createParameters(0, 1);
return ESClientYamlSuiteTestCase.createParameters(0, 1);
}
@Before

View File

@ -7,14 +7,15 @@ package org.elasticsearch.smoketest;
import com.carrotsearch.randomizedtesting.annotations.Name;
import com.carrotsearch.randomizedtesting.annotations.ParametersFactory;
import org.elasticsearch.test.rest.ESRestTestCase;
import org.elasticsearch.test.rest.ESClientYamlSuiteTestCase;
import org.elasticsearch.test.rest.RestTestCandidate;
import org.elasticsearch.test.rest.parser.RestTestParseException;
import java.io.IOException;
/** Runs rest tests against external cluster */
public class WatcherWithMustacheIT extends WatcherRestTestCase {
public class WatcherWithMustacheIT extends ESClientYamlSuiteTestCase {
public WatcherWithMustacheIT(@Name("yaml") RestTestCandidate testCandidate) {
super(testCandidate);
@ -22,7 +23,7 @@ public class WatcherWithMustacheIT extends WatcherRestTestCase {
@ParametersFactory
public static Iterable<Object[]> parameters() throws IOException, RestTestParseException {
return ESRestTestCase.createParameters(0, 1);
return ESClientYamlSuiteTestCase.createParameters(0, 1);
}
}

View File

@ -7,7 +7,8 @@ package org.elasticsearch.smoketest;
import com.carrotsearch.randomizedtesting.annotations.Name;
import com.carrotsearch.randomizedtesting.annotations.ParametersFactory;
import org.elasticsearch.test.rest.ESRestTestCase;
import org.elasticsearch.test.rest.ESClientYamlSuiteTestCase;
import org.elasticsearch.test.rest.RestTestCandidate;
import org.elasticsearch.test.rest.parser.RestTestParseException;
import org.junit.After;
@ -18,7 +19,7 @@ import java.io.IOException;
import static java.util.Collections.emptyList;
import static java.util.Collections.emptyMap;
public abstract class WatcherRestTestCase extends ESRestTestCase {
public abstract class WatcherRestTestCase extends ESClientYamlSuiteTestCase {
public WatcherRestTestCase(@Name("yaml") RestTestCandidate testCandidate) {
super(testCandidate);
@ -26,7 +27,7 @@ public abstract class WatcherRestTestCase extends ESRestTestCase {
@ParametersFactory
public static Iterable<Object[]> parameters() throws IOException, RestTestParseException {
return ESRestTestCase.createParameters(0, 1);
return ESClientYamlSuiteTestCase.createParameters(0, 1);
}
@Before

View File

@ -7,25 +7,24 @@ package org.elasticsearch.smoketest;
import com.carrotsearch.randomizedtesting.annotations.Name;
import com.carrotsearch.randomizedtesting.annotations.ParametersFactory;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.util.concurrent.ThreadContext;
import org.elasticsearch.xpack.security.authc.support.SecuredString;
import org.elasticsearch.test.rest.ESRestTestCase;
import org.elasticsearch.test.rest.ESClientYamlSuiteTestCase;
import org.elasticsearch.test.rest.RestTestCandidate;
import org.elasticsearch.test.rest.parser.RestTestParseException;
import org.elasticsearch.xpack.security.authc.support.SecuredString;
import org.junit.After;
import org.junit.Before;
import java.io.IOException;
import static java.util.Collections.emptyList;
import static java.util.Collections.emptyMap;
import static org.elasticsearch.xpack.security.authc.support.UsernamePasswordToken.basicAuthHeaderValue;
public class WatcherWithSecurityIT extends ESRestTestCase {
public class WatcherWithSecurityIT extends ESClientYamlSuiteTestCase {
private static final String TEST_ADMIN_USERNAME = "test_admin";
private static final String TEST_ADMIN_PASSWORD = "changeme";
@ -36,7 +35,7 @@ public class WatcherWithSecurityIT extends ESRestTestCase {
@ParametersFactory
public static Iterable<Object[]> parameters() throws IOException, RestTestParseException {
return ESRestTestCase.createParameters(0, 1);
return ESClientYamlSuiteTestCase.createParameters(0, 1);
}
@Before

View File

@ -7,7 +7,8 @@ package org.elasticsearch.smoketest;
import com.carrotsearch.randomizedtesting.annotations.Name;
import com.carrotsearch.randomizedtesting.annotations.ParametersFactory;
import org.elasticsearch.test.rest.ESRestTestCase;
import org.elasticsearch.test.rest.ESClientYamlSuiteTestCase;
import org.elasticsearch.test.rest.RestTestCandidate;
import org.elasticsearch.test.rest.parser.RestTestParseException;
import org.junit.After;
@ -19,7 +20,7 @@ import static java.util.Collections.emptyList;
import static java.util.Collections.emptyMap;
/** Runs rest tests against external cluster */
public class WatcherGettingStartedIT extends ESRestTestCase {
public class WatcherGettingStartedIT extends ESClientYamlSuiteTestCase {
public WatcherGettingStartedIT(@Name("yaml") RestTestCandidate testCandidate) {
super(testCandidate);
@ -27,7 +28,7 @@ public class WatcherGettingStartedIT extends ESRestTestCase {
@ParametersFactory
public static Iterable<Object[]> parameters() throws IOException, RestTestParseException {
return ESRestTestCase.createParameters(0, 1);
return ESClientYamlSuiteTestCase.createParameters(0, 1);
}
@Before

View File

@ -9,7 +9,6 @@ import org.elasticsearch.common.Strings;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.license.core.License.OperationMode;
import org.elasticsearch.license.plugin.core.AbstractLicenseeComponent;
import org.elasticsearch.license.plugin.core.LicenseState;
public class GraphLicensee extends AbstractLicenseeComponent {
@ -60,6 +59,6 @@ public class GraphLicensee extends AbstractLicenseeComponent {
boolean licensed = operationMode == OperationMode.TRIAL || operationMode == OperationMode.PLATINUM;
return licensed && localStatus.getLicenseState() != LicenseState.DISABLED;
return licensed && localStatus.isActive();
}
}

View File

@ -20,7 +20,7 @@ import org.elasticsearch.license.plugin.action.get.TransportGetLicenseAction;
import org.elasticsearch.license.plugin.action.put.PutLicenseAction;
import org.elasticsearch.license.plugin.action.put.TransportPutLicenseAction;
import org.elasticsearch.license.plugin.core.LicensesMetaData;
import org.elasticsearch.license.plugin.core.LicensesService;
import org.elasticsearch.license.plugin.core.LicenseService;
import org.elasticsearch.license.plugin.rest.RestDeleteLicenseAction;
import org.elasticsearch.license.plugin.rest.RestGetLicenseAction;
import org.elasticsearch.license.plugin.rest.RestPutLicenseAction;
@ -92,11 +92,11 @@ public class Licensing implements ActionPlugin {
WatcherLicensee watcherLicensee = new WatcherLicensee(settings);
MonitoringLicensee monitoringLicensee = new MonitoringLicensee(settings);
GraphLicensee graphLicensee = new GraphLicensee(settings);
LicensesService licensesService = new LicensesService(settings, clusterService, clock,
LicenseService licenseService = new LicenseService(settings, clusterService, clock,
environment, resourceWatcherService,
Arrays.asList(securityLicensee, watcherLicensee, monitoringLicensee, graphLicensee));
return Arrays.asList(licensesService, securityLicenseState, securityLicensee, watcherLicensee, monitoringLicensee, graphLicensee);
return Arrays.asList(licenseService, securityLicenseState, securityLicensee, watcherLicensee, monitoringLicensee, graphLicensee);
}
public List<Setting<?>> getSettings() {

View File

@ -17,21 +17,21 @@ import org.elasticsearch.cluster.block.ClusterBlockLevel;
import org.elasticsearch.cluster.metadata.IndexNameExpressionResolver;
import org.elasticsearch.common.inject.Inject;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.license.plugin.core.LicensesService;
import org.elasticsearch.license.plugin.core.LicenseService;
import org.elasticsearch.threadpool.ThreadPool;
import org.elasticsearch.transport.TransportService;
public class TransportDeleteLicenseAction extends TransportMasterNodeAction<DeleteLicenseRequest, DeleteLicenseResponse> {
private final LicensesService licensesService;
private final LicenseService licenseService;
@Inject
public TransportDeleteLicenseAction(Settings settings, TransportService transportService, ClusterService clusterService,
LicensesService licensesService, ThreadPool threadPool, ActionFilters actionFilters,
LicenseService licenseService, ThreadPool threadPool, ActionFilters actionFilters,
IndexNameExpressionResolver indexNameExpressionResolver) {
super(settings, DeleteLicenseAction.NAME, transportService, clusterService, threadPool, actionFilters,
indexNameExpressionResolver, DeleteLicenseRequest::new);
this.licensesService = licensesService;
this.licenseService = licenseService;
}
@Override
@ -52,7 +52,7 @@ public class TransportDeleteLicenseAction extends TransportMasterNodeAction<Dele
@Override
protected void masterOperation(final DeleteLicenseRequest request, ClusterState state, final ActionListener<DeleteLicenseResponse>
listener) throws ElasticsearchException {
licensesService.removeLicense(request, new ActionListener<ClusterStateUpdateResponse>() {
licenseService.removeLicense(request, new ActionListener<ClusterStateUpdateResponse>() {
@Override
public void onResponse(ClusterStateUpdateResponse clusterStateUpdateResponse) {
listener.onResponse(new DeleteLicenseResponse(clusterStateUpdateResponse.isAcknowledged()));

View File

@ -16,21 +16,21 @@ import org.elasticsearch.cluster.metadata.IndexNameExpressionResolver;
import org.elasticsearch.cluster.service.ClusterService;
import org.elasticsearch.common.inject.Inject;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.license.plugin.core.LicensesService;
import org.elasticsearch.license.plugin.core.LicenseService;
import org.elasticsearch.threadpool.ThreadPool;
import org.elasticsearch.transport.TransportService;
public class TransportGetLicenseAction extends TransportMasterNodeReadAction<GetLicenseRequest, GetLicenseResponse> {
private final LicensesService licensesService;
private final LicenseService licenseService;
@Inject
public TransportGetLicenseAction(Settings settings, TransportService transportService, ClusterService clusterService,
LicensesService licensesService, ThreadPool threadPool, ActionFilters actionFilters,
LicenseService licenseService, ThreadPool threadPool, ActionFilters actionFilters,
IndexNameExpressionResolver indexNameExpressionResolver) {
super(settings, GetLicenseAction.NAME, transportService, clusterService, threadPool, actionFilters, indexNameExpressionResolver,
GetLicenseRequest::new);
this.licensesService = licensesService;
this.licenseService = licenseService;
}
@Override
@ -51,6 +51,6 @@ public class TransportGetLicenseAction extends TransportMasterNodeReadAction<Get
@Override
protected void masterOperation(final GetLicenseRequest request, ClusterState state, final ActionListener<GetLicenseResponse>
listener) throws ElasticsearchException {
listener.onResponse(new GetLicenseResponse(licensesService.getLicense()));
listener.onResponse(new GetLicenseResponse(licenseService.getLicense()));
}
}

View File

@ -16,21 +16,21 @@ import org.elasticsearch.cluster.block.ClusterBlockLevel;
import org.elasticsearch.cluster.metadata.IndexNameExpressionResolver;
import org.elasticsearch.common.inject.Inject;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.license.plugin.core.LicensesService;
import org.elasticsearch.license.plugin.core.LicenseService;
import org.elasticsearch.threadpool.ThreadPool;
import org.elasticsearch.transport.TransportService;
public class TransportPutLicenseAction extends TransportMasterNodeAction<PutLicenseRequest, PutLicenseResponse> {
private final LicensesService licensesService;
private final LicenseService licenseService;
@Inject
public TransportPutLicenseAction(Settings settings, TransportService transportService, ClusterService clusterService,
LicensesService licensesService, ThreadPool threadPool, ActionFilters actionFilters,
LicenseService licenseService, ThreadPool threadPool, ActionFilters actionFilters,
IndexNameExpressionResolver indexNameExpressionResolver) {
super(settings, PutLicenseAction.NAME, transportService, clusterService, threadPool, actionFilters, indexNameExpressionResolver,
PutLicenseRequest::new);
this.licensesService = licensesService;
this.licenseService = licenseService;
}
@Override
@ -51,7 +51,7 @@ public class TransportPutLicenseAction extends TransportMasterNodeAction<PutLice
@Override
protected void masterOperation(final PutLicenseRequest request, ClusterState state, final ActionListener<PutLicenseResponse>
listener) throws ElasticsearchException {
licensesService.registerLicense(request, listener);
licenseService.registerLicense(request, listener);
}
}

View File

@ -1,43 +0,0 @@
/*
* 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.license.plugin.core;
import org.elasticsearch.license.core.License;
import org.elasticsearch.xpack.scheduler.SchedulerEngine;
public class LicenseSchedule implements SchedulerEngine.Schedule {
private final License license;
LicenseSchedule(License license) {
this.license = license;
}
@Override
public long nextScheduledTimeAfter(long startTime, long time) {
long nextScheduledTime = -1;
switch (LicenseState.resolve(license, time)) {
case ENABLED:
nextScheduledTime = license.expiryDate();
break;
case GRACE_PERIOD:
nextScheduledTime = license.expiryDate() + LicenseState.GRACE_PERIOD_DURATION.getMillis();
break;
case DISABLED:
if (license.issueDate() > time) {
// when we encounter a license with a future issue date
// which can happen with autogenerated license,
// we want to schedule a notification on the license issue date
// so the license is notificed once it is valid
// see https://github.com/elastic/x-plugins/issues/983
nextScheduledTime = license.issueDate();
}
break;
}
return nextScheduledTime;
}
}

View File

@ -57,11 +57,16 @@ import java.util.stream.Collectors;
* When a new license is notified as enabled to the registered listener, a notification is scheduled at the time of license expiry.
* Registered listeners are notified using {@link #onUpdate(LicensesMetaData)}
*/
public class LicensesService extends AbstractLifecycleComponent implements ClusterStateListener, SchedulerEngine.Listener {
public class LicenseService extends AbstractLifecycleComponent implements ClusterStateListener, SchedulerEngine.Listener {
// pkg private for tests
static final TimeValue TRIAL_LICENSE_DURATION = TimeValue.timeValueHours(30 * 24);
/**
* Duration of grace period after a license has expired
*/
public static final TimeValue GRACE_PERIOD_DURATION = days(7);
private final ClusterService clusterService;
/**
@ -98,8 +103,8 @@ public class LicensesService extends AbstractLifecycleComponent implements Clust
private static final String ACKNOWLEDGEMENT_HEADER = "This license update requires acknowledgement. To acknowledge the license, " +
"please read the following messages and update the license again, this time with the \"acknowledge=true\" parameter:";
public LicensesService(Settings settings, ClusterService clusterService, Clock clock, Environment env,
ResourceWatcherService resourceWatcherService, List<Licensee> registeredLicensees) {
public LicenseService(Settings settings, ClusterService clusterService, Clock clock, Environment env,
ResourceWatcherService resourceWatcherService, List<Licensee> registeredLicensees) {
super(settings);
this.clusterService = clusterService;
this.clock = clock;
@ -310,12 +315,15 @@ public class LicensesService extends AbstractLifecycleComponent implements Clust
});
}
public Licensee.Status licenseeStatus() {
final License license = getLicense();
public Licensee.Status licenseeStatus(License license) {
if (license == null) {
return Licensee.Status.MISSING;
return new Licensee.Status(License.OperationMode.MISSING, false);
}
return new Licensee.Status(license.operationMode(), LicenseState.resolve(license, clock.millis()));
long time = clock.millis();
boolean active = time >= license.issueDate() &&
time < license.expiryDate() + GRACE_PERIOD_DURATION.getMillis();
return new Licensee.Status(license.operationMode(), active);
}
public License getLicense() {
@ -433,18 +441,22 @@ public class LicensesService extends AbstractLifecycleComponent implements Clust
}
if (license != null) {
logger.debug("notifying [{}] listeners", registeredLicensees.size());
final LicenseState licenseState = LicenseState.resolve(license, clock.millis());
Licensee.Status status = new Licensee.Status(license.operationMode(), licenseState);
long time = clock.millis();
boolean active = time >= license.issueDate() &&
time < license.expiryDate() + GRACE_PERIOD_DURATION.getMillis();
Licensee.Status status = new Licensee.Status(license.operationMode(), active);
for (InternalLicensee licensee : registeredLicensees) {
licensee.onChange(status);
}
switch (status.getLicenseState()) {
case ENABLED:
logger.debug("license [{}] - valid", license.uid()); break;
case GRACE_PERIOD:
logger.warn("license [{}] - grace", license.uid()); break;
case DISABLED:
logger.warn("license [{}] - expired", license.uid()); break;
if (active) {
if (time < license.expiryDate()) {
logger.debug("license [{}] - valid", license.uid());
} else {
logger.warn("license [{}] - grace", license.uid());
}
} else {
logger.warn("license [{}] - expired", license.uid());
}
}
}
@ -464,7 +476,7 @@ public class LicensesService extends AbstractLifecycleComponent implements Clust
if (license.equals(previousLicense) == false) {
currentLicense.set(license);
license.setOperationModeFileWatcher(operationModeFileWatcher);
scheduler.add(new SchedulerEngine.Job(LICENSE_JOB, new LicenseSchedule(license)));
scheduler.add(new SchedulerEngine.Job(LICENSE_JOB, nextLicenseCheck(license)));
for (ExpirationCallback expirationCallback : expirationCallbacks) {
scheduler.add(new SchedulerEngine.Job(expirationCallback.getId(),
(startTime, now) ->
@ -479,6 +491,25 @@ public class LicensesService extends AbstractLifecycleComponent implements Clust
}
}
// pkg private for tests
static SchedulerEngine.Schedule nextLicenseCheck(License license) {
return (startTime, time) -> {
if (time < license.issueDate()) {
// when we encounter a license with a future issue date
// which can happen with autogenerated license,
// we want to schedule a notification on the license issue date
// so the license is notificed once it is valid
// see https://github.com/elastic/x-plugins/issues/983
return license.issueDate();
} else if (time < license.expiryDate()) {
return license.expiryDate();
} else if (time < license.expiryDate() + GRACE_PERIOD_DURATION.getMillis()) {
return license.expiryDate() + GRACE_PERIOD_DURATION.getMillis();
}
return -1; // license is expired, no need to check again
};
}
private void initLicensee(Licensee licensee) {
logger.debug("initializing licensee [{}]", licensee.id());
final ClusterState clusterState = clusterService.state();

View File

@ -1,68 +0,0 @@
/*
* 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.license.plugin.core;
import org.elasticsearch.common.unit.TimeValue;
import org.elasticsearch.license.core.License;
import static org.elasticsearch.license.plugin.core.LicensesService.days;
/**
* States of a registered licensee
* based on the current license
*/
public enum LicenseState {
/**
* Active license is valid.
*
* When license expires
* changes to {@link #GRACE_PERIOD}
*/
ENABLED,
/**
* Active license expired
* but grace period has not.
*
* When grace period expires
* changes to {@link #DISABLED}.
* When valid license is installed
* changes back to {@link #ENABLED}
*/
GRACE_PERIOD,
/**
* Grace period for active license
* expired.
*
* When a valid license is installed
* changes to {@link #ENABLED}, otherwise
* remains unchanged
*/
DISABLED;
/**
* Duration of grace period after a license has expired
*/
public static final TimeValue GRACE_PERIOD_DURATION = days(7);
public static LicenseState resolve(final License license, long time) {
if (license == null) {
return DISABLED;
}
if (license.issueDate() > time) {
return DISABLED;
}
if (license.expiryDate() > time) {
return ENABLED;
}
if ((license.expiryDate() + GRACE_PERIOD_DURATION.getMillis()) > time) {
return GRACE_PERIOD;
}
return DISABLED;
}
}

View File

@ -44,69 +44,60 @@ public interface Licensee {
* whenever checking different parts of the {@code Status}:
* <pre>
* Status status = this.status;
* return status.getLicenseState() != LicenseState.DISABLED &amp;&amp;
* (status.getMode() == OperationMode.TRAIL || status.getMode == OperationMode.PLATINUM);
* return status.isActive() &amp;&amp;
* (status.getMode() == OperationMode.TRIAL || status.getMode == OperationMode.PLATINUM);
* </pre>
* Otherwise the license has the potential to change in-between both checks.
*/
class Status {
public static Status ENABLED = new Status(OperationMode.TRIAL, LicenseState.ENABLED);
public static Status MISSING = new Status(OperationMode.MISSING, LicenseState.DISABLED);
public static Status ENABLED = new Status(OperationMode.TRIAL, true);
public static Status MISSING = new Status(OperationMode.MISSING, false);
private final OperationMode mode;
private final LicenseState licenseState;
private final boolean active;
public Status(OperationMode mode, LicenseState licenseState) {
public Status(OperationMode mode, boolean active) {
this.mode = mode;
this.licenseState = licenseState;
this.active = active;
}
/**
* Returns the operation mode of the license
* responsible for the current <code>licenseState</code>
* <p>
* Note: Knowing the mode does not indicate whether the {@link #getLicenseState() state} is disabled. If that matters (e.g.,
* disabling services when a license becomes disabled), then you should check it as well!
* Note: Knowing the mode does not indicate whether the license is active. If that matters (e.g.,
* disabling services when a license becomes disabled), then check {@link #isActive()}.
*/
public OperationMode getMode() {
return mode;
}
/**
* When a license is active, the state is
* {@link LicenseState#ENABLED}, upon license expiry
* the state changes to {@link LicenseState#GRACE_PERIOD}
* and after the grace period has ended the state changes
* to {@link LicenseState#DISABLED}
*/
public LicenseState getLicenseState() {
return licenseState;
/** Returns true if the license is within the issue date and grace period, or false otherwise */
public boolean isActive() {
return active;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Status status = (Status) o;
return Objects.equals(mode, status.mode) && Objects.equals(licenseState, status.licenseState);
return active == status.active &&
mode == status.mode;
}
@Override
public int hashCode() {
return Objects.hash(mode, licenseState);
return Objects.hash(mode, active);
}
@Override
public String toString() {
switch (licenseState) {
case DISABLED:
return "disabled " + mode.name().toLowerCase(Locale.ROOT);
case GRACE_PERIOD:
return mode.name().toLowerCase(Locale.ROOT) + " grace period";
default:
return mode.name().toLowerCase(Locale.ROOT);
if (active) {
return mode.name().toLowerCase(Locale.ROOT);
} else {
return "disabled " + mode.name().toLowerCase(Locale.ROOT);
}
}
}

View File

@ -10,8 +10,7 @@ import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.unit.TimeValue;
import org.elasticsearch.env.Environment;
import org.elasticsearch.license.core.License;
import org.elasticsearch.license.plugin.core.LicenseState;
import org.elasticsearch.license.plugin.core.LicensesService;
import org.elasticsearch.license.plugin.core.LicenseService;
import org.elasticsearch.plugins.Plugin;
import org.elasticsearch.test.ESIntegTestCase.ClusterScope;
import org.elasticsearch.xpack.MockNetty3Plugin;
@ -124,40 +123,40 @@ public class LicensesServiceClusterTests extends AbstractLicensesIntegrationTest
wipeAllLicenses();
internalCluster().startNode();
ensureGreen();
assertLicenseState(LicenseState.ENABLED);
assertLicenseState(true);
logger.info("--> restart node");
internalCluster().fullRestart();
ensureYellow();
logger.info("--> await node for enabled");
assertLicenseState(LicenseState.ENABLED);
assertLicenseState(true);
}
public void testClusterRestartWhileGrace() throws Exception {
wipeAllLicenses();
internalCluster().startNode();
assertLicenseState(LicenseState.ENABLED);
assertLicenseState(true);
putLicense(TestUtils.generateSignedLicense(TimeValue.timeValueMillis(0)));
ensureGreen();
assertLicenseState(LicenseState.GRACE_PERIOD);
assertLicenseState(true);
logger.info("--> restart node");
internalCluster().fullRestart();
ensureYellow();
logger.info("--> await node for grace_period");
assertLicenseState(LicenseState.GRACE_PERIOD);
assertLicenseState(true);
}
public void testClusterRestartWhileExpired() throws Exception {
wipeAllLicenses();
internalCluster().startNode();
ensureGreen();
assertLicenseState(LicenseState.ENABLED);
putLicense(TestUtils.generateExpiredLicense(System.currentTimeMillis() - LicenseState.GRACE_PERIOD_DURATION.getMillis()));
assertLicenseState(LicenseState.DISABLED);
assertLicenseState(true);
putLicense(TestUtils.generateExpiredLicense(System.currentTimeMillis() - LicenseService.GRACE_PERIOD_DURATION.getMillis()));
assertLicenseState(false);
logger.info("--> restart node");
internalCluster().fullRestart();
ensureYellow();
logger.info("--> await node for disabled");
assertLicenseState(LicenseState.DISABLED);
assertLicenseState(false);
}
public void testClusterNotRecovered() throws Exception {
@ -165,13 +164,13 @@ public class LicensesServiceClusterTests extends AbstractLicensesIntegrationTest
internalCluster().startNode(nodeSettingsBuilder(0).put("discovery.zen.minimum_master_nodes", 2).put("node.master", true));
logger.info("--> start second master out of two [recovered state]");
internalCluster().startNode(nodeSettingsBuilder(1).put("discovery.zen.minimum_master_nodes", 2).put("node.master", true));
assertLicenseState(LicenseState.ENABLED);
assertLicenseState(true);
}
private void assertLicenseState(LicenseState state) throws InterruptedException {
private void assertLicenseState(boolean active) throws InterruptedException {
boolean success = awaitBusy(() -> {
for (LicensesService service : internalCluster().getDataNodeInstances(LicensesService.class)) {
if (service.licenseeStatus().getLicenseState() == state) {
for (LicenseService service : internalCluster().getDataNodeInstances(LicenseService.class)) {
if (service.licenseeStatus(service.getLicense()).isActive() == active) {
return true;
}
}
@ -182,8 +181,8 @@ public class LicensesServiceClusterTests extends AbstractLicensesIntegrationTest
private void assertOperationMode(License.OperationMode operationMode) throws InterruptedException {
boolean success = awaitBusy(() -> {
for (LicensesService service : internalCluster().getDataNodeInstances(LicensesService.class)) {
if (service.licenseeStatus().getMode() == operationMode) {
for (LicenseService service : internalCluster().getDataNodeInstances(LicenseService.class)) {
if (service.licenseeStatus(service.getLicense()).getMode() == operationMode) {
return true;
}
}

View File

@ -21,8 +21,8 @@ import org.elasticsearch.license.core.License;
import org.elasticsearch.license.licensor.LicenseSigner;
import org.elasticsearch.license.plugin.action.put.PutLicenseRequest;
import org.elasticsearch.license.plugin.action.put.PutLicenseResponse;
import org.elasticsearch.license.plugin.core.LicenseService;
import org.elasticsearch.license.plugin.core.Licensee;
import org.elasticsearch.license.plugin.core.LicensesService;
import org.elasticsearch.license.plugin.core.LicensesStatus;
import org.junit.Assert;
@ -146,12 +146,12 @@ public class TestUtils {
return PathUtils.get(TestUtils.class.getResource(resource).toURI());
}
public static void registerAndAckSignedLicenses(final LicensesService licensesService, License license,
public static void registerAndAckSignedLicenses(final LicenseService licenseService, License license,
final LicensesStatus expectedStatus) {
PutLicenseRequest putLicenseRequest = new PutLicenseRequest().license(license).acknowledge(true);
final CountDownLatch latch = new CountDownLatch(1);
final AtomicReference<LicensesStatus> status = new AtomicReference<>();
licensesService.registerLicense(putLicenseRequest, new ActionListener<PutLicenseResponse>() {
licenseService.registerLicense(putLicenseRequest, new ActionListener<PutLicenseResponse>() {
@Override
public void onResponse(PutLicenseResponse licensesUpdateResponse) {
status.set(licensesUpdateResponse.status());

View File

@ -35,7 +35,7 @@ import static org.mockito.Mockito.when;
public abstract class AbstractLicenseServiceTestCase extends ESTestCase {
protected LicensesService licensesService;
protected LicenseService licenseService;
protected ClusterService clusterService;
protected ResourceWatcherService resourceWatcherService;
protected ClockMock clock;
@ -54,7 +54,7 @@ public abstract class AbstractLicenseServiceTestCase extends ESTestCase {
protected void setInitialState(License license, Licensee... licensees) {
Path tempDir = createTempDir();
when(environment.configFile()).thenReturn(tempDir);
licensesService = new LicensesService(Settings.EMPTY, clusterService, clock, environment,
licenseService = new LicenseService(Settings.EMPTY, clusterService, clock, environment,
resourceWatcherService, Arrays.asList(licensees));
ClusterState state = mock(ClusterState.class);
final ClusterBlocks noBlock = ClusterBlocks.builder().build();
@ -74,6 +74,6 @@ public abstract class AbstractLicenseServiceTestCase extends ESTestCase {
@After
public void after() {
licensesService.stop();
licenseService.stop();
}
}

View File

@ -122,15 +122,6 @@ public abstract class AbstractLicenseeTestCase extends ESTestCase {
return randomValueOtherThan(mode, AbstractLicenseeTestCase::randomMode);
}
/**
* Randomly get {@link LicenseState#ENABLED} or {@link LicenseState#GRACE_PERIOD}.
*
* @return Never {@code null}.
*/
public static LicenseState randomEnabledOrGracePeriodState() {
return randomFrom(LicenseState.ENABLED, LicenseState.GRACE_PERIOD);
}
/**
* Get a random value from the {@code values} that passes {@code filter}.
*
@ -163,10 +154,10 @@ public abstract class AbstractLicenseeTestCase extends ESTestCase {
}
public void disable(Licensee licensee) {
licensee.onChange(new Licensee.Status(operationMode, LicenseState.DISABLED));
licensee.onChange(new Licensee.Status(operationMode, false));
}
public void enable(Licensee licensee) {
licensee.onChange(new Licensee.Status(operationMode, randomEnabledOrGracePeriodState()));
licensee.onChange(new Licensee.Status(operationMode, true));
}
}

View File

@ -37,12 +37,12 @@ public class LicenseClusterChangeTests extends AbstractLicenseServiceTestCase {
public void setup() {
licensee = new TestUtils.AssertingLicensee("LicenseClusterChangeTests", logger);
setInitialState(null, licensee);
licensesService.start();
licenseService.start();
}
@After
public void teardown() {
licensesService.stop();
licenseService.stop();
}
@ -51,9 +51,9 @@ public class LicenseClusterChangeTests extends AbstractLicenseServiceTestCase {
final License license = TestUtils.generateSignedLicense(TimeValue.timeValueHours(24));
MetaData metaData = MetaData.builder().putCustom(LicensesMetaData.TYPE, new LicensesMetaData(license)).build();
ClusterState newState = ClusterState.builder(new ClusterName("a")).metaData(metaData).build();
licensesService.clusterChanged(new ClusterChangedEvent("simulated", newState, oldState));
licenseService.clusterChanged(new ClusterChangedEvent("simulated", newState, oldState));
assertThat(licensee.statuses.size(), equalTo(1));
assertTrue(licensee.statuses.get(0).getLicenseState() == LicenseState.ENABLED);
assertTrue(licensee.statuses.get(0).isActive());
}
public void testNoNotificationOnExistingLicense() throws Exception {
@ -61,7 +61,7 @@ public class LicenseClusterChangeTests extends AbstractLicenseServiceTestCase {
MetaData metaData = MetaData.builder().putCustom(LicensesMetaData.TYPE, new LicensesMetaData(license)).build();
ClusterState newState = ClusterState.builder(new ClusterName("a")).metaData(metaData).build();
ClusterState oldState = ClusterState.builder(newState).build();
licensesService.clusterChanged(new ClusterChangedEvent("simulated", newState, oldState));
licenseService.clusterChanged(new ClusterChangedEvent("simulated", newState, oldState));
assertThat(licensee.statuses.size(), equalTo(0));
}
@ -72,13 +72,13 @@ public class LicenseClusterChangeTests extends AbstractLicenseServiceTestCase {
when(discoveryNodes.isLocalNodeElectedMaster()).thenReturn(true);
ClusterState newState = ClusterState.builder(oldState).nodes(discoveryNodes).build();
licensesService.clusterChanged(new ClusterChangedEvent("simulated", newState, oldState));
licenseService.clusterChanged(new ClusterChangedEvent("simulated", newState, oldState));
ArgumentCaptor<ClusterStateUpdateTask> stateUpdater = ArgumentCaptor.forClass(ClusterStateUpdateTask.class);
verify(clusterService, times(1)).submitStateUpdateTask(any(), stateUpdater.capture());
ClusterState stateWithLicense = stateUpdater.getValue().execute(newState);
LicensesMetaData licenseMetaData = stateWithLicense.metaData().custom(LicensesMetaData.TYPE);
assertNotNull(licenseMetaData);
assertNotNull(licenseMetaData.getLicense());
assertEquals(clock.millis() + LicensesService.TRIAL_LICENSE_DURATION.millis(), licenseMetaData.getLicense().expiryDate());
assertEquals(clock.millis() + LicenseService.TRIAL_LICENSE_DURATION.millis(), licenseMetaData.getLicense().expiryDate());
}
}

View File

@ -25,7 +25,7 @@ public class LicenseRegistrationTests extends AbstractLicenseServiceTestCase {
"testTrialLicenseRequestOnEmptyLicenseState", logger);
setInitialState(null, licensee);
when(discoveryNodes.isLocalNodeElectedMaster()).thenReturn(true);
licensesService.start();
licenseService.start();
ClusterState state = ClusterState.builder(new ClusterName("a")).build();
ArgumentCaptor<ClusterStateUpdateTask> stateUpdater = ArgumentCaptor.forClass(ClusterStateUpdateTask.class);
@ -34,16 +34,15 @@ public class LicenseRegistrationTests extends AbstractLicenseServiceTestCase {
LicensesMetaData licenseMetaData = stateWithLicense.metaData().custom(LicensesMetaData.TYPE);
assertNotNull(licenseMetaData);
assertNotNull(licenseMetaData.getLicense());
assertEquals(clock.millis() + LicensesService.TRIAL_LICENSE_DURATION.millis(), licenseMetaData.getLicense().expiryDate());
assertEquals(clock.millis() + LicenseService.TRIAL_LICENSE_DURATION.millis(), licenseMetaData.getLicense().expiryDate());
}
public void testNotificationOnRegistration() throws Exception {
TestUtils.AssertingLicensee licensee = new TestUtils.AssertingLicensee(
"testNotificationOnRegistration", logger);
setInitialState(TestUtils.generateSignedLicense(TimeValue.timeValueHours(2)), licensee);
licensesService.start();
licenseService.start();
assertThat(licensee.statuses.size(), equalTo(1));
final LicenseState licenseState = licensee.statuses.get(0).getLicenseState();
assertTrue(licenseState == LicenseState.ENABLED);
assertTrue(licensee.statuses.get(0).isActive());
}
}

View File

@ -9,6 +9,7 @@ import org.elasticsearch.common.unit.TimeValue;
import org.elasticsearch.license.core.License;
import org.elasticsearch.license.plugin.TestUtils;
import org.elasticsearch.test.ESTestCase;
import org.elasticsearch.xpack.scheduler.SchedulerEngine;
import org.junit.Before;
import static org.hamcrest.Matchers.equalTo;
@ -16,12 +17,12 @@ import static org.hamcrest.Matchers.equalTo;
public class LicenseScheduleTests extends ESTestCase {
private License license;
private LicenseSchedule schedule;
private SchedulerEngine.Schedule schedule;
@Before
public void setuo() throws Exception {
license = TestUtils.generateSignedLicense(TimeValue.timeValueHours(12));
schedule = new LicenseSchedule(license);
schedule = LicenseService.nextLicenseCheck(license);
}
public void testEnabledLicenseSchedule() throws Exception {
@ -32,13 +33,13 @@ public class LicenseScheduleTests extends ESTestCase {
public void testGraceLicenseSchedule() throws Exception {
long triggeredTime = license.expiryDate() + between(1,
((int) LicenseState.GRACE_PERIOD_DURATION.getMillis()));
((int) LicenseService.GRACE_PERIOD_DURATION.getMillis()));
assertThat(schedule.nextScheduledTimeAfter(license.issueDate(), triggeredTime),
equalTo(license.expiryDate() + LicenseState.GRACE_PERIOD_DURATION.getMillis()));
equalTo(license.expiryDate() + LicenseService.GRACE_PERIOD_DURATION.getMillis()));
}
public void testExpiredLicenseSchedule() throws Exception {
long triggeredTime = license.expiryDate() + LicenseState.GRACE_PERIOD_DURATION.getMillis() +
long triggeredTime = license.expiryDate() + LicenseService.GRACE_PERIOD_DURATION.getMillis() +
randomIntBetween(1, 1000);
assertThat(schedule.nextScheduledTimeAfter(license.issueDate(), triggeredTime),
equalTo(-1L));

View File

@ -32,23 +32,23 @@ public class LicensesAcknowledgementTests extends AbstractLicenseServiceTestCase
String[] acknowledgeMessages = new String[] {"message"};
TestUtils.AssertingLicensee licensee = new TestUtils.AssertingLicensee(id, logger);
setInitialState(TestUtils.generateSignedLicense("trial", TimeValue.timeValueHours(2)), licensee);
licensesService.start();
licenseService.start();
licensee.setAcknowledgementMessages(acknowledgeMessages);
// try installing a signed license
License signedLicense = generateSignedLicense(TimeValue.timeValueHours(10));
PutLicenseRequest putLicenseRequest = new PutLicenseRequest().license(signedLicense);
// ensure acknowledgement message was part of the response
licensesService.registerLicense(putLicenseRequest, new AssertingLicensesUpdateResponse(false, LicensesStatus.VALID,
licenseService.registerLicense(putLicenseRequest, new AssertingLicensesUpdateResponse(false, LicensesStatus.VALID,
Collections.singletonMap(id, acknowledgeMessages)));
assertThat(licensee.acknowledgementRequested.size(), equalTo(1));
assertThat(licensee.acknowledgementRequested.get(0).v2(), equalTo(signedLicense.operationMode()));
assertThat(licensesService.getLicense(), not(signedLicense));
assertThat(licenseService.getLicense(), not(signedLicense));
// try installing a signed license with acknowledgement
putLicenseRequest = new PutLicenseRequest().license(signedLicense).acknowledge(true);
// ensure license was installed and no acknowledgment message was returned
licensee.setAcknowledgementMessages(new String[0]);
licensesService.registerLicense(putLicenseRequest, new AssertingLicensesUpdateResponse(true, LicensesStatus.VALID,
licenseService.registerLicense(putLicenseRequest, new AssertingLicensesUpdateResponse(true, LicensesStatus.VALID,
Collections.<String, String[]>emptyMap()));
verify(clusterService, times(1)).submitStateUpdateTask(any(String.class), any(ClusterStateUpdateTask.class));
assertThat(licensee.acknowledgementRequested.size(), equalTo(1));
@ -65,7 +65,7 @@ public class LicensesAcknowledgementTests extends AbstractLicenseServiceTestCase
TestUtils.AssertingLicensee licensee2 = new TestUtils.AssertingLicensee(id2, logger);
licensee2.setAcknowledgementMessages(acknowledgeMessages2);
setInitialState(TestUtils.generateSignedLicense("trial", TimeValue.timeValueHours(2)), licensee1, licensee2);
licensesService.start();
licenseService.start();
// try installing a signed license
License signedLicense = generateSignedLicense(TimeValue.timeValueHours(10));
PutLicenseRequest putLicenseRequest = new PutLicenseRequest().license(signedLicense);
@ -73,21 +73,21 @@ public class LicensesAcknowledgementTests extends AbstractLicenseServiceTestCase
final HashMap<String, String[]> expectedMessages = new HashMap<>();
expectedMessages.put(id1, acknowledgeMessages1);
expectedMessages.put(id2, acknowledgeMessages2);
licensesService.registerLicense(putLicenseRequest, new AssertingLicensesUpdateResponse(false, LicensesStatus.VALID,
licenseService.registerLicense(putLicenseRequest, new AssertingLicensesUpdateResponse(false, LicensesStatus.VALID,
expectedMessages));
verify(clusterService, times(0)).submitStateUpdateTask(any(String.class), any(ClusterStateUpdateTask.class));
assertThat(licensee2.acknowledgementRequested.size(), equalTo(1));
assertThat(licensee2.acknowledgementRequested.get(0).v2(), equalTo(signedLicense.operationMode()));
assertThat(licensee1.acknowledgementRequested.size(), equalTo(1));
assertThat(licensee1.acknowledgementRequested.get(0).v2(), equalTo(signedLicense.operationMode()));
assertThat(licensesService.getLicense(), not(signedLicense));
assertThat(licenseService.getLicense(), not(signedLicense));
// try installing a signed license with acknowledgement
putLicenseRequest = new PutLicenseRequest().license(signedLicense).acknowledge(true);
// ensure license was installed and no acknowledgment message was returned
licensee1.setAcknowledgementMessages(new String[0]);
licensee2.setAcknowledgementMessages(new String[0]);
licensesService.registerLicense(putLicenseRequest, new AssertingLicensesUpdateResponse(true, LicensesStatus.VALID,
licenseService.registerLicense(putLicenseRequest, new AssertingLicensesUpdateResponse(true, LicensesStatus.VALID,
Collections.<String, String[]>emptyMap()));
verify(clusterService, times(1)).submitStateUpdateTask(any(String.class), any(ClusterStateUpdateTask.class));
}

View File

@ -55,46 +55,46 @@ public class LicensesManagerServiceTests extends ESSingleNodeTestCase {
}
public void testStoreAndGetLicenses() throws Exception {
LicensesService licensesService = getInstanceFromNode(LicensesService.class);
LicenseService licenseService = getInstanceFromNode(LicenseService.class);
ClusterService clusterService = getInstanceFromNode(ClusterService.class);
License goldLicense = generateSignedLicense("gold", TimeValue.timeValueHours(1));
TestUtils.registerAndAckSignedLicenses(licensesService, goldLicense, LicensesStatus.VALID);
TestUtils.registerAndAckSignedLicenses(licenseService, goldLicense, LicensesStatus.VALID);
License silverLicense = generateSignedLicense("silver", TimeValue.timeValueHours(2));
TestUtils.registerAndAckSignedLicenses(licensesService, silverLicense, LicensesStatus.VALID);
TestUtils.registerAndAckSignedLicenses(licenseService, silverLicense, LicensesStatus.VALID);
License platinumLicense = generateSignedLicense("platinum", TimeValue.timeValueHours(1));
TestUtils.registerAndAckSignedLicenses(licensesService, platinumLicense, LicensesStatus.VALID);
TestUtils.registerAndAckSignedLicenses(licenseService, platinumLicense, LicensesStatus.VALID);
License basicLicense = generateSignedLicense("basic", TimeValue.timeValueHours(3));
TestUtils.registerAndAckSignedLicenses(licensesService, basicLicense, LicensesStatus.VALID);
TestUtils.registerAndAckSignedLicenses(licenseService, basicLicense, LicensesStatus.VALID);
LicensesMetaData licensesMetaData = clusterService.state().metaData().custom(LicensesMetaData.TYPE);
assertThat(licensesMetaData.getLicense(), equalTo(basicLicense));
final License getLicenses = licensesService.getLicense();
final License getLicenses = licenseService.getLicense();
assertThat(getLicenses, equalTo(basicLicense));
}
public void testEffectiveLicenses() throws Exception {
final LicensesService licensesService = getInstanceFromNode(LicensesService.class);
final LicenseService licenseService = getInstanceFromNode(LicenseService.class);
final ClusterService clusterService = getInstanceFromNode(ClusterService.class);
License goldLicense = generateSignedLicense("gold", TimeValue.timeValueSeconds(5));
// put gold license
TestUtils.registerAndAckSignedLicenses(licensesService, goldLicense, LicensesStatus.VALID);
TestUtils.registerAndAckSignedLicenses(licenseService, goldLicense, LicensesStatus.VALID);
LicensesMetaData licensesMetaData = clusterService.state().metaData().custom(LicensesMetaData.TYPE);
assertThat(licensesService.getLicense(licensesMetaData), equalTo(goldLicense));
assertThat(licenseService.getLicense(licensesMetaData), equalTo(goldLicense));
License platinumLicense = generateSignedLicense("platinum", TimeValue.timeValueSeconds(3));
// put platinum license
TestUtils.registerAndAckSignedLicenses(licensesService, platinumLicense, LicensesStatus.VALID);
TestUtils.registerAndAckSignedLicenses(licenseService, platinumLicense, LicensesStatus.VALID);
licensesMetaData = clusterService.state().metaData().custom(LicensesMetaData.TYPE);
assertThat(licensesService.getLicense(licensesMetaData), equalTo(platinumLicense));
assertThat(licenseService.getLicense(licensesMetaData), equalTo(platinumLicense));
License basicLicense = generateSignedLicense("basic", TimeValue.timeValueSeconds(3));
// put basic license
TestUtils.registerAndAckSignedLicenses(licensesService, basicLicense, LicensesStatus.VALID);
TestUtils.registerAndAckSignedLicenses(licenseService, basicLicense, LicensesStatus.VALID);
licensesMetaData = clusterService.state().metaData().custom(LicensesMetaData.TYPE);
assertThat(licensesService.getLicense(licensesMetaData), equalTo(basicLicense));
assertThat(licenseService.getLicense(licensesMetaData), equalTo(basicLicense));
}
public void testInvalidLicenseStorage() throws Exception {
LicensesService licensesService = getInstanceFromNode(LicensesService.class);
LicenseService licenseService = getInstanceFromNode(LicenseService.class);
ClusterService clusterService = getInstanceFromNode(ClusterService.class);
License signedLicense = generateSignedLicense(TimeValue.timeValueMinutes(2));
@ -105,7 +105,7 @@ public class LicensesManagerServiceTests extends ESSingleNodeTestCase {
.validate()
.build();
TestUtils.registerAndAckSignedLicenses(licensesService, tamperedLicense, LicensesStatus.INVALID);
TestUtils.registerAndAckSignedLicenses(licenseService, tamperedLicense, LicensesStatus.INVALID);
// ensure that the invalid license never made it to cluster state
LicensesMetaData licensesMetaData = clusterService.state().metaData().custom(LicensesMetaData.TYPE);
@ -113,25 +113,25 @@ public class LicensesManagerServiceTests extends ESSingleNodeTestCase {
}
public void testRemoveLicenses() throws Exception {
LicensesService licensesService = getInstanceFromNode(LicensesService.class);
LicenseService licenseService = getInstanceFromNode(LicenseService.class);
ClusterService clusterService = getInstanceFromNode(ClusterService.class);
// generate signed licenses
License license = generateSignedLicense(TimeValue.timeValueHours(1));
TestUtils.registerAndAckSignedLicenses(licensesService, license, LicensesStatus.VALID);
TestUtils.registerAndAckSignedLicenses(licenseService, license, LicensesStatus.VALID);
LicensesMetaData licensesMetaData = clusterService.state().metaData().custom(LicensesMetaData.TYPE);
assertThat(licensesMetaData.getLicense(), not(LicensesMetaData.LICENSE_TOMBSTONE));
// remove signed licenses
removeAndAckSignedLicenses(licensesService);
removeAndAckSignedLicenses(licenseService);
licensesMetaData = clusterService.state().metaData().custom(LicensesMetaData.TYPE);
assertThat(licensesMetaData.getLicense(), equalTo(LicensesMetaData.LICENSE_TOMBSTONE));
}
private void removeAndAckSignedLicenses(final LicensesService licensesService) {
private void removeAndAckSignedLicenses(final LicenseService licenseService) {
final CountDownLatch latch = new CountDownLatch(1);
final AtomicBoolean success = new AtomicBoolean(false);
licensesService.removeLicense(new DeleteLicenseRequest(), new ActionListener<ClusterStateUpdateResponse>() {
licenseService.removeLicense(new DeleteLicenseRequest(), new ActionListener<ClusterStateUpdateResponse>() {
@Override
public void onResponse(ClusterStateUpdateResponse clusterStateUpdateResponse) {
if (clusterStateUpdateResponse.isAcknowledged()) {

View File

@ -26,34 +26,33 @@ public class LicensesNotificationTests extends AbstractLicenseServiceTestCase {
assertingLicensees[i] = new AssertingLicensee("testLicenseNotification" + i, logger);
}
setInitialState(license, assertingLicensees);
licensesService.start();
licenseService.start();
for (int i = 0; i < assertingLicensees.length; i++) {
assertLicenseStates(assertingLicensees[i], LicenseState.ENABLED);
assertLicenseStates(assertingLicensees[i], true);
}
clock.fastForward(TimeValue.timeValueMillis(license.expiryDate() - clock.millis()));
final LicensesMetaData licensesMetaData = new LicensesMetaData(license);
licensesService.onUpdate(licensesMetaData);
licenseService.onUpdate(licensesMetaData);
for (AssertingLicensee assertingLicensee : assertingLicensees) {
assertLicenseStates(assertingLicensee, LicenseState.ENABLED, LicenseState.GRACE_PERIOD);
assertLicenseStates(assertingLicensee, true);
}
clock.fastForward(TimeValue.timeValueMillis((license.expiryDate() +
LicenseState.GRACE_PERIOD_DURATION.getMillis()) - clock.millis()));
licensesService.onUpdate(licensesMetaData);
LicenseService.GRACE_PERIOD_DURATION.getMillis()) - clock.millis()));
licenseService.onUpdate(licensesMetaData);
for (AssertingLicensee assertingLicensee : assertingLicensees) {
assertLicenseStates(assertingLicensee, LicenseState.ENABLED, LicenseState.GRACE_PERIOD, LicenseState.DISABLED);
assertLicenseStates(assertingLicensee, true, false);
}
clock.setTime(new DateTime(DateTimeZone.UTC));
final License newLicense = TestUtils.generateSignedLicense(TimeValue.timeValueHours(2));
clock.fastForward(TimeValue.timeValueHours(1));
LicensesMetaData licensesMetaData1 = new LicensesMetaData(newLicense);
licensesService.onUpdate(licensesMetaData1);
licenseService.onUpdate(licensesMetaData1);
for (AssertingLicensee assertingLicensee : assertingLicensees) {
assertLicenseStates(assertingLicensee, LicenseState.ENABLED, LicenseState.GRACE_PERIOD, LicenseState.DISABLED,
LicenseState.ENABLED);
assertLicenseStates(assertingLicensee, true, false, true);
}
}
private void assertLicenseStates(AssertingLicensee licensee, LicenseState... states) {
private void assertLicenseStates(AssertingLicensee licensee, boolean... states) {
StringBuilder msg = new StringBuilder();
msg.append("Actual: ");
msg.append(dumpLicensingStates(licensee.statuses));
@ -61,7 +60,7 @@ public class LicensesNotificationTests extends AbstractLicenseServiceTestCase {
msg.append(dumpLicensingStates(states));
assertThat(msg.toString(), licensee.statuses.size(), equalTo(states.length));
for (int i = 0; i < states.length; i++) {
assertThat(msg.toString(), licensee.statuses.get(i).getLicenseState(), equalTo(states[i]));
assertThat(msg.toString(), licensee.statuses.get(i).isActive(), equalTo(states[i]));
}
}
@ -70,18 +69,18 @@ public class LicensesNotificationTests extends AbstractLicenseServiceTestCase {
}
private String dumpLicensingStates(Licensee.Status... statuses) {
LicenseState[] states = new LicenseState[statuses.length];
boolean[] states = new boolean[statuses.length];
for (int i = 0; i < statuses.length; i++) {
states[i] = statuses[i].getLicenseState();
states[i] = statuses[i].isActive();
}
return dumpLicensingStates(states);
}
private String dumpLicensingStates(LicenseState... states) {
private String dumpLicensingStates(boolean... states) {
StringBuilder sb = new StringBuilder();
sb.append("[");
for (int i = 0; i < states.length; i++) {
sb.append(states[i].name());
sb.append(states[i]);
if (i != states.length - 1) {
sb.append(", ");
}

View File

@ -10,7 +10,6 @@ import org.elasticsearch.common.logging.LoggerMessageFormat;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.license.core.License.OperationMode;
import org.elasticsearch.license.plugin.core.AbstractLicenseeComponent;
import org.elasticsearch.license.plugin.core.LicenseState;
/**
* {@code MonitoringLicensee} determines whether certain features of Monitoring are enabled or disabled.
@ -73,7 +72,7 @@ public class MonitoringLicensee extends AbstractLicenseeComponent {
* @return true
*/
public boolean isAvailable() {
return status.getLicenseState() != LicenseState.DISABLED;
return status.isActive();
}
/**
@ -87,7 +86,7 @@ public class MonitoringLicensee extends AbstractLicenseeComponent {
* @return {@code true} as long as the license is valid. Otherwise {@code false}.
*/
public boolean collectionEnabled() {
return status.getLicenseState() != LicenseState.DISABLED;
return status.isActive();
}
/**
@ -98,7 +97,7 @@ public class MonitoringLicensee extends AbstractLicenseeComponent {
* @return {@code true} as long as the license is valid. Otherwise {@code false}.
*/
public boolean cleaningEnabled() {
return status.getLicenseState() != LicenseState.DISABLED;
return status.isActive();
}
/**

View File

@ -14,7 +14,7 @@ import org.elasticsearch.cluster.service.ClusterService;
import org.elasticsearch.common.inject.Inject;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.license.plugin.core.LicenseUtils;
import org.elasticsearch.license.plugin.core.LicensesService;
import org.elasticsearch.license.plugin.core.LicenseService;
import org.elasticsearch.xpack.monitoring.MonitoringLicensee;
import org.elasticsearch.xpack.monitoring.MonitoringSettings;
import org.elasticsearch.xpack.monitoring.agent.collector.AbstractCollector;
@ -40,16 +40,16 @@ public class ClusterStatsCollector extends AbstractCollector {
public static final String NAME = "cluster-stats-collector";
private final LicensesService licensesService;
private final LicenseService licenseService;
private final Client client;
@Inject
public ClusterStatsCollector(Settings settings, ClusterService clusterService,
MonitoringSettings monitoringSettings, MonitoringLicensee licensee, InternalClient client,
LicensesService licensesService) {
LicenseService licenseService) {
super(settings, NAME, clusterService, monitoringSettings, licensee);
this.client = client;
this.licensesService = licensesService;
this.licenseService = licenseService;
}
@Override
@ -85,7 +85,7 @@ public class ClusterStatsCollector extends AbstractCollector {
clusterInfoDoc.setSourceNode(sourceNode);
clusterInfoDoc.setClusterName(clusterService.getClusterName().value());
clusterInfoDoc.setVersion(Version.CURRENT.toString());
clusterInfoDoc.setLicense(licensesService.getLicense());
clusterInfoDoc.setLicense(licenseService.getLicense());
clusterInfoDoc.setClusterStats(clusterStats);
results.add(clusterInfoDoc);

View File

@ -25,9 +25,8 @@ import org.elasticsearch.common.unit.TimeValue;
import org.elasticsearch.env.Environment;
import org.elasticsearch.license.core.License;
import org.elasticsearch.license.plugin.Licensing;
import org.elasticsearch.license.plugin.core.LicenseState;
import org.elasticsearch.license.plugin.core.LicenseService;
import org.elasticsearch.license.plugin.core.Licensee;
import org.elasticsearch.license.plugin.core.LicensesService;
import org.elasticsearch.plugins.Plugin;
import org.elasticsearch.rest.RestHandler;
import org.elasticsearch.test.ESIntegTestCase;
@ -110,7 +109,7 @@ public abstract class AbstractCollectorTestCase extends MonitoringIntegTestCase
final License license = createTestingLicense(issueDate, expiryDate);
for (LicenseServiceForCollectors service : internalCluster().getInstances(LicenseServiceForCollectors.class)) {
service.onChange(license.operationMode(), LicenseState.ENABLED);
service.onChange(license.operationMode(), true);
}
for (LicenseServiceForCollectors service : internalCluster().getInstances(LicenseServiceForCollectors.class)) {
service.update(license);
@ -123,7 +122,7 @@ public abstract class AbstractCollectorTestCase extends MonitoringIntegTestCase
final License license = createTestingLicense(issueDate, expiryDate);
for (LicenseServiceForCollectors service : internalCluster().getInstances(LicenseServiceForCollectors.class)) {
service.onChange(license.operationMode(), LicenseState.GRACE_PERIOD);
service.onChange(license.operationMode(), true);
}
for (LicenseServiceForCollectors service : internalCluster().getInstances(LicenseServiceForCollectors.class)) {
service.update(license);
@ -136,7 +135,7 @@ public abstract class AbstractCollectorTestCase extends MonitoringIntegTestCase
final License license = createTestingLicense(issueDate, expiryDate);
for (LicenseServiceForCollectors service : internalCluster().getInstances(LicenseServiceForCollectors.class)) {
service.onChange(license.operationMode(), LicenseState.DISABLED);
service.onChange(license.operationMode(), false);
}
for (LicenseServiceForCollectors service : internalCluster().getInstances(LicenseServiceForCollectors.class)) {
service.update(license);
@ -149,7 +148,7 @@ public abstract class AbstractCollectorTestCase extends MonitoringIntegTestCase
final License license = createTestingLicense(issueDate, expiryDate);
for (LicenseServiceForCollectors service : internalCluster().getInstances(LicenseServiceForCollectors.class)) {
service.onChange(license.operationMode(), LicenseState.DISABLED);
service.onChange(license.operationMode(), false);
}
for (LicenseServiceForCollectors service : internalCluster().getInstances(LicenseServiceForCollectors.class)) {
service.update(license);
@ -192,7 +191,7 @@ public abstract class AbstractCollectorTestCase extends MonitoringIntegTestCase
@Override
public Collection<Module> nodeModules() {
return Collections.singletonList(b -> b.bind(LicensesService.class).to(LicenseServiceForCollectors.class));
return Collections.singletonList(b -> b.bind(LicenseService.class).to(LicenseServiceForCollectors.class));
}
@Override
@ -202,9 +201,9 @@ public abstract class AbstractCollectorTestCase extends MonitoringIntegTestCase
WatcherLicensee watcherLicensee = new WatcherLicensee(settings);
MonitoringLicensee monitoringLicensee = new MonitoringLicensee(settings);
GraphLicensee graphLicensee = new GraphLicensee(settings);
LicensesService licensesService = new LicenseServiceForCollectors(settings, environment,
LicenseService licenseService = new LicenseServiceForCollectors(settings, environment,
resourceWatcherService, Arrays.asList(watcherLicensee, monitoringLicensee, graphLicensee));
return Arrays.asList(licensesService, watcherLicensee, monitoringLicensee, graphLicensee);
return Arrays.asList(licenseService, watcherLicensee, monitoringLicensee, graphLicensee);
}
@Override
@ -226,7 +225,7 @@ public abstract class AbstractCollectorTestCase extends MonitoringIntegTestCase
}
}
public static class LicenseServiceForCollectors extends LicensesService {
public static class LicenseServiceForCollectors extends LicenseService {
private final List<Licensee> licensees;
private volatile License license;
@ -238,14 +237,14 @@ public abstract class AbstractCollectorTestCase extends MonitoringIntegTestCase
this.licensees = licensees;
}
public void onChange(License.OperationMode operationMode, LicenseState state) {
public void onChange(License.OperationMode operationMode, boolean active) {
for (Licensee licensee : licensees) {
licensee.onChange(new Licensee.Status(operationMode, state));
licensee.onChange(new Licensee.Status(operationMode, active));
}
}
@Override
public Licensee.Status licenseeStatus() {
public Licensee.Status licenseeStatus(License license) {
return null;
}

View File

@ -11,7 +11,7 @@ import org.apache.lucene.util.LuceneTestCase.BadApple;
import org.elasticsearch.Version;
import org.elasticsearch.cluster.service.ClusterService;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.license.plugin.core.LicensesService;
import org.elasticsearch.license.plugin.core.LicenseService;
import org.elasticsearch.xpack.monitoring.MonitoredSystem;
import org.elasticsearch.xpack.monitoring.MonitoringLicensee;
import org.elasticsearch.xpack.monitoring.MonitoringSettings;
@ -133,7 +133,7 @@ public class ClusterStatsCollectorTests extends AbstractCollectorTestCase {
internalCluster().getInstance(MonitoringSettings.class, nodeId),
internalCluster().getInstance(MonitoringLicensee.class, nodeId),
securedClient(nodeId),
internalCluster().getInstance(LicensesService.class, nodeId));
internalCluster().getInstance(LicenseService.class, nodeId));
}
private void assertCanCollect(AbstractCollector collector, Class<?>... classes) {

View File

@ -14,10 +14,8 @@ import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.env.Environment;
import org.elasticsearch.license.core.License;
import org.elasticsearch.license.plugin.Licensing;
import org.elasticsearch.license.plugin.core.AbstractLicenseeComponent;
import org.elasticsearch.license.plugin.core.LicenseState;
import org.elasticsearch.license.plugin.core.LicenseService;
import org.elasticsearch.license.plugin.core.Licensee;
import org.elasticsearch.license.plugin.core.LicensesService;
import org.elasticsearch.plugins.Plugin;
import org.elasticsearch.rest.RestHandler;
import org.elasticsearch.test.ESIntegTestCase.ClusterScope;
@ -31,7 +29,6 @@ import org.elasticsearch.xpack.support.clock.Clock;
import org.elasticsearch.xpack.watcher.WatcherLicensee;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
@ -41,7 +38,6 @@ import static java.util.Collections.emptyList;
import static org.elasticsearch.test.ESIntegTestCase.Scope.SUITE;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.isOneOf;
@ClusterScope(scope = SUITE, transportClientRatio = 0, numClientNodes = 0)
public class LicenseIntegrationTests extends MonitoringIntegTestCase {
@ -58,15 +54,15 @@ public class LicenseIntegrationTests extends MonitoringIntegTestCase {
}
public void testEnableDisableLicense() {
assertThat(getLicensee().getStatus().getLicenseState(), isOneOf(LicenseState.ENABLED, LicenseState.GRACE_PERIOD));
assertTrue(getLicensee().getStatus().isActive());
assertThat(getLicensee().collectionEnabled(), is(true));
disableLicensing();
assertThat(getLicensee().getStatus().getLicenseState(), equalTo(LicenseState.DISABLED));
assertThat(getLicensee().getStatus().isActive(), equalTo(false));
assertThat(getLicensee().collectionEnabled(), is(false));
enableLicensing();
assertThat(getLicensee().getStatus().getLicenseState(), isOneOf(LicenseState.ENABLED, LicenseState.GRACE_PERIOD));
assertTrue(getLicensee().getStatus().isActive());
assertThat(getLicensee().collectionEnabled(), is(true));
}
@ -96,7 +92,7 @@ public class LicenseIntegrationTests extends MonitoringIntegTestCase {
@Override
public Collection<Module> nodeModules() {
return Collections.singletonList(b -> b.bind(LicensesService.class).to(MockLicenseService.class));
return Collections.singletonList(b -> b.bind(LicenseService.class).to(MockLicenseService.class));
}
@Override
@ -106,9 +102,9 @@ public class LicenseIntegrationTests extends MonitoringIntegTestCase {
WatcherLicensee watcherLicensee = new WatcherLicensee(settings);
MonitoringLicensee monitoringLicensee = new MonitoringLicensee(settings);
GraphLicensee graphLicensee = new GraphLicensee(settings);
LicensesService licensesService = new MockLicenseService(settings, environment, resourceWatcherService,
LicenseService licenseService = new MockLicenseService(settings, environment, resourceWatcherService,
Arrays.asList(watcherLicensee, monitoringLicensee, graphLicensee));
return Arrays.asList(licensesService, watcherLicensee, monitoringLicensee, graphLicensee);
return Arrays.asList(licenseService, watcherLicensee, monitoringLicensee, graphLicensee);
}
@Override
@ -122,7 +118,7 @@ public class LicenseIntegrationTests extends MonitoringIntegTestCase {
}
}
public static class MockLicenseService extends LicensesService {
public static class MockLicenseService extends LicenseService {
private final List<Licensee> licensees;
@ -136,19 +132,18 @@ public class LicenseIntegrationTests extends MonitoringIntegTestCase {
public void enable() {
for (Licensee licensee : licensees) {
licensee.onChange(new Licensee.Status(License.OperationMode.BASIC,
randomBoolean() ? LicenseState.ENABLED : LicenseState.GRACE_PERIOD));
licensee.onChange(new Licensee.Status(License.OperationMode.BASIC, true));
}
}
public void disable() {
for (Licensee licensee : licensees) {
licensee.onChange(new Licensee.Status(License.OperationMode.BASIC, LicenseState.DISABLED));
licensee.onChange(new Licensee.Status(License.OperationMode.BASIC, false));
}
}
@Override
public Licensee.Status licenseeStatus() {
public Licensee.Status licenseeStatus(License license) {
return null;
}

View File

@ -8,7 +8,6 @@ package org.elasticsearch.xpack.monitoring.license;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.license.core.License.OperationMode;
import org.elasticsearch.license.plugin.core.AbstractLicenseeTestCase;
import org.elasticsearch.license.plugin.core.LicenseState;
import org.elasticsearch.license.plugin.core.Licensee.Status;
import org.elasticsearch.xpack.monitoring.MonitoringLicensee;
@ -46,19 +45,19 @@ public class MonitoringLicenseeTests extends AbstractLicenseeTestCase {
}
public void testCollectionEnabledIsTrueForActiveState() {
assertEnabled(randomEnabledOrGracePeriodState(), MonitoringLicensee::collectionEnabled, true);
assertEnabled(true, MonitoringLicensee::collectionEnabled, true);
}
public void testCollectionEnabledIsFalseForInactiveState() {
assertEnabled(LicenseState.DISABLED, MonitoringLicensee::collectionEnabled, false);
assertEnabled(false, MonitoringLicensee::collectionEnabled, false);
}
public void testCleaningEnabledIsTrueForActiveState() {
assertEnabled(randomEnabledOrGracePeriodState(), MonitoringLicensee::cleaningEnabled, true);
assertEnabled(true, MonitoringLicensee::cleaningEnabled, true);
}
public void testCleaningEnabledIsFalseForInactiveState() {
assertEnabled(LicenseState.DISABLED, MonitoringLicensee::cleaningEnabled, false);
assertEnabled(false, MonitoringLicensee::cleaningEnabled, false);
}
public void testAllowUpdateRetentionIsTrueForNotBasic() {
@ -77,19 +76,19 @@ public class MonitoringLicenseeTests extends AbstractLicenseeTestCase {
/**
* Assert that the {@link #licensee} is {@code predicate}d as {@code expected} when setting the {@code state}.
*
* @param state The state that should cause the {@code expected} {@code predicate}.
* @param active The state that should cause the {@code expected} {@code predicate}.
* @param predicate The method to invoke (expected to be an instance method).
* @param expected The expected outcome given the {@code state} and {@code predicate}.
*/
private void assertEnabled(LicenseState state, Predicate<MonitoringLicensee> predicate, boolean expected) {
private void assertEnabled(boolean active, Predicate<MonitoringLicensee> predicate, boolean expected) {
Status status = mock(Status.class);
when(status.getLicenseState()).thenReturn(state);
when(status.isActive()).thenReturn(active);
licensee.onChange(status);
assertThat(predicate.test(licensee), equalTo(expected));
verify(status).getLicenseState();
verify(status).isActive();
}
/**

View File

@ -19,14 +19,13 @@ import org.elasticsearch.xpack.security.authc.support.SecuredString;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Map;
import static org.elasticsearch.common.xcontent.support.XContentMapValues.extractValue;
import static org.elasticsearch.xpack.security.authc.support.UsernamePasswordToken.BASIC_AUTH_HEADER;
import static org.elasticsearch.xpack.security.authc.support.UsernamePasswordToken.basicAuthHeaderValue;
import static org.hamcrest.Matchers.nullValue;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.nullValue;
public class MonitoringSettingsFilterTests extends MonitoringIntegTestCase {
@ -63,22 +62,20 @@ public class MonitoringSettingsFilterTests extends MonitoringIntegTestCase {
} else {
headers = new Header[0];
}
try (Response response = getRestClient().performRequest("GET", "/_nodes/settings",
Collections.emptyMap(), null, headers)) {
Map<String, Object> responseMap = JsonXContent.jsonXContent.createParser(response.getEntity().getContent()).map();
Response response = getRestClient().performRequest("GET", "/_nodes/settings", headers);
Map<String, Object> responseMap = JsonXContent.jsonXContent.createParser(response.getEntity().getContent()).map();
@SuppressWarnings("unchecked")
Map<String, Object> nodes = (Map<String, Object>) responseMap.get("nodes");
for (Object node : nodes.values()) {
@SuppressWarnings("unchecked")
Map<String, Object> nodes = (Map<String, Object>) responseMap.get("nodes");
for (Object node : nodes.values()) {
@SuppressWarnings("unchecked")
Map<String, Object> settings = (Map<String, Object>) ((Map<String, Object>) node).get("settings");
assertThat(extractValue("xpack.monitoring.collection.exporters._http.type", settings), equalTo("http"));
assertThat(extractValue("xpack.monitoring.collection.exporters._http.enabled", settings), equalTo("false"));
assertNullSetting(settings, "xpack.monitoring.collection.exporters._http.auth.username");
assertNullSetting(settings, "xpack.monitoring.collection.exporters._http.auth.password");
assertNullSetting(settings, "xpack.monitoring.collection.exporters._http.ssl.truststore.path");
assertNullSetting(settings, "xpack.monitoring.collection.exporters._http.ssl.truststore.password");
assertNullSetting(settings, "xpack.monitoring.collection.exporters._http.ssl.hostname_verification");
}
Map<String, Object> settings = (Map<String, Object>) ((Map<String, Object>) node).get("settings");
assertThat(extractValue("xpack.monitoring.collection.exporters._http.type", settings), equalTo("http"));
assertThat(extractValue("xpack.monitoring.collection.exporters._http.enabled", settings), equalTo("false"));
assertNullSetting(settings, "xpack.monitoring.collection.exporters._http.auth.username");
assertNullSetting(settings, "xpack.monitoring.collection.exporters._http.auth.password");
assertNullSetting(settings, "xpack.monitoring.collection.exporters._http.ssl.truststore.path");
assertNullSetting(settings, "xpack.monitoring.collection.exporters._http.ssl.truststore.password");
assertNullSetting(settings, "xpack.monitoring.collection.exporters._http.ssl.hostname_verification");
}
}

View File

@ -20,7 +20,7 @@ import org.elasticsearch.common.util.concurrent.ThreadContext;
import org.elasticsearch.node.Node;
import org.elasticsearch.threadpool.ThreadPool;
import org.elasticsearch.xpack.security.authc.Authentication;
import org.elasticsearch.xpack.security.authc.InternalAuthenticationService;
import org.elasticsearch.xpack.security.authc.AuthenticationService;
import org.elasticsearch.xpack.security.crypto.CryptoService;
import org.elasticsearch.xpack.security.user.XPackUser;
@ -43,7 +43,7 @@ public class InternalClient extends FilterClient {
public InternalClient(Settings settings, ThreadPool threadPool, Client in, CryptoService cryptoService) {
super(settings, threadPool, in);
this.cryptoService = cryptoService;
this.signUserHeader = InternalAuthenticationService.SIGN_USER_HEADER.get(settings);
this.signUserHeader = AuthenticationService.SIGN_USER_HEADER.get(settings);
this.nodeName = Node.NODE_NAME_SETTING.get(settings);
}

View File

@ -11,7 +11,6 @@ import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
@ -75,8 +74,9 @@ import org.elasticsearch.xpack.security.audit.AuditTrailService;
import org.elasticsearch.xpack.security.audit.index.IndexAuditTrail;
import org.elasticsearch.xpack.security.audit.index.IndexNameResolver;
import org.elasticsearch.xpack.security.audit.logfile.LoggingAuditTrail;
import org.elasticsearch.xpack.security.authc.AuthenticationModule;
import org.elasticsearch.xpack.security.authc.InternalAuthenticationService;
import org.elasticsearch.xpack.security.authc.AuthenticationFailureHandler;
import org.elasticsearch.xpack.security.authc.DefaultAuthenticationFailureHandler;
import org.elasticsearch.xpack.security.authc.AuthenticationService;
import org.elasticsearch.xpack.security.authc.Realm;
import org.elasticsearch.xpack.security.authc.Realms;
import org.elasticsearch.xpack.security.authc.activedirectory.ActiveDirectoryRealm;
@ -89,13 +89,14 @@ import org.elasticsearch.xpack.security.authc.ldap.support.SessionFactory;
import org.elasticsearch.xpack.security.authc.pki.PkiRealm;
import org.elasticsearch.xpack.security.authc.support.SecuredString;
import org.elasticsearch.xpack.security.authc.support.UsernamePasswordToken;
import org.elasticsearch.xpack.security.authz.AuthorizationModule;
import org.elasticsearch.xpack.security.authz.InternalAuthorizationService;
import org.elasticsearch.xpack.security.authz.AuthorizationService;
import org.elasticsearch.xpack.security.authz.accesscontrol.SetSecurityUserProcessor;
import org.elasticsearch.xpack.security.authz.accesscontrol.OptOutQueryCache;
import org.elasticsearch.xpack.security.authz.accesscontrol.SecurityIndexSearcherWrapper;
import org.elasticsearch.xpack.security.authz.store.CompositeRolesStore;
import org.elasticsearch.xpack.security.authz.store.FileRolesStore;
import org.elasticsearch.xpack.security.authz.store.NativeRolesStore;
import org.elasticsearch.xpack.security.authz.store.ReservedRolesStore;
import org.elasticsearch.xpack.security.crypto.CryptoService;
import org.elasticsearch.xpack.security.rest.SecurityRestModule;
import org.elasticsearch.xpack.security.rest.action.RestAuthenticateAction;
@ -184,7 +185,6 @@ public class Security implements ActionPlugin, IngestPlugin {
if (enabled == false) {
return modules;
}
modules.add(new SecurityModule(settings));
modules.add(new SecurityTransportModule(settings));
modules.add(b -> {
// for transport client we still must inject these ssl classes with guice
@ -195,20 +195,16 @@ public class Security implements ActionPlugin, IngestPlugin {
return modules;
}
modules.add(new AuthenticationModule(settings));
modules.add(new AuthorizationModule(settings));
if (enabled == false || auditingEnabled(settings) == false) {
modules.add(b -> {
b.bind(AuditTrailService.class).toProvider(Providers.of(null));
b.bind(AuditTrail.class).toInstance(AuditTrail.NOOP);
});
}
modules.add(b -> XPackPlugin.bindFeatureSet(b, SecurityFeatureSet.class));
if (enabled == false) {
modules.add(b -> {
b.bind(CryptoService.class).toProvider(Providers.of(null));
b.bind(Realms.class).toProvider(Providers.of(null)); // for SecurityFeatureSet
b.bind(CompositeRolesStore.class).toProvider(Providers.of(null)); // for SecurityFeatureSet
b.bind(AuditTrailService.class)
.toInstance(new AuditTrailService(settings, Collections.emptyList(), securityLicenseState));
});
modules.add(new SecurityModule(settings));
modules.add(new SecurityTransportModule(settings));
return modules;
}
@ -221,32 +217,20 @@ public class Security implements ActionPlugin, IngestPlugin {
if (auditingEnabled(settings)) {
b.bind(AuditTrail.class).to(AuditTrailService.class); // interface used by some actions...
}
if (indexAuditLoggingEnabled(settings) == false) {
// TODO: remove this once we can construct SecurityLifecycleService without guice
b.bind(IndexAuditTrail.class).toProvider(Providers.of(null));
}
});
modules.add(new SecurityModule(settings));
modules.add(new SecurityRestModule(settings));
modules.add(new SecurityActionModule(settings));
modules.add(new SecurityTransportModule(settings));
return modules;
}
public Collection<Class<? extends LifecycleComponent>> nodeServices() {
if (enabled == false || transportClientMode == true) {
return Collections.emptyList();
}
List<Class<? extends LifecycleComponent>> list = new ArrayList<>();
list.add(FileRolesStore.class);
return list;
}
public Collection<Object> createComponents(InternalClient client, ThreadPool threadPool, ClusterService clusterService,
ResourceWatcherService resourceWatcherService, List<XPackExtension> extensions) {
if (enabled == false) {
return Collections.emptyList();
}
AnonymousUser.initialize(settings); // TODO: this is sketchy...testing is difficult b/c it is static....
List<Object> components = new ArrayList<>();
final SecurityContext securityContext = new SecurityContext(settings, threadPool, cryptoService);
components.add(securityContext);
@ -280,29 +264,69 @@ public class Security implements ActionPlugin, IngestPlugin {
components.add(realms);
// audit trails construction
IndexAuditTrail indexAuditTrail = null;
Set<AuditTrail> auditTrails = new LinkedHashSet<>();
if (AUDIT_ENABLED_SETTING.get(settings)) {
List<String> outputs = AUDIT_OUTPUTS_SETTING.get(settings);
if (outputs.isEmpty()) {
throw new IllegalArgumentException("Audit logging is enabled but there are zero output types in "
+ AUDIT_ENABLED_SETTING.getKey());
}
Set<AuditTrail> auditTrails = new LinkedHashSet<>();
for (String output : outputs) {
switch (output) {
case LoggingAuditTrail.NAME:
auditTrails.add(new LoggingAuditTrail(settings, clusterService, threadPool));
break;
case IndexAuditTrail.NAME:
IndexAuditTrail indexAuditTrail = new IndexAuditTrail(settings, client, threadPool, clusterService);
indexAuditTrail = new IndexAuditTrail(settings, client, threadPool, clusterService);
auditTrails.add(indexAuditTrail);
components.add(indexAuditTrail); // SecurityLifecycleService needs this....
break;
default:
throw new IllegalArgumentException("Unknown audit trail output [" + output + "]");
}
}
components.add(new AuditTrailService(settings, auditTrails.stream().collect(Collectors.toList()), securityLicenseState));
}
final AuditTrailService auditTrailService =
new AuditTrailService(settings, auditTrails.stream().collect(Collectors.toList()), securityLicenseState);
components.add(auditTrailService);
AuthenticationFailureHandler failureHandler = null;
String extensionName = null;
for (XPackExtension extension : extensions) {
AuthenticationFailureHandler extensionFailureHandler = extension.getAuthenticationFailureHandler();
if (extensionFailureHandler != null && failureHandler != null) {
throw new IllegalStateException("Extensions [" + extensionName +"] and [" + extension.name() + "] " +
"both set an authentication failure handler");
}
failureHandler = extensionFailureHandler;
extensionName = extension.name();
}
if (failureHandler == null) {
logger.debug("Using default authentication failure handler");
failureHandler = new DefaultAuthenticationFailureHandler();
} else {
logger.debug("Using authentication failure handler from extension [" + extensionName + "]");
}
final AuthenticationService authcService = new AuthenticationService(settings, realms, auditTrailService,
cryptoService, failureHandler, threadPool);
components.add(authcService);
final FileRolesStore fileRolesStore = new FileRolesStore(settings, env, resourceWatcherService);
final NativeRolesStore nativeRolesStore = new NativeRolesStore(settings, client, threadPool);
final ReservedRolesStore reservedRolesStore = new ReservedRolesStore(securityContext);
final CompositeRolesStore allRolesStore = new CompositeRolesStore(fileRolesStore, nativeRolesStore, reservedRolesStore);
final AuthorizationService authzService = new AuthorizationService(settings, allRolesStore, clusterService,
auditTrailService, failureHandler, threadPool);
components.add(fileRolesStore); // has lifecycle
components.add(nativeRolesStore); // used by roles actions
components.add(reservedRolesStore); // used by roles actions
components.add(allRolesStore); // for SecurityFeatureSet
components.add(authzService);
components.add(new SecurityLifecycleService(settings, clusterService, threadPool, indexAuditTrail,
nativeUsersStore, nativeRolesStore, client));
return components;
}
@ -361,8 +385,8 @@ public class Security implements ActionPlugin, IngestPlugin {
Realms.addSettings(settingsList);
NativeUsersStore.addSettings(settingsList);
NativeRolesStore.addSettings(settingsList);
InternalAuthenticationService.addSettings(settingsList);
InternalAuthorizationService.addSettings(settingsList);
AuthenticationService.addSettings(settingsList);
AuthorizationService.addSettings(settingsList);
// HTTP settings
SecurityNetty3HttpServerTransport.addSettings(settingsList);
@ -406,7 +430,7 @@ public class Security implements ActionPlugin, IngestPlugin {
module.setSearcherWrapper((indexService) -> new SecurityIndexSearcherWrapper(indexService.getIndexSettings(),
indexService.newQueryShardContext(), indexService.mapperService(),
indexService.cache().bitsetFilterCache(), indexService.getIndexServices().getThreadPool().getThreadContext(),
securityLicenseState));
securityLicenseState, indexService.getIndexServices().getScriptService()));
}
if (transportClientMode == false) {
/* We need to forcefully overwrite the query cache implementation to use security's opt out query cache implementation.

View File

@ -13,7 +13,7 @@ import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.util.concurrent.ThreadContext;
import org.elasticsearch.threadpool.ThreadPool;
import org.elasticsearch.xpack.security.authc.Authentication;
import org.elasticsearch.xpack.security.authc.InternalAuthenticationService;
import org.elasticsearch.xpack.security.authc.AuthenticationService;
import org.elasticsearch.xpack.security.crypto.CryptoService;
import org.elasticsearch.xpack.security.user.User;
@ -36,7 +36,7 @@ public class SecurityContext {
this.logger = Loggers.getLogger(getClass(), settings);
this.threadContext = threadPool.getThreadContext();
this.cryptoService = cryptoService;
this.signUserHeader = InternalAuthenticationService.SIGN_USER_HEADER.get(settings);
this.signUserHeader = AuthenticationService.SIGN_USER_HEADER.get(settings);
}
/** Returns the current user information, or null if the current request has no authentication info. */

View File

@ -18,6 +18,7 @@ import org.elasticsearch.xpack.security.authc.Realm;
import org.elasticsearch.xpack.security.authc.Realms;
import org.elasticsearch.xpack.security.authc.esnative.ReservedRealm;
import org.elasticsearch.xpack.XPackFeatureSet;
import org.elasticsearch.xpack.security.authz.store.CompositeRolesStore;
import org.elasticsearch.xpack.security.authz.store.RolesStore;
import org.elasticsearch.xpack.security.crypto.CryptoService;
import org.elasticsearch.xpack.security.transport.filter.IPFilter;
@ -43,7 +44,7 @@ public class SecurityFeatureSet implements XPackFeatureSet {
@Nullable
private final Realms realms;
@Nullable
private final RolesStore rolesStore;
private final CompositeRolesStore rolesStore;
@Nullable
private final IPFilter ipFilter;
@Nullable
@ -52,8 +53,8 @@ public class SecurityFeatureSet implements XPackFeatureSet {
private final CryptoService cryptoService;
@Inject
public SecurityFeatureSet(Settings settings, @Nullable SecurityLicenseState licenseState,
@Nullable Realms realms, NamedWriteableRegistry namedWriteableRegistry, @Nullable RolesStore rolesStore,
public SecurityFeatureSet(Settings settings, @Nullable SecurityLicenseState licenseState, @Nullable Realms realms,
NamedWriteableRegistry namedWriteableRegistry, @Nullable CompositeRolesStore rolesStore,
@Nullable IPFilter ipFilter, @Nullable AuditTrailService auditTrailService,
@Nullable CryptoService cryptoService) {
this.enabled = Security.enabled(settings);

View File

@ -6,7 +6,6 @@
package org.elasticsearch.xpack.security;
import org.elasticsearch.license.core.License.OperationMode;
import org.elasticsearch.license.plugin.core.LicenseState;
import org.elasticsearch.license.plugin.core.Licensee.Status;
@ -53,7 +52,7 @@ public class SecurityLicenseState {
* @return true if the license allows for the stats and health APIs to be used.
*/
public boolean statsAndHealthEnabled() {
return status.getLicenseState() != LicenseState.DISABLED;
return status.isActive();
}
/**

View File

@ -6,18 +6,17 @@
package org.elasticsearch.xpack.security;
import org.elasticsearch.cluster.ClusterChangedEvent;
import org.elasticsearch.cluster.service.ClusterService;
import org.elasticsearch.cluster.ClusterStateListener;
import org.elasticsearch.cluster.service.ClusterService;
import org.elasticsearch.common.component.AbstractComponent;
import org.elasticsearch.common.component.LifecycleListener;
import org.elasticsearch.common.inject.Inject;
import org.elasticsearch.common.inject.internal.Nullable;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.util.concurrent.AbstractRunnable;
import org.elasticsearch.threadpool.ThreadPool;
import org.elasticsearch.xpack.security.audit.index.IndexAuditTrail;
import org.elasticsearch.xpack.security.authc.esnative.NativeUsersStore;
import org.elasticsearch.xpack.security.authz.store.NativeRolesStore;
import org.elasticsearch.threadpool.ThreadPool;
/**
* This class is used to provide a lifecycle for services that is based on the cluster's state
@ -38,7 +37,6 @@ public class SecurityLifecycleService extends AbstractComponent implements Clust
private final NativeUsersStore nativeUserStore;
private final NativeRolesStore nativeRolesStore;
@Inject
public SecurityLifecycleService(Settings settings, ClusterService clusterService, ThreadPool threadPool,
@Nullable IndexAuditTrail indexAuditTrail, NativeUsersStore nativeUserStore,
NativeRolesStore nativeRolesStore, InternalClient client) {

View File

@ -1,34 +0,0 @@
/*
* 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.security;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.xpack.XPackPlugin;
import org.elasticsearch.xpack.security.support.AbstractSecurityModule;
/**
*
*/
public class SecurityModule extends AbstractSecurityModule {
public SecurityModule(Settings settings) {
super(settings);
}
@Override
protected void configure(boolean clientMode) {
if (clientMode) {
return;
}
XPackPlugin.bindFeatureSet(binder(), SecurityFeatureSet.class);
if (securityEnabled) {
bind(SecurityLifecycleService.class).asEagerSingleton();
}
}
}

View File

@ -22,13 +22,14 @@ import org.elasticsearch.license.plugin.core.LicenseUtils;
import org.elasticsearch.xpack.security.Security;
import org.elasticsearch.xpack.security.SecurityContext;
import org.elasticsearch.xpack.security.action.SecurityActionMapper;
import org.elasticsearch.xpack.security.audit.AuditTrailService;
import org.elasticsearch.xpack.security.authc.Authentication;
import org.elasticsearch.xpack.security.authc.AuthenticationService;
import org.elasticsearch.xpack.security.authz.AuthorizationService;
import org.elasticsearch.xpack.security.user.SystemUser;
import org.elasticsearch.xpack.security.user.User;
import org.elasticsearch.xpack.security.action.interceptor.RequestInterceptor;
import org.elasticsearch.xpack.security.audit.AuditTrail;
import org.elasticsearch.xpack.security.authc.AuthenticationService;
import org.elasticsearch.xpack.security.authz.AuthorizationService;
import org.elasticsearch.xpack.security.authz.AuthorizationUtils;
import org.elasticsearch.xpack.security.authz.privilege.HealthAndStatsPrivilege;
import org.elasticsearch.xpack.security.crypto.CryptoService;
@ -60,7 +61,7 @@ public class SecurityActionFilter extends AbstractComponent implements ActionFil
@Inject
public SecurityActionFilter(Settings settings, AuthenticationService authcService, AuthorizationService authzService,
CryptoService cryptoService, AuditTrail auditTrail, SecurityLicenseState licenseState,
CryptoService cryptoService, AuditTrailService auditTrail, SecurityLicenseState licenseState,
SecurityActionMapper actionMapper, Set<RequestInterceptor> requestInterceptors, ThreadPool threadPool,
SecurityContext securityContext) {
super(settings);

View File

@ -15,7 +15,7 @@ import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.util.concurrent.ThreadContext;
import org.elasticsearch.rest.RestStatus;
import org.elasticsearch.xpack.security.user.User;
import org.elasticsearch.xpack.security.authz.InternalAuthorizationService;
import org.elasticsearch.xpack.security.authz.AuthorizationService;
import org.elasticsearch.xpack.security.authz.accesscontrol.IndicesAccessControl;
import org.elasticsearch.threadpool.ThreadPool;
import org.elasticsearch.transport.TransportRequest;
@ -34,7 +34,7 @@ public class BulkRequestInterceptor extends AbstractComponent implements Request
}
public void intercept(BulkRequest request, User user) {
IndicesAccessControl indicesAccessControl = threadContext.getTransient(InternalAuthorizationService.INDICES_PERMISSIONS_KEY);
IndicesAccessControl indicesAccessControl = threadContext.getTransient(AuthorizationService.INDICES_PERMISSIONS_KEY);
for (IndicesRequest indicesRequest : request.subRequests()) {
for (String index : indicesRequest.indices()) {
IndicesAccessControl.IndexAccessControl indexAccessControl = indicesAccessControl.getIndexPermissions(index);

View File

@ -12,7 +12,7 @@ import org.elasticsearch.common.logging.LoggerMessageFormat;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.util.concurrent.ThreadContext;
import org.elasticsearch.xpack.security.user.User;
import org.elasticsearch.xpack.security.authz.InternalAuthorizationService;
import org.elasticsearch.xpack.security.authz.AuthorizationService;
import org.elasticsearch.xpack.security.authz.accesscontrol.IndicesAccessControl;
import java.util.Collections;
@ -42,7 +42,7 @@ public abstract class FieldAndDocumentLevelSecurityRequestInterceptor<Request> e
throw new IllegalArgumentException(LoggerMessageFormat.format("Expected a request of type [{}] or [{}] but got [{}] instead",
CompositeIndicesRequest.class, IndicesRequest.class, request.getClass()));
}
IndicesAccessControl indicesAccessControl = threadContext.getTransient(InternalAuthorizationService.INDICES_PERMISSIONS_KEY);
IndicesAccessControl indicesAccessControl = threadContext.getTransient(AuthorizationService.INDICES_PERMISSIONS_KEY);
for (IndicesRequest indicesRequest : indicesRequests) {
for (String index : indicesRequest.indices()) {
IndicesAccessControl.IndexAccessControl indexAccessControl = indicesAccessControl.getIndexPermissions(index);

View File

@ -18,88 +18,6 @@ import java.net.InetAddress;
*/
public interface AuditTrail {
AuditTrail NOOP = new AuditTrail() {
static final String NAME = "noop";
@Override
public String name() {
return NAME;
}
@Override
public void anonymousAccessDenied(String action, TransportMessage message) {
}
@Override
public void anonymousAccessDenied(RestRequest request) {
}
@Override
public void authenticationFailed(RestRequest request) {
}
@Override
public void authenticationFailed(String action, TransportMessage message) {
}
@Override
public void authenticationFailed(AuthenticationToken token, String action, TransportMessage message) {
}
@Override
public void authenticationFailed(AuthenticationToken token, RestRequest request) {
}
@Override
public void authenticationFailed(String realm, AuthenticationToken token, String action, TransportMessage message) {
}
@Override
public void authenticationFailed(String realm, AuthenticationToken token, RestRequest request) {
}
@Override
public void accessGranted(User user, String action, TransportMessage message) {
}
@Override
public void accessDenied(User user, String action, TransportMessage message) {
}
@Override
public void tamperedRequest(RestRequest request) {
}
@Override
public void tamperedRequest(String action, TransportMessage message) {
}
@Override
public void tamperedRequest(User user, String action, TransportMessage request) {
}
@Override
public void connectionGranted(InetAddress inetAddress, String profile, SecurityIpFilterRule rule) {
}
@Override
public void connectionDenied(InetAddress inetAddress, String profile, SecurityIpFilterRule rule) {
}
@Override
public void runAsGranted(User user, String action, TransportMessage message) {
}
@Override
public void runAsDenied(User user, String action, TransportMessage message) {
}
@Override
public void runAsDenied(User user, RestRequest request) {
}
};
String name();
void anonymousAccessDenied(String action, TransportMessage message);

View File

@ -1,46 +0,0 @@
/*
* 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.security.authc;
import org.elasticsearch.common.inject.util.Providers;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.xpack.security.support.AbstractSecurityModule;
import org.elasticsearch.xpack.security.user.AnonymousUser;
/**
*
*/
public class AuthenticationModule extends AbstractSecurityModule.Node {
private Class<? extends AuthenticationFailureHandler> authcFailureHandler = null;
public AuthenticationModule(Settings settings) {
super(settings);
}
@Override
protected void configureNode() {
if (!securityEnabled) {
bind(Realms.class).toProvider(Providers.of(null));
return;
}
AnonymousUser.initialize(settings);
if (authcFailureHandler == null) {
bind(AuthenticationFailureHandler.class).to(DefaultAuthenticationFailureHandler.class).asEagerSingleton();
} else {
bind(AuthenticationFailureHandler.class).to(authcFailureHandler).asEagerSingleton();
}
bind(AuthenticationService.class).to(InternalAuthenticationService.class).asEagerSingleton();
}
/**
* Sets the {@link AuthenticationFailureHandler} to the specified implementation
*/
public void setAuthenticationFailureHandler(Class<? extends AuthenticationFailureHandler> clazz) {
this.authcFailureHandler = clazz;
}
}

View File

@ -5,17 +5,64 @@
*/
package org.elasticsearch.xpack.security.authc;
import java.io.IOException;
import java.util.List;
import org.elasticsearch.ElasticsearchSecurityException;
import org.elasticsearch.common.Nullable;
import org.elasticsearch.common.Strings;
import org.elasticsearch.common.component.AbstractComponent;
import org.elasticsearch.common.settings.Setting;
import org.elasticsearch.common.settings.Setting.Property;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.util.concurrent.ThreadContext;
import org.elasticsearch.node.Node;
import org.elasticsearch.rest.RestRequest;
import org.elasticsearch.xpack.security.audit.AuditTrailService;
import org.elasticsearch.xpack.security.authc.Authentication.RealmRef;
import org.elasticsearch.xpack.security.user.AnonymousUser;
import org.elasticsearch.xpack.security.user.User;
import org.elasticsearch.xpack.security.audit.AuditTrail;
import org.elasticsearch.xpack.security.crypto.CryptoService;
import org.elasticsearch.threadpool.ThreadPool;
import org.elasticsearch.transport.TransportMessage;
import java.io.IOException;
import static org.elasticsearch.xpack.security.Security.setting;
/**
* Responsible for authenticating the Users behind requests
* An authentication service that delegates the authentication process to its configured {@link Realm realms}.
* This service also supports request level caching of authenticated users (i.e. once a user authenticated
* successfully, it is set on the request context to avoid subsequent redundant authentication process)
*/
public interface AuthenticationService {
public class AuthenticationService extends AbstractComponent {
public static final Setting<Boolean> SIGN_USER_HEADER =
Setting.boolSetting(setting("authc.sign_user_header"), true, Property.NodeScope);
public static final Setting<Boolean> RUN_AS_ENABLED =
Setting.boolSetting(setting("authc.run_as.enabled"), true, Property.NodeScope);
public static final String RUN_AS_USER_HEADER = "es-security-runas-user";
private final Realms realms;
private final AuditTrail auditTrail;
private final CryptoService cryptoService;
private final AuthenticationFailureHandler failureHandler;
private final ThreadContext threadContext;
private final String nodeName;
private final boolean signUserHeader;
private final boolean runAsEnabled;
public AuthenticationService(Settings settings, Realms realms, AuditTrailService auditTrail, CryptoService cryptoService,
AuthenticationFailureHandler failureHandler, ThreadPool threadPool) {
super(settings);
this.nodeName = Node.NODE_NAME_SETTING.get(settings);
this.realms = realms;
this.auditTrail = auditTrail;
this.cryptoService = cryptoService;
this.failureHandler = failureHandler;
this.threadContext = threadPool.getThreadContext();
this.signUserHeader = SIGN_USER_HEADER.get(settings);
this.runAsEnabled = RUN_AS_ENABLED.get(settings);
}
/**
* Authenticates the user that is associated with the given request. If the user was authenticated successfully (i.e.
@ -28,7 +75,9 @@ public interface AuthenticationService {
* user credentials were found to be invalid
* @throws IOException If an error occurs when reading or writing
*/
Authentication authenticate(RestRequest request) throws IOException, ElasticsearchSecurityException;
public Authentication authenticate(RestRequest request) throws IOException, ElasticsearchSecurityException {
return createAuthenticator(request).authenticate();
}
/**
* Authenticates the user that is associated with the given message. If the user was authenticated successfully (i.e.
@ -47,9 +96,11 @@ public interface AuthenticationService {
*
* @throws ElasticsearchSecurityException If the associated user credentials were found to be invalid or in the
* case where there was no user associated with the request, if the defautl
* token could not be authenticated.
* token could not be authenticated.
*/
Authentication authenticate(String action, TransportMessage message, User fallbackUser) throws IOException;
public Authentication authenticate(String action, TransportMessage message, User fallbackUser) throws IOException {
return createAuthenticator(action, message, fallbackUser).authenticate();
}
/**
* Checks if there's already a user header attached to the given message. If missing, a new header is
@ -57,5 +108,310 @@ public interface AuthenticationService {
*
* @param user The user to be attached if the header is missing
*/
void attachUserIfMissing(User user) throws IOException, IllegalArgumentException;
public void attachUserIfMissing(User user) throws IOException {
Authentication authentication = new Authentication(user, new RealmRef("__attach", "__attach", nodeName), null);
authentication.writeToContextIfMissing(threadContext, cryptoService, signUserHeader);
}
Authenticator createAuthenticator(RestRequest request) {
return new Authenticator(request);
}
Authenticator createAuthenticator(String action, TransportMessage message, User fallbackUser) {
return new Authenticator(action, message, fallbackUser);
}
class Authenticator {
private final AuditableRequest request;
private final User fallbackUser;
private RealmRef authenticatedBy = null;
private RealmRef lookedupBy = null;
Authenticator(RestRequest request) {
this.request = new Rest(request);
this.fallbackUser = null;
}
Authenticator(String action, TransportMessage message, User fallbackUser) {
this.request = new Transport(action, message);
this.fallbackUser = fallbackUser;
}
Authentication authenticate() throws IOException, IllegalArgumentException {
Authentication existing = getCurrentAuthentication();
if (existing != null) {
return existing;
}
AuthenticationToken token = extractToken();
if (token == null) {
return handleNullToken();
}
User user = authenticateToken(token);
if (user == null) {
throw handleNullUser(token);
}
user = lookupRunAsUserIfNecessary(user, token);
final Authentication authentication = new Authentication(user, authenticatedBy, lookedupBy);
authentication.writeToContext(threadContext, cryptoService, signUserHeader);
return authentication;
}
Authentication getCurrentAuthentication() {
Authentication authentication;
try {
authentication = Authentication.readFromContext(threadContext, cryptoService, signUserHeader);
} catch (Exception e) {
throw request.tamperedRequest();
}
// make sure this isn't a rest request since we don't allow authentication to be read via a HTTP request...
if (authentication != null && request instanceof Rest) {
throw request.tamperedRequest();
}
return authentication;
}
AuthenticationToken extractToken() {
AuthenticationToken token = null;
try {
for (Realm realm : realms) {
token = realm.token(threadContext);
if (token != null) {
logger.trace("realm [{}] resolved authentication token [{}] from [{}]", realm, token.principal(), request);
break;
}
}
} catch (Exception e) {
if (logger.isDebugEnabled()) {
logger.debug("failed to extract token from request: [{}]", e, request);
} else {
logger.warn("failed to extract token from request: [{}]: {}", request, e.getMessage());
}
throw request.exceptionProcessingRequest(e, null);
}
return token;
}
Authentication handleNullToken() throws IOException {
Authentication authentication = null;
if (fallbackUser != null) {
RealmRef authenticatedBy = new RealmRef("__fallback", "__fallback", nodeName);
authentication = new Authentication(fallbackUser, authenticatedBy, null);
} else if (AnonymousUser.enabled()) {
RealmRef authenticatedBy = new RealmRef("__anonymous", "__anonymous", nodeName);
authentication = new Authentication(AnonymousUser.INSTANCE, authenticatedBy, null);
}
if (authentication != null) {
authentication.writeToContext(threadContext, cryptoService, signUserHeader);
return authentication;
}
throw request.anonymousAccessDenied();
}
User authenticateToken(AuthenticationToken token) {
User user = null;
try {
for (Realm realm : realms) {
if (realm.supports(token)) {
user = realm.authenticate(token);
if (user != null) {
authenticatedBy = new RealmRef(realm.name(), realm.type(), nodeName);
break;
}
request.realmAuthenticationFailed(token, realm.name());
}
}
} catch (Exception e) {
logger.debug("authentication failed for principal [{}], [{}] ", e, token.principal(), request);
throw request.exceptionProcessingRequest(e, token);
} finally {
token.clearCredentials();
}
return user;
}
ElasticsearchSecurityException handleNullUser(AuthenticationToken token) {
throw request.authenticationFailed(token);
}
boolean shouldTryToRunAs(User authenticatedUser, AuthenticationToken token) {
if (runAsEnabled == false) {
return false;
}
String runAsUsername = threadContext.getHeader(RUN_AS_USER_HEADER);
if (runAsUsername == null) {
return false;
}
if (runAsUsername.isEmpty()) {
logger.debug("user [{}] attempted to runAs with an empty username", authenticatedUser.principal());
throw request.runAsDenied(new User(authenticatedUser.principal(), authenticatedUser.roles(),
new User(runAsUsername, Strings.EMPTY_ARRAY)), token);
}
return true;
}
User lookupRunAsUserIfNecessary(User authenticatedUser, AuthenticationToken token) {
User user = authenticatedUser;
if (shouldTryToRunAs(user, token) == false) {
return user;
}
final String runAsUsername = threadContext.getHeader(RUN_AS_USER_HEADER);
try {
for (Realm realm : realms) {
if (realm.userLookupSupported()) {
User runAsUser = realm.lookupUser(runAsUsername);
if (runAsUser != null) {
lookedupBy = new RealmRef(realm.name(), realm.type(), nodeName);
user = new User(user.principal(), user.roles(), runAsUser);
return user;
}
}
}
// the requested run as user does not exist, but we don't throw an error here otherwise this could let
// information leak about users in the system... instead we'll just let the authz service fail throw an
// authorization error
user = new User(user.principal(), user.roles(), new User(runAsUsername, Strings.EMPTY_ARRAY));
} catch (Exception e) {
logger.debug("run as failed for principal [{}], [{}], run as username [{}]", e, token.principal(), request, runAsUsername);
throw request.exceptionProcessingRequest(e, token);
}
return user;
}
abstract class AuditableRequest {
abstract void realmAuthenticationFailed(AuthenticationToken token, String realm);
abstract ElasticsearchSecurityException tamperedRequest();
abstract ElasticsearchSecurityException exceptionProcessingRequest(Exception e, @Nullable AuthenticationToken token);
abstract ElasticsearchSecurityException authenticationFailed(AuthenticationToken token);
abstract ElasticsearchSecurityException anonymousAccessDenied();
abstract ElasticsearchSecurityException runAsDenied(User user, AuthenticationToken token);
}
class Transport extends AuditableRequest {
private final String action;
private final TransportMessage message;
Transport(String action, TransportMessage message) {
this.action = action;
this.message = message;
}
@Override
void realmAuthenticationFailed(AuthenticationToken token, String realm) {
auditTrail.authenticationFailed(realm, token, action, message);
}
@Override
ElasticsearchSecurityException tamperedRequest() {
auditTrail.tamperedRequest(action, message);
return new ElasticsearchSecurityException("failed to verify signed authentication information");
}
@Override
ElasticsearchSecurityException exceptionProcessingRequest(Exception e, @Nullable AuthenticationToken token) {
if (token != null) {
auditTrail.authenticationFailed(token, action, message);
} else {
auditTrail.authenticationFailed(action, message);
}
return failureHandler.exceptionProcessingRequest(message, action, e, threadContext);
}
@Override
ElasticsearchSecurityException authenticationFailed(AuthenticationToken token) {
auditTrail.authenticationFailed(token, action, message);
return failureHandler.failedAuthentication(message, token, action, threadContext);
}
@Override
ElasticsearchSecurityException anonymousAccessDenied() {
auditTrail.anonymousAccessDenied(action, message);
return failureHandler.missingToken(message, action, threadContext);
}
@Override
ElasticsearchSecurityException runAsDenied(User user, AuthenticationToken token) {
auditTrail.runAsDenied(user, action, message);
return failureHandler.failedAuthentication(message, token, action, threadContext);
}
public String toString() {
return "transport request action [" + action + "]";
}
}
class Rest extends AuditableRequest {
private final RestRequest request;
Rest(RestRequest request) {
this.request = request;
}
@Override
void realmAuthenticationFailed(AuthenticationToken token, String realm) {
auditTrail.authenticationFailed(realm, token, request);
}
@Override
ElasticsearchSecurityException tamperedRequest() {
auditTrail.tamperedRequest(request);
return new ElasticsearchSecurityException("rest request attempted to inject a user");
}
@Override
ElasticsearchSecurityException exceptionProcessingRequest(Exception e, @Nullable AuthenticationToken token) {
if (token != null) {
auditTrail.authenticationFailed(token, request);
} else {
auditTrail.authenticationFailed(request);
}
return failureHandler.exceptionProcessingRequest(request, e, threadContext);
}
@Override
ElasticsearchSecurityException authenticationFailed(AuthenticationToken token) {
auditTrail.authenticationFailed(token, request);
return failureHandler.failedAuthentication(request, token, threadContext);
}
@Override
ElasticsearchSecurityException anonymousAccessDenied() {
auditTrail.anonymousAccessDenied(request);
return failureHandler.missingToken(request, threadContext);
}
@Override
ElasticsearchSecurityException runAsDenied(User user, AuthenticationToken token) {
auditTrail.runAsDenied(user, request);
return failureHandler.failedAuthentication(request, token, threadContext);
}
public String toString() {
return "rest request uri [" + request.uri() + "]";
}
}
}
public static void addSettings(List<Setting<?>> settings) {
settings.add(SIGN_USER_HEADER);
settings.add(RUN_AS_ENABLED);
}
}

View File

@ -1,386 +0,0 @@
/*
* 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.security.authc;
import org.elasticsearch.ElasticsearchSecurityException;
import org.elasticsearch.common.Nullable;
import org.elasticsearch.common.Strings;
import org.elasticsearch.common.component.AbstractComponent;
import org.elasticsearch.common.inject.Inject;
import org.elasticsearch.common.settings.Setting;
import org.elasticsearch.common.settings.Setting.Property;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.util.concurrent.ThreadContext;
import org.elasticsearch.rest.RestController;
import org.elasticsearch.node.Node;
import org.elasticsearch.rest.RestRequest;
import org.elasticsearch.xpack.security.authc.Authentication.RealmRef;
import org.elasticsearch.xpack.security.user.AnonymousUser;
import org.elasticsearch.xpack.security.user.User;
import org.elasticsearch.xpack.security.audit.AuditTrail;
import org.elasticsearch.xpack.security.crypto.CryptoService;
import org.elasticsearch.threadpool.ThreadPool;
import org.elasticsearch.transport.TransportMessage;
import java.io.IOException;
import java.util.List;
import static org.elasticsearch.xpack.security.Security.setting;
/**
* An authentication service that delegates the authentication process to its configured {@link Realm realms}.
* This service also supports request level caching of authenticated users (i.e. once a user authenticated
* successfully, it is set on the request context to avoid subsequent redundant authentication process)
*/
public class InternalAuthenticationService extends AbstractComponent implements AuthenticationService {
public static final Setting<Boolean> SIGN_USER_HEADER =
Setting.boolSetting(setting("authc.sign_user_header"), true, Property.NodeScope);
public static final Setting<Boolean> RUN_AS_ENABLED =
Setting.boolSetting(setting("authc.run_as.enabled"), true, Property.NodeScope);
public static final String RUN_AS_USER_HEADER = "es-security-runas-user";
private final Realms realms;
private final AuditTrail auditTrail;
private final CryptoService cryptoService;
private final AuthenticationFailureHandler failureHandler;
private final ThreadContext threadContext;
private final String nodeName;
private final boolean signUserHeader;
private final boolean runAsEnabled;
@Inject
public InternalAuthenticationService(Settings settings, Realms realms, AuditTrail auditTrail, CryptoService cryptoService,
AuthenticationFailureHandler failureHandler, ThreadPool threadPool) {
super(settings);
this.nodeName = Node.NODE_NAME_SETTING.get(settings);
this.realms = realms;
this.auditTrail = auditTrail;
this.cryptoService = cryptoService;
this.failureHandler = failureHandler;
this.threadContext = threadPool.getThreadContext();
this.signUserHeader = SIGN_USER_HEADER.get(settings);
this.runAsEnabled = RUN_AS_ENABLED.get(settings);
}
@Override
public Authentication authenticate(RestRequest request) throws IOException, ElasticsearchSecurityException {
return createAuthenticator(request).authenticate();
}
@Override
public Authentication authenticate(String action, TransportMessage message, User fallbackUser) throws IOException {
return createAuthenticator(action, message, fallbackUser).authenticate();
}
@Override
public void attachUserIfMissing(User user) throws IOException {
Authentication authentication = new Authentication(user, new RealmRef("__attach", "__attach", nodeName), null);
authentication.writeToContextIfMissing(threadContext, cryptoService, signUserHeader);
}
Authenticator createAuthenticator(RestRequest request) {
return new Authenticator(request);
}
Authenticator createAuthenticator(String action, TransportMessage message, User fallbackUser) {
return new Authenticator(action, message, fallbackUser);
}
class Authenticator {
private final AuditableRequest request;
private final User fallbackUser;
private RealmRef authenticatedBy = null;
private RealmRef lookedupBy = null;
Authenticator(RestRequest request) {
this.request = new Rest(request);
this.fallbackUser = null;
}
Authenticator(String action, TransportMessage message, User fallbackUser) {
this.request = new Transport(action, message);
this.fallbackUser = fallbackUser;
}
Authentication authenticate() throws IOException, IllegalArgumentException {
Authentication existing = getCurrentAuthentication();
if (existing != null) {
return existing;
}
AuthenticationToken token = extractToken();
if (token == null) {
return handleNullToken();
}
User user = authenticateToken(token);
if (user == null) {
throw handleNullUser(token);
}
user = lookupRunAsUserIfNecessary(user, token);
final Authentication authentication = new Authentication(user, authenticatedBy, lookedupBy);
authentication.writeToContext(threadContext, cryptoService, signUserHeader);
return authentication;
}
Authentication getCurrentAuthentication() {
Authentication authentication;
try {
authentication = Authentication.readFromContext(threadContext, cryptoService, signUserHeader);
} catch (Exception e) {
throw request.tamperedRequest();
}
// make sure this isn't a rest request since we don't allow authentication to be read via a HTTP request...
if (authentication != null && request instanceof Rest) {
throw request.tamperedRequest();
}
return authentication;
}
AuthenticationToken extractToken() {
AuthenticationToken token = null;
try {
for (Realm realm : realms) {
token = realm.token(threadContext);
if (token != null) {
logger.trace("realm [{}] resolved authentication token [{}] from [{}]", realm, token.principal(), request);
break;
}
}
} catch (Exception e) {
if (logger.isDebugEnabled()) {
logger.debug("failed to extract token from request: [{}]", e, request);
} else {
logger.warn("failed to extract token from request: [{}]: {}", request, e.getMessage());
}
throw request.exceptionProcessingRequest(e, null);
}
return token;
}
Authentication handleNullToken() throws IOException {
Authentication authentication = null;
if (fallbackUser != null) {
RealmRef authenticatedBy = new RealmRef("__fallback", "__fallback", nodeName);
authentication = new Authentication(fallbackUser, authenticatedBy, null);
} else if (AnonymousUser.enabled()) {
RealmRef authenticatedBy = new RealmRef("__anonymous", "__anonymous", nodeName);
authentication = new Authentication(AnonymousUser.INSTANCE, authenticatedBy, null);
}
if (authentication != null) {
authentication.writeToContext(threadContext, cryptoService, signUserHeader);
return authentication;
}
throw request.anonymousAccessDenied();
}
User authenticateToken(AuthenticationToken token) {
User user = null;
try {
for (Realm realm : realms) {
if (realm.supports(token)) {
user = realm.authenticate(token);
if (user != null) {
authenticatedBy = new RealmRef(realm.name(), realm.type(), nodeName);
break;
}
request.realmAuthenticationFailed(token, realm.name());
}
}
} catch (Exception e) {
logger.debug("authentication failed for principal [{}], [{}] ", e, token.principal(), request);
throw request.exceptionProcessingRequest(e, token);
} finally {
token.clearCredentials();
}
return user;
}
ElasticsearchSecurityException handleNullUser(AuthenticationToken token) {
throw request.authenticationFailed(token);
}
boolean shouldTryToRunAs(User authenticatedUser, AuthenticationToken token) {
if (runAsEnabled == false) {
return false;
}
String runAsUsername = threadContext.getHeader(RUN_AS_USER_HEADER);
if (runAsUsername == null) {
return false;
}
if (runAsUsername.isEmpty()) {
logger.debug("user [{}] attempted to runAs with an empty username", authenticatedUser.principal());
throw request.runAsDenied(new User(authenticatedUser.principal(), authenticatedUser.roles(),
new User(runAsUsername, Strings.EMPTY_ARRAY)), token);
}
return true;
}
User lookupRunAsUserIfNecessary(User authenticatedUser, AuthenticationToken token) {
User user = authenticatedUser;
if (shouldTryToRunAs(user, token) == false) {
return user;
}
final String runAsUsername = threadContext.getHeader(RUN_AS_USER_HEADER);
try {
for (Realm realm : realms) {
if (realm.userLookupSupported()) {
User runAsUser = realm.lookupUser(runAsUsername);
if (runAsUser != null) {
lookedupBy = new RealmRef(realm.name(), realm.type(), nodeName);
user = new User(user.principal(), user.roles(), runAsUser);
return user;
}
}
}
// the requested run as user does not exist, but we don't throw an error here otherwise this could let
// information leak about users in the system... instead we'll just let the authz service fail throw an
// authorization error
user = new User(user.principal(), user.roles(), new User(runAsUsername, Strings.EMPTY_ARRAY));
} catch (Exception e) {
logger.debug("run as failed for principal [{}], [{}], run as username [{}]", e, token.principal(), request, runAsUsername);
throw request.exceptionProcessingRequest(e, token);
}
return user;
}
abstract class AuditableRequest {
abstract void realmAuthenticationFailed(AuthenticationToken token, String realm);
abstract ElasticsearchSecurityException tamperedRequest();
abstract ElasticsearchSecurityException exceptionProcessingRequest(Exception e, @Nullable AuthenticationToken token);
abstract ElasticsearchSecurityException authenticationFailed(AuthenticationToken token);
abstract ElasticsearchSecurityException anonymousAccessDenied();
abstract ElasticsearchSecurityException runAsDenied(User user, AuthenticationToken token);
}
class Transport extends AuditableRequest {
private final String action;
private final TransportMessage message;
Transport(String action, TransportMessage message) {
this.action = action;
this.message = message;
}
@Override
void realmAuthenticationFailed(AuthenticationToken token, String realm) {
auditTrail.authenticationFailed(realm, token, action, message);
}
@Override
ElasticsearchSecurityException tamperedRequest() {
auditTrail.tamperedRequest(action, message);
return new ElasticsearchSecurityException("failed to verify signed authentication information");
}
@Override
ElasticsearchSecurityException exceptionProcessingRequest(Exception e, @Nullable AuthenticationToken token) {
if (token != null) {
auditTrail.authenticationFailed(token, action, message);
} else {
auditTrail.authenticationFailed(action, message);
}
return failureHandler.exceptionProcessingRequest(message, action, e, threadContext);
}
@Override
ElasticsearchSecurityException authenticationFailed(AuthenticationToken token) {
auditTrail.authenticationFailed(token, action, message);
return failureHandler.failedAuthentication(message, token, action, threadContext);
}
@Override
ElasticsearchSecurityException anonymousAccessDenied() {
auditTrail.anonymousAccessDenied(action, message);
return failureHandler.missingToken(message, action, threadContext);
}
@Override
ElasticsearchSecurityException runAsDenied(User user, AuthenticationToken token) {
auditTrail.runAsDenied(user, action, message);
return failureHandler.failedAuthentication(message, token, action, threadContext);
}
public String toString() {
return "transport request action [" + action + "]";
}
}
class Rest extends AuditableRequest {
private final RestRequest request;
Rest(RestRequest request) {
this.request = request;
}
@Override
void realmAuthenticationFailed(AuthenticationToken token, String realm) {
auditTrail.authenticationFailed(realm, token, request);
}
@Override
ElasticsearchSecurityException tamperedRequest() {
auditTrail.tamperedRequest(request);
return new ElasticsearchSecurityException("rest request attempted to inject a user");
}
@Override
ElasticsearchSecurityException exceptionProcessingRequest(Exception e, @Nullable AuthenticationToken token) {
if (token != null) {
auditTrail.authenticationFailed(token, request);
} else {
auditTrail.authenticationFailed(request);
}
return failureHandler.exceptionProcessingRequest(request, e, threadContext);
}
@Override
ElasticsearchSecurityException authenticationFailed(AuthenticationToken token) {
auditTrail.authenticationFailed(token, request);
return failureHandler.failedAuthentication(request, token, threadContext);
}
@Override
ElasticsearchSecurityException anonymousAccessDenied() {
auditTrail.anonymousAccessDenied(request);
return failureHandler.missingToken(request, threadContext);
}
@Override
ElasticsearchSecurityException runAsDenied(User user, AuthenticationToken token) {
auditTrail.runAsDenied(user, request);
return failureHandler.failedAuthentication(request, token, threadContext);
}
public String toString() {
return "rest request uri [" + request.uri() + "]";
}
}
}
public static void addSettings(List<Setting<?>> settings) {
settings.add(SIGN_USER_HEADER);
settings.add(RUN_AS_ENABLED);
}
}

View File

@ -1,42 +0,0 @@
/*
* 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.security.authz;
import org.elasticsearch.common.inject.util.Providers;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.xpack.security.authz.store.CompositeRolesStore;
import org.elasticsearch.xpack.security.authz.store.FileRolesStore;
import org.elasticsearch.xpack.security.authz.store.NativeRolesStore;
import org.elasticsearch.xpack.security.authz.store.ReservedRolesStore;
import org.elasticsearch.xpack.security.authz.store.RolesStore;
import org.elasticsearch.xpack.security.support.AbstractSecurityModule;
/**
* Module used to bind various classes necessary for authorization
*/
public class AuthorizationModule extends AbstractSecurityModule.Node {
public AuthorizationModule(Settings settings) {
super(settings);
}
@Override
protected void configureNode() {
if (securityEnabled == false) {
bind(RolesStore.class).toProvider(Providers.of(null));
return;
}
// First the file and native roles stores must be bound...
bind(ReservedRolesStore.class).asEagerSingleton();
bind(FileRolesStore.class).asEagerSingleton();
bind(NativeRolesStore.class).asEagerSingleton();
// Then the composite roles store (which combines both) can be bound
bind(RolesStore.class).to(CompositeRolesStore.class).asEagerSingleton();
bind(AuthorizationService.class).to(InternalAuthorizationService.class).asEagerSingleton();
}
}

View File

@ -5,17 +5,94 @@
*/
package org.elasticsearch.xpack.security.authz;
import org.elasticsearch.ElasticsearchSecurityException;
import org.elasticsearch.xpack.security.authc.Authentication;
import org.elasticsearch.xpack.security.user.User;
import org.elasticsearch.transport.TransportRequest;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.function.Predicate;
import org.elasticsearch.ElasticsearchSecurityException;
import org.elasticsearch.action.CompositeIndicesRequest;
import org.elasticsearch.action.IndicesRequest;
import org.elasticsearch.action.admin.indices.alias.Alias;
import org.elasticsearch.action.admin.indices.create.CreateIndexRequest;
import org.elasticsearch.action.search.ClearScrollAction;
import org.elasticsearch.action.search.SearchScrollAction;
import org.elasticsearch.cluster.ClusterState;
import org.elasticsearch.cluster.metadata.AliasOrIndex;
import org.elasticsearch.cluster.metadata.IndexNameExpressionResolver;
import org.elasticsearch.cluster.metadata.MetaData;
import org.elasticsearch.cluster.service.ClusterService;
import org.elasticsearch.common.Strings;
import org.elasticsearch.common.component.AbstractComponent;
import org.elasticsearch.common.settings.Setting;
import org.elasticsearch.common.settings.Setting.Property;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.util.concurrent.ThreadContext;
import org.elasticsearch.common.util.set.Sets;
import org.elasticsearch.search.action.SearchTransportService;
import org.elasticsearch.threadpool.ThreadPool;
import org.elasticsearch.transport.TransportRequest;
import org.elasticsearch.xpack.security.SecurityTemplateService;
import org.elasticsearch.xpack.security.audit.AuditTrail;
import org.elasticsearch.xpack.security.authc.Authentication;
import org.elasticsearch.xpack.security.authc.AuthenticationFailureHandler;
import org.elasticsearch.xpack.security.authz.accesscontrol.IndicesAccessControl;
import org.elasticsearch.xpack.security.authz.indicesresolver.DefaultIndicesAndAliasesResolver;
import org.elasticsearch.xpack.security.authz.indicesresolver.IndicesAndAliasesResolver;
import org.elasticsearch.xpack.security.authz.permission.ClusterPermission;
import org.elasticsearch.xpack.security.authz.permission.DefaultRole;
import org.elasticsearch.xpack.security.authz.permission.GlobalPermission;
import org.elasticsearch.xpack.security.authz.permission.Role;
import org.elasticsearch.xpack.security.authz.permission.RunAsPermission;
import org.elasticsearch.xpack.security.authz.privilege.ClusterPrivilege;
import org.elasticsearch.xpack.security.authz.privilege.IndexPrivilege;
import org.elasticsearch.xpack.security.authz.store.CompositeRolesStore;
import org.elasticsearch.xpack.security.audit.AuditTrailService;
import org.elasticsearch.xpack.security.user.AnonymousUser;
import org.elasticsearch.xpack.security.user.SystemUser;
import org.elasticsearch.xpack.security.user.User;
import org.elasticsearch.xpack.security.user.XPackUser;
import org.elasticsearch.xpack.security.authz.store.RolesStore;
import static org.elasticsearch.xpack.security.Security.setting;
import static org.elasticsearch.xpack.security.support.Exceptions.authorizationError;
/**
*
*/
public interface AuthorizationService {
public class AuthorizationService extends AbstractComponent {
public static final Setting<Boolean> ANONYMOUS_AUTHORIZATION_EXCEPTION_SETTING =
Setting.boolSetting(setting("authc.anonymous.authz_exception"), true, Property.NodeScope);
public static final String INDICES_PERMISSIONS_KEY = "_indices_permissions";
static final String ORIGINATING_ACTION_KEY = "_originating_action_name";
private static final Predicate<String> MONITOR_INDEX_PREDICATE = IndexPrivilege.MONITOR.predicate();
private final ClusterService clusterService;
private final CompositeRolesStore rolesStore;
private final AuditTrailService auditTrail;
private final IndicesAndAliasesResolver[] indicesAndAliasesResolvers;
private final AuthenticationFailureHandler authcFailureHandler;
private final ThreadContext threadContext;
private final boolean anonymousAuthzExceptionEnabled;
public AuthorizationService(Settings settings, CompositeRolesStore rolesStore, ClusterService clusterService,
AuditTrailService auditTrail, AuthenticationFailureHandler authcFailureHandler,
ThreadPool threadPool) {
super(settings);
this.rolesStore = rolesStore;
this.clusterService = clusterService;
this.auditTrail = auditTrail;
this.indicesAndAliasesResolvers = new IndicesAndAliasesResolver[] {
new DefaultIndicesAndAliasesResolver(this, new IndexNameExpressionResolver(settings))
};
this.authcFailureHandler = authcFailureHandler;
this.threadContext = threadPool.getThreadContext();
this.anonymousAuthzExceptionEnabled = ANONYMOUS_AUTHORIZATION_EXCEPTION_SETTING.get(settings);
}
/**
* Returns all indices and aliases the given user is allowed to execute the given action on.
@ -23,7 +100,49 @@ public interface AuthorizationService {
* @param user The user
* @param action The action
*/
List<String> authorizedIndicesAndAliases(User user, String action);
public List<String> authorizedIndicesAndAliases(User user, String action) {
final String[] anonymousRoles = AnonymousUser.enabled() ? AnonymousUser.getRoles() : Strings.EMPTY_ARRAY;
String[] rolesNames = user.roles();
if (rolesNames.length == 0 && anonymousRoles.length == 0) {
return Collections.emptyList();
}
List<Predicate<String>> predicates = new ArrayList<>();
for (String roleName : rolesNames) {
Role role = rolesStore.role(roleName);
if (role != null) {
predicates.add(role.indices().allowedIndicesMatcher(action));
}
}
if (AnonymousUser.is(user) == false) {
for (String roleName : anonymousRoles) {
Role role = rolesStore.role(roleName);
if (role != null) {
predicates.add(role.indices().allowedIndicesMatcher(action));
}
}
}
Predicate<String> predicate = predicates.stream().reduce(s -> false, (p1, p2) -> p1.or(p2));
List<String> indicesAndAliases = new ArrayList<>();
MetaData metaData = clusterService.state().metaData();
// TODO: can this be done smarter? I think there are usually more indices/aliases in the cluster then indices defined a roles?
for (Map.Entry<String, AliasOrIndex> entry : metaData.getAliasAndIndexLookup().entrySet()) {
String aliasOrIndex = entry.getKey();
if (predicate.test(aliasOrIndex)) {
indicesAndAliases.add(aliasOrIndex);
}
}
if (XPackUser.is(user) == false) {
// we should filter out the .security index from wildcards
if (indicesAndAliases.remove(SecurityTemplateService.SECURITY_INDEX_NAME)) {
logger.debug("removed [{}] from user [{}] list of authorized indices",
SecurityTemplateService.SECURITY_INDEX_NAME, user.principal());
}
}
return Collections.unmodifiableList(indicesAndAliases);
}
/**
* Verifies that the given user can execute the given request (and action). If the user doesn't
@ -35,6 +154,226 @@ public interface AuthorizationService {
* @param request The request
* @throws ElasticsearchSecurityException If the given user is no allowed to execute the given request
*/
void authorize(Authentication authentication, String action, TransportRequest request) throws ElasticsearchSecurityException;
public void authorize(Authentication authentication, String action, TransportRequest request) throws ElasticsearchSecurityException {
// prior to doing any authorization lets set the originating action in the context only
setOriginatingAction(action);
// first we need to check if the user is the system. If it is, we'll just authorize the system access
if (SystemUser.is(authentication.getRunAsUser())) {
if (SystemUser.isAuthorized(action) && SystemUser.is(authentication.getUser())) {
setIndicesAccessControl(IndicesAccessControl.ALLOW_ALL);
grant(authentication, action, request);
return;
}
throw denial(authentication, action, request);
}
// get the roles of the authenticated user, which may be different than the effective
GlobalPermission permission = permission(authentication.getUser().roles());
final boolean isRunAs = authentication.getUser() != authentication.getRunAsUser();
// permission can be null as it might be that the user's role
// is unknown
if (permission == null || permission.isEmpty()) {
if (isRunAs) {
// the request is a run as request so we should call the specific audit event for a denied run as attempt
throw denyRunAs(authentication, action, request);
} else {
throw denial(authentication, action, request);
}
}
// check if the request is a run as request
if (isRunAs) {
// first we must authorize for the RUN_AS action
RunAsPermission runAs = permission.runAs();
if (runAs != null && runAs.check(authentication.getRunAsUser().principal())) {
grantRunAs(authentication, action, request);
permission = permission(authentication.getRunAsUser().roles());
// permission can be null as it might be that the user's role
// is unknown
if (permission == null || permission.isEmpty()) {
throw denial(authentication, action, request);
}
} else {
throw denyRunAs(authentication, action, request);
}
}
// first, we'll check if the action is a cluster action. If it is, we'll only check it
// against the cluster permissions
if (ClusterPrivilege.ACTION_MATCHER.test(action)) {
ClusterPermission cluster = permission.cluster();
// we use the effectiveUser for permission checking since we are running as a user!
if (cluster != null && cluster.check(action, request, authentication)) {
setIndicesAccessControl(IndicesAccessControl.ALLOW_ALL);
grant(authentication, action, request);
return;
}
throw denial(authentication, action, request);
}
// ok... this is not a cluster action, let's verify it's an indices action
if (!IndexPrivilege.ACTION_MATCHER.test(action)) {
throw denial(authentication, action, request);
}
// some APIs are indices requests that are not actually associated with indices. For example,
// search scroll request, is categorized under the indices context, but doesn't hold indices names
// (in this case, the security check on the indices was done on the search request that initialized
// the scroll... and we rely on the signed scroll id to provide security over this request).
// so we only check indices if indeed the request is an actual IndicesRequest, if it's not,
// we just grant it if it's a scroll, deny otherwise
if (!(request instanceof IndicesRequest) && !(request instanceof CompositeIndicesRequest)) {
if (isScrollRelatedAction(action)) {
//note that clear scroll shard level actions can originate from a clear scroll all, which doesn't require any
//indices permission as it's categorized under cluster. This is why the scroll check is performed
//even before checking if the user has any indices permission.
grant(authentication, action, request);
return;
}
assert false : "only scroll related requests are known indices api that don't support retrieving the indices they relate to";
throw denial(authentication, action, request);
}
if (permission.indices() == null || permission.indices().isEmpty()) {
throw denial(authentication, action, request);
}
ClusterState clusterState = clusterService.state();
Set<String> indexNames = resolveIndices(authentication, action, request, clusterState);
assert !indexNames.isEmpty() : "every indices request needs to have its indices set thus the resolved indices must not be empty";
MetaData metaData = clusterState.metaData();
IndicesAccessControl indicesAccessControl = permission.authorize(action, indexNames, metaData);
if (!indicesAccessControl.isGranted()) {
throw denial(authentication, action, request);
} else if (indicesAccessControl.getIndexPermissions(SecurityTemplateService.SECURITY_INDEX_NAME) != null
&& indicesAccessControl.getIndexPermissions(SecurityTemplateService.SECURITY_INDEX_NAME).isGranted()
&& XPackUser.is(authentication.getRunAsUser()) == false
&& MONITOR_INDEX_PREDICATE.test(action) == false) {
// only the XPackUser is allowed to work with this index, but we should allow indices monitoring actions through for debugging
// purposes. These monitor requests also sometimes resolve indices concretely and then requests them
// FIXME its not just the XPackUser. We said the elastic user and superusers could access this!
logger.debug("user [{}] attempted to directly perform [{}] against the security index [{}]",
authentication.getRunAsUser().principal(), action, SecurityTemplateService.SECURITY_INDEX_NAME);
throw denial(authentication, action, request);
} else {
setIndicesAccessControl(indicesAccessControl);
}
//if we are creating an index we need to authorize potential aliases created at the same time
if (IndexPrivilege.CREATE_INDEX_MATCHER.test(action)) {
assert request instanceof CreateIndexRequest;
Set<Alias> aliases = ((CreateIndexRequest) request).aliases();
if (!aliases.isEmpty()) {
Set<String> aliasesAndIndices = Sets.newHashSet(indexNames);
for (Alias alias : aliases) {
aliasesAndIndices.add(alias.name());
}
indicesAccessControl = permission.authorize("indices:admin/aliases", aliasesAndIndices, metaData);
if (!indicesAccessControl.isGranted()) {
throw denial(authentication, "indices:admin/aliases", request);
}
// no need to re-add the indicesAccessControl in the context,
// because the create index call doesn't do anything FLS or DLS
}
}
grant(authentication, action, request);
}
private void setIndicesAccessControl(IndicesAccessControl accessControl) {
if (threadContext.getTransient(INDICES_PERMISSIONS_KEY) == null) {
threadContext.putTransient(INDICES_PERMISSIONS_KEY, accessControl);
}
}
private void setOriginatingAction(String action) {
String originatingAction = threadContext.getTransient(ORIGINATING_ACTION_KEY);
if (originatingAction == null) {
threadContext.putTransient(ORIGINATING_ACTION_KEY, action);
}
}
private GlobalPermission permission(String[] roleNames) {
if (roleNames.length == 0) {
return DefaultRole.INSTANCE;
}
if (roleNames.length == 1) {
Role role = rolesStore.role(roleNames[0]);
return role == null ? DefaultRole.INSTANCE : GlobalPermission.Compound.builder().add(DefaultRole.INSTANCE).add(role).build();
}
// we'll take all the roles and combine their associated permissions
GlobalPermission.Compound.Builder roles = GlobalPermission.Compound.builder().add(DefaultRole.INSTANCE);
for (String roleName : roleNames) {
Role role = rolesStore.role(roleName);
if (role != null) {
roles.add(role);
}
}
return roles.build();
}
private Set<String> resolveIndices(Authentication authentication, String action, TransportRequest request, ClusterState clusterState) {
MetaData metaData = clusterState.metaData();
for (IndicesAndAliasesResolver resolver : indicesAndAliasesResolvers) {
if (resolver.requestType().isInstance(request)) {
return resolver.resolve(authentication.getRunAsUser(), action, request, metaData);
}
}
assert false : "we should be able to resolve indices for any known request that requires indices privileges";
throw denial(authentication, action, request);
}
private static boolean isScrollRelatedAction(String action) {
return action.equals(SearchScrollAction.NAME) ||
action.equals(SearchTransportService.FETCH_ID_SCROLL_ACTION_NAME) ||
action.equals(SearchTransportService.QUERY_FETCH_SCROLL_ACTION_NAME) ||
action.equals(SearchTransportService.QUERY_SCROLL_ACTION_NAME) ||
action.equals(SearchTransportService.FREE_CONTEXT_SCROLL_ACTION_NAME) ||
action.equals(ClearScrollAction.NAME) ||
action.equals(SearchTransportService.CLEAR_SCROLL_CONTEXTS_ACTION_NAME);
}
private ElasticsearchSecurityException denial(Authentication authentication, String action, TransportRequest request) {
auditTrail.accessDenied(authentication.getUser(), action, request);
return denialException(authentication, action);
}
private ElasticsearchSecurityException denyRunAs(Authentication authentication, String action, TransportRequest request) {
auditTrail.runAsDenied(authentication.getUser(), action, request);
return denialException(authentication, action);
}
private void grant(Authentication authentication, String action, TransportRequest request) {
auditTrail.accessGranted(authentication.getUser(), action, request);
}
private void grantRunAs(Authentication authentication, String action, TransportRequest request) {
auditTrail.runAsGranted(authentication.getUser(), action, request);
}
private ElasticsearchSecurityException denialException(Authentication authentication, String action) {
final User user = authentication.getUser();
// Special case for anonymous user
if (AnonymousUser.enabled() && AnonymousUser.is(user)) {
if (anonymousAuthzExceptionEnabled == false) {
throw authcFailureHandler.authenticationRequired(action, threadContext);
}
}
// check for run as
if (user != authentication.getRunAsUser()) {
return authorizationError("action [{}] is unauthorized for user [{}] run as [{}]", action, user.principal(),
authentication.getRunAsUser().principal());
}
return authorizationError("action [{}] is unauthorized for user [{}]", action, user.principal());
}
public static void addSettings(List<Setting<?>> settings) {
settings.add(ANONYMOUS_AUTHORIZATION_EXCEPTION_SETTING);
}
}

View File

@ -8,8 +8,6 @@ package org.elasticsearch.xpack.security.authz;
import org.elasticsearch.common.util.concurrent.ThreadContext;
import org.elasticsearch.xpack.security.authc.Authentication;
import org.elasticsearch.xpack.security.user.SystemUser;
import org.elasticsearch.xpack.security.user.User;
import org.elasticsearch.xpack.security.authc.InternalAuthenticationService;
import org.elasticsearch.xpack.security.support.AutomatonPredicate;
import org.elasticsearch.xpack.security.support.Automatons;
@ -52,7 +50,7 @@ public final class AuthorizationUtils {
// we have a internal action being executed by a user that is not the system user, lets verify that there is a
// originating action that is not a internal action
final String originatingAction = threadContext.getTransient(InternalAuthorizationService.ORIGINATING_ACTION_KEY);
final String originatingAction = threadContext.getTransient(AuthorizationService.ORIGINATING_ACTION_KEY);
if (originatingAction != null && isInternalAction(originatingAction) == false) {
return true;
}

View File

@ -1,365 +0,0 @@
/*
* 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.security.authz;
import org.elasticsearch.ElasticsearchSecurityException;
import org.elasticsearch.action.CompositeIndicesRequest;
import org.elasticsearch.action.IndicesRequest;
import org.elasticsearch.action.admin.indices.alias.Alias;
import org.elasticsearch.action.admin.indices.create.CreateIndexRequest;
import org.elasticsearch.action.search.ClearScrollAction;
import org.elasticsearch.action.search.SearchScrollAction;
import org.elasticsearch.cluster.metadata.IndexNameExpressionResolver;
import org.elasticsearch.cluster.service.ClusterService;
import org.elasticsearch.cluster.ClusterState;
import org.elasticsearch.cluster.metadata.AliasOrIndex;
import org.elasticsearch.cluster.metadata.MetaData;
import org.elasticsearch.common.Strings;
import org.elasticsearch.common.component.AbstractComponent;
import org.elasticsearch.common.inject.Inject;
import org.elasticsearch.common.settings.Setting;
import org.elasticsearch.common.settings.Setting.Property;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.util.concurrent.ThreadContext;
import org.elasticsearch.common.util.set.Sets;
import org.elasticsearch.search.action.SearchTransportService;
import org.elasticsearch.xpack.security.SecurityTemplateService;
import org.elasticsearch.xpack.security.authc.Authentication;
import org.elasticsearch.xpack.security.user.AnonymousUser;
import org.elasticsearch.xpack.security.user.SystemUser;
import org.elasticsearch.xpack.security.user.User;
import org.elasticsearch.xpack.security.user.XPackUser;
import org.elasticsearch.xpack.security.audit.AuditTrail;
import org.elasticsearch.xpack.security.authc.AuthenticationFailureHandler;
import org.elasticsearch.xpack.security.authz.accesscontrol.IndicesAccessControl;
import org.elasticsearch.xpack.security.authz.indicesresolver.DefaultIndicesAndAliasesResolver;
import org.elasticsearch.xpack.security.authz.indicesresolver.IndicesAndAliasesResolver;
import org.elasticsearch.xpack.security.authz.permission.ClusterPermission;
import org.elasticsearch.xpack.security.authz.permission.DefaultRole;
import org.elasticsearch.xpack.security.authz.permission.GlobalPermission;
import org.elasticsearch.xpack.security.authz.permission.Role;
import org.elasticsearch.xpack.security.authz.permission.RunAsPermission;
import org.elasticsearch.xpack.security.authz.privilege.ClusterPrivilege;
import org.elasticsearch.xpack.security.authz.privilege.IndexPrivilege;
import org.elasticsearch.xpack.security.authz.store.RolesStore;
import org.elasticsearch.threadpool.ThreadPool;
import org.elasticsearch.transport.TransportRequest;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.function.Predicate;
import static org.elasticsearch.xpack.security.Security.setting;
import static org.elasticsearch.xpack.security.support.Exceptions.authorizationError;
/**
*
*/
public class InternalAuthorizationService extends AbstractComponent implements AuthorizationService {
public static final Setting<Boolean> ANONYMOUS_AUTHORIZATION_EXCEPTION_SETTING =
Setting.boolSetting(setting("authc.anonymous.authz_exception"), true, Property.NodeScope);
public static final String INDICES_PERMISSIONS_KEY = "_indices_permissions";
static final String ORIGINATING_ACTION_KEY = "_originating_action_name";
private static final Predicate<String> MONITOR_INDEX_PREDICATE = IndexPrivilege.MONITOR.predicate();
private final ClusterService clusterService;
private final RolesStore rolesStore;
private final AuditTrail auditTrail;
private final IndicesAndAliasesResolver[] indicesAndAliasesResolvers;
private final AuthenticationFailureHandler authcFailureHandler;
private final ThreadContext threadContext;
private final boolean anonymousAuthzExceptionEnabled;
@Inject
public InternalAuthorizationService(Settings settings, RolesStore rolesStore, ClusterService clusterService,
AuditTrail auditTrail, AuthenticationFailureHandler authcFailureHandler,
ThreadPool threadPool, IndexNameExpressionResolver nameExpressionResolver) {
super(settings);
this.rolesStore = rolesStore;
this.clusterService = clusterService;
this.auditTrail = auditTrail;
this.indicesAndAliasesResolvers = new IndicesAndAliasesResolver[] {
new DefaultIndicesAndAliasesResolver(this, nameExpressionResolver)
};
this.authcFailureHandler = authcFailureHandler;
this.threadContext = threadPool.getThreadContext();
this.anonymousAuthzExceptionEnabled = ANONYMOUS_AUTHORIZATION_EXCEPTION_SETTING.get(settings);
}
@Override
public List<String> authorizedIndicesAndAliases(User user, String action) {
final String[] anonymousRoles = AnonymousUser.enabled() ? AnonymousUser.getRoles() : Strings.EMPTY_ARRAY;
String[] rolesNames = user.roles();
if (rolesNames.length == 0 && anonymousRoles.length == 0) {
return Collections.emptyList();
}
List<Predicate<String>> predicates = new ArrayList<>();
for (String roleName : rolesNames) {
Role role = rolesStore.role(roleName);
if (role != null) {
predicates.add(role.indices().allowedIndicesMatcher(action));
}
}
if (AnonymousUser.is(user) == false) {
for (String roleName : anonymousRoles) {
Role role = rolesStore.role(roleName);
if (role != null) {
predicates.add(role.indices().allowedIndicesMatcher(action));
}
}
}
Predicate<String> predicate = predicates.stream().reduce(s -> false, (p1, p2) -> p1.or(p2));
List<String> indicesAndAliases = new ArrayList<>();
MetaData metaData = clusterService.state().metaData();
// TODO: can this be done smarter? I think there are usually more indices/aliases in the cluster then indices defined a roles?
for (Map.Entry<String, AliasOrIndex> entry : metaData.getAliasAndIndexLookup().entrySet()) {
String aliasOrIndex = entry.getKey();
if (predicate.test(aliasOrIndex)) {
indicesAndAliases.add(aliasOrIndex);
}
}
if (XPackUser.is(user) == false) {
// we should filter out the .security index from wildcards
if (indicesAndAliases.remove(SecurityTemplateService.SECURITY_INDEX_NAME)) {
logger.debug("removed [{}] from user [{}] list of authorized indices",
SecurityTemplateService.SECURITY_INDEX_NAME, user.principal());
}
}
return Collections.unmodifiableList(indicesAndAliases);
}
@Override
public void authorize(Authentication authentication, String action, TransportRequest request) throws ElasticsearchSecurityException {
// prior to doing any authorization lets set the originating action in the context only
setOriginatingAction(action);
// first we need to check if the user is the system. If it is, we'll just authorize the system access
if (SystemUser.is(authentication.getRunAsUser())) {
if (SystemUser.isAuthorized(action) && SystemUser.is(authentication.getUser())) {
setIndicesAccessControl(IndicesAccessControl.ALLOW_ALL);
grant(authentication, action, request);
return;
}
throw denial(authentication, action, request);
}
// get the roles of the authenticated user, which may be different than the effective
GlobalPermission permission = permission(authentication.getUser().roles());
final boolean isRunAs = authentication.getUser() != authentication.getRunAsUser();
// permission can be null as it might be that the user's role
// is unknown
if (permission == null || permission.isEmpty()) {
if (isRunAs) {
// the request is a run as request so we should call the specific audit event for a denied run as attempt
throw denyRunAs(authentication, action, request);
} else {
throw denial(authentication, action, request);
}
}
// check if the request is a run as request
if (isRunAs) {
// first we must authorize for the RUN_AS action
RunAsPermission runAs = permission.runAs();
if (runAs != null && runAs.check(authentication.getRunAsUser().principal())) {
grantRunAs(authentication, action, request);
permission = permission(authentication.getRunAsUser().roles());
// permission can be null as it might be that the user's role
// is unknown
if (permission == null || permission.isEmpty()) {
throw denial(authentication, action, request);
}
} else {
throw denyRunAs(authentication, action, request);
}
}
// first, we'll check if the action is a cluster action. If it is, we'll only check it
// against the cluster permissions
if (ClusterPrivilege.ACTION_MATCHER.test(action)) {
ClusterPermission cluster = permission.cluster();
// we use the effectiveUser for permission checking since we are running as a user!
if (cluster != null && cluster.check(action, request, authentication)) {
setIndicesAccessControl(IndicesAccessControl.ALLOW_ALL);
grant(authentication, action, request);
return;
}
throw denial(authentication, action, request);
}
// ok... this is not a cluster action, let's verify it's an indices action
if (!IndexPrivilege.ACTION_MATCHER.test(action)) {
throw denial(authentication, action, request);
}
// some APIs are indices requests that are not actually associated with indices. For example,
// search scroll request, is categorized under the indices context, but doesn't hold indices names
// (in this case, the security check on the indices was done on the search request that initialized
// the scroll... and we rely on the signed scroll id to provide security over this request).
// so we only check indices if indeed the request is an actual IndicesRequest, if it's not,
// we just grant it if it's a scroll, deny otherwise
if (!(request instanceof IndicesRequest) && !(request instanceof CompositeIndicesRequest)) {
if (isScrollRelatedAction(action)) {
//note that clear scroll shard level actions can originate from a clear scroll all, which doesn't require any
//indices permission as it's categorized under cluster. This is why the scroll check is performed
//even before checking if the user has any indices permission.
grant(authentication, action, request);
return;
}
assert false : "only scroll related requests are known indices api that don't support retrieving the indices they relate to";
throw denial(authentication, action, request);
}
if (permission.indices() == null || permission.indices().isEmpty()) {
throw denial(authentication, action, request);
}
ClusterState clusterState = clusterService.state();
Set<String> indexNames = resolveIndices(authentication, action, request, clusterState);
assert !indexNames.isEmpty() : "every indices request needs to have its indices set thus the resolved indices must not be empty";
MetaData metaData = clusterState.metaData();
IndicesAccessControl indicesAccessControl = permission.authorize(action, indexNames, metaData);
if (!indicesAccessControl.isGranted()) {
throw denial(authentication, action, request);
} else if (indicesAccessControl.getIndexPermissions(SecurityTemplateService.SECURITY_INDEX_NAME) != null
&& indicesAccessControl.getIndexPermissions(SecurityTemplateService.SECURITY_INDEX_NAME).isGranted()
&& XPackUser.is(authentication.getRunAsUser()) == false
&& MONITOR_INDEX_PREDICATE.test(action) == false) {
// only the XPackUser is allowed to work with this index, but we should allow indices monitoring actions through for debugging
// purposes. These monitor requests also sometimes resolve indices concretely and then requests them
// FIXME its not just the XPackUser. We said the elastic user and superusers could access this!
logger.debug("user [{}] attempted to directly perform [{}] against the security index [{}]",
authentication.getRunAsUser().principal(), action, SecurityTemplateService.SECURITY_INDEX_NAME);
throw denial(authentication, action, request);
} else {
setIndicesAccessControl(indicesAccessControl);
}
//if we are creating an index we need to authorize potential aliases created at the same time
if (IndexPrivilege.CREATE_INDEX_MATCHER.test(action)) {
assert request instanceof CreateIndexRequest;
Set<Alias> aliases = ((CreateIndexRequest) request).aliases();
if (!aliases.isEmpty()) {
Set<String> aliasesAndIndices = Sets.newHashSet(indexNames);
for (Alias alias : aliases) {
aliasesAndIndices.add(alias.name());
}
indicesAccessControl = permission.authorize("indices:admin/aliases", aliasesAndIndices, metaData);
if (!indicesAccessControl.isGranted()) {
throw denial(authentication, "indices:admin/aliases", request);
}
// no need to re-add the indicesAccessControl in the context,
// because the create index call doesn't do anything FLS or DLS
}
}
grant(authentication, action, request);
}
private void setIndicesAccessControl(IndicesAccessControl accessControl) {
if (threadContext.getTransient(INDICES_PERMISSIONS_KEY) == null) {
threadContext.putTransient(INDICES_PERMISSIONS_KEY, accessControl);
}
}
private void setOriginatingAction(String action) {
String originatingAction = threadContext.getTransient(ORIGINATING_ACTION_KEY);
if (originatingAction == null) {
threadContext.putTransient(ORIGINATING_ACTION_KEY, action);
}
}
private GlobalPermission permission(String[] roleNames) {
if (roleNames.length == 0) {
return DefaultRole.INSTANCE;
}
if (roleNames.length == 1) {
Role role = rolesStore.role(roleNames[0]);
return role == null ? DefaultRole.INSTANCE : GlobalPermission.Compound.builder().add(DefaultRole.INSTANCE).add(role).build();
}
// we'll take all the roles and combine their associated permissions
GlobalPermission.Compound.Builder roles = GlobalPermission.Compound.builder().add(DefaultRole.INSTANCE);
for (String roleName : roleNames) {
Role role = rolesStore.role(roleName);
if (role != null) {
roles.add(role);
}
}
return roles.build();
}
private Set<String> resolveIndices(Authentication authentication, String action, TransportRequest request, ClusterState clusterState) {
MetaData metaData = clusterState.metaData();
for (IndicesAndAliasesResolver resolver : indicesAndAliasesResolvers) {
if (resolver.requestType().isInstance(request)) {
return resolver.resolve(authentication.getRunAsUser(), action, request, metaData);
}
}
assert false : "we should be able to resolve indices for any known request that requires indices privileges";
throw denial(authentication, action, request);
}
private static boolean isScrollRelatedAction(String action) {
return action.equals(SearchScrollAction.NAME) ||
action.equals(SearchTransportService.FETCH_ID_SCROLL_ACTION_NAME) ||
action.equals(SearchTransportService.QUERY_FETCH_SCROLL_ACTION_NAME) ||
action.equals(SearchTransportService.QUERY_SCROLL_ACTION_NAME) ||
action.equals(SearchTransportService.FREE_CONTEXT_SCROLL_ACTION_NAME) ||
action.equals(ClearScrollAction.NAME) ||
action.equals(SearchTransportService.CLEAR_SCROLL_CONTEXTS_ACTION_NAME);
}
private ElasticsearchSecurityException denial(Authentication authentication, String action, TransportRequest request) {
auditTrail.accessDenied(authentication.getUser(), action, request);
return denialException(authentication, action);
}
private ElasticsearchSecurityException denyRunAs(Authentication authentication, String action, TransportRequest request) {
auditTrail.runAsDenied(authentication.getUser(), action, request);
return denialException(authentication, action);
}
private void grant(Authentication authentication, String action, TransportRequest request) {
auditTrail.accessGranted(authentication.getUser(), action, request);
}
private void grantRunAs(Authentication authentication, String action, TransportRequest request) {
auditTrail.runAsGranted(authentication.getUser(), action, request);
}
private ElasticsearchSecurityException denialException(Authentication authentication, String action) {
final User user = authentication.getUser();
// Special case for anonymous user
if (AnonymousUser.enabled() && AnonymousUser.is(user)) {
if (anonymousAuthzExceptionEnabled == false) {
throw authcFailureHandler.authenticationRequired(action, threadContext);
}
}
// check for run as
if (user != authentication.getRunAsUser()) {
return authorizationError("action [{}] is unauthorized for user [{}] run as [{}]", action, user.principal(),
authentication.getRunAsUser().principal());
}
return authorizationError("action [{}] is unauthorized for user [{}]", action, user.principal());
}
public static void addSettings(List<Setting<?>> settings) {
settings.add(ANONYMOUS_AUTHORIZATION_EXCEPTION_SETTING);
}
}

View File

@ -14,7 +14,7 @@ import org.elasticsearch.index.IndexSettings;
import org.elasticsearch.index.cache.query.QueryCache;
import org.elasticsearch.indices.IndicesQueryCache;
import org.elasticsearch.search.internal.ShardSearchRequest;
import org.elasticsearch.xpack.security.authz.InternalAuthorizationService;
import org.elasticsearch.xpack.security.authz.AuthorizationService;
import java.util.HashSet;
import java.util.Set;
@ -50,7 +50,7 @@ public final class OptOutQueryCache extends AbstractIndexComponent implements Qu
throw new IllegalStateException("opting out of the query cache. current request can't be found");
}
IndicesAccessControl indicesAccessControl = context.getThreadContext().getTransient(
InternalAuthorizationService.INDICES_PERMISSIONS_KEY);
AuthorizationService.INDICES_PERMISSIONS_KEY);
if (indicesAccessControl == null) {
logger.debug("opting out of the query cache. current request doesn't hold indices permissions");
return weight;

View File

@ -22,7 +22,9 @@ import org.apache.lucene.util.BitSet;
import org.apache.lucene.util.BitSetIterator;
import org.apache.lucene.util.Bits;
import org.apache.lucene.util.SparseFixedBitSet;
import org.elasticsearch.ElasticsearchParseException;
import org.elasticsearch.ExceptionsHelper;
import org.elasticsearch.common.ParseFieldMatcher;
import org.elasticsearch.common.bytes.BytesReference;
import org.elasticsearch.common.logging.ESLogger;
import org.elasticsearch.common.logging.LoggerMessageFormat;
@ -43,16 +45,24 @@ import org.elasticsearch.index.query.QueryShardContext;
import org.elasticsearch.index.shard.IndexSearcherWrapper;
import org.elasticsearch.index.shard.ShardId;
import org.elasticsearch.index.shard.ShardUtils;
import org.elasticsearch.xpack.security.authz.InternalAuthorizationService;
import org.elasticsearch.script.ExecutableScript;
import org.elasticsearch.script.Script;
import org.elasticsearch.script.ScriptContext;
import org.elasticsearch.script.ScriptService;
import org.elasticsearch.xpack.security.authc.Authentication;
import org.elasticsearch.xpack.security.authz.AuthorizationService;
import org.elasticsearch.xpack.security.authz.accesscontrol.DocumentSubsetReader.DocumentSubsetDirectoryReader;
import org.elasticsearch.xpack.security.SecurityLicenseState;
import org.elasticsearch.xpack.security.support.Exceptions;
import org.elasticsearch.xpack.security.user.User;
import java.io.IOException;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
@ -78,10 +88,13 @@ public class SecurityIndexSearcherWrapper extends IndexSearcherWrapper {
private final SecurityLicenseState securityLicenseState;
private final ThreadContext threadContext;
private final ESLogger logger;
private final ScriptService scriptService;
public SecurityIndexSearcherWrapper(IndexSettings indexSettings, QueryShardContext queryShardContext,
MapperService mapperService, BitsetFilterCache bitsetFilterCache,
ThreadContext threadContext, SecurityLicenseState securityLicenseState) {
ThreadContext threadContext, SecurityLicenseState securityLicenseState,
ScriptService scriptService) {
this.scriptService = scriptService;
this.logger = Loggers.getLogger(getClass(), indexSettings.getSettings());
this.mapperService = mapperService;
this.queryShardContext = queryShardContext;
@ -124,6 +137,7 @@ public class SecurityIndexSearcherWrapper extends IndexSearcherWrapper {
BooleanQuery.Builder filter = new BooleanQuery.Builder();
for (BytesReference bytesReference : permissions.getQueries()) {
QueryShardContext queryShardContext = copyQueryShardContext(this.queryShardContext);
bytesReference = evaluateTemplate(bytesReference);
try (XContentParser parser = XContentFactory.xContent(bytesReference).createParser(bytesReference)) {
Optional<QueryBuilder> queryBuilder = queryShardContext.newParseContext(parser).parseInnerQueryBuilder();
if (queryBuilder.isPresent()) {
@ -262,11 +276,55 @@ public class SecurityIndexSearcherWrapper extends IndexSearcherWrapper {
}
}
BytesReference evaluateTemplate(BytesReference querySource) throws IOException {
try (XContentParser parser = XContentFactory.xContent(querySource).createParser(querySource)) {
XContentParser.Token token = parser.nextToken();
if (token != XContentParser.Token.START_OBJECT) {
throw new ElasticsearchParseException("Unexpected token [" + token + "]");
}
token = parser.nextToken();
if (token != XContentParser.Token.FIELD_NAME) {
throw new ElasticsearchParseException("Unexpected token [" + token + "]");
}
if ("template".equals(parser.currentName())) {
token = parser.nextToken();
if (token != XContentParser.Token.START_OBJECT) {
throw new ElasticsearchParseException("Unexpected token [" + token + "]");
}
Script script = Script.parse(parser, ParseFieldMatcher.EMPTY);
// Add the user details to the params
Map<String, Object> params = new HashMap<>();
if (script.getParams() != null) {
params.putAll(script.getParams());
}
User user = getUser();
Map<String, Object> userModel = new HashMap<>();
userModel.put("username", user.principal());
userModel.put("full_name", user.fullName());
userModel.put("email", user.email());
userModel.put("roles", Arrays.asList(user.roles()));
userModel.put("metadata", Collections.unmodifiableMap(user.metadata()));
params.put("_user", userModel);
// Always enforce mustache script lang:
script = new Script(script.getScript(), script.getType(), "mustache", params, script.getContentType());
ExecutableScript executable = scriptService.executable(script, ScriptContext.Standard.SEARCH, Collections.emptyMap());
return (BytesReference) executable.run();
} else {
return querySource;
}
}
}
protected IndicesAccessControl getIndicesAccessControl() {
IndicesAccessControl indicesAccessControl = threadContext.getTransient(InternalAuthorizationService.INDICES_PERMISSIONS_KEY);
IndicesAccessControl indicesAccessControl = threadContext.getTransient(AuthorizationService.INDICES_PERMISSIONS_KEY);
if (indicesAccessControl == null) {
throw Exceptions.authorizationError("no indices permissions found");
}
return indicesAccessControl;
}
protected User getUser(){
Authentication authentication = Authentication.getAuthentication(threadContext);
return authentication.getUser();
}
}

View File

@ -7,9 +7,7 @@ package org.elasticsearch.xpack.security.authz.indicesresolver;
import org.elasticsearch.action.AliasesRequest;
import org.elasticsearch.action.CompositeIndicesRequest;
import org.elasticsearch.action.DocumentRequest;
import org.elasticsearch.action.IndicesRequest;
import org.elasticsearch.action.admin.indices.create.CreateIndexRequest;
import org.elasticsearch.action.admin.indices.mapping.put.PutMappingRequest;
import org.elasticsearch.action.support.IndicesOptions;
import org.elasticsearch.cluster.metadata.AliasOrIndex;
@ -19,8 +17,8 @@ import org.elasticsearch.cluster.metadata.MetaData;
import org.elasticsearch.common.regex.Regex;
import org.elasticsearch.common.util.set.Sets;
import org.elasticsearch.index.IndexNotFoundException;
import org.elasticsearch.xpack.security.user.User;
import org.elasticsearch.xpack.security.authz.AuthorizationService;
import org.elasticsearch.xpack.security.user.User;
import org.elasticsearch.transport.TransportRequest;
import java.util.ArrayList;

View File

@ -21,7 +21,6 @@ public class CompositeRolesStore implements RolesStore {
private final NativeRolesStore nativeRolesStore;
private final ReservedRolesStore reservedRolesStore;
@Inject
public CompositeRolesStore(FileRolesStore fileRolesStore, NativeRolesStore nativeRolesStore, ReservedRolesStore reservedRolesStore) {
this.fileRolesStore = fileRolesStore;
this.nativeRolesStore = nativeRolesStore;

View File

@ -5,31 +5,6 @@
*/
package org.elasticsearch.xpack.security.authz.store;
import com.fasterxml.jackson.dataformat.yaml.snakeyaml.error.YAMLException;
import org.elasticsearch.ElasticsearchException;
import org.elasticsearch.ElasticsearchParseException;
import org.elasticsearch.common.Nullable;
import org.elasticsearch.common.component.AbstractLifecycleComponent;
import org.elasticsearch.common.inject.Inject;
import org.elasticsearch.common.logging.ESLogger;
import org.elasticsearch.common.settings.Setting;
import org.elasticsearch.common.settings.Setting.Property;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.xcontent.XContentParser;
import org.elasticsearch.common.xcontent.yaml.YamlXContent;
import org.elasticsearch.env.Environment;
import org.elasticsearch.xpack.security.Security;
import org.elasticsearch.xpack.security.authc.support.RefreshListener;
import org.elasticsearch.xpack.security.authz.RoleDescriptor;
import org.elasticsearch.xpack.security.authz.permission.IndicesPermission.Group;
import org.elasticsearch.xpack.security.authz.permission.Role;
import org.elasticsearch.xpack.security.support.NoOpLogger;
import org.elasticsearch.xpack.security.support.Validation;
import org.elasticsearch.watcher.FileChangesListener;
import org.elasticsearch.watcher.FileWatcher;
import org.elasticsearch.watcher.ResourceWatcherService;
import org.elasticsearch.xpack.XPackPlugin;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
@ -41,6 +16,30 @@ import java.util.Map;
import java.util.Set;
import java.util.regex.Pattern;
import com.fasterxml.jackson.dataformat.yaml.snakeyaml.error.YAMLException;
import org.elasticsearch.ElasticsearchException;
import org.elasticsearch.ElasticsearchParseException;
import org.elasticsearch.common.Nullable;
import org.elasticsearch.common.component.AbstractLifecycleComponent;
import org.elasticsearch.common.logging.ESLogger;
import org.elasticsearch.common.settings.Setting;
import org.elasticsearch.common.settings.Setting.Property;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.xcontent.XContentParser;
import org.elasticsearch.common.xcontent.yaml.YamlXContent;
import org.elasticsearch.env.Environment;
import org.elasticsearch.watcher.FileChangesListener;
import org.elasticsearch.watcher.FileWatcher;
import org.elasticsearch.watcher.ResourceWatcherService;
import org.elasticsearch.xpack.XPackPlugin;
import org.elasticsearch.xpack.security.Security;
import org.elasticsearch.xpack.security.authc.support.RefreshListener;
import org.elasticsearch.xpack.security.authz.RoleDescriptor;
import org.elasticsearch.xpack.security.authz.permission.IndicesPermission.Group;
import org.elasticsearch.xpack.security.authz.permission.Role;
import org.elasticsearch.xpack.security.support.NoOpLogger;
import org.elasticsearch.xpack.security.support.Validation;
import static java.util.Collections.emptyMap;
import static java.util.Collections.emptySet;
import static java.util.Collections.unmodifiableMap;
@ -59,7 +58,6 @@ public class FileRolesStore extends AbstractLifecycleComponent implements RolesS
private volatile Map<String, Role> permissions;
@Inject
public FileRolesStore(Settings settings, Environment env, ResourceWatcherService watcherService) {
this(settings, env, watcherService, RefreshListener.NOOP);
}

View File

@ -5,6 +5,21 @@
*/
package org.elasticsearch.xpack.security.authz.store;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.BiFunction;
import java.util.function.Function;
import org.elasticsearch.ElasticsearchException;
import org.elasticsearch.action.ActionListener;
import org.elasticsearch.action.LatchedActionListener;
@ -29,7 +44,6 @@ import org.elasticsearch.cluster.ClusterStateListener;
import org.elasticsearch.common.Nullable;
import org.elasticsearch.common.bytes.BytesReference;
import org.elasticsearch.common.component.AbstractComponent;
import org.elasticsearch.common.inject.Inject;
import org.elasticsearch.common.settings.Setting;
import org.elasticsearch.common.settings.Setting.Property;
import org.elasticsearch.common.settings.Settings;
@ -42,6 +56,8 @@ import org.elasticsearch.index.get.GetResult;
import org.elasticsearch.index.query.QueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.threadpool.ThreadPool;
import org.elasticsearch.threadpool.ThreadPool.Names;
import org.elasticsearch.xpack.security.InternalClient;
import org.elasticsearch.xpack.security.SecurityTemplateService;
import org.elasticsearch.xpack.security.action.role.ClearRolesCacheRequest;
@ -52,24 +68,7 @@ import org.elasticsearch.xpack.security.authz.RoleDescriptor;
import org.elasticsearch.xpack.security.authz.permission.IndicesPermission.Group;
import org.elasticsearch.xpack.security.authz.permission.Role;
import org.elasticsearch.xpack.security.client.SecurityClient;
import org.elasticsearch.threadpool.ThreadPool;
import org.elasticsearch.threadpool.ThreadPool.Cancellable;
import org.elasticsearch.threadpool.ThreadPool.Names;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.BiFunction;
import java.util.function.Function;
import static org.elasticsearch.common.xcontent.XContentFactory.jsonBuilder;
import static org.elasticsearch.xpack.security.Security.setting;
@ -116,7 +115,6 @@ public class NativeRolesStore extends AbstractComponent implements RolesStore, C
private volatile boolean securityIndexExists = false;
@Inject
public NativeRolesStore(Settings settings, InternalClient client, ThreadPool threadPool) {
super(settings);
this.client = client;

View File

@ -5,7 +5,12 @@
*/
package org.elasticsearch.xpack.security.authz.store;
import org.elasticsearch.common.inject.Inject;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Map;
import java.util.Set;
import org.elasticsearch.common.util.set.Sets;
import org.elasticsearch.xpack.security.SecurityContext;
import org.elasticsearch.xpack.security.authz.RoleDescriptor;
@ -17,12 +22,6 @@ import org.elasticsearch.xpack.security.authz.permission.TransportClientRole;
import org.elasticsearch.xpack.security.user.KibanaUser;
import org.elasticsearch.xpack.security.user.SystemUser;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Map;
import java.util.Set;
/**
*
*/
@ -30,7 +29,6 @@ public class ReservedRolesStore implements RolesStore {
private final SecurityContext securityContext;
@Inject
public ReservedRolesStore(SecurityContext securityContext) {
this.securityContext = securityContext;
}

View File

@ -5,26 +5,26 @@
*/
package org.elasticsearch.xpack.security.transport;
import org.elasticsearch.common.logging.ESLogger;
import org.elasticsearch.common.logging.Loggers;
import org.elasticsearch.common.util.concurrent.ThreadContext;
import org.elasticsearch.transport.TcpTransportChannel;
import org.elasticsearch.xpack.security.authc.Authentication;
import org.elasticsearch.xpack.security.action.SecurityActionMapper;
import org.elasticsearch.xpack.security.authc.AuthenticationService;
import org.elasticsearch.xpack.security.authc.pki.PkiRealm;
import org.elasticsearch.xpack.security.authz.AuthorizationService;
import org.elasticsearch.transport.DelegatingTransportChannel;
import org.elasticsearch.transport.TransportChannel;
import org.elasticsearch.transport.TransportRequest;
import org.jboss.netty.channel.Channel;
import org.jboss.netty.handler.ssl.SslHandler;
import javax.net.ssl.SSLPeerUnverifiedException;
import java.io.IOException;
import java.security.cert.Certificate;
import java.security.cert.X509Certificate;
import org.elasticsearch.common.logging.ESLogger;
import org.elasticsearch.common.logging.Loggers;
import org.elasticsearch.common.util.concurrent.ThreadContext;
import org.elasticsearch.transport.DelegatingTransportChannel;
import org.elasticsearch.transport.TcpTransportChannel;
import org.elasticsearch.transport.TransportChannel;
import org.elasticsearch.transport.TransportRequest;
import org.elasticsearch.xpack.security.action.SecurityActionMapper;
import org.elasticsearch.xpack.security.authc.Authentication;
import org.elasticsearch.xpack.security.authc.AuthenticationService;
import org.elasticsearch.xpack.security.authc.pki.PkiRealm;
import org.elasticsearch.xpack.security.authz.AuthorizationService;
import org.jboss.netty.channel.Channel;
import org.jboss.netty.handler.ssl.SslHandler;
import static org.elasticsearch.xpack.security.support.Exceptions.authenticationError;
/**

View File

@ -19,6 +19,7 @@ import org.elasticsearch.common.transport.TransportAddress;
import org.elasticsearch.xpack.security.audit.AuditTrail;
import org.elasticsearch.xpack.security.SecurityLicenseState;
import org.elasticsearch.transport.TransportSettings;
import org.elasticsearch.xpack.security.audit.AuditTrailService;
import java.net.InetAddress;
import java.util.ArrayList;
@ -105,7 +106,7 @@ public class IPFilter {
private final SetOnce<Map<String, BoundTransportAddress>> profileBoundAddress = new SetOnce<>();
@Inject
public IPFilter(final Settings settings, AuditTrail auditTrail, ClusterSettings clusterSettings,
public IPFilter(final Settings settings, AuditTrailService auditTrail, ClusterSettings clusterSettings,
SecurityLicenseState licenseState) {
this.logger = Loggers.getLogger(getClass(), settings);
this.auditTrail = auditTrail;

View File

@ -7,12 +7,12 @@ package org.elasticsearch.integration;
import org.apache.http.HttpEntity;
import org.apache.http.StatusLine;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.StringEntity;
import org.apache.http.message.BasicHeader;
import org.apache.http.util.EntityUtils;
import org.elasticsearch.client.Response;
import org.elasticsearch.client.ResponseException;
import org.elasticsearch.client.RestClient;
import org.elasticsearch.test.SecurityIntegTestCase;
import org.elasticsearch.xpack.security.authc.support.Hasher;
import org.elasticsearch.xpack.security.authc.support.SecuredString;
@ -36,14 +36,13 @@ public abstract class AbstractPrivilegeTestCase extends SecurityIntegTestCase {
protected void assertAccessIsAllowed(String user, String method, String uri, String body,
Map<String, String> params) throws IOException {
try (Response response = getRestClient().performRequest(method, uri, params, entityOrNull(body),
Response response = getRestClient().performRequest(method, uri, params, entityOrNull(body),
new BasicHeader(UsernamePasswordToken.BASIC_AUTH_HEADER,
UsernamePasswordToken.basicAuthHeaderValue(user, new SecuredString("passwd".toCharArray()))))) {
StatusLine statusLine = response.getStatusLine();
String message = String.format(Locale.ROOT, "%s %s: Expected no error got %s %s with body %s", method, uri,
statusLine.getStatusCode(), statusLine.getReasonPhrase(), EntityUtils.toString(response.getEntity()));
assertThat(message, statusLine.getStatusCode(), is(not(greaterThanOrEqualTo(400))));
}
UsernamePasswordToken.basicAuthHeaderValue(user, new SecuredString("passwd".toCharArray()))));
StatusLine statusLine = response.getStatusLine();
String message = String.format(Locale.ROOT, "%s %s: Expected no error got %s %s with body %s", method, uri,
statusLine.getStatusCode(), statusLine.getReasonPhrase(), EntityUtils.toString(response.getEntity()));
assertThat(message, statusLine.getStatusCode(), is(not(greaterThanOrEqualTo(400))));
}
protected void assertAccessIsAllowed(String user, String method, String uri, String body) throws IOException {
@ -72,7 +71,7 @@ public abstract class AbstractPrivilegeTestCase extends SecurityIntegTestCase {
} catch(ResponseException e) {
StatusLine statusLine = e.getResponse().getStatusLine();
String message = String.format(Locale.ROOT, "%s %s body %s: Expected 403, got %s %s with body %s", method, uri, body,
statusLine.getStatusCode(), statusLine.getReasonPhrase(), e.getResponseBody());
statusLine.getStatusCode(), statusLine.getReasonPhrase(), EntityUtils.toString(e.getResponse().getEntity()));
assertThat(message, statusLine.getStatusCode(), is(403));
}
}
@ -80,7 +79,7 @@ public abstract class AbstractPrivilegeTestCase extends SecurityIntegTestCase {
private static HttpEntity entityOrNull(String body) {
HttpEntity entity = null;
if (body != null) {
entity = new StringEntity(body, RestClient.JSON_CONTENT_TYPE);
entity = new StringEntity(body, ContentType.APPLICATION_JSON);
}
return entity;
}

View File

@ -6,6 +6,7 @@
package org.elasticsearch.integration;
import org.apache.http.Header;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.StringEntity;
import org.apache.http.message.BasicHeader;
import org.apache.http.util.EntityUtils;
@ -13,15 +14,14 @@ import org.elasticsearch.action.bulk.BulkResponse;
import org.elasticsearch.action.get.GetResponse;
import org.elasticsearch.action.update.UpdateResponse;
import org.elasticsearch.client.Response;
import org.elasticsearch.client.RestClient;
import org.elasticsearch.common.network.NetworkModule;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.xpack.security.Security;
import org.elasticsearch.xpack.security.authc.support.SecuredString;
import org.elasticsearch.xpack.security.authc.support.UsernamePasswordToken;
import org.elasticsearch.test.SecurityIntegTestCase;
import org.elasticsearch.test.SecuritySettingsSource;
import org.elasticsearch.xpack.XPackPlugin;
import org.elasticsearch.xpack.security.Security;
import org.elasticsearch.xpack.security.authc.support.SecuredString;
import org.elasticsearch.xpack.security.authc.support.UsernamePasswordToken;
import java.io.IOException;
import java.util.Collections;
@ -79,50 +79,42 @@ public class BulkUpdateTests extends SecurityIntegTestCase {
UsernamePasswordToken.basicAuthHeaderValue(SecuritySettingsSource.DEFAULT_USER_NAME,
new SecuredString(SecuritySettingsSource.DEFAULT_PASSWORD.toCharArray())));
StringEntity body = new StringEntity("{\"test\":\"test\"}", RestClient.JSON_CONTENT_TYPE);
try (Response response = getRestClient().performRequest("PUT", path, Collections.emptyMap(), body, basicAuthHeader)) {
assertThat(response.getStatusLine().getStatusCode(), equalTo(201));
}
StringEntity body = new StringEntity("{\"test\":\"test\"}", ContentType.APPLICATION_JSON);
Response response = getRestClient().performRequest("PUT", path, Collections.emptyMap(), body, basicAuthHeader);
assertThat(response.getStatusLine().getStatusCode(), equalTo(201));
try (Response response = getRestClient().performRequest("GET", path, basicAuthHeader)) {
assertThat(response.getStatusLine().getStatusCode(), equalTo(200));
assertThat(EntityUtils.toString(response.getEntity()), containsString("\"test\":\"test\""));
}
response = getRestClient().performRequest("GET", path, basicAuthHeader);
assertThat(response.getStatusLine().getStatusCode(), equalTo(200));
assertThat(EntityUtils.toString(response.getEntity()), containsString("\"test\":\"test\""));
if (randomBoolean()) {
flushAndRefresh();
}
//update with new field
body = new StringEntity("{\"doc\": {\"not test\": \"not test\"}}", RestClient.JSON_CONTENT_TYPE);
try (Response response = getRestClient().performRequest("POST", path + "/_update",
Collections.emptyMap(), body, basicAuthHeader)) {
assertThat(response.getStatusLine().getStatusCode(), equalTo(200));
}
body = new StringEntity("{\"doc\": {\"not test\": \"not test\"}}", ContentType.APPLICATION_JSON);
response = getRestClient().performRequest("POST", path + "/_update", Collections.emptyMap(), body, basicAuthHeader);
assertThat(response.getStatusLine().getStatusCode(), equalTo(200));
try (Response response = getRestClient().performRequest("GET", path, basicAuthHeader)) {
assertThat(response.getStatusLine().getStatusCode(), equalTo(200));
String responseBody = EntityUtils.toString(response.getEntity());
assertThat(responseBody, containsString("\"test\":\"test\""));
assertThat(responseBody, containsString("\"not test\":\"not test\""));
}
response = getRestClient().performRequest("GET", path, basicAuthHeader);
assertThat(response.getStatusLine().getStatusCode(), equalTo(200));
String responseBody = EntityUtils.toString(response.getEntity());
assertThat(responseBody, containsString("\"test\":\"test\""));
assertThat(responseBody, containsString("\"not test\":\"not test\""));
// this part is important. Without this, the document may be read from the translog which would bypass the bug where
// FLS kicks in because the request can't be found and only returns meta fields
flushAndRefresh();
body = new StringEntity("{\"update\": {\"_index\": \"index1\", \"_type\": \"type\", \"_id\": \"1\"}}\n" +
"{\"doc\": {\"bulk updated\":\"bulk updated\"}}\n", RestClient.JSON_CONTENT_TYPE);
try (Response response = getRestClient().performRequest("POST", "/_bulk",
Collections.emptyMap(), body, basicAuthHeader)) {
assertThat(response.getStatusLine().getStatusCode(), equalTo(200));
}
"{\"doc\": {\"bulk updated\":\"bulk updated\"}}\n", ContentType.APPLICATION_JSON);
response = getRestClient().performRequest("POST", "/_bulk", Collections.emptyMap(), body, basicAuthHeader);
assertThat(response.getStatusLine().getStatusCode(), equalTo(200));
try (Response response = getRestClient().performRequest("GET", path, basicAuthHeader)) {
String responseBody = EntityUtils.toString(response.getEntity());
assertThat(responseBody, containsString("\"test\":\"test\""));
assertThat(responseBody, containsString("\"not test\":\"not test\""));
assertThat(responseBody, containsString("\"bulk updated\":\"bulk updated\""));
}
response = getRestClient().performRequest("GET", path, basicAuthHeader);
responseBody = EntityUtils.toString(response.getEntity());
assertThat(responseBody, containsString("\"test\":\"test\""));
assertThat(responseBody, containsString("\"not test\":\"not test\""));
assertThat(responseBody, containsString("\"bulk updated\":\"bulk updated\""));
}
}

View File

@ -162,13 +162,12 @@ public class ClearRealmsCacheTests extends SecurityIntegTestCase {
}
static void executeHttpRequest(String path, Map<String, String> params) throws Exception {
try (Response response = getRestClient().performRequest("POST", path, params,
Response response = getRestClient().performRequest("POST", path, params,
new BasicHeader(UsernamePasswordToken.BASIC_AUTH_HEADER,
UsernamePasswordToken.basicAuthHeaderValue(SecuritySettingsSource.DEFAULT_USER_NAME,
new SecuredString(SecuritySettingsSource.DEFAULT_PASSWORD.toCharArray()))))) {
assertNotNull(response.getEntity());
assertTrue(EntityUtils.toString(response.getEntity()).contains("cluster_name"));
}
new SecuredString(SecuritySettingsSource.DEFAULT_PASSWORD.toCharArray()))));
assertNotNull(response.getEntity());
assertTrue(EntityUtils.toString(response.getEntity()).contains("cluster_name"));
}
}

View File

@ -137,12 +137,11 @@ public class ClearRolesCacheTests extends NativeRealmIntegTestCase {
} else {
path = "/_xpack/security/role/" + Strings.arrayToCommaDelimitedString(rolesToClear) + "/_clear_cache";
}
try (Response response = getRestClient().performRequest("POST", path,
Response response = getRestClient().performRequest("POST", path,
new BasicHeader(UsernamePasswordToken.BASIC_AUTH_HEADER,
UsernamePasswordToken.basicAuthHeaderValue(SecuritySettingsSource.DEFAULT_USER_NAME,
new SecuredString(SecuritySettingsSource.DEFAULT_PASSWORD.toCharArray()))))) {
assertThat(response.getStatusLine().getStatusCode(), is(RestStatus.OK.getStatus()));
}
new SecuredString(SecuritySettingsSource.DEFAULT_PASSWORD.toCharArray()))));
assertThat(response.getStatusLine().getStatusCode(), is(RestStatus.OK.getStatus()));
} else {
securityClient.prepareClearRolesCache().names(rolesToClear).get();
}

View File

@ -34,9 +34,8 @@ import org.elasticsearch.common.util.concurrent.ThreadContext;
import org.elasticsearch.env.Environment;
import org.elasticsearch.license.core.License.OperationMode;
import org.elasticsearch.license.plugin.Licensing;
import org.elasticsearch.license.plugin.core.LicenseState;
import org.elasticsearch.license.plugin.core.LicenseService;
import org.elasticsearch.license.plugin.core.Licensee;
import org.elasticsearch.license.plugin.core.LicensesService;
import org.elasticsearch.plugins.Plugin;
import org.elasticsearch.rest.RestHandler;
import org.elasticsearch.rest.RestStatus;
@ -185,10 +184,9 @@ public class LicensingTests extends SecurityIntegTestCase {
}
public void testRestAuthenticationByLicenseType() throws Exception {
try (Response response = getRestClient().performRequest("GET", "/")) {
// the default of the licensing tests is basic
assertThat(response.getStatusLine().getStatusCode(), is(200));
}
Response response = getRestClient().performRequest("GET", "/");
// the default of the licensing tests is basic
assertThat(response.getStatusLine().getStatusCode(), is(200));
// generate a new license with a mode that enables auth
OperationMode mode = randomFrom(OperationMode.GOLD, OperationMode.TRIAL, OperationMode.PLATINUM, OperationMode.STANDARD);
@ -238,7 +236,7 @@ public class LicensingTests extends SecurityIntegTestCase {
}
public static void disableLicensing(OperationMode operationMode) {
for (TestLicensesService service : internalCluster().getInstances(TestLicensesService.class)) {
for (TestLicenseService service : internalCluster().getInstances(TestLicenseService.class)) {
service.disable(operationMode);
}
}
@ -248,7 +246,7 @@ public class LicensingTests extends SecurityIntegTestCase {
}
public static void enableLicensing(OperationMode operationMode) {
for (TestLicensesService service : internalCluster().getInstances(TestLicensesService.class)) {
for (TestLicenseService service : internalCluster().getInstances(TestLicenseService.class)) {
service.enable(operationMode);
}
}
@ -257,7 +255,7 @@ public class LicensingTests extends SecurityIntegTestCase {
@Override
public Collection<Module> nodeModules() {
return Collections.singletonList(b -> b.bind(LicensesService.class).to(TestLicensesService.class));
return Collections.singletonList(b -> b.bind(LicenseService.class).to(TestLicenseService.class));
}
@Override
@ -268,7 +266,7 @@ public class LicensingTests extends SecurityIntegTestCase {
WatcherLicensee watcherLicensee = new WatcherLicensee(settings);
MonitoringLicensee monitoringLicensee = new MonitoringLicensee(settings);
GraphLicensee graphLicensee = new GraphLicensee(settings);
TestLicensesService licensesService = new TestLicensesService(settings, environment, resourceWatcherService,
TestLicenseService licensesService = new TestLicenseService(settings, environment, resourceWatcherService,
Arrays.asList(securityLicensee, watcherLicensee, monitoringLicensee, graphLicensee));
return Arrays.asList(securityLicensee, licensesService, watcherLicensee, monitoringLicensee,
graphLicensee, securityLicenseState);
@ -297,12 +295,12 @@ public class LicensingTests extends SecurityIntegTestCase {
}
}
public static class TestLicensesService extends LicensesService {
public static class TestLicenseService extends LicenseService {
private final List<Licensee> licensees;
public TestLicensesService(Settings settings, Environment env, ResourceWatcherService resourceWatcherService,
List<Licensee> licensees) {
public TestLicenseService(Settings settings, Environment env, ResourceWatcherService resourceWatcherService,
List<Licensee> licensees) {
super(settings, null, null, env, resourceWatcherService, Collections.emptyList());
this.licensees = licensees;
enable(OperationMode.BASIC);
@ -310,13 +308,13 @@ public class LicensingTests extends SecurityIntegTestCase {
void enable(OperationMode operationMode) {
for (Licensee licensee : licensees) {
licensee.onChange(new Licensee.Status(operationMode, LicenseState.ENABLED));
licensee.onChange(new Licensee.Status(operationMode, true));
}
}
void disable(OperationMode operationMode) {
for (Licensee licensee : licensees) {
licensee.onChange(new Licensee.Status(operationMode, LicenseState.DISABLED));
licensee.onChange(new Licensee.Status(operationMode, false));
}
}

View File

@ -13,6 +13,7 @@ import org.elasticsearch.xpack.XPackFeatureSet;
import org.elasticsearch.xpack.security.audit.AuditTrailService;
import org.elasticsearch.xpack.security.authc.Realm;
import org.elasticsearch.xpack.security.authc.Realms;
import org.elasticsearch.xpack.security.authz.store.CompositeRolesStore;
import org.elasticsearch.xpack.security.authz.store.RolesStore;
import org.elasticsearch.xpack.security.crypto.CryptoService;
import org.elasticsearch.xpack.security.transport.filter.IPFilter;
@ -44,7 +45,7 @@ public class SecurityFeatureSetTests extends ESTestCase {
private Realms realms;
private NamedWriteableRegistry namedWriteableRegistry;
private IPFilter ipFilter;
private RolesStore rolesStore;
private CompositeRolesStore rolesStore;
private AuditTrailService auditTrail;
private CryptoService cryptoService;
@ -55,7 +56,7 @@ public class SecurityFeatureSetTests extends ESTestCase {
realms = mock(Realms.class);
namedWriteableRegistry = mock(NamedWriteableRegistry.class);
ipFilter = mock(IPFilter.class);
rolesStore = mock(RolesStore.class);
rolesStore = mock(CompositeRolesStore.class);
auditTrail = mock(AuditTrailService.class);
cryptoService = mock(CryptoService.class);
}

View File

@ -7,7 +7,6 @@ package org.elasticsearch.xpack.security;
import org.elasticsearch.license.core.License;
import org.elasticsearch.license.core.License.OperationMode;
import org.elasticsearch.license.plugin.core.LicenseState;
import org.elasticsearch.license.plugin.core.Licensee;
import org.elasticsearch.xpack.security.SecurityLicenseState.EnabledRealmType;
import org.elasticsearch.test.ESTestCase;
@ -31,8 +30,7 @@ public class SecurityLicenseStateTests extends ESTestCase {
public void testBasic() {
SecurityLicenseState licenseState = new SecurityLicenseState();
licenseState.updateStatus(new Licensee.Status(License.OperationMode.BASIC,
randomBoolean() ? LicenseState.ENABLED : LicenseState.GRACE_PERIOD));
licenseState.updateStatus(new Licensee.Status(License.OperationMode.BASIC, true));
assertThat(licenseState.authenticationAndAuthorizationEnabled(), is(false));
assertThat(licenseState.ipFilteringEnabled(), is(false));
@ -44,7 +42,7 @@ public class SecurityLicenseStateTests extends ESTestCase {
public void testBasicExpired() {
SecurityLicenseState licenseState = new SecurityLicenseState();
licenseState.updateStatus(new Licensee.Status(License.OperationMode.BASIC, LicenseState.DISABLED));
licenseState.updateStatus(new Licensee.Status(License.OperationMode.BASIC, false));
assertThat(licenseState.authenticationAndAuthorizationEnabled(), is(false));
assertThat(licenseState.ipFilteringEnabled(), is(false));
@ -56,8 +54,7 @@ public class SecurityLicenseStateTests extends ESTestCase {
public void testStandard() {
SecurityLicenseState licenseState = new SecurityLicenseState();
licenseState.updateStatus(new Licensee.Status(OperationMode.STANDARD,
randomBoolean() ? LicenseState.ENABLED : LicenseState.GRACE_PERIOD));
licenseState.updateStatus(new Licensee.Status(OperationMode.STANDARD, true));
assertThat(licenseState.authenticationAndAuthorizationEnabled(), is(true));
assertThat(licenseState.ipFilteringEnabled(), is(false));
@ -69,7 +66,7 @@ public class SecurityLicenseStateTests extends ESTestCase {
public void testStandardExpired() {
SecurityLicenseState licenseState = new SecurityLicenseState();
licenseState.updateStatus(new Licensee.Status(OperationMode.STANDARD, LicenseState.DISABLED));
licenseState.updateStatus(new Licensee.Status(OperationMode.STANDARD, false));
assertThat(licenseState.authenticationAndAuthorizationEnabled(), is(true));
assertThat(licenseState.ipFilteringEnabled(), is(false));
@ -81,8 +78,7 @@ public class SecurityLicenseStateTests extends ESTestCase {
public void testGold() {
SecurityLicenseState licenseState = new SecurityLicenseState();
licenseState.updateStatus(new Licensee.Status(License.OperationMode.GOLD,
randomBoolean() ? LicenseState.ENABLED : LicenseState.GRACE_PERIOD));
licenseState.updateStatus(new Licensee.Status(License.OperationMode.GOLD, true));
assertThat(licenseState.authenticationAndAuthorizationEnabled(), is(true));
assertThat(licenseState.ipFilteringEnabled(), is(true));
@ -94,7 +90,7 @@ public class SecurityLicenseStateTests extends ESTestCase {
public void testGoldExpired() {
SecurityLicenseState licenseState = new SecurityLicenseState();
licenseState.updateStatus(new Licensee.Status(License.OperationMode.GOLD, LicenseState.DISABLED));
licenseState.updateStatus(new Licensee.Status(License.OperationMode.GOLD, false));
assertThat(licenseState.authenticationAndAuthorizationEnabled(), is(true));
assertThat(licenseState.ipFilteringEnabled(), is(true));
@ -106,8 +102,7 @@ public class SecurityLicenseStateTests extends ESTestCase {
public void testPlatinum() {
SecurityLicenseState licenseState = new SecurityLicenseState();
licenseState.updateStatus(new Licensee.Status(License.OperationMode.PLATINUM,
randomBoolean() ? LicenseState.ENABLED : LicenseState.GRACE_PERIOD));
licenseState.updateStatus(new Licensee.Status(License.OperationMode.PLATINUM, true));
assertThat(licenseState.authenticationAndAuthorizationEnabled(), is(true));
assertThat(licenseState.ipFilteringEnabled(), is(true));
@ -119,7 +114,7 @@ public class SecurityLicenseStateTests extends ESTestCase {
public void testPlatinumExpired() {
SecurityLicenseState licenseState = new SecurityLicenseState();
licenseState.updateStatus(new Licensee.Status(License.OperationMode.PLATINUM, LicenseState.DISABLED));
licenseState.updateStatus(new Licensee.Status(License.OperationMode.PLATINUM, false));
assertThat(licenseState.authenticationAndAuthorizationEnabled(), is(true));
assertThat(licenseState.ipFilteringEnabled(), is(true));

View File

@ -41,11 +41,10 @@ public class SecurityPluginTests extends SecurityIntegTestCase {
}
logger.info("executing authorized request to /_xpack infos");
try (Response response = getRestClient().performRequest("GET", "/_xpack",
Response response = getRestClient().performRequest("GET", "/_xpack",
new BasicHeader(UsernamePasswordToken.BASIC_AUTH_HEADER,
basicAuthHeaderValue(SecuritySettingsSource.DEFAULT_USER_NAME,
new SecuredString(SecuritySettingsSource.DEFAULT_PASSWORD.toCharArray()))))) {
assertThat(response.getStatusLine().getStatusCode(), is(OK.getStatus()));
}
new SecuredString(SecuritySettingsSource.DEFAULT_PASSWORD.toCharArray()))));
assertThat(response.getStatusLine().getStatusCode(), is(OK.getStatus()));
}
}

View File

@ -9,18 +9,18 @@ import java.io.IOException;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import org.elasticsearch.cluster.service.ClusterService;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.env.Environment;
import org.elasticsearch.test.ESTestCase;
import org.elasticsearch.threadpool.ThreadPool;
import org.elasticsearch.xpack.extensions.XPackExtension;
import org.elasticsearch.xpack.security.audit.AuditTrail;
import org.elasticsearch.xpack.security.audit.AuditTrailService;
import org.elasticsearch.xpack.security.audit.index.IndexAuditTrail;
import org.elasticsearch.xpack.security.audit.logfile.LoggingAuditTrail;
import org.elasticsearch.xpack.security.authc.AuthenticationService;
import org.elasticsearch.xpack.security.authc.Realm;
import org.elasticsearch.xpack.security.authc.Realms;
import org.elasticsearch.xpack.security.authc.file.FileRealm;
@ -54,7 +54,8 @@ public class SecurityTests extends ESTestCase {
Environment env = new Environment(settings);
Security security = new Security(settings, env);
ThreadPool threadPool = mock(ThreadPool.class);
return security.createComponents(null, threadPool, null, null, Arrays.asList(extensions));
ClusterService clusterService = mock(ClusterService.class);
return security.createComponents(null, threadPool, clusterService, null, Arrays.asList(extensions));
}
private <T> T findComponent(Class<T> type, Collection<Object> components) {
@ -91,7 +92,8 @@ public class SecurityTests extends ESTestCase {
public void testDisabledByDefault() throws Exception {
Collection<Object> components = createComponents(Settings.EMPTY);
assertNull(findComponent(AuditTrailService.class, components));
AuditTrailService auditTrailService = findComponent(AuditTrailService.class, components);
assertEquals(0, auditTrailService.getAuditTrails().size());
}
public void testIndexAuditTrail() throws Exception {

View File

@ -5,6 +5,8 @@
*/
package org.elasticsearch.xpack.security.action.filter;
import java.util.HashSet;
import org.elasticsearch.ElasticsearchSecurityException;
import org.elasticsearch.action.ActionListener;
import org.elasticsearch.action.ActionRequest;
@ -12,24 +14,22 @@ import org.elasticsearch.action.search.SearchScrollRequest;
import org.elasticsearch.action.support.ActionFilterChain;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.util.concurrent.ThreadContext;
import org.elasticsearch.xpack.security.SecurityContext;
import org.elasticsearch.xpack.security.action.SecurityActionMapper;
import org.elasticsearch.xpack.security.authc.Authentication;
import org.elasticsearch.xpack.security.authc.Authentication.RealmRef;
import org.elasticsearch.xpack.security.user.SystemUser;
import org.elasticsearch.xpack.security.user.User;
import org.elasticsearch.xpack.security.audit.AuditTrail;
import org.elasticsearch.xpack.security.authc.AuthenticationService;
import org.elasticsearch.xpack.security.authz.AuthorizationService;
import org.elasticsearch.xpack.security.crypto.CryptoService;
import org.elasticsearch.xpack.security.SecurityLicenseState;
import org.elasticsearch.tasks.Task;
import org.elasticsearch.test.ESTestCase;
import org.elasticsearch.threadpool.ThreadPool;
import org.elasticsearch.xpack.security.SecurityContext;
import org.elasticsearch.xpack.security.SecurityLicenseState;
import org.elasticsearch.xpack.security.action.SecurityActionMapper;
import org.elasticsearch.xpack.security.audit.AuditTrailService;
import org.elasticsearch.xpack.security.authc.Authentication;
import org.elasticsearch.xpack.security.authc.Authentication.RealmRef;
import org.elasticsearch.xpack.security.authc.AuthenticationService;
import org.elasticsearch.xpack.security.authz.AuthorizationService;
import org.elasticsearch.xpack.security.user.SystemUser;
import org.elasticsearch.xpack.security.user.User;
import org.elasticsearch.xpack.security.crypto.CryptoService;
import org.junit.Before;
import java.util.HashSet;
import static org.hamcrest.Matchers.equalTo;
import static org.mockito.Matchers.eq;
import static org.mockito.Matchers.isA;
@ -49,7 +49,7 @@ public class SecurityActionFilterTests extends ESTestCase {
private AuthenticationService authcService;
private AuthorizationService authzService;
private CryptoService cryptoService;
private AuditTrail auditTrail;
private AuditTrailService auditTrail;
private SecurityLicenseState securityLicenseState;
private SecurityActionFilter filter;
@ -58,7 +58,7 @@ public class SecurityActionFilterTests extends ESTestCase {
authcService = mock(AuthenticationService.class);
authzService = mock(AuthorizationService.class);
cryptoService = mock(CryptoService.class);
auditTrail = mock(AuditTrail.class);
auditTrail = mock(AuditTrailService.class);
securityLicenseState = mock(SecurityLicenseState.class);
when(securityLicenseState.authenticationAndAuthorizationEnabled()).thenReturn(true);
when(securityLicenseState.statsAndHealthEnabled()).thenReturn(true);

View File

@ -14,6 +14,8 @@ import org.elasticsearch.test.ESIntegTestCase.Scope;
import org.elasticsearch.test.InternalTestCluster;
import org.elasticsearch.test.SecurityIntegTestCase;
import org.elasticsearch.test.SecuritySettingsSource;
import org.elasticsearch.xpack.security.audit.AuditTrail;
import org.elasticsearch.xpack.security.audit.AuditTrailService;
import org.junit.After;
import org.junit.Before;
@ -22,6 +24,7 @@ import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.concurrent.TimeUnit;
@ -137,10 +140,13 @@ public class RemoteIndexAuditTrailStartingTests extends SecurityIntegTestCase {
}
public void testThatRemoteAuditInstancesAreStarted() throws Exception {
Iterable<IndexAuditTrail> auditTrails = remoteCluster.getInstances(IndexAuditTrail.class);
for (final IndexAuditTrail auditTrail : auditTrails) {
awaitBusy(() -> auditTrail.state() == IndexAuditTrail.State.STARTED, 2L, TimeUnit.SECONDS);
assertThat(auditTrail.state(), is(IndexAuditTrail.State.STARTED));
}
AuditTrailService auditTrailService = remoteCluster.getInstance(AuditTrailService.class);
Optional<AuditTrail> auditTrail = auditTrailService.getAuditTrails().stream()
.filter(t -> t.name().equals(IndexAuditTrail.NAME)).findFirst();
assertTrue(auditTrail.isPresent());
IndexAuditTrail indexAuditTrail = (IndexAuditTrail)auditTrail.get();
awaitBusy(() -> indexAuditTrail.state() == IndexAuditTrail.State.STARTED, 2L, TimeUnit.SECONDS);
assertThat(indexAuditTrail.state(), is(IndexAuditTrail.State.STARTED));
}
}

View File

@ -5,6 +5,10 @@
*/
package org.elasticsearch.xpack.security.authc;
import java.io.IOException;
import java.util.Arrays;
import java.util.Collections;
import org.elasticsearch.ElasticsearchException;
import org.elasticsearch.ElasticsearchSecurityException;
import org.elasticsearch.common.io.stream.BytesStreamOutput;
@ -12,33 +16,28 @@ import org.elasticsearch.common.io.stream.StreamInput;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.util.concurrent.ThreadContext;
import org.elasticsearch.env.Environment;
import org.elasticsearch.rest.RestController;
import org.elasticsearch.rest.RestRequest;
import org.elasticsearch.xpack.security.authc.Authentication.RealmRef;
import org.elasticsearch.xpack.security.authc.InternalAuthenticationService.Authenticator;
import org.elasticsearch.xpack.security.SecurityLicenseState.EnabledRealmType;
import org.elasticsearch.xpack.security.user.AnonymousUser;
import org.elasticsearch.xpack.security.user.SystemUser;
import org.elasticsearch.xpack.security.user.User;
import org.elasticsearch.xpack.security.audit.AuditTrail;
import org.elasticsearch.xpack.security.authc.esnative.ReservedRealm;
import org.elasticsearch.xpack.security.authc.support.SecuredString;
import org.elasticsearch.xpack.security.authc.support.UsernamePasswordToken;
import org.elasticsearch.xpack.security.crypto.CryptoService;
import org.elasticsearch.xpack.security.SecurityLicenseState;
import org.elasticsearch.test.ESTestCase;
import org.elasticsearch.test.rest.FakeRestRequest;
import org.elasticsearch.threadpool.ThreadPool;
import org.elasticsearch.transport.TransportMessage;
import org.elasticsearch.xpack.security.SecurityLicenseState;
import org.elasticsearch.xpack.security.SecurityLicenseState.EnabledRealmType;
import org.elasticsearch.xpack.security.audit.AuditTrailService;
import org.elasticsearch.xpack.security.authc.Authentication.RealmRef;
import org.elasticsearch.xpack.security.authc.AuthenticationService.Authenticator;
import org.elasticsearch.xpack.security.authc.esnative.ReservedRealm;
import org.elasticsearch.xpack.security.authc.support.SecuredString;
import org.elasticsearch.xpack.security.authc.support.UsernamePasswordToken;
import org.elasticsearch.xpack.security.crypto.CryptoService;
import org.elasticsearch.xpack.security.user.AnonymousUser;
import org.elasticsearch.xpack.security.user.SystemUser;
import org.elasticsearch.xpack.security.user.User;
import org.junit.After;
import org.junit.Before;
import java.io.IOException;
import java.util.Arrays;
import java.util.Collections;
import static org.elasticsearch.xpack.security.support.Exceptions.authenticationError;
import static org.elasticsearch.test.SecurityTestsUtils.assertAuthenticationException;
import static org.elasticsearch.xpack.security.support.Exceptions.authenticationError;
import static org.hamcrest.Matchers.arrayContaining;
import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.equalTo;
@ -60,15 +59,15 @@ import static org.mockito.Mockito.when;
/**
*
*/
public class InternalAuthenticationServiceTests extends ESTestCase {
public class AuthenticationServiceTests extends ESTestCase {
InternalAuthenticationService service;
AuthenticationService service;
TransportMessage message;
RestRequest restRequest;
Realms realms;
Realm firstRealm;
Realm secondRealm;
AuditTrail auditTrail;
AuditTrailService auditTrail;
AuthenticationToken token;
CryptoService cryptoService;
ThreadPool threadPool;
@ -105,12 +104,12 @@ public class InternalAuthenticationServiceTests extends ESTestCase {
realms.start();
cryptoService = mock(CryptoService.class);
auditTrail = mock(AuditTrail.class);
auditTrail = mock(AuditTrailService.class);
threadPool = mock(ThreadPool.class);
threadContext = new ThreadContext(Settings.EMPTY);
when(threadPool.getThreadContext()).thenReturn(threadContext);
when(cryptoService.sign(any(String.class))).thenReturn("_signed_auth");
service = new InternalAuthenticationService(settings, realms, auditTrail, cryptoService,
service = new AuthenticationService(settings, realms, auditTrail, cryptoService,
new DefaultAuthenticationFailureHandler(), threadPool);
}
@ -309,7 +308,7 @@ public class InternalAuthenticationServiceTests extends ESTestCase {
InternalMessage message1 = new InternalMessage();
ThreadContext threadContext1 = new ThreadContext(Settings.EMPTY);
when(threadPool.getThreadContext()).thenReturn(threadContext1);
service = new InternalAuthenticationService(Settings.EMPTY, realms, auditTrail, cryptoService,
service = new AuthenticationService(Settings.EMPTY, realms, auditTrail, cryptoService,
new DefaultAuthenticationFailureHandler(), threadPool);
threadContext1.putTransient(Authentication.AUTHENTICATION_KEY, threadContext.getTransient(Authentication.AUTHENTICATION_KEY));
@ -323,7 +322,7 @@ public class InternalAuthenticationServiceTests extends ESTestCase {
// checking authentication from the user header
threadContext1 = new ThreadContext(Settings.EMPTY);
when(threadPool.getThreadContext()).thenReturn(threadContext1);
service = new InternalAuthenticationService(Settings.EMPTY, realms, auditTrail, cryptoService,
service = new AuthenticationService(Settings.EMPTY, realms, auditTrail, cryptoService,
new DefaultAuthenticationFailureHandler(), threadPool);
threadContext1.putHeader(Authentication.AUTHENTICATION_KEY, threadContext.getHeader(Authentication.AUTHENTICATION_KEY));
when(cryptoService.unsignAndVerify("_signed_auth")).thenReturn(authentication.encode());
@ -335,7 +334,7 @@ public class InternalAuthenticationServiceTests extends ESTestCase {
threadContext1.readHeaders(input);
when(threadPool.getThreadContext()).thenReturn(threadContext1);
service = new InternalAuthenticationService(Settings.EMPTY, realms, auditTrail, cryptoService,
service = new AuthenticationService(Settings.EMPTY, realms, auditTrail, cryptoService,
new DefaultAuthenticationFailureHandler(), threadPool);
Authentication result = service.authenticate("_action", new InternalMessage(), SystemUser.INSTANCE);
assertThat(result, notNullValue());
@ -344,8 +343,8 @@ public class InternalAuthenticationServiceTests extends ESTestCase {
}
public void testAuthenticateTransportContextAndHeaderNoSigning() throws Exception {
Settings settings = Settings.builder().put(InternalAuthenticationService.SIGN_USER_HEADER.getKey(), false).build();
service = new InternalAuthenticationService(settings, realms, auditTrail, cryptoService,
Settings settings = Settings.builder().put(AuthenticationService.SIGN_USER_HEADER.getKey(), false).build();
service = new AuthenticationService(settings, realms, auditTrail, cryptoService,
new DefaultAuthenticationFailureHandler(), threadPool);
User user1 = new User("username", "r1", "r2");
@ -362,7 +361,7 @@ public class InternalAuthenticationServiceTests extends ESTestCase {
InternalMessage message1 = new InternalMessage();
ThreadContext threadContext1 = new ThreadContext(Settings.EMPTY);
when(threadPool.getThreadContext()).thenReturn(threadContext1);
service = new InternalAuthenticationService(Settings.EMPTY, realms, auditTrail, cryptoService,
service = new AuthenticationService(Settings.EMPTY, realms, auditTrail, cryptoService,
new DefaultAuthenticationFailureHandler(), threadPool);
threadContext1.putTransient(Authentication.AUTHENTICATION_KEY, threadContext.getTransient(Authentication.AUTHENTICATION_KEY));
threadContext1.putHeader(Authentication.AUTHENTICATION_KEY, threadContext.getHeader(Authentication.AUTHENTICATION_KEY));
@ -382,7 +381,7 @@ public class InternalAuthenticationServiceTests extends ESTestCase {
threadContext1.readHeaders(input);
when(threadPool.getThreadContext()).thenReturn(threadContext1);
service = new InternalAuthenticationService(settings, realms, auditTrail, cryptoService,
service = new AuthenticationService(settings, realms, auditTrail, cryptoService,
new DefaultAuthenticationFailureHandler(), threadPool);
Authentication result = service.authenticate("_action", new InternalMessage(), SystemUser.INSTANCE);
assertThat(result, notNullValue());
@ -445,7 +444,7 @@ public class InternalAuthenticationServiceTests extends ESTestCase {
}
Settings settings = builder.build();
AnonymousUser.initialize(settings);
service = new InternalAuthenticationService(settings, realms, auditTrail, cryptoService, new DefaultAuthenticationFailureHandler(),
service = new AuthenticationService(settings, realms, auditTrail, cryptoService, new DefaultAuthenticationFailureHandler(),
threadPool);
RestRequest request = new FakeRestRequest();
@ -461,7 +460,7 @@ public class InternalAuthenticationServiceTests extends ESTestCase {
.putArray(AnonymousUser.ROLES_SETTING.getKey(), "r1", "r2", "r3")
.build();
AnonymousUser.initialize(settings);
service = new InternalAuthenticationService(settings, realms, auditTrail, cryptoService,
service = new AuthenticationService(settings, realms, auditTrail, cryptoService,
new DefaultAuthenticationFailureHandler(), threadPool);
InternalMessage message = new InternalMessage();
@ -476,7 +475,7 @@ public class InternalAuthenticationServiceTests extends ESTestCase {
.putArray(AnonymousUser.ROLES_SETTING.getKey(), "r1", "r2", "r3")
.build();
AnonymousUser.initialize(settings);
service = new InternalAuthenticationService(settings, realms, auditTrail, cryptoService,
service = new AuthenticationService(settings, realms, auditTrail, cryptoService,
new DefaultAuthenticationFailureHandler(), threadPool);
InternalMessage message = new InternalMessage();
@ -565,7 +564,7 @@ public class InternalAuthenticationServiceTests extends ESTestCase {
public void testRealmLookupThrowingException() throws Exception {
AuthenticationToken token = mock(AuthenticationToken.class);
threadContext.putHeader(InternalAuthenticationService.RUN_AS_USER_HEADER, "run_as");
threadContext.putHeader(AuthenticationService.RUN_AS_USER_HEADER, "run_as");
when(secondRealm.token(threadContext)).thenReturn(token);
when(secondRealm.supports(token)).thenReturn(true);
when(secondRealm.authenticate(token)).thenReturn(new User("lookup user", new String[]{"user"}));
@ -583,7 +582,7 @@ public class InternalAuthenticationServiceTests extends ESTestCase {
public void testRealmLookupThrowingExceptionRest() throws Exception {
AuthenticationToken token = mock(AuthenticationToken.class);
threadContext.putHeader(InternalAuthenticationService.RUN_AS_USER_HEADER, "run_as");
threadContext.putHeader(AuthenticationService.RUN_AS_USER_HEADER, "run_as");
when(secondRealm.token(threadContext)).thenReturn(token);
when(secondRealm.supports(token)).thenReturn(true);
when(secondRealm.authenticate(token)).thenReturn(new User("lookup user", new String[]{"user"}));
@ -601,7 +600,7 @@ public class InternalAuthenticationServiceTests extends ESTestCase {
public void testRunAsLookupSameRealm() throws Exception {
AuthenticationToken token = mock(AuthenticationToken.class);
threadContext.putHeader(InternalAuthenticationService.RUN_AS_USER_HEADER, "run_as");
threadContext.putHeader(AuthenticationService.RUN_AS_USER_HEADER, "run_as");
when(secondRealm.token(threadContext)).thenReturn(token);
when(secondRealm.supports(token)).thenReturn(true);
when(secondRealm.authenticate(token)).thenReturn(new User("lookup user", new String[]{"user"}));
@ -628,7 +627,7 @@ public class InternalAuthenticationServiceTests extends ESTestCase {
public void testRunAsLookupDifferentRealm() throws Exception {
AuthenticationToken token = mock(AuthenticationToken.class);
threadContext.putHeader(InternalAuthenticationService.RUN_AS_USER_HEADER, "run_as");
threadContext.putHeader(AuthenticationService.RUN_AS_USER_HEADER, "run_as");
when(secondRealm.token(threadContext)).thenReturn(token);
when(secondRealm.supports(token)).thenReturn(true);
when(secondRealm.authenticate(token)).thenReturn(new User("lookup user", new String[]{"user"}));
@ -657,7 +656,7 @@ public class InternalAuthenticationServiceTests extends ESTestCase {
public void testRunAsWithEmptyRunAsUsernameRest() throws Exception {
AuthenticationToken token = mock(AuthenticationToken.class);
User user = new User("lookup user", new String[]{"user"});
threadContext.putHeader(InternalAuthenticationService.RUN_AS_USER_HEADER, "");
threadContext.putHeader(AuthenticationService.RUN_AS_USER_HEADER, "");
when(secondRealm.token(threadContext)).thenReturn(token);
when(secondRealm.supports(token)).thenReturn(true);
when(secondRealm.authenticate(token)).thenReturn(user);
@ -675,7 +674,7 @@ public class InternalAuthenticationServiceTests extends ESTestCase {
public void testRunAsWithEmptyRunAsUsername() throws Exception {
AuthenticationToken token = mock(AuthenticationToken.class);
User user = new User("lookup user", new String[]{"user"});
threadContext.putHeader(InternalAuthenticationService.RUN_AS_USER_HEADER, "");
threadContext.putHeader(AuthenticationService.RUN_AS_USER_HEADER, "");
when(secondRealm.token(threadContext)).thenReturn(token);
when(secondRealm.supports(token)).thenReturn(true);
when(secondRealm.authenticate(token)).thenReturn(user);

View File

@ -95,7 +95,7 @@ public class RunAsIntegTests extends SecurityIntegTestCase {
// let's run as without authorization
try {
Map<String, String> headers = Collections.singletonMap(InternalAuthenticationService.RUN_AS_USER_HEADER,
Map<String, String> headers = Collections.singletonMap(AuthenticationService.RUN_AS_USER_HEADER,
SecuritySettingsSource.DEFAULT_USER_NAME);
client.filterWithHeader(headers)
.admin().cluster().prepareHealth().get();
@ -108,7 +108,7 @@ public class RunAsIntegTests extends SecurityIntegTestCase {
Map<String, String> headers = new HashMap<>();
headers.put("Authorization", UsernamePasswordToken.basicAuthHeaderValue(RUN_AS_USER,
new SecuredString(SecuritySettingsSource.DEFAULT_PASSWORD.toCharArray())));
headers.put(InternalAuthenticationService.RUN_AS_USER_HEADER, SecuritySettingsSource.DEFAULT_USER_NAME);
headers.put(AuthenticationService.RUN_AS_USER_HEADER, SecuritySettingsSource.DEFAULT_USER_NAME);
// lets set the user
ClusterHealthResponse response = client.filterWithHeader(headers).admin().cluster().prepareHealth().get();
assertThat(response.isTimedOut(), is(false));
@ -122,7 +122,7 @@ public class RunAsIntegTests extends SecurityIntegTestCase {
new BasicHeader(UsernamePasswordToken.BASIC_AUTH_HEADER,
UsernamePasswordToken.basicAuthHeaderValue(TRANSPORT_CLIENT_USER,
SecuredStringTests.build(SecuritySettingsSource.DEFAULT_PASSWORD))),
new BasicHeader(InternalAuthenticationService.RUN_AS_USER_HEADER, SecuritySettingsSource.DEFAULT_USER_NAME));
new BasicHeader(AuthenticationService.RUN_AS_USER_HEADER, SecuritySettingsSource.DEFAULT_USER_NAME));
fail("request should have failed");
} catch(ResponseException e) {
assertThat(e.getResponse().getStatusLine().getStatusCode(), is(403));
@ -140,13 +140,12 @@ public class RunAsIntegTests extends SecurityIntegTestCase {
}
// but when running as a different user it should work
try (Response response = getRestClient().performRequest("GET", "/_nodes",
Response response = getRestClient().performRequest("GET", "/_nodes",
new BasicHeader(UsernamePasswordToken.BASIC_AUTH_HEADER,
UsernamePasswordToken.basicAuthHeaderValue(RUN_AS_USER,
SecuredStringTests.build(SecuritySettingsSource.DEFAULT_PASSWORD))),
new BasicHeader(InternalAuthenticationService.RUN_AS_USER_HEADER, SecuritySettingsSource.DEFAULT_USER_NAME))) {
assertThat(response.getStatusLine().getStatusCode(), is(200));
}
new BasicHeader(AuthenticationService.RUN_AS_USER_HEADER, SecuritySettingsSource.DEFAULT_USER_NAME));
assertThat(response.getStatusLine().getStatusCode(), is(200));
}
public void testEmptyUserImpersonationHeader() throws Exception {
@ -161,7 +160,7 @@ public class RunAsIntegTests extends SecurityIntegTestCase {
Map<String, String> headers = new HashMap<>();
headers.put("Authorization", UsernamePasswordToken.basicAuthHeaderValue(RUN_AS_USER,
new SecuredString(SecuritySettingsSource.DEFAULT_PASSWORD.toCharArray())));
headers.put(InternalAuthenticationService.RUN_AS_USER_HEADER, "");
headers.put(AuthenticationService.RUN_AS_USER_HEADER, "");
client.filterWithHeader(headers).admin().cluster().prepareHealth().get();
fail("run as header should not be allowed to be empty");
@ -177,7 +176,7 @@ public class RunAsIntegTests extends SecurityIntegTestCase {
new BasicHeader(UsernamePasswordToken.BASIC_AUTH_HEADER,
UsernamePasswordToken.basicAuthHeaderValue(RUN_AS_USER,
SecuredStringTests.build(SecuritySettingsSource.DEFAULT_PASSWORD))),
new BasicHeader(InternalAuthenticationService.RUN_AS_USER_HEADER, ""));
new BasicHeader(AuthenticationService.RUN_AS_USER_HEADER, ""));
fail("request should have failed");
} catch(ResponseException e) {
assertThat(e.getResponse().getStatusLine().getStatusCode(), is(401));
@ -196,7 +195,7 @@ public class RunAsIntegTests extends SecurityIntegTestCase {
Map<String, String> headers = new HashMap<>();
headers.put("Authorization", UsernamePasswordToken.basicAuthHeaderValue(RUN_AS_USER,
new SecuredString(SecuritySettingsSource.DEFAULT_PASSWORD.toCharArray())));
headers.put(InternalAuthenticationService.RUN_AS_USER_HEADER, "idontexist");
headers.put(AuthenticationService.RUN_AS_USER_HEADER, "idontexist");
client.filterWithHeader(headers).admin().cluster().prepareHealth().get();
fail("run as header should not accept non-existent users");
@ -212,7 +211,7 @@ public class RunAsIntegTests extends SecurityIntegTestCase {
new BasicHeader(UsernamePasswordToken.BASIC_AUTH_HEADER,
UsernamePasswordToken.basicAuthHeaderValue(RUN_AS_USER,
SecuredStringTests.build(SecuritySettingsSource.DEFAULT_PASSWORD))),
new BasicHeader(InternalAuthenticationService.RUN_AS_USER_HEADER, "idontexist"));
new BasicHeader(AuthenticationService.RUN_AS_USER_HEADER, "idontexist"));
fail("request should have failed");
} catch (ResponseException e) {
assertThat(e.getResponse().getStatusLine().getStatusCode(), is(403));

View File

@ -5,12 +5,11 @@
*/
package org.elasticsearch.xpack.security.authc.pki;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.message.BasicHeader;
import org.apache.http.nio.conn.ssl.SSLIOSessionStrategy;
import org.elasticsearch.client.Response;
import org.elasticsearch.client.ResponseException;
import org.elasticsearch.client.RestClient;
import org.elasticsearch.client.SSLSocketFactoryHttpConfigCallback;
import org.elasticsearch.client.transport.TransportClient;
import org.elasticsearch.common.network.NetworkModule;
import org.elasticsearch.common.settings.Settings;
@ -79,8 +78,8 @@ public class PkiOptionalClientAuthTests extends SecurityIntegTestCase {
}
public void testRestClientWithoutClientCertificate() throws Exception {
SSLConnectionSocketFactory sslConnectionSocketFactory = new SSLConnectionSocketFactory(getSSLContext());
try (RestClient restClient = createRestClient(new SSLSocketFactoryHttpConfigCallback(sslConnectionSocketFactory), "https")) {
SSLIOSessionStrategy sessionStrategy = new SSLIOSessionStrategy(getSSLContext());
try (RestClient restClient = createRestClient(httpClientBuilder -> httpClientBuilder.setSSLStrategy(sessionStrategy), "https")) {
try {
restClient.performRequest("GET", "_nodes");
fail("request should have failed");
@ -88,12 +87,11 @@ public class PkiOptionalClientAuthTests extends SecurityIntegTestCase {
assertThat(e.getResponse().getStatusLine().getStatusCode(), is(401));
}
try (Response response = restClient.performRequest("GET", "_nodes",
Response response = restClient.performRequest("GET", "_nodes",
new BasicHeader(UsernamePasswordToken.BASIC_AUTH_HEADER,
UsernamePasswordToken.basicAuthHeaderValue(SecuritySettingsSource.DEFAULT_USER_NAME,
new SecuredString(SecuritySettingsSource.DEFAULT_PASSWORD.toCharArray()))))) {
assertThat(response.getStatusLine().getStatusCode(), is(200));
}
new SecuredString(SecuritySettingsSource.DEFAULT_PASSWORD.toCharArray()))));
assertThat(response.getStatusLine().getStatusCode(), is(200));
}
}

View File

@ -6,12 +6,11 @@
package org.elasticsearch.xpack.security.authc.pki;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.message.BasicHeader;
import org.apache.http.nio.conn.ssl.SSLIOSessionStrategy;
import org.elasticsearch.client.Client;
import org.elasticsearch.client.Response;
import org.elasticsearch.client.RestClient;
import org.elasticsearch.client.SSLSocketFactoryHttpConfigCallback;
import org.elasticsearch.common.network.NetworkModule;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.test.ESIntegTestCase.ClusterScope;
@ -78,14 +77,13 @@ public class PkiWithoutClientAuthenticationTests extends SecurityIntegTestCase {
public void testThatHttpWorks() throws Exception {
SSLContext sc = SSLContext.getInstance("SSL");
sc.init(null, trustAllCerts, new SecureRandom());
SSLConnectionSocketFactory sslConnectionSocketFactory = new SSLConnectionSocketFactory(sc);
try (RestClient restClient = createRestClient(new SSLSocketFactoryHttpConfigCallback(sslConnectionSocketFactory), "https")) {
try (Response response = restClient.performRequest("GET", "/_nodes",
SSLIOSessionStrategy sessionStrategy = new SSLIOSessionStrategy(sc);
try (RestClient restClient = createRestClient(httpClientBuilder -> httpClientBuilder.setSSLStrategy(sessionStrategy), "https")) {
Response response = restClient.performRequest("GET", "/_nodes",
new BasicHeader(UsernamePasswordToken.BASIC_AUTH_HEADER,
UsernamePasswordToken.basicAuthHeaderValue(SecuritySettingsSource.DEFAULT_USER_NAME,
new SecuredString(SecuritySettingsSource.DEFAULT_PASSWORD.toCharArray()))))) {
assertThat(response.getStatusLine().getStatusCode(), is(200));
}
new SecuredString(SecuritySettingsSource.DEFAULT_PASSWORD.toCharArray()))));
assertThat(response.getStatusLine().getStatusCode(), is(200));
}
}
}

View File

@ -41,11 +41,10 @@ public class PkiWithoutSSLTests extends SecurityIntegTestCase {
}
public void testThatHttpWorks() throws Exception {
try (Response response = getRestClient().performRequest("GET", "/_nodes",
Response response = getRestClient().performRequest("GET", "/_nodes",
new BasicHeader(UsernamePasswordToken.BASIC_AUTH_HEADER,
UsernamePasswordToken.basicAuthHeaderValue(SecuritySettingsSource.DEFAULT_USER_NAME,
new SecuredString(SecuritySettingsSource.DEFAULT_PASSWORD.toCharArray()))))) {
assertThat(response.getStatusLine().getStatusCode(), is(200));
}
new SecuredString(SecuritySettingsSource.DEFAULT_PASSWORD.toCharArray()))));
assertThat(response.getStatusLine().getStatusCode(), is(200));
}
}

View File

@ -5,6 +5,9 @@
*/
package org.elasticsearch.xpack.security.authz;
import java.util.ArrayList;
import java.util.List;
import org.elasticsearch.ElasticsearchSecurityException;
import org.elasticsearch.Version;
import org.elasticsearch.action.admin.cluster.health.ClusterHealthAction;
@ -45,47 +48,42 @@ import org.elasticsearch.action.termvectors.TermVectorsAction;
import org.elasticsearch.action.termvectors.TermVectorsRequest;
import org.elasticsearch.action.update.UpdateAction;
import org.elasticsearch.action.update.UpdateRequest;
import org.elasticsearch.cluster.metadata.IndexNameExpressionResolver;
import org.elasticsearch.cluster.service.ClusterService;
import org.elasticsearch.cluster.ClusterState;
import org.elasticsearch.cluster.metadata.AliasMetaData;
import org.elasticsearch.cluster.metadata.IndexMetaData;
import org.elasticsearch.cluster.metadata.MetaData;
import org.elasticsearch.cluster.service.ClusterService;
import org.elasticsearch.common.collect.Tuple;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.util.concurrent.ThreadContext;
import org.elasticsearch.search.action.SearchTransportService;
import org.elasticsearch.test.ESTestCase;
import org.elasticsearch.threadpool.ThreadPool;
import org.elasticsearch.transport.TransportRequest;
import org.elasticsearch.xpack.security.SecurityTemplateService;
import org.elasticsearch.xpack.security.audit.AuditTrailService;
import org.elasticsearch.xpack.security.authc.Authentication;
import org.elasticsearch.xpack.security.authc.Authentication.RealmRef;
import org.elasticsearch.xpack.security.authz.store.CompositeRolesStore;
import org.elasticsearch.xpack.security.user.AnonymousUser;
import org.elasticsearch.xpack.security.user.SystemUser;
import org.elasticsearch.xpack.security.user.User;
import org.elasticsearch.xpack.security.user.XPackUser;
import org.elasticsearch.xpack.security.audit.AuditTrail;
import org.elasticsearch.xpack.security.authc.DefaultAuthenticationFailureHandler;
import org.elasticsearch.xpack.security.authz.permission.Role;
import org.elasticsearch.xpack.security.authz.permission.SuperuserRole;
import org.elasticsearch.xpack.security.authz.privilege.ClusterPrivilege;
import org.elasticsearch.xpack.security.authz.privilege.GeneralPrivilege;
import org.elasticsearch.xpack.security.authz.privilege.IndexPrivilege;
import org.elasticsearch.xpack.security.authz.store.RolesStore;
import org.elasticsearch.test.ESTestCase;
import org.elasticsearch.threadpool.ThreadPool;
import org.elasticsearch.transport.TransportRequest;
import org.junit.After;
import org.junit.Before;
import java.util.ArrayList;
import java.util.List;
import static org.elasticsearch.test.SecurityTestsUtils.assertAuthenticationException;
import static org.elasticsearch.test.SecurityTestsUtils.assertAuthorizationException;
import static org.hamcrest.Matchers.containsInAnyOrder;
import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.notNullValue;
import static org.mockito.AdditionalAnswers.returnsFirstArg;
import static org.mockito.Matchers.any;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
@ -93,27 +91,25 @@ import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.when;
public class InternalAuthorizationServiceTests extends ESTestCase {
private AuditTrail auditTrail;
private RolesStore rolesStore;
public class AuthorizationServiceTests extends ESTestCase {
private AuditTrailService auditTrail;
private CompositeRolesStore rolesStore;
private ClusterService clusterService;
private InternalAuthorizationService internalAuthorizationService;
private AuthorizationService authorizationService;
private ThreadContext threadContext;
private ThreadPool threadPool;
@Before
public void setup() {
rolesStore = mock(RolesStore.class);
rolesStore = mock(CompositeRolesStore.class);
clusterService = mock(ClusterService.class);
auditTrail = mock(AuditTrail.class);
auditTrail = mock(AuditTrailService.class);
threadContext = new ThreadContext(Settings.EMPTY);
threadPool = mock(ThreadPool.class);
when(threadPool.getThreadContext()).thenReturn(threadContext);
IndexNameExpressionResolver nameExpressionResolver = mock(IndexNameExpressionResolver.class);
when(nameExpressionResolver.resolveDateMathExpression(any(String.class))).thenAnswer(returnsFirstArg());
internalAuthorizationService = new InternalAuthorizationService(Settings.EMPTY, rolesStore, clusterService,
auditTrail, new DefaultAuthenticationFailureHandler(), threadPool, nameExpressionResolver);
authorizationService = new AuthorizationService(Settings.EMPTY, rolesStore, clusterService,
auditTrail, new DefaultAuthenticationFailureHandler(), threadPool);
}
@After
@ -125,10 +121,10 @@ public class InternalAuthorizationServiceTests extends ESTestCase {
TransportRequest request = mock(TransportRequest.class);
// A failure would throw an exception
internalAuthorizationService.authorize(createAuthentication(SystemUser.INSTANCE), "indices:monitor/whatever", request);
authorizationService.authorize(createAuthentication(SystemUser.INSTANCE), "indices:monitor/whatever", request);
verify(auditTrail).accessGranted(SystemUser.INSTANCE, "indices:monitor/whatever", request);
internalAuthorizationService.authorize(createAuthentication(SystemUser.INSTANCE), "internal:whatever", request);
authorizationService.authorize(createAuthentication(SystemUser.INSTANCE), "internal:whatever", request);
verify(auditTrail).accessGranted(SystemUser.INSTANCE, "internal:whatever", request);
verifyNoMoreInteractions(auditTrail);
}
@ -136,7 +132,7 @@ public class InternalAuthorizationServiceTests extends ESTestCase {
public void testIndicesActionsAreNotAuthorized() {
TransportRequest request = mock(TransportRequest.class);
try {
internalAuthorizationService.authorize(createAuthentication(SystemUser.INSTANCE), "indices:", request);
authorizationService.authorize(createAuthentication(SystemUser.INSTANCE), "indices:", request);
fail("action beginning with indices should have failed");
} catch (ElasticsearchSecurityException e) {
assertAuthorizationException(e,
@ -149,7 +145,7 @@ public class InternalAuthorizationServiceTests extends ESTestCase {
public void testClusterAdminActionsAreNotAuthorized() {
TransportRequest request = mock(TransportRequest.class);
try {
internalAuthorizationService.authorize(createAuthentication(SystemUser.INSTANCE), "cluster:admin/whatever", request);
authorizationService.authorize(createAuthentication(SystemUser.INSTANCE), "cluster:admin/whatever", request);
fail("action beginning with cluster:admin/whatever should have failed");
} catch (ElasticsearchSecurityException e) {
assertAuthorizationException(e,
@ -162,7 +158,7 @@ public class InternalAuthorizationServiceTests extends ESTestCase {
public void testClusterAdminSnapshotStatusActionIsNotAuthorized() {
TransportRequest request = mock(TransportRequest.class);
try {
internalAuthorizationService.authorize(createAuthentication(SystemUser.INSTANCE), "cluster:admin/snapshot/status", request);
authorizationService.authorize(createAuthentication(SystemUser.INSTANCE), "cluster:admin/snapshot/status", request);
fail("action beginning with cluster:admin/snapshot/status should have failed");
} catch (ElasticsearchSecurityException e) {
assertAuthorizationException(e, containsString("action [cluster:admin/snapshot/status] is unauthorized for user [" +
@ -176,7 +172,7 @@ public class InternalAuthorizationServiceTests extends ESTestCase {
TransportRequest request = new SearchRequest();
User user = new User("test user");
try {
internalAuthorizationService.authorize(createAuthentication(user), "indices:a", request);
authorizationService.authorize(createAuthentication(user), "indices:a", request);
fail("user without roles should be denied");
} catch (ElasticsearchSecurityException e) {
assertAuthorizationException(e, containsString("action [indices:a] is unauthorized for user [test user]"));
@ -189,7 +185,7 @@ public class InternalAuthorizationServiceTests extends ESTestCase {
TransportRequest request = new SearchRequest();
User user = new User("test user", "non-existent-role");
try {
internalAuthorizationService.authorize(createAuthentication(user), "indices:a", request);
authorizationService.authorize(createAuthentication(user), "indices:a", request);
fail("user with unknown role only should have been denied");
} catch (ElasticsearchSecurityException e) {
assertAuthorizationException(e, containsString("action [indices:a] is unauthorized for user [test user]"));
@ -204,7 +200,7 @@ public class InternalAuthorizationServiceTests extends ESTestCase {
when(rolesStore.role("a_all")).thenReturn(Role.builder("a_role").add(IndexPrivilege.ALL, "a").build());
try {
internalAuthorizationService.authorize(createAuthentication(user), "whatever", request);
authorizationService.authorize(createAuthentication(user), "whatever", request);
fail("non indices and non cluster requests should be denied");
} catch (ElasticsearchSecurityException e) {
assertAuthorizationException(e, containsString("action [whatever] is unauthorized for user [test user]"));
@ -219,7 +215,7 @@ public class InternalAuthorizationServiceTests extends ESTestCase {
when(rolesStore.role("no_indices")).thenReturn(Role.builder("no_indices").cluster(ClusterPrivilege.action("")).build());
try {
internalAuthorizationService.authorize(createAuthentication(user), "indices:a", request);
authorizationService.authorize(createAuthentication(user), "indices:a", request);
fail("user only has cluster roles so indices requests should fail");
} catch (ElasticsearchSecurityException e) {
assertAuthorizationException(e, containsString("action [indices:a] is unauthorized for user [test user]"));
@ -233,29 +229,29 @@ public class InternalAuthorizationServiceTests extends ESTestCase {
when(rolesStore.role("a_all")).thenReturn(Role.builder("a_role").add(IndexPrivilege.ALL, "a").build());
ClearScrollRequest clearScrollRequest = new ClearScrollRequest();
internalAuthorizationService.authorize(createAuthentication(user), ClearScrollAction.NAME, clearScrollRequest);
authorizationService.authorize(createAuthentication(user), ClearScrollAction.NAME, clearScrollRequest);
verify(auditTrail).accessGranted(user, ClearScrollAction.NAME, clearScrollRequest);
SearchScrollRequest searchScrollRequest = new SearchScrollRequest();
internalAuthorizationService.authorize(createAuthentication(user), SearchScrollAction.NAME, searchScrollRequest);
authorizationService.authorize(createAuthentication(user), SearchScrollAction.NAME, searchScrollRequest);
verify(auditTrail).accessGranted(user, SearchScrollAction.NAME, searchScrollRequest);
// We have to use a mock request for other Scroll actions as the actual requests are package private to SearchTransportService
TransportRequest request = mock(TransportRequest.class);
internalAuthorizationService
authorizationService
.authorize(createAuthentication(user), SearchTransportService.CLEAR_SCROLL_CONTEXTS_ACTION_NAME, request);
verify(auditTrail).accessGranted(user, SearchTransportService.CLEAR_SCROLL_CONTEXTS_ACTION_NAME, request);
internalAuthorizationService.authorize(createAuthentication(user), SearchTransportService.FETCH_ID_SCROLL_ACTION_NAME, request);
authorizationService.authorize(createAuthentication(user), SearchTransportService.FETCH_ID_SCROLL_ACTION_NAME, request);
verify(auditTrail).accessGranted(user, SearchTransportService.FETCH_ID_SCROLL_ACTION_NAME, request);
internalAuthorizationService.authorize(createAuthentication(user), SearchTransportService.QUERY_FETCH_SCROLL_ACTION_NAME, request);
authorizationService.authorize(createAuthentication(user), SearchTransportService.QUERY_FETCH_SCROLL_ACTION_NAME, request);
verify(auditTrail).accessGranted(user, SearchTransportService.QUERY_FETCH_SCROLL_ACTION_NAME, request);
internalAuthorizationService.authorize(createAuthentication(user), SearchTransportService.QUERY_SCROLL_ACTION_NAME, request);
authorizationService.authorize(createAuthentication(user), SearchTransportService.QUERY_SCROLL_ACTION_NAME, request);
verify(auditTrail).accessGranted(user, SearchTransportService.QUERY_SCROLL_ACTION_NAME, request);
internalAuthorizationService.authorize(createAuthentication(user), SearchTransportService.FREE_CONTEXT_SCROLL_ACTION_NAME, request);
authorizationService.authorize(createAuthentication(user), SearchTransportService.FREE_CONTEXT_SCROLL_ACTION_NAME, request);
verify(auditTrail).accessGranted(user, SearchTransportService.FREE_CONTEXT_SCROLL_ACTION_NAME, request);
verifyNoMoreInteractions(auditTrail);
}
@ -269,7 +265,7 @@ public class InternalAuthorizationServiceTests extends ESTestCase {
when(state.metaData()).thenReturn(MetaData.EMPTY_META_DATA);
try {
internalAuthorizationService.authorize(createAuthentication(user), "indices:a", request);
authorizationService.authorize(createAuthentication(user), "indices:a", request);
fail("indices request for b should be denied since there is no such index");
} catch (ElasticsearchSecurityException e) {
assertAuthorizationException(e, containsString("action [indices:a] is unauthorized for user [test user]"));
@ -290,7 +286,7 @@ public class InternalAuthorizationServiceTests extends ESTestCase {
when(state.metaData()).thenReturn(MetaData.EMPTY_META_DATA);
try {
internalAuthorizationService.authorize(createAuthentication(user), CreateIndexAction.NAME, request);
authorizationService.authorize(createAuthentication(user), CreateIndexAction.NAME, request);
fail("indices creation request with alias should be denied since user does not have permission to alias");
} catch (ElasticsearchSecurityException e) {
assertAuthorizationException(e,
@ -311,7 +307,7 @@ public class InternalAuthorizationServiceTests extends ESTestCase {
when(clusterService.state()).thenReturn(state);
when(state.metaData()).thenReturn(MetaData.EMPTY_META_DATA);
internalAuthorizationService.authorize(createAuthentication(user), CreateIndexAction.NAME, request);
authorizationService.authorize(createAuthentication(user), CreateIndexAction.NAME, request);
verify(auditTrail).accessGranted(user, CreateIndexAction.NAME, request);
verifyNoMoreInteractions(auditTrail);
@ -322,7 +318,7 @@ public class InternalAuthorizationServiceTests extends ESTestCase {
public void testIndicesAliasesWithNoRolesUser() {
User user = new User("test user");
List<String> list = internalAuthorizationService.authorizedIndicesAndAliases(user, "");
List<String> list = authorizationService.authorizedIndicesAndAliases(user, "");
assertThat(list.isEmpty(), is(true));
}
@ -347,7 +343,7 @@ public class InternalAuthorizationServiceTests extends ESTestCase {
.build(), true)
.build());
List<String> list = internalAuthorizationService.authorizedIndicesAndAliases(user, SearchAction.NAME);
List<String> list = authorizationService.authorizedIndicesAndAliases(user, SearchAction.NAME);
assertThat(list, containsInAnyOrder("a1", "a2", "aaaaaa", "b", "ab"));
assertThat(list.contains("bbbbb"), is(false));
assertThat(list.contains("ba"), is(false));
@ -357,17 +353,15 @@ public class InternalAuthorizationServiceTests extends ESTestCase {
TransportRequest request = new IndicesExistsRequest("b");
ClusterState state = mock(ClusterState.class);
AnonymousUser.initialize(Settings.builder().put(AnonymousUser.ROLES_SETTING.getKey(), "a_all").build());
IndexNameExpressionResolver nameExpressionResolver = mock(IndexNameExpressionResolver.class);
when(nameExpressionResolver.resolveDateMathExpression(any(String.class))).thenAnswer(returnsFirstArg());
internalAuthorizationService = new InternalAuthorizationService(Settings.EMPTY, rolesStore, clusterService, auditTrail,
new DefaultAuthenticationFailureHandler(), threadPool, nameExpressionResolver);
authorizationService = new AuthorizationService(Settings.EMPTY, rolesStore, clusterService, auditTrail,
new DefaultAuthenticationFailureHandler(), threadPool);
when(rolesStore.role("a_all")).thenReturn(Role.builder("a_all").add(IndexPrivilege.ALL, "a").build());
when(clusterService.state()).thenReturn(state);
when(state.metaData()).thenReturn(MetaData.EMPTY_META_DATA);
try {
internalAuthorizationService.authorize(createAuthentication(AnonymousUser.INSTANCE), "indices:a", request);
authorizationService.authorize(createAuthentication(AnonymousUser.INSTANCE), "indices:a", request);
fail("indices request for b should be denied since there is no such index");
} catch (ElasticsearchSecurityException e) {
assertAuthorizationException(e,
@ -384,21 +378,19 @@ public class InternalAuthorizationServiceTests extends ESTestCase {
ClusterState state = mock(ClusterState.class);
AnonymousUser.initialize(Settings.builder()
.put(AnonymousUser.ROLES_SETTING.getKey(), "a_all")
.put(InternalAuthorizationService.ANONYMOUS_AUTHORIZATION_EXCEPTION_SETTING.getKey(), false)
.put(AuthorizationService.ANONYMOUS_AUTHORIZATION_EXCEPTION_SETTING.getKey(), false)
.build());
User anonymousUser = AnonymousUser.INSTANCE;
IndexNameExpressionResolver nameExpressionResolver = mock(IndexNameExpressionResolver.class);
when(nameExpressionResolver.resolveDateMathExpression(any(String.class))).thenAnswer(returnsFirstArg());
internalAuthorizationService = new InternalAuthorizationService(
Settings.builder().put(InternalAuthorizationService.ANONYMOUS_AUTHORIZATION_EXCEPTION_SETTING.getKey(), false).build(),
rolesStore, clusterService, auditTrail, new DefaultAuthenticationFailureHandler(), threadPool, nameExpressionResolver);
authorizationService = new AuthorizationService(
Settings.builder().put(AuthorizationService.ANONYMOUS_AUTHORIZATION_EXCEPTION_SETTING.getKey(), false).build(),
rolesStore, clusterService, auditTrail, new DefaultAuthenticationFailureHandler(), threadPool);
when(rolesStore.role("a_all")).thenReturn(Role.builder("a_all").add(IndexPrivilege.ALL, "a").build());
when(clusterService.state()).thenReturn(state);
when(state.metaData()).thenReturn(MetaData.EMPTY_META_DATA);
try {
internalAuthorizationService.authorize(createAuthentication(anonymousUser), "indices:a", request);
authorizationService.authorize(createAuthentication(anonymousUser), "indices:a", request);
fail("indices request for b should be denied since there is no such index");
} catch (ElasticsearchSecurityException e) {
assertAuthenticationException(e, containsString("action [indices:a] requires authentication"));
@ -414,7 +406,7 @@ public class InternalAuthorizationServiceTests extends ESTestCase {
User user = new User("test user", null, new User("run as me", new String[] { "admin" }));
assertThat(user.runAs(), is(notNullValue()));
try {
internalAuthorizationService.authorize(createAuthentication(user), "indices:a", request);
authorizationService.authorize(createAuthentication(user), "indices:a", request);
fail("user without roles should be denied for run as");
} catch (ElasticsearchSecurityException e) {
assertAuthorizationException(e, containsString("action [indices:a] is unauthorized for user [test user] run as [run as me]"));
@ -434,7 +426,7 @@ public class InternalAuthorizationServiceTests extends ESTestCase {
.build());
try {
internalAuthorizationService.authorize(createAuthentication(user), "indices:a", request);
authorizationService.authorize(createAuthentication(user), "indices:a", request);
fail("user without roles should be denied for run as");
} catch (ElasticsearchSecurityException e) {
assertAuthorizationException(e, containsString("action [indices:a] is unauthorized for user [test user] run as [run as me]"));
@ -468,7 +460,7 @@ public class InternalAuthorizationServiceTests extends ESTestCase {
}
try {
internalAuthorizationService.authorize(createAuthentication(user), "indices:a", request);
authorizationService.authorize(createAuthentication(user), "indices:a", request);
fail("the run as user's role doesn't exist so they should not get authorized");
} catch (ElasticsearchSecurityException e) {
assertAuthorizationException(e, containsString("action [indices:a] is unauthorized for user [test user] run as [run as me]"));
@ -499,7 +491,7 @@ public class InternalAuthorizationServiceTests extends ESTestCase {
.add(IndexPrivilege.ALL, "b")
.build());
internalAuthorizationService.authorize(createAuthentication(user), "indices:a", request);
authorizationService.authorize(createAuthentication(user), "indices:a", request);
verify(auditTrail).runAsGranted(user, "indices:a", request);
verify(auditTrail).accessGranted(user, "indices:a", request);
verifyNoMoreInteractions(auditTrail);
@ -538,7 +530,7 @@ public class InternalAuthorizationServiceTests extends ESTestCase {
String action = requestTuple.v1();
TransportRequest request = requestTuple.v2();
try {
internalAuthorizationService.authorize(createAuthentication(user), action, request);
authorizationService.authorize(createAuthentication(user), action, request);
fail("only the xpack user can execute operation [" + action + "] against the internal index");
} catch (ElasticsearchSecurityException e) {
assertAuthorizationException(e, containsString("action [" + action + "] is unauthorized for user [all_access_user]"));
@ -549,12 +541,12 @@ public class InternalAuthorizationServiceTests extends ESTestCase {
// we should allow waiting for the health of the index or any index if the user has this permission
ClusterHealthRequest request = new ClusterHealthRequest(SecurityTemplateService.SECURITY_INDEX_NAME);
internalAuthorizationService.authorize(createAuthentication(user), ClusterHealthAction.NAME, request);
authorizationService.authorize(createAuthentication(user), ClusterHealthAction.NAME, request);
verify(auditTrail).accessGranted(user, ClusterHealthAction.NAME, request);
// multiple indices
request = new ClusterHealthRequest(SecurityTemplateService.SECURITY_INDEX_NAME, "foo", "bar");
internalAuthorizationService.authorize(createAuthentication(user), ClusterHealthAction.NAME, request);
authorizationService.authorize(createAuthentication(user), ClusterHealthAction.NAME, request);
verify(auditTrail).accessGranted(user, ClusterHealthAction.NAME, request);
}
@ -586,7 +578,7 @@ public class InternalAuthorizationServiceTests extends ESTestCase {
for (Tuple<String, ? extends TransportRequest> requestTuple : requests) {
String action = requestTuple.v1();
TransportRequest request = requestTuple.v2();
internalAuthorizationService.authorize(createAuthentication(user), action, request);
authorizationService.authorize(createAuthentication(user), action, request);
verify(auditTrail).accessGranted(user, action, request);
}
}
@ -620,7 +612,7 @@ public class InternalAuthorizationServiceTests extends ESTestCase {
for (Tuple<String, TransportRequest> requestTuple : requests) {
String action = requestTuple.v1();
TransportRequest request = requestTuple.v2();
internalAuthorizationService.authorize(createAuthentication(XPackUser.INSTANCE), action, request);
authorizationService.authorize(createAuthentication(XPackUser.INSTANCE), action, request);
verify(auditTrail).accessGranted(XPackUser.INSTANCE, action, request);
}
}

View File

@ -44,7 +44,7 @@ public class AuthorizationUtilsTests extends ESTestCase {
User user = new User(randomAsciiOfLength(6), new String[] {});
Authentication authentication = new Authentication(user, new RealmRef("test", "test", "foo"), null);
threadContext.putTransient(Authentication.AUTHENTICATION_KEY, authentication);
threadContext.putTransient(InternalAuthorizationService.ORIGINATING_ACTION_KEY, randomFrom("indices:foo", "cluster:bar"));
threadContext.putTransient(AuthorizationService.ORIGINATING_ACTION_KEY, randomFrom("indices:foo", "cluster:bar"));
assertThat(AuthorizationUtils.shouldReplaceUserWithSystem(threadContext, "internal:something"), is(true));
}
@ -52,7 +52,7 @@ public class AuthorizationUtilsTests extends ESTestCase {
User user = new User(randomAsciiOfLength(6), new String[] {});
Authentication authentication = new Authentication(user, new RealmRef("test", "test", "foo"), null);
threadContext.putTransient(Authentication.AUTHENTICATION_KEY, authentication);
threadContext.putTransient(InternalAuthorizationService.ORIGINATING_ACTION_KEY, randomFrom("internal:foo/bar"));
threadContext.putTransient(AuthorizationService.ORIGINATING_ACTION_KEY, randomFrom("internal:foo/bar"));
assertThat(AuthorizationUtils.shouldReplaceUserWithSystem(threadContext, "internal:something"), is(false));
}
}

View File

@ -34,6 +34,7 @@ import org.elasticsearch.index.query.QueryParseContext;
import org.elasticsearch.index.query.QueryShardContext;
import org.elasticsearch.index.query.TermQueryBuilder;
import org.elasticsearch.index.shard.ShardId;
import org.elasticsearch.script.ScriptService;
import org.elasticsearch.xpack.security.SecurityLicenseState;
import org.elasticsearch.test.ESTestCase;
import org.elasticsearch.test.IndexSettingsModule;
@ -55,13 +56,14 @@ public class SecurityIndexSearcherWrapperIntegrationTests extends ESTestCase {
public void testDLS() throws Exception {
ShardId shardId = new ShardId("_index", "_na_", 0);
MapperService mapperService = mock(MapperService.class);
ScriptService scriptService = mock(ScriptService.class);
when(mapperService.docMappers(anyBoolean())).thenReturn(Collections.emptyList());
when(mapperService.simpleMatchToIndexNames(anyString()))
.then(invocationOnMock -> Collections.singletonList((String) invocationOnMock.getArguments()[0]));
ThreadContext threadContext = new ThreadContext(Settings.EMPTY);
IndicesAccessControl.IndexAccessControl indexAccessControl = new IndicesAccessControl.IndexAccessControl(true, null,
singleton(new BytesArray("{}")));
singleton(new BytesArray("{\"match_all\" : {}}")));
IndexSettings indexSettings = IndexSettingsModule.newIndexSettings(shardId.getIndex(), Settings.EMPTY);
QueryShardContext queryShardContext = mock(QueryShardContext.class);
QueryParseContext queryParseContext = mock(QueryParseContext.class);
@ -79,7 +81,7 @@ public class SecurityIndexSearcherWrapperIntegrationTests extends ESTestCase {
SecurityLicenseState licenseState = mock(SecurityLicenseState.class);
when(licenseState.documentAndFieldLevelSecurityEnabled()).thenReturn(true);
SecurityIndexSearcherWrapper wrapper = new SecurityIndexSearcherWrapper(indexSettings, queryShardContext, mapperService,
bitsetFilterCache, threadContext, licenseState) {
bitsetFilterCache, threadContext, licenseState, scriptService) {
@Override
protected QueryShardContext copyQueryShardContext(QueryShardContext context) {
@ -140,7 +142,7 @@ public class SecurityIndexSearcherWrapperIntegrationTests extends ESTestCase {
ParsedQuery parsedQuery = new ParsedQuery(new TermQuery(new Term("field", values[i])));
when(queryShardContext.newParseContext(any(XContentParser.class))).thenReturn(queryParseContext);
when(queryParseContext.parseInnerQueryBuilder())
.thenReturn(Optional.of((QueryBuilder) new TermQueryBuilder("field", values[i])));
.thenReturn(Optional.of(new TermQueryBuilder("field", values[i])));
when(queryShardContext.toQuery(any(QueryBuilder.class))).thenReturn(parsedQuery);
DirectoryReader wrappedDirectoryReader = wrapper.wrap(directoryReader);
IndexSearcher indexSearcher = wrapper.wrap(new IndexSearcher(wrappedDirectoryReader));

View File

@ -35,33 +35,48 @@ import org.apache.lucene.util.BitSet;
import org.apache.lucene.util.FixedBitSet;
import org.apache.lucene.util.IOUtils;
import org.apache.lucene.util.SparseFixedBitSet;
import org.elasticsearch.common.bytes.BytesArray;
import org.elasticsearch.common.bytes.BytesReference;
import org.elasticsearch.common.compress.CompressedXContent;
import org.elasticsearch.common.io.stream.NamedWriteableRegistry;
import org.elasticsearch.common.lucene.index.ElasticsearchDirectoryReader;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.util.concurrent.ThreadContext;
import org.elasticsearch.common.xcontent.ToXContent;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.env.Environment;
import org.elasticsearch.index.Index;
import org.elasticsearch.index.IndexSettings;
import org.elasticsearch.index.analysis.AnalysisService;
import org.elasticsearch.index.cache.bitset.BitsetFilterCache;
import org.elasticsearch.index.mapper.MapperService;
import org.elasticsearch.index.mapper.internal.ParentFieldMapper;
import org.elasticsearch.index.query.TermQueryBuilder;
import org.elasticsearch.index.shard.IndexShard;
import org.elasticsearch.index.shard.ShardId;
import org.elasticsearch.index.similarity.SimilarityService;
import org.elasticsearch.indices.IndicesModule;
import org.elasticsearch.script.ExecutableScript;
import org.elasticsearch.script.Script;
import org.elasticsearch.script.ScriptContext;
import org.elasticsearch.script.ScriptService;
import org.elasticsearch.search.aggregations.LeafBucketCollector;
import org.elasticsearch.watcher.ResourceWatcherService;
import org.elasticsearch.xpack.security.authz.accesscontrol.DocumentSubsetReader.DocumentSubsetDirectoryReader;
import org.elasticsearch.xpack.security.SecurityLicenseState;
import org.elasticsearch.test.ESTestCase;
import org.elasticsearch.test.IndexSettingsModule;
import org.elasticsearch.xpack.security.user.User;
import org.junit.After;
import org.junit.Before;
import org.mockito.ArgumentCaptor;
import java.io.IOException;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.IdentityHashMap;
import java.util.Map;
import java.util.Set;
import static java.util.Collections.emptyList;
@ -75,13 +90,18 @@ import static org.hamcrest.Matchers.instanceOf;
import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.not;
import static org.hamcrest.Matchers.sameInstance;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyZeroInteractions;
import static org.mockito.Mockito.when;
public class SecurityIndexSearcherWrapperUnitTests extends ESTestCase {
private ThreadContext threadContext;
private MapperService mapperService;
private ScriptService scriptService;
private SecurityIndexSearcherWrapper securityIndexSearcherWrapper;
private ElasticsearchDirectoryReader esIn;
private SecurityLicenseState licenseState;
@ -90,6 +110,7 @@ public class SecurityIndexSearcherWrapperUnitTests extends ESTestCase {
@Before
public void before() throws Exception {
Index index = new Index("_index", "testUUID");
scriptService = mock(ScriptService.class);
indexSettings = IndexSettingsModule.newIndexSettings(index, Settings.EMPTY);
AnalysisService analysisService = new AnalysisService(indexSettings, Collections.emptyMap(), Collections.emptyMap(),
Collections.emptyMap(), Collections.emptyMap());
@ -125,7 +146,7 @@ public class SecurityIndexSearcherWrapperUnitTests extends ESTestCase {
mapperService.merge("type", new CompressedXContent(mappingSource.string()), MapperService.MergeReason.MAPPING_UPDATE, false);
securityIndexSearcherWrapper =
new SecurityIndexSearcherWrapper(indexSettings, null, mapperService, null, threadContext, licenseState) {
new SecurityIndexSearcherWrapper(indexSettings, null, mapperService, null, threadContext, licenseState, scriptService) {
@Override
protected IndicesAccessControl getIndicesAccessControl() {
IndicesAccessControl.IndexAccessControl indexAccessControl = new IndicesAccessControl.IndexAccessControl(true,
@ -156,14 +177,14 @@ public class SecurityIndexSearcherWrapperUnitTests extends ESTestCase {
public void testWrapReaderWhenFeatureDisabled() throws Exception {
when(licenseState.documentAndFieldLevelSecurityEnabled()).thenReturn(false);
securityIndexSearcherWrapper =
new SecurityIndexSearcherWrapper(indexSettings, null, mapperService, null, threadContext, licenseState);
new SecurityIndexSearcherWrapper(indexSettings, null, mapperService, null, threadContext, licenseState, scriptService);
DirectoryReader reader = securityIndexSearcherWrapper.wrap(esIn);
assertThat(reader, sameInstance(esIn));
}
public void testWrapSearcherWhenFeatureDisabled() throws Exception {
securityIndexSearcherWrapper =
new SecurityIndexSearcherWrapper(indexSettings, null, mapperService, null, threadContext, licenseState);
new SecurityIndexSearcherWrapper(indexSettings, null, mapperService, null, threadContext, licenseState, scriptService);
IndexSearcher indexSearcher = new IndexSearcher(esIn);
IndexSearcher result = securityIndexSearcherWrapper.wrap(indexSearcher);
assertThat(result, sameInstance(indexSearcher));
@ -275,7 +296,7 @@ public class SecurityIndexSearcherWrapperUnitTests extends ESTestCase {
DirectoryReader directoryReader = DocumentSubsetReader.wrap(esIn, bitsetFilterCache, new MatchAllDocsQuery());
IndexSearcher indexSearcher = new IndexSearcher(directoryReader);
securityIndexSearcherWrapper =
new SecurityIndexSearcherWrapper(indexSettings, null, mapperService, null, threadContext, licenseState);
new SecurityIndexSearcherWrapper(indexSettings, null, mapperService, null, threadContext, licenseState, scriptService);
IndexSearcher result = securityIndexSearcherWrapper.wrap(indexSearcher);
assertThat(result, not(sameInstance(indexSearcher)));
assertThat(result.getSimilarity(true), sameInstance(indexSearcher.getSimilarity(true)));
@ -284,7 +305,7 @@ public class SecurityIndexSearcherWrapperUnitTests extends ESTestCase {
public void testIntersectScorerAndRoleBits() throws Exception {
securityIndexSearcherWrapper =
new SecurityIndexSearcherWrapper(indexSettings, null, mapperService, null, threadContext, licenseState);
new SecurityIndexSearcherWrapper(indexSettings, null, mapperService, null, threadContext, licenseState, scriptService);
final Directory directory = newDirectory();
IndexWriter iw = new IndexWriter(
directory,
@ -373,7 +394,7 @@ public class SecurityIndexSearcherWrapperUnitTests extends ESTestCase {
private void assertResolvedFields(String expression, String... expectedFields) {
securityIndexSearcherWrapper =
new SecurityIndexSearcherWrapper(indexSettings, null, mapperService, null, threadContext, licenseState) {
new SecurityIndexSearcherWrapper(indexSettings, null, mapperService, null, threadContext, licenseState, scriptService) {
@Override
protected IndicesAccessControl getIndicesAccessControl() {
IndicesAccessControl.IndexAccessControl indexAccessControl = new IndicesAccessControl.IndexAccessControl(true,
@ -407,6 +428,60 @@ public class SecurityIndexSearcherWrapperUnitTests extends ESTestCase {
doTestIndexSearcherWrapper(false, true);
}
public void testTemplating() throws Exception {
User user = new User("_username", new String[]{"role1", "role2"}, "_full_name", "_email",
Collections.singletonMap("key", "value"));
securityIndexSearcherWrapper =
new SecurityIndexSearcherWrapper(indexSettings, null, mapperService, null, threadContext, licenseState, scriptService) {
@Override
protected User getUser() {
return user;
}
};
ExecutableScript executableScript = mock(ExecutableScript.class);
when(scriptService.executable(any(Script.class), eq(ScriptContext.Standard.SEARCH), eq(Collections.emptyMap())))
.thenReturn(executableScript);
XContentBuilder builder = jsonBuilder();
String query = new TermQueryBuilder("field", "{{_user.username}}").toXContent(builder, ToXContent.EMPTY_PARAMS).string();
Script script = new Script(query, ScriptService.ScriptType.INLINE, null, Collections.singletonMap("custom", "value"));
builder = jsonBuilder().startObject().field("template");
script.toXContent(builder, ToXContent.EMPTY_PARAMS);
BytesReference querySource = builder.endObject().bytes();
securityIndexSearcherWrapper.evaluateTemplate(querySource);
ArgumentCaptor<Script> argument = ArgumentCaptor.forClass(Script.class);
verify(scriptService).executable(argument.capture(), eq(ScriptContext.Standard.SEARCH), eq(Collections.emptyMap()));
Script usedScript = argument.getValue();
assertThat(usedScript.getScript(), equalTo(script.getScript()));
assertThat(usedScript.getType(), equalTo(script.getType()));
assertThat(usedScript.getLang(), equalTo("mustache"));
assertThat(usedScript.getContentType(), equalTo(script.getContentType()));
assertThat(usedScript.getParams().size(), equalTo(2));
assertThat(usedScript.getParams().get("custom"), equalTo("value"));
Map<String, Object> userModel = new HashMap<>();
userModel.put("username", user.principal());
userModel.put("full_name", user.fullName());
userModel.put("email", user.email());
userModel.put("roles", Arrays.asList(user.roles()));
userModel.put("metadata", user.metadata());
assertThat(usedScript.getParams().get("_user"), equalTo(userModel));
}
public void testSkipTemplating() throws Exception {
securityIndexSearcherWrapper =
new SecurityIndexSearcherWrapper(indexSettings, null, mapperService, null, threadContext, licenseState, scriptService);
XContentBuilder builder = jsonBuilder();
BytesReference querySource = new TermQueryBuilder("field", "value").toXContent(builder, ToXContent.EMPTY_PARAMS).bytes();
BytesReference result = securityIndexSearcherWrapper.evaluateTemplate(querySource);
assertThat(result, sameInstance(querySource));
verifyZeroInteractions(scriptService);
}
static class CreateScorerOnceWeight extends Weight {
private final Weight weight;

View File

@ -5,6 +5,8 @@
*/
package org.elasticsearch.xpack.security.authz.indicesresolver;
import java.util.Set;
import org.elasticsearch.Version;
import org.elasticsearch.action.admin.indices.alias.IndicesAliasesAction;
import org.elasticsearch.action.admin.indices.alias.IndicesAliasesRequest;
@ -20,32 +22,31 @@ import org.elasticsearch.action.search.SearchAction;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.support.IndicesOptions;
import org.elasticsearch.client.Requests;
import org.elasticsearch.cluster.metadata.IndexNameExpressionResolver;
import org.elasticsearch.cluster.service.ClusterService;
import org.elasticsearch.cluster.ClusterState;
import org.elasticsearch.cluster.metadata.AliasAction;
import org.elasticsearch.cluster.metadata.AliasMetaData;
import org.elasticsearch.cluster.metadata.IndexMetaData;
import org.elasticsearch.cluster.metadata.IndexNameExpressionResolver;
import org.elasticsearch.cluster.metadata.MetaData;
import org.elasticsearch.cluster.service.ClusterService;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.index.IndexNotFoundException;
import org.elasticsearch.test.ESTestCase;
import org.elasticsearch.threadpool.ThreadPool;
import org.elasticsearch.xpack.security.SecurityTemplateService;
import org.elasticsearch.xpack.security.authz.store.CompositeRolesStore;
import org.elasticsearch.xpack.security.user.User;
import org.elasticsearch.xpack.security.user.XPackUser;
import org.elasticsearch.xpack.security.audit.AuditTrail;
import org.elasticsearch.xpack.security.audit.AuditTrailService;
import org.elasticsearch.xpack.security.authc.DefaultAuthenticationFailureHandler;
import org.elasticsearch.xpack.security.authz.InternalAuthorizationService;
import org.elasticsearch.xpack.security.authz.AuthorizationService;
import org.elasticsearch.xpack.security.authz.permission.Role;
import org.elasticsearch.xpack.security.authz.permission.SuperuserRole;
import org.elasticsearch.xpack.security.authz.privilege.ClusterPrivilege;
import org.elasticsearch.xpack.security.authz.privilege.IndexPrivilege;
import org.elasticsearch.xpack.security.authz.store.RolesStore;
import org.elasticsearch.test.ESTestCase;
import org.elasticsearch.threadpool.ThreadPool;
import org.junit.Before;
import java.util.Set;
import static org.hamcrest.Matchers.arrayContaining;
import static org.hamcrest.Matchers.arrayContainingInAnyOrder;
import static org.hamcrest.Matchers.equalTo;
@ -60,7 +61,7 @@ public class DefaultIndicesResolverTests extends ESTestCase {
private User user;
private User userNoIndices;
private RolesStore rolesStore;
private CompositeRolesStore rolesStore;
private MetaData metaData;
private DefaultIndicesAndAliasesResolver defaultIndicesResolver;
private IndexNameExpressionResolver indexNameExpressionResolver;
@ -91,7 +92,7 @@ public class DefaultIndicesResolverTests extends ESTestCase {
user = new User("user", "role");
userNoIndices = new User("test", "test");
rolesStore = mock(RolesStore.class);
rolesStore = mock(CompositeRolesStore.class);
String[] authorizedIndices = new String[] { "bar", "bar-closed", "foofoobar", "foofoo", "missing", "foofoo-closed" };
when(rolesStore.role("role")).thenReturn(Role.builder("role").add(IndexPrivilege.ALL, authorizedIndices).build());
when(rolesStore.role("test")).thenReturn(Role.builder("test").cluster(ClusterPrivilege.MONITOR).build());
@ -101,8 +102,8 @@ public class DefaultIndicesResolverTests extends ESTestCase {
when(clusterService.state()).thenReturn(state);
when(state.metaData()).thenReturn(metaData);
InternalAuthorizationService authzService = new InternalAuthorizationService(settings, rolesStore, clusterService,
mock(AuditTrail.class), new DefaultAuthenticationFailureHandler(), mock(ThreadPool.class), indexNameExpressionResolver);
AuthorizationService authzService = new AuthorizationService(settings, rolesStore, clusterService,
mock(AuditTrailService.class), new DefaultAuthenticationFailureHandler(), mock(ThreadPool.class));
defaultIndicesResolver = new DefaultIndicesAndAliasesResolver(authzService, indexNameExpressionResolver);
}

View File

@ -13,10 +13,10 @@ import org.elasticsearch.rest.RestController;
import org.elasticsearch.rest.RestFilterChain;
import org.elasticsearch.rest.RestRequest;
import org.elasticsearch.xpack.security.authc.Authentication;
import org.elasticsearch.xpack.security.authc.AuthenticationService;
import org.elasticsearch.xpack.security.SecurityLicenseState;
import org.elasticsearch.test.ESTestCase;
import org.elasticsearch.threadpool.ThreadPool;
import org.elasticsearch.xpack.security.authc.AuthenticationService;
import org.junit.Before;
import static org.elasticsearch.xpack.security.support.Exceptions.authenticationError;

Some files were not shown because too many files have changed in this diff Show More