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 {
+
+ }
}