6161import org .elasticsearch .index .mapper .NumberFieldMapper ;
6262import org .elasticsearch .index .mapper .ObjectMapper ;
6363import org .elasticsearch .index .mapper .RootObjectMapper ;
64+ import org .elasticsearch .index .mapper .RootObjectMapperNamespaceValidator ;
6465import org .elasticsearch .index .mapper .RuntimeField ;
6566import org .elasticsearch .index .mapper .SourceFieldMapper ;
6667import org .elasticsearch .index .mapper .TestRuntimeField ;
@@ -331,6 +332,89 @@ public void testSearchRequestRuntimeFields() {
331332 assertThat (context .getMatchingFieldNames ("*" ), equalTo (Set .of ("cat" , "dog" , "pig" , "runtime" )));
332333 }
333334
335+ public void testSearchRequestRuntimeFieldsWithNamespaceValidator () {
336+ final String errorMessage = "error 12345" ;
337+ final String disallowed = "_project" ;
338+
339+ RootObjectMapperNamespaceValidator validator = new RootObjectMapperNamespaceValidator () {
340+ @ Override
341+ public void validateNamespace (ObjectMapper .Subobjects subobjects , String name ) {
342+ if (name .equals (disallowed )) {
343+ throw new IllegalArgumentException (errorMessage );
344+ } else if (subobjects != ObjectMapper .Subobjects .ENABLED ) {
345+ // name here will be something like _project.my_field, rather than just _project
346+ if (name .startsWith (disallowed + "." )) {
347+ throw new IllegalArgumentException (errorMessage );
348+ }
349+ }
350+ }
351+ };
352+
353+ {
354+ Map <String , Object > runtimeMappings = Map .ofEntries (
355+ Map .entry (disallowed , Map .of ("type" , randomFrom ("keyword" , "long" ))),
356+ Map .entry ("dog" , Map .of ("type" , "long" ))
357+ );
358+
359+ Exception e = expectThrows (
360+ IllegalArgumentException .class ,
361+ () -> createSearchExecutionContext (
362+ "uuid" ,
363+ null ,
364+ createMappingLookup (
365+ List .of (new MockFieldMapper .FakeFieldType ("pig" ), new MockFieldMapper .FakeFieldType ("cat" )),
366+ List .of (new TestRuntimeField ("runtime" , "long" ))
367+ ),
368+ runtimeMappings ,
369+ validator
370+ )
371+ );
372+ assertThat (e .getMessage (), equalTo (errorMessage ));
373+ }
374+
375+ {
376+ Map <String , Object > runtimeMappings = Map .ofEntries (
377+ Map .entry (disallowed + ".subfield" , Map .of ("type" , randomFrom ("keyword" , "long" ))),
378+ Map .entry ("dog" , Map .of ("type" , "long" ))
379+ );
380+
381+ Exception e = expectThrows (
382+ IllegalArgumentException .class ,
383+ () -> createSearchExecutionContext (
384+ "uuid" ,
385+ null ,
386+ createMappingLookup (
387+ List .of (new MockFieldMapper .FakeFieldType ("pig" ), new MockFieldMapper .FakeFieldType ("cat" )),
388+ List .of (new TestRuntimeField ("runtime" , "long" ))
389+ ),
390+ runtimeMappings ,
391+ validator
392+ )
393+ );
394+ assertThat (e .getMessage (), equalTo (errorMessage ));
395+ }
396+
397+ // _projectx should be allowed
398+ {
399+ Map <String , Object > runtimeMappings = Map .ofEntries (
400+ Map .entry (disallowed + "x" , Map .of ("type" , "keyword" )),
401+ Map .entry ("dog" , Map .of ("type" , "long" ))
402+ );
403+
404+ SearchExecutionContext searchExecutionContext = createSearchExecutionContext (
405+ "uuid" ,
406+ null ,
407+ createMappingLookup (
408+ List .of (new MockFieldMapper .FakeFieldType ("pig" ), new MockFieldMapper .FakeFieldType ("cat" )),
409+ List .of (new TestRuntimeField ("runtime" , "long" ))
410+ ),
411+ runtimeMappings ,
412+ validator
413+ );
414+ assertNotNull (searchExecutionContext );
415+ }
416+ }
417+
334418 public void testSearchRequestRuntimeFieldsWrongFormat () {
335419 Map <String , Object > runtimeMappings = new HashMap <>();
336420 runtimeMappings .put ("field" , Arrays .asList ("test1" , "test2" ));
@@ -500,12 +584,22 @@ private static SearchExecutionContext createSearchExecutionContext(
500584 String clusterAlias ,
501585 MappingLookup mappingLookup ,
502586 Map <String , Object > runtimeMappings
587+ ) {
588+ return createSearchExecutionContext (indexUuid , clusterAlias , mappingLookup , runtimeMappings , null );
589+ }
590+
591+ private static SearchExecutionContext createSearchExecutionContext (
592+ String indexUuid ,
593+ String clusterAlias ,
594+ MappingLookup mappingLookup ,
595+ Map <String , Object > runtimeMappings ,
596+ RootObjectMapperNamespaceValidator namespaceValidator
503597 ) {
504598 IndexMetadata .Builder indexMetadataBuilder = new IndexMetadata .Builder ("index" );
505599 indexMetadataBuilder .settings (indexSettings (IndexVersion .current (), 1 , 1 ).put (IndexMetadata .SETTING_INDEX_UUID , indexUuid ));
506600 IndexMetadata indexMetadata = indexMetadataBuilder .build ();
507601 IndexSettings indexSettings = new IndexSettings (indexMetadata , Settings .EMPTY );
508- MapperService mapperService = createMapperService (indexSettings , mappingLookup );
602+ MapperService mapperService = createMapperServiceWithNamespaceValidator (indexSettings , mappingLookup , namespaceValidator );
509603 final long nowInMillis = randomNonNegativeLong ();
510604 return new SearchExecutionContext (
511605 0 ,
@@ -532,8 +626,16 @@ private static SearchExecutionContext createSearchExecutionContext(
532626 }
533627
534628 private static MapperService createMapperService (IndexSettings indexSettings , MappingLookup mappingLookup ) {
629+ return createMapperServiceWithNamespaceValidator (indexSettings , mappingLookup , null );
630+ }
631+
632+ private static MapperService createMapperServiceWithNamespaceValidator (
633+ IndexSettings indexSettings ,
634+ MappingLookup mappingLookup ,
635+ RootObjectMapperNamespaceValidator namespaceValidator
636+ ) {
535637 IndexAnalyzers indexAnalyzers = IndexAnalyzers .of (singletonMap ("default" , new NamedAnalyzer ("default" , AnalyzerScope .INDEX , null )));
536- IndicesModule indicesModule = new IndicesModule (Collections .emptyList ());
638+ IndicesModule indicesModule = new IndicesModule (Collections .emptyList (), namespaceValidator );
537639 MapperRegistry mapperRegistry = indicesModule .getMapperRegistry ();
538640 Supplier <SearchExecutionContext > searchExecutionContextSupplier = () -> { throw new UnsupportedOperationException (); };
539641 MapperService mapperService = mock (MapperService .class );
@@ -552,7 +654,8 @@ private static MapperService createMapperService(IndexSettings indexSettings, Ma
552654 indexSettings .getMode ().buildIdFieldMapper (() -> true ),
553655 query -> {
554656 throw new UnsupportedOperationException ();
555- }
657+ },
658+ namespaceValidator
556659 )
557660 );
558661 when (mapperService .isMultiField (anyString ())).then (
0 commit comments