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 9e6c18893f1 NIFI-16418 Added Parameter Context evaluation for new 
Parameter Provider (#11803)
9e6c18893f1 is described below

commit 9e6c18893f1b1df713dac55d98c2b2944d235844
Author: David Handermann <[email protected]>
AuthorDate: Wed Oct 14 04:47:30 2026 -0500

    NIFI-16418 Added Parameter Context evaluation for new Parameter Provider 
(#11803)
    
    - Added authorization checking when Parameter Reference changes include 
attaching a Parameter Provider
---
 .../authorization/AuthorizeParameterReference.java |  30 +++-
 .../AuthorizeParameterReferenceTest.java           | 196 +++++++++++++++++++++
 2 files changed, 223 insertions(+), 3 deletions(-)

diff --git 
a/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/authorization/AuthorizeParameterReference.java
 
b/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/authorization/AuthorizeParameterReference.java
index 47f535a8b58..2572612e1fb 100644
--- 
a/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/authorization/AuthorizeParameterReference.java
+++ 
b/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/authorization/AuthorizeParameterReference.java
@@ -31,6 +31,7 @@ import org.apache.nifi.web.NiFiServiceFacade;
 
 import java.util.List;
 import java.util.Map;
+import java.util.Objects;
 import java.util.Set;
 import java.util.stream.Collectors;
 
@@ -133,7 +134,9 @@ public class AuthorizeParameterReference {
     /**
      * Ensures that any Parameter Context that is referenced by the given 
VersionedParameterContext is readable by the given user. If the Versioned 
Parameter Context references a Parameter Context
      * (by name) that does not exist in the current flow, ensures that the 
user has permissions to create a new Parameter Context. If the Versioned 
Parameter Context contains any Parameters that
-     * do not currently exist in the Parameter Context that is referenced, 
ensures that the usre has permissions to WRITE to the Parameter Context so that 
the additional Parameter can be added.
+     * do not currently exist in the Parameter Context that is referenced, 
changes the inherited Parameter Context list, or attaches a Parameter Provider 
to a context that does not already have one,
+     * ensures that the user has permissions to WRITE to the Parameter 
Context. When a Parameter Provider association is proposed for a Parameter 
Provider that already exists locally, also requires
+     * READ on that provider.
      *
      * @param versionedParameterContext the Versioned Parameter Context
      * @param serviceFacade the Service Facade
@@ -144,10 +147,12 @@ public class AuthorizeParameterReference {
     public static void authorizeParameterContextAddition(final 
VersionedParameterContext versionedParameterContext, final NiFiServiceFacade 
serviceFacade, final Authorizer authorizer,
                                                          final 
AuthorizableLookup lookup, final NiFiUser user) {
         final ParameterContext parameterContext = 
serviceFacade.getParameterContextByName(versionedParameterContext.getName(), 
user);
+        final String proposedProviderId = 
versionedParameterContext.getParameterProvider();
 
         if (parameterContext == null) {
             // If Parameter Context does not yet exist, authorize that the 
user is allowed to create it.
             lookup.getParameterContexts().authorize(authorizer, 
RequestAction.WRITE, user);
+            authorizeLocalParameterProviderRead(proposedProviderId, 
authorizer, lookup, user);
             return;
         }
 
@@ -160,6 +165,7 @@ public class AuthorizeParameterReference {
             .collect(Collectors.toSet());
 
         final List<String> existingParameterContextNames = 
parameterContext.getInheritedParameterContextNames();
+        final boolean providerAttaching = proposedProviderId != null && 
parameterContext.getParameterProvider() == null;
 
         boolean requiresAddition = false;
         for (final VersionedParameter versionedParameter : 
versionedParameterContext.getParameters()) {
@@ -173,9 +179,27 @@ public class AuthorizeParameterReference {
             }
         }
 
-        if (requiresAddition || 
!existingParameterContextNames.equals(versionedParameterContext.getInheritedParameterContexts()))
 {
-            // User is required to have WRITE permission to the Parameter 
Context in order to add one or more parameters.
+        if (requiresAddition || providerAttaching || 
!existingParameterContextNames.equals(versionedParameterContext.getInheritedParameterContexts()))
 {
+            // User is required to have WRITE permission to the Parameter 
Context in order to add parameters, change inheritance, or attach a Parameter 
Provider.
             parameterContext.authorize(authorizer, RequestAction.WRITE, user);
         }
+
+        if (providerAttaching) {
+            authorizeLocalParameterProviderRead(proposedProviderId, 
authorizer, lookup, user);
+        }
+    }
+
+    private static void authorizeLocalParameterProviderRead(
+            final String parameterProviderId,
+            final Authorizer authorizer,
+            final AuthorizableLookup lookup,
+            final NiFiUser user
+    ) {
+        if (parameterProviderId == null) {
+            return;
+        }
+
+        lookup.getParameterProviders(parameterProvider -> 
Objects.equals(parameterProviderId, parameterProvider.getIdentifier()))
+                .forEach(componentAuthorizable -> 
componentAuthorizable.getAuthorizable().authorize(authorizer, 
RequestAction.READ, user));
     }
 }
diff --git 
a/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/java/org/apache/nifi/authorization/AuthorizeParameterReferenceTest.java
 
b/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/java/org/apache/nifi/authorization/AuthorizeParameterReferenceTest.java
new file mode 100644
index 00000000000..0b740949459
--- /dev/null
+++ 
b/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/java/org/apache/nifi/authorization/AuthorizeParameterReferenceTest.java
@@ -0,0 +1,196 @@
+/*
+ * 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.flow.VersionedParameterContext;
+import org.apache.nifi.parameter.ParameterContext;
+import org.apache.nifi.parameter.ParameterProvider;
+import org.apache.nifi.web.NiFiServiceFacade;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.mockito.Mock;
+import org.mockito.junit.jupiter.MockitoExtension;
+
+import java.util.Collections;
+import java.util.List;
+import java.util.Set;
+
+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.doNothing;
+import static org.mockito.Mockito.doThrow;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+@ExtendWith(MockitoExtension.class)
+class AuthorizeParameterReferenceTest {
+
+    private static final String PARAMETER_CONTEXT_NAME = "Parameter Context";
+
+    private static final String PARAMETER_PROVIDER_ID = 
"parameter-provider-id";
+
+    private static final String ACCESS_DENIED_MESSAGE = "denied";
+
+    @Mock
+    private Authorizer authorizer;
+
+    @Mock
+    private AuthorizableLookup lookup;
+
+    @Mock
+    private NiFiServiceFacade serviceFacade;
+
+    @Mock
+    private NiFiUser user;
+
+    @Mock
+    private ParameterContext parameterContext;
+
+    @Mock
+    private Authorizable parameterContexts;
+
+    @Mock
+    private ParameterProvider currentParameterProvider;
+
+    @Mock
+    private ComponentAuthorizable parameterProviderComponent;
+
+    @Mock
+    private Authorizable parameterProviderAuthorizable;
+
+    @Test
+    void 
testAuthorizeParameterContextAdditionDeniedWhenAttachingProviderWithoutWrite() {
+        final VersionedParameterContext versionedParameterContext = 
createVersionedParameterContext(PARAMETER_PROVIDER_ID);
+        stubExistingParameterContext();
+        when(parameterContext.getParameterProvider()).thenReturn(null);
+        doNothing().when(parameterContext).authorize(eq(authorizer), 
eq(RequestAction.READ), eq(user));
+        doThrow(new 
AccessDeniedException(ACCESS_DENIED_MESSAGE)).when(parameterContext).authorize(eq(authorizer),
 eq(RequestAction.WRITE), eq(user));
+
+        assertThrows(AccessDeniedException.class,
+                () -> 
AuthorizeParameterReference.authorizeParameterContextAddition(versionedParameterContext,
 serviceFacade, authorizer, lookup, user));
+
+        verify(parameterContext).authorize(eq(authorizer), 
eq(RequestAction.READ), eq(user));
+        verify(parameterContext).authorize(eq(authorizer), 
eq(RequestAction.WRITE), eq(user));
+        verify(lookup, never()).getParameterProviders(any());
+    }
+
+    @Test
+    void 
testAuthorizeParameterContextAdditionRequiresWriteAndProviderReadWhenAttachingProvider()
 {
+        final VersionedParameterContext versionedParameterContext = 
createVersionedParameterContext(PARAMETER_PROVIDER_ID);
+        stubExistingParameterContext();
+        when(parameterContext.getParameterProvider()).thenReturn(null);
+        stubLocalParameterProvider();
+
+        
AuthorizeParameterReference.authorizeParameterContextAddition(versionedParameterContext,
 serviceFacade, authorizer, lookup, user);
+
+        verify(parameterContext).authorize(eq(authorizer), 
eq(RequestAction.READ), eq(user));
+        verify(parameterContext).authorize(eq(authorizer), 
eq(RequestAction.WRITE), eq(user));
+        verify(parameterProviderAuthorizable).authorize(eq(authorizer), 
eq(RequestAction.READ), eq(user));
+    }
+
+    @Test
+    void 
testAuthorizeParameterContextAdditionDoesNotRequireWriteWhenProviderAlreadyConfigured()
 {
+        final VersionedParameterContext versionedParameterContext = 
createVersionedParameterContext(PARAMETER_PROVIDER_ID);
+        stubExistingParameterContext();
+        
when(parameterContext.getParameterProvider()).thenReturn(currentParameterProvider);
+
+        
AuthorizeParameterReference.authorizeParameterContextAddition(versionedParameterContext,
 serviceFacade, authorizer, lookup, user);
+
+        verify(parameterContext).authorize(eq(authorizer), 
eq(RequestAction.READ), eq(user));
+        verify(parameterContext, never()).authorize(eq(authorizer), 
eq(RequestAction.WRITE), eq(user));
+        verify(lookup, never()).getParameterProviders(any());
+    }
+
+    @Test
+    void testAuthorizeParameterContextAdditionDeniedWhenProviderReadDenied() {
+        final VersionedParameterContext versionedParameterContext = 
createVersionedParameterContext(PARAMETER_PROVIDER_ID);
+        stubExistingParameterContext();
+        when(parameterContext.getParameterProvider()).thenReturn(null);
+        stubLocalParameterProvider();
+        doThrow(new 
AccessDeniedException(ACCESS_DENIED_MESSAGE)).when(parameterProviderAuthorizable).authorize(eq(authorizer),
 eq(RequestAction.READ), eq(user));
+
+        assertThrows(AccessDeniedException.class,
+                () -> 
AuthorizeParameterReference.authorizeParameterContextAddition(versionedParameterContext,
 serviceFacade, authorizer, lookup, user));
+
+        verify(parameterContext).authorize(eq(authorizer), 
eq(RequestAction.WRITE), eq(user));
+        verify(parameterProviderAuthorizable).authorize(eq(authorizer), 
eq(RequestAction.READ), eq(user));
+    }
+
+    @Test
+    void 
testAuthorizeParameterContextAdditionRequiresCreateAndProviderReadForNewContext()
 {
+        final VersionedParameterContext versionedParameterContext = 
createVersionedParameterContext(PARAMETER_PROVIDER_ID);
+        
when(serviceFacade.getParameterContextByName(eq(PARAMETER_CONTEXT_NAME), 
eq(user))).thenReturn(null);
+        when(lookup.getParameterContexts()).thenReturn(parameterContexts);
+        stubLocalParameterProvider();
+
+        
AuthorizeParameterReference.authorizeParameterContextAddition(versionedParameterContext,
 serviceFacade, authorizer, lookup, user);
+
+        verify(parameterContexts).authorize(eq(authorizer), 
eq(RequestAction.WRITE), eq(user));
+        verify(parameterProviderAuthorizable).authorize(eq(authorizer), 
eq(RequestAction.READ), eq(user));
+    }
+
+    @Test
+    void 
testAuthorizeParameterContextAdditionDoesNotRequireWriteWhenNoProviderProposed()
 {
+        final VersionedParameterContext versionedParameterContext = 
createVersionedParameterContext(null);
+        
when(serviceFacade.getParameterContextByName(eq(PARAMETER_CONTEXT_NAME), 
eq(user))).thenReturn(parameterContext);
+        
when(parameterContext.getParameters()).thenReturn(Collections.emptyMap());
+        
when(parameterContext.getInheritedParameterContextNames()).thenReturn(List.of());
+
+        
AuthorizeParameterReference.authorizeParameterContextAddition(versionedParameterContext,
 serviceFacade, authorizer, lookup, user);
+
+        verify(parameterContext).authorize(eq(authorizer), 
eq(RequestAction.READ), eq(user));
+        verify(parameterContext, never()).authorize(eq(authorizer), 
eq(RequestAction.WRITE), eq(user));
+        verify(lookup, never()).getParameterProviders(any());
+    }
+
+    @Test
+    void 
testAuthorizeParameterContextAdditionRequiresWriteWhenProviderIsNotLocal() {
+        final VersionedParameterContext versionedParameterContext = 
createVersionedParameterContext(PARAMETER_PROVIDER_ID);
+        stubExistingParameterContext();
+        when(parameterContext.getParameterProvider()).thenReturn(null);
+        when(lookup.getParameterProviders(any())).thenReturn(Set.of());
+
+        
AuthorizeParameterReference.authorizeParameterContextAddition(versionedParameterContext,
 serviceFacade, authorizer, lookup, user);
+
+        verify(parameterContext).authorize(eq(authorizer), 
eq(RequestAction.WRITE), eq(user));
+        verify(lookup).getParameterProviders(any());
+    }
+
+    private void stubExistingParameterContext() {
+        
when(serviceFacade.getParameterContextByName(eq(PARAMETER_CONTEXT_NAME), 
eq(user))).thenReturn(parameterContext);
+        
when(parameterContext.getParameters()).thenReturn(Collections.emptyMap());
+        
when(parameterContext.getInheritedParameterContextNames()).thenReturn(List.of());
+    }
+
+    private void stubLocalParameterProvider() {
+        
when(lookup.getParameterProviders(any())).thenReturn(Set.of(parameterProviderComponent));
+        
when(parameterProviderComponent.getAuthorizable()).thenReturn(parameterProviderAuthorizable);
+    }
+
+    private VersionedParameterContext createVersionedParameterContext(final 
String parameterProviderId) {
+        final VersionedParameterContext versionedParameterContext = new 
VersionedParameterContext();
+        versionedParameterContext.setName(PARAMETER_CONTEXT_NAME);
+        versionedParameterContext.setParameters(Set.of());
+        versionedParameterContext.setInheritedParameterContexts(List.of());
+        versionedParameterContext.setParameterProvider(parameterProviderId);
+        return versionedParameterContext;
+    }
+}

Reply via email to