This is an automated email from the ASF dual-hosted git repository.

pvillard31 pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/nifi.git


The following commit(s) were added to refs/heads/main by this push:
     new 7a68425144a NIFI-15896 Standardized Authorization for Flow Analysis 
Methods (#11196)
7a68425144a is described below

commit 7a68425144a64021686d73dd735beb296d0e4b23
Author: David Handermann <[email protected]>
AuthorDate: Mon May 4 04:07:57 2026 -0500

    NIFI-15896 Standardized Authorization for Flow Analysis Methods (#11196)
---
 .../authorization/AuthorizeComponentAnalysis.java  | 76 ++++++++++++++++++++++
 .../apache/nifi/web/api/ControllerResource.java    | 11 +++-
 .../nifi/web/api/ControllerServiceResource.java    |  4 +-
 .../nifi/web/api/ParameterProviderResource.java    |  4 +-
 .../org/apache/nifi/web/api/ProcessorResource.java |  3 +-
 .../apache/nifi/web/api/ReportingTaskResource.java |  3 +-
 ...st.java => AuthorizeComponentAnalysisTest.java} | 25 ++++---
 .../AuthorizeComponentReferenceTest.java           | 55 ++++++++++++++++
 8 files changed, 161 insertions(+), 20 deletions(-)

diff --git 
a/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/authorization/AuthorizeComponentAnalysis.java
 
b/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/authorization/AuthorizeComponentAnalysis.java
new file mode 100644
index 00000000000..e1e63df92b3
--- /dev/null
+++ 
b/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/authorization/AuthorizeComponentAnalysis.java
@@ -0,0 +1,76 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.nifi.authorization;
+
+import org.apache.nifi.authorization.resource.Authorizable;
+import org.apache.nifi.authorization.user.NiFiUser;
+import org.apache.nifi.authorization.user.NiFiUserUtils;
+
+import java.util.Map;
+
+/**
+ * Authorizes configuration analysis requests.
+ * Requires READ on the target component and READ on any Controller Services 
and Parameter Context
+ * consulted for the proposed property values.
+ */
+public final class AuthorizeComponentAnalysis {
+
+    private AuthorizeComponentAnalysis() {
+    }
+
+    /**
+     * Authorize a configuration analysis request: READ on the component, then 
READ on referenced
+     * Controller Services and on the Parameter Context when proposed 
properties reference parameters.
+     *
+     * @param authorizer Authorizer used for determining results
+     * @param lookup Authorizable Lookup used to resolve referenced Controller 
Services
+     * @param component Component whose configuration is being analyzed
+     * @param proposedProperties Properties submitted for analysis
+     * @param parameterContext Parameter Context used to resolve parameter 
references, or {@code null} when none applies
+     */
+    public static void authorize(
+            final Authorizer authorizer,
+            final AuthorizableLookup lookup,
+            final ComponentAuthorizable component,
+            final Map<String, String> proposedProperties,
+            final Authorizable parameterContext
+    ) {
+        final NiFiUser user = NiFiUserUtils.getNiFiUser();
+        component.getAuthorizable().authorize(authorizer, RequestAction.READ, 
user);
+        
AuthorizeComponentReference.authorizeComponentConfiguration(authorizer, lookup, 
component, proposedProperties, parameterContext);
+    }
+
+    /**
+     * Authorize only proposed Controller Service and Parameter Context 
references for an analysis request.
+     * Use when another resource (e.g. the controller root) has already been 
authorized for READ and the
+     * target component does not require a separate READ policy for this 
operation.
+     *
+     * @param authorizer Authorizer used for determining results
+     * @param lookup Authorizable Lookup used to resolve referenced Controller 
Services
+     * @param component Component whose proposed configuration references are 
evaluated
+     * @param proposedProperties Properties submitted for analysis
+     * @param parameterContext Parameter Context used to resolve parameter 
references, or {@code null} when none applies
+     */
+    public static void authorizeProposedReferences(
+            final Authorizer authorizer,
+            final AuthorizableLookup lookup,
+            final ComponentAuthorizable component,
+            final Map<String, String> proposedProperties,
+            final Authorizable parameterContext) {
+        
AuthorizeComponentReference.authorizeComponentConfiguration(authorizer, lookup, 
component, proposedProperties, parameterContext);
+    }
+}
diff --git 
a/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ControllerResource.java
 
b/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ControllerResource.java
index 78f4c97a048..788536d4087 100644
--- 
a/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ControllerResource.java
+++ 
b/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ControllerResource.java
@@ -40,6 +40,7 @@ import jakarta.ws.rs.core.MediaType;
 import jakarta.ws.rs.core.Response;
 import jakarta.ws.rs.core.StreamingOutput;
 import org.apache.commons.lang3.StringUtils;
+import org.apache.nifi.authorization.AuthorizeComponentAnalysis;
 import org.apache.nifi.authorization.AuthorizeComponentReference;
 import org.apache.nifi.authorization.AuthorizeConfigVerification;
 import org.apache.nifi.authorization.AuthorizeControllerServiceReference;
@@ -1221,7 +1222,11 @@ public class ControllerResource extends 
ApplicationResource {
         return withWriteLock(
                 serviceFacade,
                 configurationAnalysis,
-                lookup -> authorizeController(RequestAction.READ),
+                lookup -> {
+                    authorizeController(RequestAction.READ);
+                    final ComponentAuthorizable authorizable = 
lookup.getFlowAnalysisRule(flowAnalysisRuleId);
+                    
AuthorizeComponentAnalysis.authorizeProposedReferences(authorizer, lookup, 
authorizable, configurationAnalysis.getConfigurationAnalysis().getProperties(), 
null);
+                },
                 () -> {
                 },
                 entity -> {
@@ -1567,8 +1572,8 @@ public class ControllerResource extends 
ApplicationResource {
                 serviceFacade,
                 configurationAnalysis,
                 lookup -> {
-                    final Authorizable authorizable = 
lookup.getFlowRegistryClient(registryClientId).getAuthorizable();
-                    authorizable.authorize(authorizer, RequestAction.READ, 
NiFiUserUtils.getNiFiUser());
+                    final ComponentAuthorizable registryClient = 
lookup.getFlowRegistryClient(registryClientId);
+                    AuthorizeComponentAnalysis.authorize(authorizer, lookup, 
registryClient, 
configurationAnalysis.getConfigurationAnalysis().getProperties(), null);
                 },
                 () -> {
                 },
diff --git 
a/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ControllerServiceResource.java
 
b/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ControllerServiceResource.java
index 64fb165b429..ee4c2ca7dfd 100644
--- 
a/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ControllerServiceResource.java
+++ 
b/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ControllerServiceResource.java
@@ -39,6 +39,7 @@ import jakarta.ws.rs.core.Context;
 import jakarta.ws.rs.core.MediaType;
 import jakarta.ws.rs.core.Response;
 import org.apache.commons.lang3.StringUtils;
+import org.apache.nifi.authorization.AuthorizeComponentAnalysis;
 import org.apache.nifi.authorization.AuthorizeComponentReference;
 import org.apache.nifi.authorization.AuthorizeConfigVerification;
 import org.apache.nifi.authorization.AuthorizeControllerServiceReference;
@@ -961,7 +962,8 @@ public class ControllerServiceResource extends 
ApplicationResource {
                 configurationAnalysis,
                 lookup -> {
                     final ComponentAuthorizable controllerService = 
lookup.getControllerService(controllerServiceId);
-                    controllerService.getAuthorizable().authorize(authorizer, 
RequestAction.READ, NiFiUserUtils.getNiFiUser());
+                    final Map<String, String> properties = 
configurationAnalysis.getConfigurationAnalysis().getProperties();
+                    AuthorizeComponentAnalysis.authorize(authorizer, lookup, 
controllerService, properties, controllerService.getParameterContext());
                 },
                 () -> {
                 },
diff --git 
a/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ParameterProviderResource.java
 
b/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ParameterProviderResource.java
index 71eda3b6675..418ab7325bc 100644
--- 
a/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ParameterProviderResource.java
+++ 
b/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ParameterProviderResource.java
@@ -42,6 +42,7 @@ import jakarta.ws.rs.core.MediaType;
 import jakarta.ws.rs.core.Response;
 import org.apache.commons.lang3.StringUtils;
 import org.apache.nifi.authorization.AuthorizableLookup;
+import org.apache.nifi.authorization.AuthorizeComponentAnalysis;
 import org.apache.nifi.authorization.AuthorizeComponentReference;
 import org.apache.nifi.authorization.AuthorizeConfigVerification;
 import org.apache.nifi.authorization.AuthorizeControllerServiceReference;
@@ -1195,7 +1196,8 @@ public class ParameterProviderResource extends 
AbstractParameterResource {
                 configurationAnalysis,
                 lookup -> {
                     final ComponentAuthorizable parameterProvider = 
lookup.getParameterProvider(parameterProviderId);
-                    parameterProvider.getAuthorizable().authorize(authorizer, 
RequestAction.READ, NiFiUserUtils.getNiFiUser());
+                    final Map<String, String> properties = 
configurationAnalysis.getConfigurationAnalysis().getProperties();
+                    AuthorizeComponentAnalysis.authorize(authorizer, lookup, 
parameterProvider, properties, parameterProvider.getParameterContext());
                 },
                 () -> {
                 },
diff --git 
a/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ProcessorResource.java
 
b/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ProcessorResource.java
index 0a6192cd407..fd0aa5eec4c 100644
--- 
a/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ProcessorResource.java
+++ 
b/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ProcessorResource.java
@@ -39,6 +39,7 @@ import jakarta.ws.rs.core.Context;
 import jakarta.ws.rs.core.MediaType;
 import jakarta.ws.rs.core.Response;
 import org.apache.commons.lang3.StringUtils;
+import org.apache.nifi.authorization.AuthorizeComponentAnalysis;
 import org.apache.nifi.authorization.AuthorizeComponentReference;
 import org.apache.nifi.authorization.AuthorizeConfigVerification;
 import org.apache.nifi.authorization.AuthorizeControllerServiceReference;
@@ -672,7 +673,7 @@ public class ProcessorResource extends ApplicationResource {
                 configurationAnalysis,
                 lookup -> {
                     final ComponentAuthorizable processor = 
lookup.getProcessor(processorId);
-                    processor.getAuthorizable().authorize(authorizer, 
RequestAction.READ, NiFiUserUtils.getNiFiUser());
+                    AuthorizeComponentAnalysis.authorize(authorizer, lookup, 
processor, configurationAnalysis.getConfigurationAnalysis().getProperties(), 
processor.getParameterContext());
                 },
                 () -> {
                 },
diff --git 
a/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ReportingTaskResource.java
 
b/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ReportingTaskResource.java
index a486d6e8704..2905ff15755 100644
--- 
a/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ReportingTaskResource.java
+++ 
b/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ReportingTaskResource.java
@@ -39,6 +39,7 @@ import jakarta.ws.rs.core.Context;
 import jakarta.ws.rs.core.MediaType;
 import jakarta.ws.rs.core.Response;
 import org.apache.commons.lang3.StringUtils;
+import org.apache.nifi.authorization.AuthorizeComponentAnalysis;
 import org.apache.nifi.authorization.AuthorizeComponentReference;
 import org.apache.nifi.authorization.AuthorizeConfigVerification;
 import org.apache.nifi.authorization.AuthorizeControllerServiceReference;
@@ -764,7 +765,7 @@ public class ReportingTaskResource extends 
ApplicationResource {
                 configurationAnalysis,
                 lookup -> {
                     final ComponentAuthorizable reportingTask = 
lookup.getReportingTask(reportingTaskId);
-                    reportingTask.getAuthorizable().authorize(authorizer, 
RequestAction.READ, NiFiUserUtils.getNiFiUser());
+                    AuthorizeComponentAnalysis.authorize(authorizer, lookup, 
reportingTask, 
configurationAnalysis.getConfigurationAnalysis().getProperties(), 
reportingTask.getParameterContext());
                 },
                 () -> {
                 },
diff --git 
a/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/java/org/apache/nifi/authorization/AuthorizeComponentReferenceTest.java
 
b/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/java/org/apache/nifi/authorization/AuthorizeComponentAnalysisTest.java
similarity index 62%
copy from 
nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/java/org/apache/nifi/authorization/AuthorizeComponentReferenceTest.java
copy to 
nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/java/org/apache/nifi/authorization/AuthorizeComponentAnalysisTest.java
index 0d62d01b687..59eb4a0b61d 100644
--- 
a/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/java/org/apache/nifi/authorization/AuthorizeComponentReferenceTest.java
+++ 
b/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/java/org/apache/nifi/authorization/AuthorizeComponentAnalysisTest.java
@@ -17,7 +17,6 @@
 package org.apache.nifi.authorization;
 
 import org.apache.nifi.authorization.resource.Authorizable;
-import org.apache.nifi.web.api.dto.BundleDTO;
 import org.junit.jupiter.api.Test;
 import org.junit.jupiter.api.extension.ExtendWith;
 import org.mockito.Mock;
@@ -25,16 +24,14 @@ import org.mockito.junit.jupiter.MockitoExtension;
 
 import java.util.Map;
 
+import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
 @ExtendWith(MockitoExtension.class)
-class AuthorizeComponentReferenceTest {
-    private static final String COMPONENT_TYPE = 
ComponentAuthorizable.class.getName();
-
-    private static final BundleDTO COMPONENT_BUNDLE = new BundleDTO();
+class AuthorizeComponentAnalysisTest {
 
     @Mock
     private Authorizer authorizer;
@@ -45,23 +42,25 @@ class AuthorizeComponentReferenceTest {
     @Mock
     private ComponentAuthorizable componentAuthorizable;
 
+    @Mock
+    private Authorizable componentDelegate;
+
     @Mock
     private Authorizable parameterContext;
 
     @Test
-    void testAuthorizeComponentConfigurationComponentType() {
-        when(authorizableLookup.getConfigurableComponent(eq(COMPONENT_TYPE), 
eq(COMPONENT_BUNDLE))).thenReturn(componentAuthorizable);
+    void testAuthorizeReadOnComponentThenReferences() {
+        
when(componentAuthorizable.getAuthorizable()).thenReturn(componentDelegate);
 
-        
AuthorizeComponentReference.authorizeComponentConfiguration(authorizer, 
authorizableLookup, COMPONENT_TYPE, COMPONENT_BUNDLE, Map.of(), 
parameterContext);
+        AuthorizeComponentAnalysis.authorize(authorizer, authorizableLookup, 
componentAuthorizable, Map.of(), parameterContext);
 
-        verify(componentAuthorizable).cleanUpResources();
+        verify(componentDelegate).authorize(eq(authorizer), 
eq(RequestAction.READ), any());
     }
 
     @Test
-    void testAuthorizeComponentConfigurationComponentAuthorizable() {
-        
AuthorizeComponentReference.authorizeComponentConfiguration(authorizer, 
authorizableLookup, componentAuthorizable, Map.of(), parameterContext);
+    void testAuthorizeProposedReferencesSkipsComponentRead() {
+        AuthorizeComponentAnalysis.authorizeProposedReferences(authorizer, 
authorizableLookup, componentAuthorizable, Map.of(), parameterContext);
 
-        verify(componentAuthorizable, never()).cleanUpResources();
+        verify(componentAuthorizable, never()).getAuthorizable();
     }
-
 }
diff --git 
a/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/java/org/apache/nifi/authorization/AuthorizeComponentReferenceTest.java
 
b/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/java/org/apache/nifi/authorization/AuthorizeComponentReferenceTest.java
index 0d62d01b687..8544a79b110 100644
--- 
a/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/java/org/apache/nifi/authorization/AuthorizeComponentReferenceTest.java
+++ 
b/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/java/org/apache/nifi/authorization/AuthorizeComponentReferenceTest.java
@@ -17,6 +17,8 @@
 package org.apache.nifi.authorization;
 
 import org.apache.nifi.authorization.resource.Authorizable;
+import org.apache.nifi.components.PropertyDescriptor;
+import org.apache.nifi.controller.AbstractControllerService;
 import org.apache.nifi.web.api.dto.BundleDTO;
 import org.junit.jupiter.api.Test;
 import org.junit.jupiter.api.extension.ExtendWith;
@@ -25,7 +27,11 @@ import org.mockito.junit.jupiter.MockitoExtension;
 
 import java.util.Map;
 
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.doThrow;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
@@ -36,6 +42,14 @@ class AuthorizeComponentReferenceTest {
 
     private static final BundleDTO COMPONENT_BUNDLE = new BundleDTO();
 
+    private static final String CONTROLLER_SERVICE_PROPERTY = "Controller 
Service";
+
+    private static final String CONTROLLER_SERVICE_ID = 
"controller-service-id";
+
+    private static final String PARAMETER_PROPERTY = "text";
+
+    private static final String PARAMETER_REFERENCE = "#{param}";
+
     @Mock
     private Authorizer authorizer;
 
@@ -48,6 +62,18 @@ class AuthorizeComponentReferenceTest {
     @Mock
     private Authorizable parameterContext;
 
+    @Mock
+    private PropertyDescriptor controllerServicePropertyDescriptor;
+
+    @Mock
+    private PropertyDescriptor nonControllerServicePropertyDescriptor;
+
+    @Mock
+    private ComponentAuthorizable referencedControllerService;
+
+    @Mock
+    private Authorizable referencedControllerServiceAuthorizable;
+
     @Test
     void testAuthorizeComponentConfigurationComponentType() {
         when(authorizableLookup.getConfigurableComponent(eq(COMPONENT_TYPE), 
eq(COMPONENT_BUNDLE))).thenReturn(componentAuthorizable);
@@ -64,4 +90,33 @@ class AuthorizeComponentReferenceTest {
         verify(componentAuthorizable, never()).cleanUpResources();
     }
 
+    @Test
+    void 
testAuthorizeComponentConfigurationDeniedReferencedControllerService() {
+        
doReturn(MockControllerService.class).when(controllerServicePropertyDescriptor).getControllerServiceDefinition();
+        
when(componentAuthorizable.getPropertyDescriptor(eq(CONTROLLER_SERVICE_PROPERTY))).thenReturn(controllerServicePropertyDescriptor);
+        
when(componentAuthorizable.getValue(eq(controllerServicePropertyDescriptor))).thenReturn(null);
+        
when(authorizableLookup.getControllerService(eq(CONTROLLER_SERVICE_ID))).thenReturn(referencedControllerService);
+        
when(referencedControllerService.getAuthorizable()).thenReturn(referencedControllerServiceAuthorizable);
+        doThrow(new 
AccessDeniedException("denied")).when(referencedControllerServiceAuthorizable).authorize(eq(authorizer),
 eq(RequestAction.READ), any());
+
+        final Map<String, String> properties = 
Map.of(CONTROLLER_SERVICE_PROPERTY, CONTROLLER_SERVICE_ID);
+
+        assertThrows(AccessDeniedException.class,
+                () -> 
AuthorizeComponentReference.authorizeComponentConfiguration(authorizer, 
authorizableLookup, componentAuthorizable, properties, parameterContext));
+    }
+
+    @Test
+    void testAuthorizeComponentConfigurationDeniedParameterContextReference() {
+        
when(componentAuthorizable.getPropertyDescriptor(eq(PARAMETER_PROPERTY))).thenReturn(nonControllerServicePropertyDescriptor);
+        doThrow(new 
AccessDeniedException("denied")).when(parameterContext).authorize(eq(authorizer),
 eq(RequestAction.READ), any());
+
+        final Map<String, String> properties = Map.of(PARAMETER_PROPERTY, 
PARAMETER_REFERENCE);
+
+        assertThrows(AccessDeniedException.class,
+                () -> 
AuthorizeComponentReference.authorizeComponentConfiguration(authorizer, 
authorizableLookup, componentAuthorizable, properties, parameterContext));
+    }
+
+    private static class MockControllerService extends 
AbstractControllerService {
+
+    }
 }

Reply via email to