2626import org .elasticsearch .action .index .IndexRequest ;
2727import org .elasticsearch .action .ingest .DeletePipelineRequest ;
2828import org .elasticsearch .action .ingest .PutPipelineRequest ;
29+ import org .elasticsearch .action .ingest .ReservedPipelineAction ;
2930import org .elasticsearch .action .support .RefCountingRunnable ;
3031import org .elasticsearch .action .support .master .AcknowledgedResponse ;
3132import org .elasticsearch .client .internal .Client ;
5556import org .elasticsearch .common .logging .DeprecationLogger ;
5657import org .elasticsearch .common .regex .Regex ;
5758import org .elasticsearch .common .settings .Settings ;
59+ import org .elasticsearch .common .streams .StreamType ;
60+ import org .elasticsearch .common .streams .StreamsPermissionsUtils ;
5861import org .elasticsearch .common .util .CollectionUtils ;
5962import org .elasticsearch .common .util .Maps ;
6063import org .elasticsearch .common .util .concurrent .AbstractRunnable ;
7578import org .elasticsearch .node .ReportingService ;
7679import org .elasticsearch .plugins .IngestPlugin ;
7780import org .elasticsearch .plugins .internal .XContentParserDecorator ;
81+ import org .elasticsearch .script .Metadata ;
7882import org .elasticsearch .script .ScriptService ;
7983import org .elasticsearch .threadpool .Scheduler ;
8084import org .elasticsearch .threadpool .ThreadPool ;
@@ -154,6 +158,7 @@ public static boolean locallySupportedIngestFeature(NodeFeature nodeFeature) {
154158 private volatile ClusterState state ;
155159 private final ProjectResolver projectResolver ;
156160 private final FeatureService featureService ;
161+ private final StreamsPermissionsUtils streamsPermissionsUtils ;
157162
158163 private static BiFunction <Long , Runnable , Scheduler .ScheduledCancellable > createScheduler (ThreadPool threadPool ) {
159164 return (delay , command ) -> threadPool .schedule (command , TimeValue .timeValueMillis (delay ), threadPool .generic ());
@@ -241,7 +246,8 @@ public IngestService(
241246 MatcherWatchdog matcherWatchdog ,
242247 FailureStoreMetrics failureStoreMetrics ,
243248 ProjectResolver projectResolver ,
244- FeatureService featureService
249+ FeatureService featureService ,
250+ StreamsPermissionsUtils streamsPermissionsUtils
245251 ) {
246252 this .clusterService = clusterService ;
247253 this .scriptService = scriptService ;
@@ -265,6 +271,7 @@ public IngestService(
265271 this .failureStoreMetrics = failureStoreMetrics ;
266272 this .projectResolver = projectResolver ;
267273 this .featureService = featureService ;
274+ this .streamsPermissionsUtils = streamsPermissionsUtils ;
268275 }
269276
270277 /**
@@ -283,6 +290,7 @@ public IngestService(
283290 this .failureStoreMetrics = ingestService .failureStoreMetrics ;
284291 this .projectResolver = ingestService .projectResolver ;
285292 this .featureService = ingestService .featureService ;
293+ streamsPermissionsUtils = ingestService .streamsPermissionsUtils ;
286294 }
287295
288296 private static Map <String , Processor .Factory > processorFactories (List <IngestPlugin > ingestPlugins , Processor .Parameters parameters ) {
@@ -301,15 +309,15 @@ private static Map<String, Processor.Factory> processorFactories(List<IngestPlug
301309
302310 /**
303311 * Resolves the potential pipelines (default and final) from the requests or templates associated to the index and then **mutates**
304- * the {@link org.elasticsearch.action.index. IndexRequest} passed object with the pipeline information.
312+ * the {@link IndexRequest} passed object with the pipeline information.
305313 * <p>
306314 * Also, this method marks the request as `isPipelinesResolved = true`: Due to the request could be rerouted from a coordinating node
307315 * to an ingest node, we have to be able to avoid double resolving the pipelines and also able to distinguish that either the pipeline
308316 * comes as part of the request or resolved from this method. All this is made to later be able to reject the request in case the
309317 * pipeline was set by a required pipeline **and** the request also has a pipeline request too.
310318 *
311319 * @param originalRequest Original write request received.
312- * @param indexRequest The {@link org.elasticsearch.action.index. IndexRequest} object to update.
320+ * @param indexRequest The {@link IndexRequest} object to update.
313321 * @param projectMetadata Project metadata from the cluster state from where the pipeline information is derived.
314322 */
315323 public static void resolvePipelinesAndUpdateIndexRequest (
@@ -411,7 +419,7 @@ public void delete(ProjectId projectId, DeletePipelineRequest request, ActionLis
411419 }
412420
413421 /**
414- * Used by this class and {@link org.elasticsearch.action.ingest. ReservedPipelineAction}
422+ * Used by this class and {@link ReservedPipelineAction}
415423 */
416424 public static class DeletePipelineClusterStateUpdateTask extends PipelineClusterStateUpdateTask {
417425 private final DeletePipelineRequest request ;
@@ -672,7 +680,7 @@ private static void collectProcessorMetrics(
672680 }
673681
674682 /**
675- * Used in this class and externally by the {@link org.elasticsearch.action.ingest. ReservedPipelineAction}
683+ * Used in this class and externally by the {@link ReservedPipelineAction}
676684 */
677685 public static class PutPipelineClusterStateUpdateTask extends PipelineClusterStateUpdateTask {
678686 private final PutPipelineRequest request ;
@@ -683,7 +691,7 @@ public static class PutPipelineClusterStateUpdateTask extends PipelineClusterSta
683691 }
684692
685693 /**
686- * Used by {@link org.elasticsearch.action.ingest. ReservedPipelineAction}
694+ * Used by {@link ReservedPipelineAction}
687695 */
688696 public PutPipelineClusterStateUpdateTask (ProjectId projectId , PutPipelineRequest request ) {
689697 this (projectId , null , request );
@@ -904,7 +912,7 @@ protected void doRun() {
904912 final int slot = i ;
905913 final Releasable ref = refs .acquire ();
906914 final IngestDocument ingestDocument = newIngestDocument (indexRequest );
907- final org . elasticsearch . script . Metadata originalDocumentMetadata = ingestDocument .getMetadata ().clone ();
915+ final Metadata originalDocumentMetadata = ingestDocument .getMetadata ().clone ();
908916 // the document listener gives us three-way logic: a document can fail processing (1), or it can
909917 // be successfully processed. a successfully processed document can be kept (2) or dropped (3).
910918 final ActionListener <IngestPipelinesExecutionResult > documentListener = ActionListener .runAfter (
@@ -1198,6 +1206,31 @@ private void executePipelines(
11981206 return ; // document failed!
11991207 }
12001208
1209+ StreamsPermissionsUtils permissionsUtils = StreamsPermissionsUtils .getInstance ();
1210+ for (StreamType streamType : StreamType .values ()) {
1211+ if (permissionsUtils .streamTypeIsEnabled (streamType , project )) {
1212+ if (newIndex .startsWith (streamType .getStreamName () + "." )
1213+ && ingestDocument .getIndexHistory ().stream ().noneMatch (s -> s .equals (streamType .getStreamName ()))) {
1214+ exceptionHandler .accept (
1215+ new IngestPipelineException (
1216+ pipelineId ,
1217+ new IllegalArgumentException (
1218+ format (
1219+ "Pipelines can't re-route documents to child streams, but pipeline [%s] tried to reroute "
1220+ + "this document from index [%s] to index [%s]. Reroute history: %s" ,
1221+ pipelineId ,
1222+ originalIndex ,
1223+ newIndex ,
1224+ String .join (" -> " , ingestDocument .getIndexHistory ())
1225+ )
1226+ )
1227+ )
1228+ );
1229+ return ; // document failed!
1230+ }
1231+ }
1232+ }
1233+
12011234 // add the index to the document's index history, and check for cycles in the visited indices
12021235 boolean cycle = ingestDocument .updateIndexHistory (newIndex ) == false ;
12031236 if (cycle ) {
@@ -1352,7 +1385,7 @@ private static IngestDocument newIngestDocument(final IndexRequest request) {
13521385 /**
13531386 * Updates an index request based on the metadata of an ingest document.
13541387 */
1355- private static void updateIndexRequestMetadata (final IndexRequest request , final org . elasticsearch . script . Metadata metadata ) {
1388+ private static void updateIndexRequestMetadata (final IndexRequest request , final Metadata metadata ) {
13561389 // it's fine to set all metadata fields all the time, as ingest document holds their starting values
13571390 // before ingestion, which might also get modified during ingestion.
13581391 request .index (metadata .getIndex ());
0 commit comments