|
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