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