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 dd6bf9c8e29 NIFI-16291 Added Deprecation Logging for Component-level
Policies (#11622)
dd6bf9c8e29 is described below
commit dd6bf9c8e291f59775c0590592241c805b0e98c9
Author: David Handermann <[email protected]>
AuthorDate: Fri Sep 4 06:08:19 2026 -0500
NIFI-16291 Added Deprecation Logging for Component-level Policies (#11622)
---
.../ComponentAccessPolicyDeprecationLogger.java | 134 ++++++++++++++++
.../org/apache/nifi/controller/FlowController.java | 3 +
...ComponentAccessPolicyDeprecationLoggerTest.java | 177 +++++++++++++++++++++
3 files changed, 314 insertions(+)
diff --git
a/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/authorization/ComponentAccessPolicyDeprecationLogger.java
b/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/authorization/ComponentAccessPolicyDeprecationLogger.java
new file mode 100644
index 00000000000..0f6876d968a
--- /dev/null
+++
b/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/authorization/ComponentAccessPolicyDeprecationLogger.java
@@ -0,0 +1,134 @@
+/*
+ * 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.ResourceType;
+import org.apache.nifi.deprecation.log.DeprecationLogger;
+import org.apache.nifi.deprecation.log.DeprecationLoggerFactory;
+
+import java.util.LinkedHashSet;
+import java.util.Set;
+
+/**
+ * Log deprecation warnings for Access Policies configured against individual
components. Authorization for
+ * individual components will be replaced with authorization for the
Controller per NIP-39
+ */
+public final class ComponentAccessPolicyDeprecationLogger {
+
+ private static final DeprecationLogger deprecationLogger =
DeprecationLoggerFactory.getLogger(ComponentAccessPolicyDeprecationLogger.class);
+
+ /** Resource Types that identify single component instances */
+ private static final Set<ResourceType> COMPONENT_RESOURCE_TYPES = Set.of(
+ ResourceType.ProcessGroup,
+ ResourceType.Processor,
+ ResourceType.ControllerService,
+ ResourceType.InputPort,
+ ResourceType.OutputPort,
+ ResourceType.Funnel,
+ ResourceType.Label,
+ ResourceType.RemoteProcessGroup,
+ ResourceType.ParameterContext,
+ ResourceType.ParameterProvider
+ );
+
+ /** Resource Types that can precede other Types */
+ private static final Set<ResourceType> RESOURCE_PREFIX_TYPES = Set.of(
+ ResourceType.Data,
+ ResourceType.DataTransfer,
+ ResourceType.Operation,
+ ResourceType.Policy,
+ ResourceType.ProvenanceData
+ );
+
+ private static final String RESOURCE_SEPARATOR = "/";
+
+ /**
+ * Log deprecation warnings when the configured Authorizer provides Access
Policies for individual components
+ *
+ * @param authorizer Configured Authorizer
+ * @param rootGroupId Identifier of the root Process Group, for which
Access Policies are not deprecated
+ */
+ public static void logComponentPolicies(final Authorizer authorizer, final
String rootGroupId) {
+ if (authorizer instanceof final ManagedAuthorizer managedAuthorizer) {
+ final AccessPolicyProvider accessPolicyProvider =
managedAuthorizer.getAccessPolicyProvider();
+ final Set<AccessPolicy> configuredAccessPolicies =
accessPolicyProvider.getAccessPolicies();
+ final Set<AccessPolicy> componentPolicies =
findComponentPolicies(configuredAccessPolicies, rootGroupId);
+
+ if (!componentPolicies.isEmpty()) {
+ deprecationLogger.warn("Found [{}] component Access Policies
deprecated for removal in NIP-39", componentPolicies.size());
+ }
+ }
+ }
+
+ static Set<AccessPolicy> findComponentPolicies(final Set<AccessPolicy>
policies, final String rootGroupId) {
+ final Set<AccessPolicy> componentPolicies = new LinkedHashSet<>();
+
+ for (final AccessPolicy policy : policies) {
+ final String resource = policy.getResource();
+ if (isComponentPolicy(resource, rootGroupId)) {
+ componentPolicies.add(policy);
+ }
+ }
+
+ return componentPolicies;
+ }
+
+ private static boolean isComponentPolicy(final String resource, final
String rootGroupId) {
+ boolean componentPolicyFound = false;
+
+ final String componentResource = removeResourcePrefix(resource);
+
+ for (final ResourceType componentResourceType :
COMPONENT_RESOURCE_TYPES) {
+ final String componentResourcePrefix =
componentResourceType.getValue() + RESOURCE_SEPARATOR;
+
+ if (componentResource.startsWith(componentResourcePrefix)) {
+ if (ResourceType.ProcessGroup == componentResourceType) {
+ final String groupIdentifier =
componentResource.substring(componentResourcePrefix.length());
+ if (groupIdentifier.equals(rootGroupId)) {
+ // Root Group Identifier expected and ignored
+ continue;
+ } else {
+ componentPolicyFound = true;
+ }
+ } else {
+ componentPolicyFound = true;
+ }
+ break;
+ }
+ }
+
+ return componentPolicyFound;
+ }
+
+ private static String removeResourcePrefix(final String resource) {
+ String resourceNormalized = resource;
+
+ for (final ResourceType resourcePrefixType : RESOURCE_PREFIX_TYPES) {
+ final String resourcePrefixValue = resourcePrefixType.getValue();
+ final String resourcePrefixSeparator = resourcePrefixValue +
RESOURCE_SEPARATOR;
+
+ if (resource.startsWith(resourcePrefixSeparator)) {
+ resourceNormalized =
resource.substring(resourcePrefixValue.length());
+ break;
+ }
+ }
+
+ return resourceNormalized;
+ }
+
+ private ComponentAccessPolicyDeprecationLogger() { }
+}
diff --git
a/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/controller/FlowController.java
b/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/controller/FlowController.java
index fcf76a0ac9a..155b0520113 100644
---
a/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/controller/FlowController.java
+++
b/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/controller/FlowController.java
@@ -29,6 +29,7 @@ import
org.apache.nifi.asset.StandardAssetManagerInitializationContext;
import org.apache.nifi.asset.StandardAssetReferenceLookup;
import org.apache.nifi.asset.StandardConnectorAssetManager;
import org.apache.nifi.authorization.Authorizer;
+import org.apache.nifi.authorization.ComponentAccessPolicyDeprecationLogger;
import org.apache.nifi.authorization.Resource;
import org.apache.nifi.authorization.resource.Authorizable;
import org.apache.nifi.authorization.resource.ResourceFactory;
@@ -1618,6 +1619,8 @@ public class FlowController implements
ReportingTaskProvider, FlowAnalysisRulePr
final Runnable discoverPythonExtensions = () ->
extensionManager.discoverNewPythonExtensions(pythonBundle);
timerDrivenEngineRef.get().scheduleWithFixedDelay(discoverPythonExtensions, 1,
1, TimeUnit.MINUTES);
+
+
ComponentAccessPolicyDeprecationLogger.logComponentPolicies(authorizer,
flowManager.getRootGroupId());
} finally {
writeLock.unlock("onFlowInitialized");
}
diff --git
a/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/test/java/org/apache/nifi/authorization/ComponentAccessPolicyDeprecationLoggerTest.java
b/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/test/java/org/apache/nifi/authorization/ComponentAccessPolicyDeprecationLoggerTest.java
new file mode 100644
index 00000000000..54cf5815015
--- /dev/null
+++
b/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/test/java/org/apache/nifi/authorization/ComponentAccessPolicyDeprecationLoggerTest.java
@@ -0,0 +1,177 @@
+/*
+ * 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.ResourceType;
+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.LinkedHashSet;
+import java.util.Set;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoInteractions;
+import static org.mockito.Mockito.when;
+
+@ExtendWith(MockitoExtension.class)
+class ComponentAccessPolicyDeprecationLoggerTest {
+
+ private static final String RESOURCE_FORMAT = "%s%s/%s";
+
+ private static final String EMPTY_PREFIX = "";
+
+ private static final String ROOT_GROUP_ID =
"a1d4c1b0-0000-1000-0000-000000000001";
+
+ private static final String CHILD_GROUP_ID =
"a1d4c1b0-0000-1000-0000-000000000002";
+
+ private static final String COMPONENT_ID =
"a1d4c1b0-0000-1000-0000-000000000003";
+
+ private static final String ROOT_GROUP_RESOURCE =
getComponentResource(ResourceType.ProcessGroup, ROOT_GROUP_ID);
+
+ private static final String CHILD_GROUP_RESOURCE =
getComponentResource(ResourceType.ProcessGroup, CHILD_GROUP_ID);
+
+ @Mock
+ private Authorizer authorizer;
+
+ @Mock
+ private ManagedAuthorizer managedAuthorizer;
+
+ @Mock
+ private AccessPolicyProvider accessPolicyProvider;
+
+ @Test
+ void testRootProcessGroupAndSharedResourcesNotDeprecated() {
+ final Set<AccessPolicy> policies = getPolicies(
+ ROOT_GROUP_RESOURCE,
+ getComponentResource(ResourceType.Data,
ResourceType.ProcessGroup, ROOT_GROUP_ID),
+ getComponentResource(ResourceType.Policy,
ResourceType.ProcessGroup, ROOT_GROUP_ID),
+ getComponentResource(ResourceType.Operation,
ResourceType.ProcessGroup, ROOT_GROUP_ID),
+ getComponentResource(ResourceType.ProvenanceData,
ResourceType.ProcessGroup, ROOT_GROUP_ID),
+ ResourceType.Flow.getValue(),
+ ResourceType.Controller.getValue(),
+ ResourceType.Tenant.getValue(),
+ ResourceType.Policy.getValue(),
+ ResourceType.Proxy.getValue(),
+ ResourceType.System.getValue(),
+ ResourceType.Counters.getValue(),
+ ResourceType.Provenance.getValue(),
+ ResourceType.ParameterContext.getValue()
+ );
+
+ assertEquals(Set.of(), getDeprecatedResources(policies,
ROOT_GROUP_ID));
+ }
+
+ @Test
+ void testResourceTypesOutsideComponentScopeNotDeprecated() {
+ final Set<AccessPolicy> policies = getPolicies(
+ getComponentResource(ResourceType.RegistryClient,
COMPONENT_ID),
+ getComponentResource(ResourceType.FlowAnalysisRule,
COMPONENT_ID),
+ getComponentResource(ResourceType.ReportingTask, COMPONENT_ID),
+ getComponentResource(ResourceType.Connector, COMPONENT_ID)
+ );
+
+ assertEquals(Set.of(), getDeprecatedResources(policies,
ROOT_GROUP_ID));
+ }
+
+ @Test
+ void testProcessGroupOtherThanRootGroupDeprecated() {
+ final String childGroupDataResource =
getComponentResource(ResourceType.Data, ResourceType.ProcessGroup,
CHILD_GROUP_ID);
+ final String childGroupPolicyResource =
getComponentResource(ResourceType.Policy, ResourceType.ProcessGroup,
CHILD_GROUP_ID);
+
+ final Set<AccessPolicy> policies = getPolicies(ROOT_GROUP_RESOURCE,
CHILD_GROUP_RESOURCE, childGroupDataResource, childGroupPolicyResource);
+
+ assertEquals(Set.of(CHILD_GROUP_RESOURCE, childGroupDataResource,
childGroupPolicyResource), getDeprecatedResources(policies, ROOT_GROUP_ID));
+ assertEquals(Set.of(ROOT_GROUP_RESOURCE, CHILD_GROUP_RESOURCE,
childGroupDataResource, childGroupPolicyResource),
getDeprecatedResources(policies, null));
+ }
+
+ @Test
+ void testComponentResourceTypesDeprecated() {
+ final Set<String> componentResources = Set.of(
+ getComponentResource(ResourceType.Processor, COMPONENT_ID),
+ getComponentResource(ResourceType.ControllerService,
COMPONENT_ID),
+ getComponentResource(ResourceType.InputPort, COMPONENT_ID),
+ getComponentResource(ResourceType.OutputPort, COMPONENT_ID),
+ getComponentResource(ResourceType.Funnel, COMPONENT_ID),
+ getComponentResource(ResourceType.Label, COMPONENT_ID),
+ getComponentResource(ResourceType.RemoteProcessGroup,
COMPONENT_ID),
+ getComponentResource(ResourceType.ParameterContext,
COMPONENT_ID),
+ getComponentResource(ResourceType.ParameterProvider,
COMPONENT_ID),
+ getComponentResource(ResourceType.Data,
ResourceType.Processor, COMPONENT_ID),
+ getComponentResource(ResourceType.DataTransfer,
ResourceType.InputPort, COMPONENT_ID),
+ getComponentResource(ResourceType.Operation,
ResourceType.Processor, COMPONENT_ID),
+ getComponentResource(ResourceType.ProvenanceData,
ResourceType.Processor, COMPONENT_ID)
+ );
+
+ final Set<AccessPolicy> policies =
getPolicies(componentResources.toArray(new String[0]));
+
+ assertEquals(componentResources, getDeprecatedResources(policies,
ROOT_GROUP_ID));
+ }
+
+ @Test
+ void testLogComponentPoliciesManagedAuthorizer() {
+
when(managedAuthorizer.getAccessPolicyProvider()).thenReturn(accessPolicyProvider);
+
when(accessPolicyProvider.getAccessPolicies()).thenReturn(getPolicies(getComponentResource(ResourceType.Processor,
COMPONENT_ID)));
+
+
ComponentAccessPolicyDeprecationLogger.logComponentPolicies(managedAuthorizer,
ROOT_GROUP_ID);
+
+ verify(accessPolicyProvider).getAccessPolicies();
+ }
+
+ @Test
+ void testLogComponentPoliciesAuthorizerWithoutAccessPolicies() {
+
ComponentAccessPolicyDeprecationLogger.logComponentPolicies(authorizer,
ROOT_GROUP_ID);
+
+ verifyNoInteractions(authorizer);
+ }
+
+ private static String getComponentResource(final ResourceType
resourceType, final String identifier) {
+ return RESOURCE_FORMAT.formatted(EMPTY_PREFIX,
resourceType.getValue(), identifier);
+ }
+
+ private static String getComponentResource(final ResourceType
prefixResourceType, final ResourceType resourceType, final String identifier) {
+ return RESOURCE_FORMAT.formatted(prefixResourceType.getValue(),
resourceType.getValue(), identifier);
+ }
+
+ private static Set<String> getDeprecatedResources(final Set<AccessPolicy>
policies, final String rootGroupId) {
+ final Set<String> resources = new LinkedHashSet<>();
+
+ for (final AccessPolicy policy :
ComponentAccessPolicyDeprecationLogger.findComponentPolicies(policies,
rootGroupId)) {
+ resources.add(policy.getResource());
+ }
+
+ return resources;
+ }
+
+ private static Set<AccessPolicy> getPolicies(final String... resources) {
+ final Set<AccessPolicy> policies = new LinkedHashSet<>();
+
+ for (final String resource : resources) {
+ final AccessPolicy policy = new AccessPolicy.Builder()
+ .identifierGenerateFromSeed(resource)
+ .resource(resource)
+ .action(RequestAction.READ)
+ .build();
+
+ policies.add(policy);
+ }
+
+ return policies;
+ }
+}