Check TooComplex exception for HasPrivileges body (#128870)

The index pattern provided in the body of `_has_privileges` can
trigger a `TooComplexToDeterminizeException` which is then bubbled up
(badly).

This change catches that exception and provides a better message
This commit is contained in:
Tim Vernum 2025-06-04 21:55:23 +10:00 committed by GitHub
parent 46e5f1c46f
commit fb874848ef
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 48 additions and 5 deletions

View File

@ -0,0 +1,5 @@
pr: 128870
summary: Check `TooComplex` exception for `HasPrivileges` body
area: Authorization
type: enhancement
issues: []

View File

@ -6,8 +6,11 @@
*/
package org.elasticsearch.xpack.core.security.authz.permission;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.apache.lucene.util.automaton.Automaton;
import org.apache.lucene.util.automaton.Operations;
import org.apache.lucene.util.automaton.TooComplexToDeterminizeException;
import org.elasticsearch.action.admin.indices.mapping.put.TransportAutoPutMappingAction;
import org.elasticsearch.action.admin.indices.mapping.put.TransportPutMappingAction;
import org.elasticsearch.action.support.IndexComponentSelector;
@ -43,8 +46,10 @@ import java.util.Objects;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.BiPredicate;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.function.Supplier;
import java.util.stream.Collectors;
import static java.util.Collections.unmodifiableMap;
@ -54,6 +59,8 @@ import static java.util.Collections.unmodifiableMap;
*/
public final class IndicesPermission {
private final Logger logger = LogManager.getLogger(getClass());
private static final DeprecationLogger deprecationLogger = DeprecationLogger.getLogger(IndicesPermission.class);
public static final IndicesPermission NONE = new IndicesPermission(new RestrictedIndices(Automatons.EMPTY), Group.EMPTY_ARRAY);
@ -330,11 +337,23 @@ public final class IndicesPermission {
combineIndexGroups && checkForIndexPatterns.stream().anyMatch(Automatons::isLuceneRegex),
IndexComponentSelector.FAILURES
);
for (String forIndexPattern : checkForIndexPatterns) {
Automaton checkIndexAutomaton = Automatons.patterns(forIndexPattern);
if (false == allowRestrictedIndices && false == isConcreteRestrictedIndex(forIndexPattern)) {
checkIndexAutomaton = Automatons.minusAndMinimize(checkIndexAutomaton, restrictedIndices.getAutomaton());
}
Map<String, Automaton> checkIndexPatterns = checkForIndexPatterns.stream()
.collect(Collectors.toMap(Function.identity(), pattern -> {
try {
Automaton automaton = Automatons.patterns(pattern);
if (false == allowRestrictedIndices && false == isConcreteRestrictedIndex(pattern)) {
automaton = Automatons.minusAndMinimize(automaton, restrictedIndices.getAutomaton());
}
return automaton;
} catch (TooComplexToDeterminizeException e) {
final String text = pattern.length() > 260 ? Strings.cleanTruncate(pattern, 256) + "..." : pattern;
logger.info("refusing to check privileges against complex index pattern [{}]", text);
throw new IllegalArgumentException("the provided index pattern [" + text + "] is too complex to be evaluated", e);
}
}));
for (var entry : checkIndexPatterns.entrySet()) {
final String forIndexPattern = entry.getKey();
final Automaton checkIndexAutomaton = entry.getValue();
if (false == Operations.isEmpty(checkIndexAutomaton)) {
Automaton allowedPrivilegesAutomatonForDataSelector = getIndexPrivilegesAutomaton(
indexGroupAutomatonsForDataSelector,

View File

@ -6,6 +6,7 @@
*/
package org.elasticsearch.xpack.security.authz.accesscontrol;
import org.apache.lucene.util.automaton.TooComplexToDeterminizeException;
import org.elasticsearch.ElasticsearchSecurityException;
import org.elasticsearch.TransportVersion;
import org.elasticsearch.action.admin.indices.mapping.put.TransportAutoPutMappingAction;
@ -55,6 +56,7 @@ import static org.elasticsearch.xpack.core.security.test.TestRestrictedIndices.R
import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.hasSize;
import static org.hamcrest.Matchers.instanceOf;
import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.notNullValue;
import static org.hamcrest.Matchers.nullValue;
@ -1090,6 +1092,23 @@ public class IndicesPermissionTests extends ESTestCase {
assertThat(predicate.test(concreteIndexF, IndexComponentSelector.FAILURES), is(true));
}
public void testCheckResourcePrivilegesWithTooComplexAutomaton() {
IndicesPermission permission = new IndicesPermission.Builder(RESTRICTED_INDICES).addGroup(
IndexPrivilege.ALL,
FieldPermissions.DEFAULT,
null,
false,
"my-index"
).build();
var ex = expectThrows(
IllegalArgumentException.class,
() -> permission.checkResourcePrivileges(Set.of("****a*b?c**d**e*f??*g**h???i??*j*k*l*m*n???o*"), false, Set.of("read"), null)
);
assertThat(ex.getMessage(), containsString("index pattern [****a*b?c**d**e*f??*g**h???i??*j*k*l*m*n???o*]"));
assertThat(ex.getCause(), instanceOf(TooComplexToDeterminizeException.class));
}
private static IndexAbstraction concreteIndexAbstraction(String name) {
return new IndexAbstraction.ConcreteIndex(
IndexMetadata.builder(name).settings(indexSettings(IndexVersion.current(), 1, 0)).build()