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 28e9f774192 NIFI-16154 Aligned Asset Owner ID checking in Delete and 
Get methods (#11485)
28e9f774192 is described below

commit 28e9f774192c072e5b30aa0819925a98331c3fb9
Author: David Handermann <[email protected]>
AuthorDate: Wed Jul 29 13:42:26 2026 -0500

    NIFI-16154 Aligned Asset Owner ID checking in Delete and Get methods 
(#11485)
---
 .../apache/nifi/web/StandardNiFiServiceFacade.java |  20 +++-
 .../nifi/web/StandardNiFiServiceFacadeTest.java    | 111 +++++++++++++++++++++
 2 files changed, 126 insertions(+), 5 deletions(-)

diff --git 
a/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/StandardNiFiServiceFacade.java
 
b/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/StandardNiFiServiceFacade.java
index 3a897f0804c..f725c56b0ba 100644
--- 
a/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/StandardNiFiServiceFacade.java
+++ 
b/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/StandardNiFiServiceFacade.java
@@ -8499,11 +8499,21 @@ public class StandardNiFiServiceFacade implements 
NiFiServiceFacade {
 
     @Override
     public void verifyDeleteAsset(final String parameterContextId, final 
String assetId) {
-        final ParameterContext parameterContext = 
parameterContextDAO.getParameterContext(parameterContextId);
-        final Set<String> referencingParameterNames = 
getReferencingParameterNames(parameterContext, assetId);
-        if (!referencingParameterNames.isEmpty()) {
-            final String joinedParametersNames = String.join(", ", 
referencingParameterNames);
-            throw new IllegalStateException("Unable to delete Asset [%s] 
because it is currently references by Parameters [%s]".formatted(assetId, 
joinedParametersNames));
+        final Asset asset = assetManager.getAsset(assetId)
+                .orElseThrow(
+                        () -> new ResourceNotFoundException("Asset [%s] not 
found".formatted(assetId))
+                );
+
+        final String ownerIdentifier = asset.getOwnerIdentifier();
+        if (ownerIdentifier.equals(parameterContextId)) {
+            final ParameterContext parameterContext = 
parameterContextDAO.getParameterContext(parameterContextId);
+            final Set<String> referencingParameterNames = 
getReferencingParameterNames(parameterContext, assetId);
+            if (!referencingParameterNames.isEmpty()) {
+                final String joinedParametersNames = String.join(", ", 
referencingParameterNames);
+                throw new IllegalStateException("Unable to delete Asset [%s] 
because it is currently references by Parameters [%s]".formatted(assetId, 
joinedParametersNames));
+            }
+        } else {
+            throw new ResourceNotFoundException("Asset [%s] not 
found".formatted(assetId));
         }
     }
 
diff --git 
a/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/java/org/apache/nifi/web/StandardNiFiServiceFacadeTest.java
 
b/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/java/org/apache/nifi/web/StandardNiFiServiceFacadeTest.java
index 604ac10ceaf..38aea7c7eac 100644
--- 
a/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/java/org/apache/nifi/web/StandardNiFiServiceFacadeTest.java
+++ 
b/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/java/org/apache/nifi/web/StandardNiFiServiceFacadeTest.java
@@ -20,6 +20,8 @@ import org.apache.nifi.action.Component;
 import org.apache.nifi.action.FlowChangeAction;
 import org.apache.nifi.action.Operation;
 import org.apache.nifi.admin.service.AuditService;
+import org.apache.nifi.asset.Asset;
+import org.apache.nifi.asset.AssetManager;
 import org.apache.nifi.authorization.AccessDeniedException;
 import org.apache.nifi.authorization.AuthorizableLookup;
 import org.apache.nifi.authorization.AuthorizationRequest;
@@ -76,8 +78,10 @@ import org.apache.nifi.groups.VersionedComponentAdditions;
 import org.apache.nifi.history.History;
 import org.apache.nifi.history.HistoryQuery;
 import org.apache.nifi.nar.ExtensionManager;
+import org.apache.nifi.parameter.Parameter;
 import org.apache.nifi.parameter.ParameterContext;
 import org.apache.nifi.parameter.ParameterContextLookup;
+import org.apache.nifi.parameter.ParameterDescriptor;
 import org.apache.nifi.processor.Processor;
 import org.apache.nifi.registry.flow.FlowRegistryClientNode;
 import org.apache.nifi.registry.flow.FlowRegistryClientUserContext;
@@ -131,6 +135,7 @@ import org.apache.nifi.web.api.dto.search.SearchResultsDTO;
 import org.apache.nifi.web.api.dto.status.StatusHistoryDTO;
 import org.apache.nifi.web.api.entity.ActionEntity;
 import org.apache.nifi.web.api.entity.AffectedComponentEntity;
+import org.apache.nifi.web.api.entity.AssetEntity;
 import org.apache.nifi.web.api.entity.BacklogEntity;
 import org.apache.nifi.web.api.entity.ClearBulletinsForGroupResultsEntity;
 import org.apache.nifi.web.api.entity.ClearBulletinsResultEntity;
@@ -149,6 +154,7 @@ import org.apache.nifi.web.dao.ComponentStateDAO;
 import org.apache.nifi.web.dao.ConnectorDAO;
 import org.apache.nifi.web.dao.ConnectorManagedComponentLookup;
 import org.apache.nifi.web.dao.FlowRegistryDAO;
+import org.apache.nifi.web.dao.ParameterContextDAO;
 import org.apache.nifi.web.dao.ProcessGroupDAO;
 import org.apache.nifi.web.dao.ProcessorDAO;
 import org.apache.nifi.web.dao.RemoteProcessGroupDAO;
@@ -171,6 +177,7 @@ import 
org.springframework.security.authentication.TestingAuthenticationToken;
 import org.springframework.security.core.Authentication;
 import org.springframework.security.core.context.SecurityContextHolder;
 
+import java.io.File;
 import java.io.IOException;
 import java.nio.charset.StandardCharsets;
 import java.time.Instant;
@@ -249,6 +256,12 @@ public class StandardNiFiServiceFacadeTest {
     private static final String PATH_TO_GROUP_2 = "Path2";
     private static final String RANDOM_GROUP_ID = "randomGroupId";
 
+    private static final String ASSET_ID = "asset-1";
+    private static final String ASSET_NAME = "asset-1.bin";
+    private static final String ASSET_PARAMETER_CONTEXT_ID = 
"parameter-context-1";
+    private static final String OTHER_PARAMETER_CONTEXT_ID = 
"parameter-context-2";
+    private static final String REFERENCING_PARAMETER_NAME = "asset-parameter";
+
     private StandardNiFiServiceFacade serviceFacade;
     private Authorizer authorizer;
     private FlowController flowController;
@@ -2752,4 +2765,102 @@ public class StandardNiFiServiceFacadeTest {
 
         serviceFacade.verifyCanReportConnectorBacklog(connectorId);
     }
+
+    @Test
+    public void 
testVerifyDeleteAssetWithUnknownAssetIdThrowsResourceNotFound() {
+        final ParameterContext parameterContext = mock(ParameterContext.class);
+        final AssetManager assetManager = configureAssets(null, 
parameterContext);
+
+        assertThrows(ResourceNotFoundException.class, () -> 
serviceFacade.verifyDeleteAsset(ASSET_PARAMETER_CONTEXT_ID, ASSET_ID));
+
+        verify(parameterContext, never()).getParameters();
+        verify(assetManager, never()).deleteAsset(anyString());
+    }
+
+    @Test
+    public void 
testVerifyDeleteAssetOwnedByDifferentContextThrowsResourceNotFound() {
+        final Asset asset = createAsset(ASSET_ID, OTHER_PARAMETER_CONTEXT_ID);
+        final ParameterContext parameterContext = mock(ParameterContext.class);
+        final AssetManager assetManager = configureAssets(asset, 
parameterContext);
+
+        assertThrows(ResourceNotFoundException.class, () -> 
serviceFacade.verifyDeleteAsset(ASSET_PARAMETER_CONTEXT_ID, ASSET_ID));
+
+        verify(parameterContext, never()).getParameters();
+        verify(assetManager, never()).deleteAsset(anyString());
+    }
+
+    @Test
+    public void testVerifyDeleteAssetOwnedByContextWithNoReferencesSucceeds() {
+        final Asset asset = createAsset(ASSET_ID, ASSET_PARAMETER_CONTEXT_ID);
+        final ParameterContext parameterContext = mock(ParameterContext.class);
+        when(parameterContext.getParameters()).thenReturn(Map.of());
+        configureAssets(asset, parameterContext);
+
+        serviceFacade.verifyDeleteAsset(ASSET_PARAMETER_CONTEXT_ID, ASSET_ID);
+    }
+
+    @Test
+    public void 
testVerifyDeleteAssetOwnedByContextThrowsWhenReferencedByParameter() {
+        final Asset asset = createAsset(ASSET_ID, ASSET_PARAMETER_CONTEXT_ID);
+        final ParameterDescriptor descriptor = new 
ParameterDescriptor.Builder().name(REFERENCING_PARAMETER_NAME).build();
+        final Parameter parameter = new 
Parameter.Builder().descriptor(descriptor).referencedAssets(List.of(asset)).build();
+        final ParameterContext parameterContext = mock(ParameterContext.class);
+        when(parameterContext.getParameters()).thenReturn(Map.of(descriptor, 
parameter));
+        final AssetManager assetManager = configureAssets(asset, 
parameterContext);
+
+        final IllegalStateException exception = 
assertThrows(IllegalStateException.class,
+                () -> 
serviceFacade.verifyDeleteAsset(ASSET_PARAMETER_CONTEXT_ID, ASSET_ID));
+        
assertTrue(exception.getMessage().contains(REFERENCING_PARAMETER_NAME));
+
+        verify(assetManager, never()).deleteAsset(anyString());
+    }
+
+    @Test
+    public void testDeleteAssetOwnedByDifferentContextDoesNotRemoveAsset() {
+        final Asset asset = createAsset(ASSET_ID, OTHER_PARAMETER_CONTEXT_ID);
+        final AssetManager assetManager = configureAssets(asset, 
mock(ParameterContext.class));
+
+        assertThrows(ResourceNotFoundException.class, () -> 
serviceFacade.deleteAsset(ASSET_PARAMETER_CONTEXT_ID, ASSET_ID));
+
+        verify(assetManager, never()).deleteAsset(anyString());
+    }
+
+    @Test
+    public void testDeleteAssetOwnedByContextRemovesAsset() {
+        final Asset asset = createAsset(ASSET_ID, ASSET_PARAMETER_CONTEXT_ID);
+        when(asset.getDigest()).thenReturn(Optional.empty());
+        final ParameterContext parameterContext = mock(ParameterContext.class);
+        when(parameterContext.getParameters()).thenReturn(Map.of());
+        final AssetManager assetManager = configureAssets(asset, 
parameterContext);
+
+        final AssetEntity assetEntity = 
serviceFacade.deleteAsset(ASSET_PARAMETER_CONTEXT_ID, ASSET_ID);
+
+        assertNotNull(assetEntity);
+        assertEquals(ASSET_ID, assetEntity.getAsset().getId());
+        verify(assetManager).deleteAsset(ASSET_ID);
+    }
+
+    private Asset createAsset(final String assetId, final String ownerId) {
+        final Asset asset = mock(Asset.class);
+        when(asset.getIdentifier()).thenReturn(assetId);
+        when(asset.getOwnerIdentifier()).thenReturn(ownerId);
+        when(asset.getName()).thenReturn(ASSET_NAME);
+        when(asset.getFile()).thenReturn(new File(ASSET_NAME));
+        return asset;
+    }
+
+    private AssetManager configureAssets(final Asset asset, final 
ParameterContext parameterContext) {
+        final AssetManager assetManager = mock(AssetManager.class);
+        if (asset != null) {
+            
when(assetManager.getAsset(asset.getIdentifier())).thenReturn(Optional.of(asset));
+            
when(assetManager.deleteAsset(asset.getIdentifier())).thenReturn(Optional.of(asset));
+        }
+
+        final ParameterContextDAO parameterContextDAO = 
mock(ParameterContextDAO.class);
+        
when(parameterContextDAO.getParameterContext(anyString())).thenReturn(parameterContext);
+
+        serviceFacade.setAssetManager(assetManager);
+        serviceFacade.setParameterContextDAO(parameterContextDAO);
+        return assetManager;
+    }
 }

Reply via email to