diff --git a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/authc/support/mapper/expressiondsl/FieldExpression.java b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/authc/support/mapper/expressiondsl/FieldExpression.java index bea4bbb1cc8fa..ec524420cdffe 100644 --- a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/authc/support/mapper/expressiondsl/FieldExpression.java +++ b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/authc/support/mapper/expressiondsl/FieldExpression.java @@ -119,17 +119,13 @@ public FieldValue(Object value) { private static CharacterRunAutomaton buildAutomaton(Object value) { if (value instanceof String) { final String str = (String) value; - if (Regex.isSimpleMatchPattern(str) || isLuceneRegex(str)) { + if (Regex.isSimpleMatchPattern(str) || Automatons.isLuceneRegex(str)) { return new CharacterRunAutomaton(Automatons.patterns(str)); } } return null; } - private static boolean isLuceneRegex(String str) { - return str.length() > 1 && str.charAt(0) == '/' && str.charAt(str.length() - 1) == '/'; - } - public Object getValue() { return value; } diff --git a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/authz/permission/IndicesPermission.java b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/authz/permission/IndicesPermission.java index 2e51e07bc7676..b68b9dd79204a 100644 --- a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/authz/permission/IndicesPermission.java +++ b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/authz/permission/IndicesPermission.java @@ -15,6 +15,7 @@ import org.elasticsearch.common.Nullable; import org.elasticsearch.common.Strings; import org.elasticsearch.common.bytes.BytesReference; +import org.elasticsearch.common.regex.Regex; import org.elasticsearch.xpack.core.security.authz.accesscontrol.IndicesAccessControl; import org.elasticsearch.xpack.core.security.authz.privilege.IndexPrivilege; import org.elasticsearch.xpack.core.security.index.RestrictedIndicesNames; @@ -139,7 +140,7 @@ public ResourcePrivilegesMap checkResourcePrivileges(Set checkForIndexPa final Map predicateCache = new HashMap<>(); for (String forIndexPattern : checkForIndexPatterns) { Automaton checkIndexAutomaton = Automatons.patterns(forIndexPattern); - if (false == allowRestrictedIndices && false == RestrictedIndicesNames.RESTRICTED_NAMES.contains(forIndexPattern)) { + if (false == allowRestrictedIndices && false == isConcreteRestrictedIndex(forIndexPattern)) { checkIndexAutomaton = Automatons.minusAndMinimize(checkIndexAutomaton, RestrictedIndicesNames.NAMES_AUTOMATON); } if (false == Operations.isEmpty(checkIndexAutomaton)) { @@ -268,6 +269,13 @@ public Map authorize(String act return unmodifiableMap(indexPermissions); } + private boolean isConcreteRestrictedIndex(String indexPattern) { + if (Regex.isSimpleMatchPattern(indexPattern) || Automatons.isLuceneRegex(indexPattern)) { + return false; + } + return RestrictedIndicesNames.isRestricted(indexPattern); + } + public static class Group { private final IndexPrivilege privilege; private final Predicate actionMatcher; @@ -316,7 +324,7 @@ private boolean check(String action) { private boolean check(String action, String index) { assert index != null; return check(action) && indexNameMatcher.test(index) - && (allowRestrictedIndices || (false == RestrictedIndicesNames.RESTRICTED_NAMES.contains(index))); + && (allowRestrictedIndices || (false == RestrictedIndicesNames.isRestricted(index))); } boolean hasQuery() { @@ -351,13 +359,13 @@ private static Predicate buildIndexMatcherPredicateForAction(String acti final Predicate predicate; if (restrictedIndices.isEmpty()) { predicate = indexMatcher(ordinaryIndices) - .and(index -> false == RestrictedIndicesNames.RESTRICTED_NAMES.contains(index)); + .and(index -> false == RestrictedIndicesNames.isRestricted(index)); } else if (ordinaryIndices.isEmpty()) { predicate = indexMatcher(restrictedIndices); } else { predicate = indexMatcher(restrictedIndices) .or(indexMatcher(ordinaryIndices) - .and(index -> false == RestrictedIndicesNames.RESTRICTED_NAMES.contains(index))); + .and(index -> false == RestrictedIndicesNames.isRestricted(index))); } return predicate; } diff --git a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/index/RestrictedIndicesNames.java b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/index/RestrictedIndicesNames.java index 80c17c484739c..77f6c537b6f81 100644 --- a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/index/RestrictedIndicesNames.java +++ b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/index/RestrictedIndicesNames.java @@ -10,6 +10,7 @@ import org.elasticsearch.common.util.set.Sets; import org.elasticsearch.xpack.core.security.support.Automatons; +import java.util.Arrays; import java.util.Collections; import java.util.Set; @@ -21,10 +22,20 @@ public final class RestrictedIndicesNames { public static final String INTERNAL_SECURITY_TOKENS_INDEX_7 = ".security-tokens-7"; public static final String SECURITY_TOKENS_ALIAS = ".security-tokens"; + // public for tests + public static final String ASYNC_SEARCH_PREFIX = ".async-search-"; + private static final Automaton ASYNC_SEARCH_AUTOMATON = Automatons.patterns(ASYNC_SEARCH_PREFIX + "*"); + + // public for tests public static final Set RESTRICTED_NAMES = Collections.unmodifiableSet(Sets.newHashSet(SECURITY_MAIN_ALIAS, INTERNAL_SECURITY_MAIN_INDEX_6, INTERNAL_SECURITY_MAIN_INDEX_7, INTERNAL_SECURITY_TOKENS_INDEX_7, SECURITY_TOKENS_ALIAS)); - public static final Automaton NAMES_AUTOMATON = Automatons.patterns(RESTRICTED_NAMES); + public static boolean isRestricted(String concreteIndexName) { + return RESTRICTED_NAMES.contains(concreteIndexName) || concreteIndexName.startsWith(ASYNC_SEARCH_PREFIX); + } + + public static final Automaton NAMES_AUTOMATON = Automatons.unionAndMinimize(Arrays.asList(Automatons.patterns(RESTRICTED_NAMES), + ASYNC_SEARCH_AUTOMATON)); private RestrictedIndicesNames() { } diff --git a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/support/Automatons.java b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/support/Automatons.java index 7e6fd7ca46283..c0e4af238b1bd 100644 --- a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/support/Automatons.java +++ b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/support/Automatons.java @@ -107,6 +107,13 @@ static Automaton pattern(String pattern) { } } + /** + * Is the str a lucene type of pattern + */ + public static boolean isLuceneRegex(String str) { + return str.length() > 1 && str.charAt(0) == '/' && str.charAt(str.length() - 1) == '/'; + } + private static Automaton buildAutomaton(String pattern) { if (pattern.startsWith("/")) { // it's a lucene regexp if (pattern.length() == 1 || !pattern.endsWith("/")) { diff --git a/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/security/authz/store/ReservedRolesStoreTests.java b/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/security/authz/store/ReservedRolesStoreTests.java index f90f6b7b17009..e7166f0c94fef 100644 --- a/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/security/authz/store/ReservedRolesStoreTests.java +++ b/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/security/authz/store/ReservedRolesStoreTests.java @@ -254,8 +254,11 @@ public void testSnapshotUserRole() { // but that depends on how users are supposed to perform snapshots of those new indices. assertThat(snapshotUserRole.indices().allowedIndicesMatcher(GetIndexAction.NAME).test(index), is(true)); } + assertThat(snapshotUserRole.indices().allowedIndicesMatcher(GetIndexAction.NAME).test( + RestrictedIndicesNames.ASYNC_SEARCH_PREFIX + randomAlphaOfLengthBetween(0, 2)), is(true)); assertNoAccessAllowed(snapshotUserRole, RestrictedIndicesNames.RESTRICTED_NAMES); + assertNoAccessAllowed(snapshotUserRole, RestrictedIndicesNames.ASYNC_SEARCH_PREFIX + randomAlphaOfLengthBetween(0, 2)); } public void testIngestAdminRole() { @@ -285,6 +288,7 @@ public void testIngestAdminRole() { is(false)); assertNoAccessAllowed(ingestAdminRole, RestrictedIndicesNames.RESTRICTED_NAMES); + assertNoAccessAllowed(ingestAdminRole, RestrictedIndicesNames.ASYNC_SEARCH_PREFIX + randomAlphaOfLengthBetween(0, 2)); } public void testKibanaSystemRole() { @@ -402,6 +406,7 @@ public void testKibanaSystemRole() { assertThat(kibanaRole.indices().allowedIndicesMatcher(READ_CROSS_CLUSTER_NAME).test(index), is(false)); assertNoAccessAllowed(kibanaRole, RestrictedIndicesNames.RESTRICTED_NAMES); + assertNoAccessAllowed(kibanaRole, RestrictedIndicesNames.ASYNC_SEARCH_PREFIX + randomAlphaOfLengthBetween(0, 2)); } public void testKibanaUserRole() { @@ -441,6 +446,7 @@ public void testKibanaUserRole() { "*"), is(false)); assertNoAccessAllowed(kibanaUserRole, RestrictedIndicesNames.RESTRICTED_NAMES); + assertNoAccessAllowed(kibanaUserRole, RestrictedIndicesNames.ASYNC_SEARCH_PREFIX + randomAlphaOfLengthBetween(0, 2)); } public void testMonitoringUserRole() { @@ -488,6 +494,7 @@ public void testMonitoringUserRole() { assertThat(monitoringUserRole.indices().allowedIndicesMatcher(READ_CROSS_CLUSTER_NAME).test(index), is(true)); assertNoAccessAllowed(monitoringUserRole, RestrictedIndicesNames.RESTRICTED_NAMES); + assertNoAccessAllowed(monitoringUserRole, RestrictedIndicesNames.ASYNC_SEARCH_PREFIX + randomAlphaOfLengthBetween(0, 2)); final String kibanaApplicationWithRandomIndex = "kibana-" + randomFrom(randomAlphaOfLengthBetween(8, 24), ".kibana"); assertThat(monitoringUserRole.application().grants( @@ -562,6 +569,7 @@ public void testRemoteMonitoringAgentRole() { assertThat(remoteMonitoringAgentRole.indices().allowedIndicesMatcher(GetAction.NAME).test(metricbeatIndex), is(false)); assertNoAccessAllowed(remoteMonitoringAgentRole, RestrictedIndicesNames.RESTRICTED_NAMES); + assertNoAccessAllowed(remoteMonitoringAgentRole, RestrictedIndicesNames.ASYNC_SEARCH_PREFIX + randomAlphaOfLengthBetween(0, 2)); } public void testRemoteMonitoringCollectorRole() { @@ -615,29 +623,50 @@ public void testRemoteMonitoringCollectorRole() { // (but ideally, the monitoring user should see all indices). assertThat(remoteMonitoringAgentRole.indices().allowedIndicesMatcher(GetSettingsAction.NAME) .test(randomFrom(RestrictedIndicesNames.RESTRICTED_NAMES)), is(true)); + assertThat(remoteMonitoringAgentRole.indices().allowedIndicesMatcher(GetSettingsAction.NAME) + .test(RestrictedIndicesNames.ASYNC_SEARCH_PREFIX + randomAlphaOfLengthBetween(0, 2)), is(true)); assertThat(remoteMonitoringAgentRole.indices().allowedIndicesMatcher(IndicesShardStoresAction.NAME) .test(randomFrom(RestrictedIndicesNames.RESTRICTED_NAMES)), is(true)); + assertThat(remoteMonitoringAgentRole.indices().allowedIndicesMatcher(IndicesShardStoresAction.NAME) + .test(RestrictedIndicesNames.ASYNC_SEARCH_PREFIX + randomAlphaOfLengthBetween(0, 2)), is(true)); assertThat(remoteMonitoringAgentRole.indices().allowedIndicesMatcher(UpgradeStatusAction.NAME) .test(randomFrom(RestrictedIndicesNames.RESTRICTED_NAMES)), is(true)); + assertThat(remoteMonitoringAgentRole.indices().allowedIndicesMatcher(UpgradeStatusAction.NAME) + .test(RestrictedIndicesNames.ASYNC_SEARCH_PREFIX + randomAlphaOfLengthBetween(0, 2)), is(true)); assertThat(remoteMonitoringAgentRole.indices().allowedIndicesMatcher(RecoveryAction.NAME) .test(randomFrom(RestrictedIndicesNames.RESTRICTED_NAMES)), is(true)); + assertThat(remoteMonitoringAgentRole.indices().allowedIndicesMatcher(RecoveryAction.NAME) + .test(RestrictedIndicesNames.ASYNC_SEARCH_PREFIX + randomAlphaOfLengthBetween(0, 2)), is(true)); assertThat(remoteMonitoringAgentRole.indices().allowedIndicesMatcher(IndicesStatsAction.NAME) .test(randomFrom(RestrictedIndicesNames.RESTRICTED_NAMES)), is(true)); + assertThat(remoteMonitoringAgentRole.indices().allowedIndicesMatcher(IndicesStatsAction.NAME) + .test(RestrictedIndicesNames.ASYNC_SEARCH_PREFIX + randomAlphaOfLengthBetween(0, 2)), is(true)); assertThat(remoteMonitoringAgentRole.indices().allowedIndicesMatcher(IndicesSegmentsAction.NAME) .test(randomFrom(RestrictedIndicesNames.RESTRICTED_NAMES)), is(true)); + assertThat(remoteMonitoringAgentRole.indices().allowedIndicesMatcher(IndicesSegmentsAction.NAME) + .test(RestrictedIndicesNames.ASYNC_SEARCH_PREFIX + randomAlphaOfLengthBetween(0, 2)), is(true)); assertThat(remoteMonitoringAgentRole.indices().allowedIndicesMatcher(SearchAction.NAME) .test(randomFrom(RestrictedIndicesNames.RESTRICTED_NAMES)), is(false)); + assertThat(remoteMonitoringAgentRole.indices().allowedIndicesMatcher(SearchAction.NAME) + .test(RestrictedIndicesNames.ASYNC_SEARCH_PREFIX + randomAlphaOfLengthBetween(0, 2)), is(false)); assertThat(remoteMonitoringAgentRole.indices().allowedIndicesMatcher(GetAction.NAME) .test(randomFrom(RestrictedIndicesNames.RESTRICTED_NAMES)), is(false)); + assertThat(remoteMonitoringAgentRole.indices().allowedIndicesMatcher(GetAction.NAME) + .test(RestrictedIndicesNames.ASYNC_SEARCH_PREFIX + randomAlphaOfLengthBetween(0, 2)), is(false)); assertThat(remoteMonitoringAgentRole.indices().allowedIndicesMatcher(DeleteAction.NAME) .test(randomFrom(RestrictedIndicesNames.RESTRICTED_NAMES)), is(false)); + assertThat(remoteMonitoringAgentRole.indices().allowedIndicesMatcher(DeleteAction.NAME) + .test(RestrictedIndicesNames.ASYNC_SEARCH_PREFIX + randomAlphaOfLengthBetween(0, 2)), is(false)); assertThat(remoteMonitoringAgentRole.indices().allowedIndicesMatcher(IndexAction.NAME) .test(randomFrom(RestrictedIndicesNames.RESTRICTED_NAMES)), is(false)); + assertThat(remoteMonitoringAgentRole.indices().allowedIndicesMatcher(IndexAction.NAME) + .test(RestrictedIndicesNames.ASYNC_SEARCH_PREFIX + randomAlphaOfLengthBetween(0, 2)), is(false)); assertMonitoringOnRestrictedIndices(remoteMonitoringAgentRole); assertNoAccessAllowed(remoteMonitoringAgentRole, RestrictedIndicesNames.RESTRICTED_NAMES); + assertNoAccessAllowed(remoteMonitoringAgentRole, RestrictedIndicesNames.ASYNC_SEARCH_PREFIX + randomAlphaOfLengthBetween(0, 2)); } private void assertMonitoringOnRestrictedIndices(Role role) { @@ -656,11 +685,13 @@ private void assertMonitoringOnRestrictedIndices(Role role) { final List indexMonitoringActionNamesList = Arrays.asList(IndicesStatsAction.NAME, IndicesSegmentsAction.NAME, GetSettingsAction.NAME, IndicesShardStoresAction.NAME, UpgradeStatusAction.NAME, RecoveryAction.NAME); for (final String indexMonitoringActionName : indexMonitoringActionNamesList) { + String asyncSearchIndex = RestrictedIndicesNames.ASYNC_SEARCH_PREFIX + randomAlphaOfLengthBetween(0, 2); final Map authzMap = role.indices().authorize(indexMonitoringActionName, - Sets.newHashSet(internalSecurityIndex, RestrictedIndicesNames.SECURITY_MAIN_ALIAS), + Sets.newHashSet(internalSecurityIndex, RestrictedIndicesNames.SECURITY_MAIN_ALIAS, asyncSearchIndex), metaData.getAliasAndIndexLookup(), fieldPermissionsCache); assertThat(authzMap.get(internalSecurityIndex).isGranted(), is(true)); assertThat(authzMap.get(RestrictedIndicesNames.SECURITY_MAIN_ALIAS).isGranted(), is(true)); + assertThat(authzMap.get(asyncSearchIndex).isGranted(), is(true)); } } @@ -704,6 +735,7 @@ public void testReportingUserRole() { assertThat(reportingUserRole.indices().allowedIndicesMatcher(BulkAction.NAME).test(index), is(false)); assertNoAccessAllowed(reportingUserRole, RestrictedIndicesNames.RESTRICTED_NAMES); + assertNoAccessAllowed(reportingUserRole, RestrictedIndicesNames.ASYNC_SEARCH_PREFIX + randomAlphaOfLengthBetween(0, 2)); } public void testKibanaDashboardOnlyUserRole() { @@ -740,6 +772,7 @@ public void testKibanaDashboardOnlyUserRole() { new ApplicationPrivilege(applicationWithRandomIndex, "app-random-index", "all"), "*"), is(false)); assertNoAccessAllowed(dashboardsOnlyUserRole, RestrictedIndicesNames.RESTRICTED_NAMES); + assertNoAccessAllowed(dashboardsOnlyUserRole, RestrictedIndicesNames.ASYNC_SEARCH_PREFIX + randomAlphaOfLengthBetween(0, 2)); } public void testSuperuserRole() { @@ -840,6 +873,7 @@ public void testLogstashSystemRole() { is(false)); assertNoAccessAllowed(logstashSystemRole, RestrictedIndicesNames.RESTRICTED_NAMES); + assertNoAccessAllowed(logstashSystemRole, RestrictedIndicesNames.ASYNC_SEARCH_PREFIX + randomAlphaOfLengthBetween(0, 2)); } public void testBeatsAdminRole() { @@ -880,6 +914,7 @@ public void testBeatsAdminRole() { assertThat(beatsAdminRole.indices().allowedIndicesMatcher(GetAction.NAME).test(index), is(true)); assertNoAccessAllowed(beatsAdminRole, RestrictedIndicesNames.RESTRICTED_NAMES); + assertNoAccessAllowed(beatsAdminRole, RestrictedIndicesNames.ASYNC_SEARCH_PREFIX + randomAlphaOfLengthBetween(0, 2)); } public void testBeatsSystemRole() { @@ -915,6 +950,7 @@ public void testBeatsSystemRole() { assertThat(beatsSystemRole.indices().allowedIndicesMatcher(BulkAction.NAME).test(index), is(true)); assertNoAccessAllowed(beatsSystemRole, RestrictedIndicesNames.RESTRICTED_NAMES); + assertNoAccessAllowed(beatsSystemRole, RestrictedIndicesNames.ASYNC_SEARCH_PREFIX + randomAlphaOfLengthBetween(0, 2)); } public void testAPMSystemRole() { @@ -955,7 +991,7 @@ public void testAPMSystemRole() { "indices:data/write/index:op_type/" + randomAlphaOfLengthBetween(3,5)).test(index), is(false)); assertNoAccessAllowed(APMSystemRole, RestrictedIndicesNames.RESTRICTED_NAMES); - + assertNoAccessAllowed(APMSystemRole, RestrictedIndicesNames.ASYNC_SEARCH_PREFIX + randomAlphaOfLengthBetween(0, 2)); } public void testAPMUserRole() { @@ -1049,6 +1085,7 @@ public void testMachineLearningAdminRole() { assertReadWriteDocsButNotDeleteIndexAllowed(role, AnnotationIndex.INDEX_NAME); assertNoAccessAllowed(role, RestrictedIndicesNames.RESTRICTED_NAMES); + assertNoAccessAllowed(role, RestrictedIndicesNames.ASYNC_SEARCH_PREFIX + randomAlphaOfLengthBetween(0, 2)); final String kibanaApplicationWithRandomIndex = "kibana-" + randomFrom(randomAlphaOfLengthBetween(8, 24), ".kibana"); assertThat(role.application().grants( @@ -1135,6 +1172,7 @@ public void testMachineLearningUserRole() { assertReadWriteDocsButNotDeleteIndexAllowed(role, AnnotationIndex.INDEX_NAME); assertNoAccessAllowed(role, RestrictedIndicesNames.RESTRICTED_NAMES); + assertNoAccessAllowed(role, RestrictedIndicesNames.ASYNC_SEARCH_PREFIX + randomAlphaOfLengthBetween(0, 2)); final String kibanaApplicationWithRandomIndex = "kibana-" + randomFrom(randomAlphaOfLengthBetween(8, 24), ".kibana"); @@ -1182,6 +1220,7 @@ public void testTransformAdminRole() { assertNoAccessAllowed(role, TransformInternalIndexConstants.LATEST_INDEX_NAME); // internal use only assertNoAccessAllowed(role, RestrictedIndicesNames.RESTRICTED_NAMES); + assertNoAccessAllowed(role, RestrictedIndicesNames.ASYNC_SEARCH_PREFIX + randomAlphaOfLengthBetween(0, 2)); final String kibanaApplicationWithRandomIndex = "kibana-" + randomFrom(randomAlphaOfLengthBetween(8, 24), ".kibana"); assertThat(role.application().grants( @@ -1234,6 +1273,7 @@ public void testDataFrameTransformsUserRole() { assertNoAccessAllowed(role, TransformInternalIndexConstants.LATEST_INDEX_NAME); assertNoAccessAllowed(role, RestrictedIndicesNames.RESTRICTED_NAMES); + assertNoAccessAllowed(role, RestrictedIndicesNames.ASYNC_SEARCH_PREFIX + randomAlphaOfLengthBetween(0, 2)); final String kibanaApplicationWithRandomIndex = "kibana-" + randomFrom(randomAlphaOfLengthBetween(8, 24), ".kibana"); assertThat(role.application().grants( @@ -1284,6 +1324,7 @@ public void testWatcherAdminRole() { } assertNoAccessAllowed(role, RestrictedIndicesNames.RESTRICTED_NAMES); + assertNoAccessAllowed(role, RestrictedIndicesNames.ASYNC_SEARCH_PREFIX + randomAlphaOfLengthBetween(0, 2)); } public void testWatcherUserRole() { @@ -1317,6 +1358,7 @@ public void testWatcherUserRole() { } assertNoAccessAllowed(role, RestrictedIndicesNames.RESTRICTED_NAMES); + assertNoAccessAllowed(role, RestrictedIndicesNames.ASYNC_SEARCH_PREFIX + randomAlphaOfLengthBetween(0, 2)); } private void assertReadWriteDocsButNotDeleteIndexAllowed(Role role, String index) { @@ -1341,6 +1383,7 @@ private void assertOnlyReadAllowed(Role role, String index) { assertThat(role.indices().allowedIndicesMatcher(BulkAction.NAME).test(index), is(false)); assertNoAccessAllowed(role, RestrictedIndicesNames.RESTRICTED_NAMES); + assertNoAccessAllowed(role, RestrictedIndicesNames.ASYNC_SEARCH_PREFIX + randomAlphaOfLengthBetween(0, 2)); } private void assertNoAccessAllowed(Role role, Collection indices) { diff --git a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authz/RBACEngineTests.java b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authz/RBACEngineTests.java index fd84afea365be..06a5d646d7a81 100644 --- a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authz/RBACEngineTests.java +++ b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authz/RBACEngineTests.java @@ -544,6 +544,117 @@ public void testCheckingIndexPermissionsDefinedOnDifferentPatterns() throws Exce )); } + public void testCheckRestrictedIndexPatternPermission() throws Exception { + User user = new User(randomAlphaOfLengthBetween(4, 12)); + Authentication authentication = mock(Authentication.class); + when(authentication.getUser()).thenReturn(user); + final String patternPrefix = RestrictedIndicesNames.ASYNC_SEARCH_PREFIX.substring(0, + randomIntBetween(1, RestrictedIndicesNames.ASYNC_SEARCH_PREFIX.length() - 2)); + Role role = Role.builder("role") + .add(FieldPermissions.DEFAULT, null, IndexPrivilege.INDEX, false, patternPrefix + "*") + .build(); + RBACAuthorizationInfo authzInfo = new RBACAuthorizationInfo(role, null); + + String prePatternPrefix = patternPrefix.substring(0, randomIntBetween(1, patternPrefix.length() - 1)) + "*"; + HasPrivilegesResponse response = hasPrivileges(RoleDescriptor.IndicesPrivileges.builder() + .indices(prePatternPrefix) + .allowRestrictedIndices(randomBoolean()) + .privileges("index") + .build(), authentication, authzInfo, Collections.emptyList(), Strings.EMPTY_ARRAY); + assertThat(response.isCompleteMatch(), is(false)); + assertThat(response.getIndexPrivileges(), Matchers.iterableWithSize(1)); + assertThat(response.getIndexPrivileges(), containsInAnyOrder( + ResourcePrivileges.builder(prePatternPrefix) + .addPrivileges(MapBuilder.newMapBuilder(new LinkedHashMap()) + .put("index", false).map()).build())); + + String matchesPatternPrefix = RestrictedIndicesNames.ASYNC_SEARCH_PREFIX.substring(0, patternPrefix.length() + 1); + response = hasPrivileges(RoleDescriptor.IndicesPrivileges.builder() + .indices(matchesPatternPrefix + "*") + .allowRestrictedIndices(false) + .privileges("index") + .build(), authentication, authzInfo, Collections.emptyList(), Strings.EMPTY_ARRAY); + assertThat(response.isCompleteMatch(), is(true)); + assertThat(response.getIndexPrivileges(), Matchers.iterableWithSize(1)); + assertThat(response.getIndexPrivileges(), containsInAnyOrder( + ResourcePrivileges.builder(matchesPatternPrefix + "*") + .addPrivileges(MapBuilder.newMapBuilder(new LinkedHashMap()) + .put("index", true).map()).build())); + response = hasPrivileges(RoleDescriptor.IndicesPrivileges.builder() + .indices(matchesPatternPrefix + "*") + .allowRestrictedIndices(true) + .privileges("index") + .build(), authentication, authzInfo, Collections.emptyList(), Strings.EMPTY_ARRAY); + assertThat(response.isCompleteMatch(), is(false)); + assertThat(response.getIndexPrivileges(), Matchers.iterableWithSize(1)); + assertThat(response.getIndexPrivileges(), containsInAnyOrder( + ResourcePrivileges.builder(matchesPatternPrefix + "*") + .addPrivileges(MapBuilder.newMapBuilder(new LinkedHashMap()) + .put("index", false).map()).build())); + response = hasPrivileges(RoleDescriptor.IndicesPrivileges.builder() + .indices(matchesPatternPrefix) + .allowRestrictedIndices(randomBoolean()) + .privileges("index") + .build(), authentication, authzInfo, Collections.emptyList(), Strings.EMPTY_ARRAY); + assertThat(response.isCompleteMatch(), is(true)); + assertThat(response.getIndexPrivileges(), Matchers.iterableWithSize(1)); + assertThat(response.getIndexPrivileges(), containsInAnyOrder( + ResourcePrivileges.builder(matchesPatternPrefix) + .addPrivileges(MapBuilder.newMapBuilder(new LinkedHashMap()) + .put("index", true).map()).build())); + + final String restrictedIndexMatchingWildcard = RestrictedIndicesNames.ASYNC_SEARCH_PREFIX + randomAlphaOfLengthBetween(0, 2); + response = hasPrivileges(RoleDescriptor.IndicesPrivileges.builder() + .indices(restrictedIndexMatchingWildcard + "*") + .allowRestrictedIndices(true) + .privileges("index") + .build(), authentication, authzInfo, Collections.emptyList(), Strings.EMPTY_ARRAY); + assertThat(response.isCompleteMatch(), is(false)); + assertThat(response.getIndexPrivileges(), Matchers.iterableWithSize(1)); + assertThat(response.getIndexPrivileges(), containsInAnyOrder( + ResourcePrivileges.builder(restrictedIndexMatchingWildcard + "*") + .addPrivileges(MapBuilder.newMapBuilder(new LinkedHashMap()) + .put("index", false).map()).build())); + response = hasPrivileges(RoleDescriptor.IndicesPrivileges.builder() + .indices(restrictedIndexMatchingWildcard + "*") + .allowRestrictedIndices(false) + .privileges("index") + .build(), authentication, authzInfo, Collections.emptyList(), Strings.EMPTY_ARRAY); + assertThat(response.isCompleteMatch(), is(false)); + assertThat(response.getIndexPrivileges(), Matchers.iterableWithSize(1)); + assertThat(response.getIndexPrivileges(), containsInAnyOrder( + ResourcePrivileges.builder(restrictedIndexMatchingWildcard + "*") + .addPrivileges(MapBuilder.newMapBuilder(new LinkedHashMap()) + .put("index", false).map()).build())); + response = hasPrivileges(RoleDescriptor.IndicesPrivileges.builder() + .indices(restrictedIndexMatchingWildcard) + .allowRestrictedIndices(randomBoolean()) + .privileges("index") + .build(), authentication, authzInfo, Collections.emptyList(), Strings.EMPTY_ARRAY); + assertThat(response.isCompleteMatch(), is(false)); + assertThat(response.getIndexPrivileges(), Matchers.iterableWithSize(1)); + assertThat(response.getIndexPrivileges(), containsInAnyOrder( + ResourcePrivileges.builder(restrictedIndexMatchingWildcard) + .addPrivileges(MapBuilder.newMapBuilder(new LinkedHashMap()) + .put("index", false).map()).build())); + + role = Role.builder("role") + .add(FieldPermissions.DEFAULT, null, IndexPrivilege.INDEX, true, patternPrefix + "*") + .build(); + authzInfo = new RBACAuthorizationInfo(role, null); + response = hasPrivileges(RoleDescriptor.IndicesPrivileges.builder() + .indices(matchesPatternPrefix + "*") + .allowRestrictedIndices(randomBoolean()) + .privileges("index") + .build(), authentication, authzInfo, Collections.emptyList(), Strings.EMPTY_ARRAY); + assertThat(response.isCompleteMatch(), is(true)); + assertThat(response.getIndexPrivileges(), Matchers.iterableWithSize(1)); + assertThat(response.getIndexPrivileges(), containsInAnyOrder( + ResourcePrivileges.builder(matchesPatternPrefix + "*") + .addPrivileges(MapBuilder.newMapBuilder(new LinkedHashMap()) + .put("index", true).map()).build())); + } + public void testCheckExplicitRestrictedIndexPermissions() throws Exception { User user = new User(randomAlphaOfLengthBetween(4, 12)); Authentication authentication = mock(Authentication.class); diff --git a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authz/accesscontrol/IndicesPermissionTests.java b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authz/accesscontrol/IndicesPermissionTests.java index be73972f3a1e3..ee10a29083ad3 100644 --- a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authz/accesscontrol/IndicesPermissionTests.java +++ b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authz/accesscontrol/IndicesPermissionTests.java @@ -322,6 +322,32 @@ public void testSecurityIndicesPermissions() { assertThat(authzMap.get(RestrictedIndicesNames.SECURITY_MAIN_ALIAS).isGranted(), is(true)); } + public void testAsyncSearchIndicesPermissions() { + final Settings indexSettings = Settings.builder().put("index.version.created", Version.CURRENT).build(); + final String asyncSearchIndex = RestrictedIndicesNames.ASYNC_SEARCH_PREFIX + randomAlphaOfLengthBetween(0, 2); + final MetaData metaData = new MetaData.Builder() + .put(new IndexMetaData.Builder(asyncSearchIndex) + .settings(indexSettings) + .numberOfShards(1) + .numberOfReplicas(0) + .build(), true) + .build(); + FieldPermissionsCache fieldPermissionsCache = new FieldPermissionsCache(Settings.EMPTY); + SortedMap lookup = metaData.getAliasAndIndexLookup(); + + // allow_restricted_indices: false + IndicesPermission.Group group = new IndicesPermission.Group(IndexPrivilege.ALL, new FieldPermissions(), null, false, "*"); + Map authzMap = new IndicesPermission(group).authorize(SearchAction.NAME, + Sets.newHashSet(asyncSearchIndex), lookup, fieldPermissionsCache); + assertThat(authzMap.get(asyncSearchIndex).isGranted(), is(false)); + + // allow_restricted_indices: true + group = new IndicesPermission.Group(IndexPrivilege.ALL, new FieldPermissions(), null, true, "*"); + authzMap = new IndicesPermission(group).authorize(SearchAction.NAME, + Sets.newHashSet(asyncSearchIndex), lookup, fieldPermissionsCache); + assertThat(authzMap.get(asyncSearchIndex).isGranted(), is(true)); + } + private static FieldPermissionsDefinition fieldPermissionDef(String[] granted, String[] denied) { return new FieldPermissionsDefinition(granted, denied); } diff --git a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/user/XPackUserTests.java b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/user/XPackUserTests.java index c8b20e1b4604c..6c7fb2abdabf6 100644 --- a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/user/XPackUserTests.java +++ b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/user/XPackUserTests.java @@ -34,6 +34,7 @@ public void testXPackUserCannotAccessRestrictedIndices() { for (String index : RestrictedIndicesNames.RESTRICTED_NAMES) { assertThat(predicate.test(index), Matchers.is(false)); } + assertThat(predicate.test(RestrictedIndicesNames.ASYNC_SEARCH_PREFIX + randomAlphaOfLengthBetween(0, 2)), Matchers.is(false)); } public void testXPackUserCanReadAuditTrail() { diff --git a/x-pack/plugin/sql/qa/security/src/test/java/org/elasticsearch/xpack/sql/qa/security/SqlSecurityTestCase.java b/x-pack/plugin/sql/qa/security/src/test/java/org/elasticsearch/xpack/sql/qa/security/SqlSecurityTestCase.java index 7340a1ab9332e..abd2026d437af 100644 --- a/x-pack/plugin/sql/qa/security/src/test/java/org/elasticsearch/xpack/sql/qa/security/SqlSecurityTestCase.java +++ b/x-pack/plugin/sql/qa/security/src/test/java/org/elasticsearch/xpack/sql/qa/security/SqlSecurityTestCase.java @@ -41,6 +41,7 @@ import java.util.Map; import java.util.TreeMap; import java.util.function.Function; +import java.util.stream.Collectors; import static java.util.Arrays.asList; import static java.util.Collections.singletonMap; @@ -650,7 +651,8 @@ public void assertLogs() throws Exception { * SQL drops them from the interface. So we might have access to them, but we * don't show them. */ - indices.removeAll(RestrictedIndicesNames.RESTRICTED_NAMES); + indices = indices.stream().filter( + idx -> false == RestrictedIndicesNames.isRestricted(idx)).collect(Collectors.toList()); } } // Use a sorted list for indices for consistent error reporting