disable core caches to ensure users are always authorized
The IndicesTermsFilter Cache in core can leak data by not authorizing users prior to retrieving data from the cache. We work around this by ensuring that the cache has a maximum size of 0, effectively disabling it. A test is also added to ensure that data is not leaked by this cache or the cache used by the ScriptService in core. Closes elastic/elasticsearch#854 Original commit: elastic/x-pack-elasticsearch@8a48bdad98
This commit is contained in:
parent
0f56bd37d8
commit
7c62e4c82c
|
@ -0,0 +1,131 @@
|
|||
/*
|
||||
* 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.integration;
|
||||
|
||||
import org.elasticsearch.Version;
|
||||
import org.elasticsearch.action.index.IndexRequest;
|
||||
import org.elasticsearch.action.search.SearchPhaseExecutionException;
|
||||
import org.elasticsearch.action.search.SearchResponse;
|
||||
import org.elasticsearch.index.query.QueryBuilders;
|
||||
import org.elasticsearch.script.ScriptService;
|
||||
import org.elasticsearch.shield.authc.support.SecuredString;
|
||||
import org.elasticsearch.test.ShieldIntegrationTest;
|
||||
import org.elasticsearch.test.ShieldSettingsSource;
|
||||
import org.junit.Before;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.util.Collections;
|
||||
|
||||
import static org.elasticsearch.shield.authc.support.UsernamePasswordToken.basicAuthHeaderValue;
|
||||
import static org.hamcrest.Matchers.*;
|
||||
|
||||
public class ShieldCachePermissionTests extends ShieldIntegrationTest {
|
||||
|
||||
static final String READ_ONE_IDX_USER = "read_user";
|
||||
|
||||
@Override
|
||||
public String configUsers() {
|
||||
return super.configUsers()
|
||||
+ READ_ONE_IDX_USER + ":" + ShieldSettingsSource.DEFAULT_PASSWORD_HASHED + "\n";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String configRoles() {
|
||||
return super.configRoles()
|
||||
+ "\nread_one_idx:\n"
|
||||
+ " indices:\n"
|
||||
+ " 'data': READ\n";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String configUsersRoles() {
|
||||
return super.configUsersRoles()
|
||||
+ "read_one_idx:" + READ_ONE_IDX_USER + "\n";
|
||||
}
|
||||
|
||||
@BeforeClass
|
||||
public static void checkVersion() {
|
||||
assumeTrue("These tests are only valid with elasticsearch 1.6.0+", Version.CURRENT.id >= 1060099);
|
||||
}
|
||||
|
||||
@Before
|
||||
public void loadData() {
|
||||
index("data", "a", "1", "{ \"name\": \"John\", \"token\": \"token1\" }");
|
||||
index("tokens", "tokens", "1", "{ \"group\": \"1\", \"tokens\": [\"token1\", \"token2\"] }");
|
||||
client().preparePutIndexedScript().setOpType(IndexRequest.OpType.CREATE).setSource("{\n" +
|
||||
"\"template\": {\n" +
|
||||
" \"query\": {\n" +
|
||||
" \"filtered\": {\n" +
|
||||
" \"filter\": {\n" +
|
||||
" \"exists\": {\n" +
|
||||
" \"field\": \"{{name}}\"\n" +
|
||||
" }\n" +
|
||||
" }\n" +
|
||||
" }\n" +
|
||||
" }\n" +
|
||||
" }\n" +
|
||||
"}")
|
||||
.setScriptLang("mustache")
|
||||
.setId("testTemplate")
|
||||
.execute().actionGet();
|
||||
refresh();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testThatTermsFilterQueryDoesntLeakData() {
|
||||
SearchResponse response = client().prepareSearch("data").setTypes("a").setQuery(QueryBuilders.filteredQuery(
|
||||
QueryBuilders.matchAllQuery(),
|
||||
QueryBuilders.termsLookupQuery("token")
|
||||
.lookupIndex("tokens")
|
||||
.lookupType("tokens")
|
||||
.lookupId("1")
|
||||
.lookupPath("tokens")))
|
||||
.execute().actionGet();
|
||||
assertThat(response.isTimedOut(), is(false));
|
||||
assertThat(response.getHits().hits().length, is(1));
|
||||
|
||||
// Repeat with unauthorized user!!!!
|
||||
try {
|
||||
client().prepareSearch("data").setTypes("a").setQuery(QueryBuilders.filteredQuery(
|
||||
QueryBuilders.matchAllQuery(),
|
||||
QueryBuilders.termsLookupQuery("token")
|
||||
.lookupIndex("tokens")
|
||||
.lookupType("tokens")
|
||||
.lookupId("1")
|
||||
.lookupPath("tokens")))
|
||||
.putHeader("Authorization", basicAuthHeaderValue(READ_ONE_IDX_USER, new SecuredString("changeme".toCharArray())))
|
||||
.execute().actionGet();
|
||||
fail("search phase exception should have been thrown");
|
||||
} catch (SearchPhaseExecutionException e) {
|
||||
assertThat(e.toString(), containsString("AuthorizationException"));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testThatScriptServiceDoesntLeakData() {
|
||||
SearchResponse response = client().prepareSearch("data").setTypes("a")
|
||||
.setTemplateType(ScriptService.ScriptType.INDEXED)
|
||||
.setTemplateName("testTemplate")
|
||||
.setTemplateParams(Collections.<String, Object>singletonMap("name", "token"))
|
||||
.execute().actionGet();
|
||||
assertThat(response.isTimedOut(), is(false));
|
||||
assertThat(response.getHits().hits().length, is(1));
|
||||
|
||||
// Repeat with unauthorized user!!!!
|
||||
try {
|
||||
client().prepareSearch("data").setTypes("a")
|
||||
.setTemplateType(ScriptService.ScriptType.INDEXED)
|
||||
.setTemplateName("testTemplate")
|
||||
.setTemplateParams(Collections.<String, Object>singletonMap("name", "token"))
|
||||
.putHeader("Authorization", basicAuthHeaderValue(READ_ONE_IDX_USER, new SecuredString("changeme".toCharArray())))
|
||||
.execute().actionGet();
|
||||
fail("search phase exception should have been thrown");
|
||||
} catch (SearchPhaseExecutionException e) {
|
||||
assertThat(e.toString(), containsString("AuthorizationException"));
|
||||
}
|
||||
}
|
||||
}
|
|
@ -46,7 +46,7 @@ public class ShieldSettingsSource extends ClusterDiscoveryConfiguration.UnicastZ
|
|||
|
||||
public static final String DEFAULT_USER_NAME = "test_user";
|
||||
public static final String DEFAULT_PASSWORD = "changeme";
|
||||
private static final String DEFAULT_PASSWORD_HASHED = new String(Hasher.BCRYPT.hash(new SecuredString(DEFAULT_PASSWORD.toCharArray())));
|
||||
public static final String DEFAULT_PASSWORD_HASHED = new String(Hasher.BCRYPT.hash(new SecuredString(DEFAULT_PASSWORD.toCharArray())));
|
||||
public static final String DEFAULT_ROLE = "user";
|
||||
|
||||
public static final String DEFAULT_TRANSPORT_CLIENT_ROLE = "trans_client_user";
|
||||
|
|
Loading…
Reference in New Issue