|
9 | 9 |
|
10 | 10 | import org.apache.logging.log4j.LogManager;
|
11 | 11 | import org.apache.logging.log4j.Logger;
|
| 12 | +import org.apache.lucene.search.BooleanClause; |
12 | 13 | import org.apache.lucene.search.QueryCachingPolicy;
|
| 14 | +import org.apache.lucene.search.QueryVisitor; |
13 | 15 | import org.apache.lucene.search.Weight;
|
14 | 16 | import org.elasticsearch.common.util.concurrent.ThreadContext;
|
15 | 17 | import org.elasticsearch.index.Index;
|
|
18 | 20 | import org.elasticsearch.xpack.core.security.authz.AuthorizationServiceField;
|
19 | 21 | import org.elasticsearch.xpack.core.security.authz.accesscontrol.IndicesAccessControl;
|
20 | 22 |
|
21 |
| -import java.util.HashSet; |
22 | 23 | import java.util.Objects;
|
23 |
| -import java.util.Set; |
24 | 24 |
|
25 | 25 | /**
|
26 | 26 | * Opts out of the query cache if field level security is active for the current request, and it is unsafe to cache.
|
@@ -63,24 +63,33 @@ public Weight doCache(Weight weight, QueryCachingPolicy policy) {
|
63 | 63 | */
|
64 | 64 | static boolean cachingIsSafe(Weight weight, IndicesAccessControl.IndexAccessControl permissions) {
|
65 | 65 | // support caching for common queries, by inspecting the field
|
66 |
| - // TODO: If in the future there is a Query#extractFields() then we can do a better job |
67 |
| - Set<String> fields = new HashSet<>(); |
68 | 66 | try {
|
69 |
| - FieldExtractor.extractFields(weight.getQuery(), fields); |
70 |
| - } catch (UnsupportedOperationException ok) { |
71 |
| - // we don't know how to safely extract the fields of this query, don't cache. |
72 |
| - return false; |
73 |
| - } |
| 67 | + weight.getQuery().visit(new QueryVisitor() { |
| 68 | + @Override |
| 69 | + public QueryVisitor getSubVisitor(BooleanClause.Occur occur, org.apache.lucene.search.Query parent) { |
| 70 | + return this; // we want to use the same visitor for must_not clauses too |
| 71 | + } |
74 | 72 |
|
75 |
| - // we successfully extracted the set of fields: check each one |
76 |
| - for (String field : fields) { |
77 |
| - // don't cache any internal fields (e.g. _field_names), these are complicated. |
78 |
| - if (field.startsWith("_") || permissions.getFieldPermissions().grantsAccessTo(field) == false) { |
79 |
| - return false; |
80 |
| - } |
| 73 | + @Override |
| 74 | + public boolean acceptField(String field) { |
| 75 | + // don't cache any internal fields (e.g. _field_names), these are complicated. |
| 76 | + if (field.startsWith("_") || permissions.getFieldPermissions().grantsAccessTo(field) == false) { |
| 77 | + throw new FLSQueryNotCacheable("Query field has FLS permissions"); |
| 78 | + } |
| 79 | + return super.acceptField(field); |
| 80 | + } |
| 81 | + }); |
| 82 | + } catch (FLSQueryNotCacheable e) { |
| 83 | + return false; |
81 | 84 | }
|
82 | 85 | // we can cache, all fields are ok
|
83 | 86 | return true;
|
84 | 87 | }
|
85 | 88 |
|
| 89 | + private static class FLSQueryNotCacheable extends RuntimeException { |
| 90 | + FLSQueryNotCacheable(String message) { |
| 91 | + // don't waste time filling in the stacktrace |
| 92 | + super(message, null, false, false); |
| 93 | + } |
| 94 | + } |
86 | 95 | }
|
0 commit comments