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 1d4f8849486 NIFI-16205 Improved Connector Referenced Object 
Authorization (#11547)
1d4f8849486 is described below

commit 1d4f884948655499fad6c6c406c6227239006ccb
Author: David Handermann <[email protected]>
AuthorDate: Sat Aug 15 07:59:20 2026 -0500

    NIFI-16205 Improved Connector Referenced Object Authorization (#11547)
    
    - Added Asset and Secret checking during verification
---
 .../nifi/authorization/AuthorizableLookup.java     |  19 ++
 .../AuthorizeConnectorConfigReferences.java        | 141 +++++++++++++
 .../authorization/StandardAuthorizableLookup.java  |  62 ++++++
 .../org/apache/nifi/web/api/ConnectorResource.java |  19 +-
 .../AuthorizeConnectorConfigReferencesTest.java    | 224 +++++++++++++++++++++
 .../StandardAuthorizableLookupTest.java            | 143 +++++++++++++
 .../apache/nifi/web/api/TestConnectorResource.java |  99 +++++++++
 .../tests/system/connectors/ConnectorAssetsIT.java |   1 +
 8 files changed, 698 insertions(+), 10 deletions(-)

diff --git 
a/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/authorization/AuthorizableLookup.java
 
b/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/authorization/AuthorizableLookup.java
index ff152f4f40f..418d2165594 100644
--- 
a/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/authorization/AuthorizableLookup.java
+++ 
b/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/authorization/AuthorizableLookup.java
@@ -333,6 +333,25 @@ public interface AuthorizableLookup {
      */
     Authorizable getConnector(String connectorId);
 
+    /**
+     * Get the authorizable governing access to an Asset referenced by a 
Connector configuration
+     *
+     * @param connectorId the ID of the connector that must own the referenced 
Asset
+     * @param assetId the ID of the referenced Asset
+     * @return Authorizable for the Asset owned by the Connector
+     */
+    Authorizable getConnectorAsset(String connectorId, String assetId);
+
+    /**
+     * Get the authorizable for the Parameter Provider that backs a Secret 
referenced by a connector configuration
+     *
+     * @param secretProviderId the identifier of the Parameter Provider, if 
known
+     * @param secretProviderName the name of the Parameter Provider, if known
+     * @param fullyQualifiedSecretName the fully qualified Secret name, whose 
leading segment identifies the provider name
+     * @return the authorizable for the Parameter Provider that backs the 
referenced Secret
+     */
+    Authorizable getConnectorSecretProvider(String secretProviderId, String 
secretProviderName, String fullyQualifiedSecretName);
+
     /**
      * Get the authorizable for access to the System resource.
      *
diff --git 
a/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/authorization/AuthorizeConnectorConfigReferences.java
 
b/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/authorization/AuthorizeConnectorConfigReferences.java
new file mode 100644
index 00000000000..9927c5355cc
--- /dev/null
+++ 
b/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/authorization/AuthorizeConnectorConfigReferences.java
@@ -0,0 +1,141 @@
+/*
+ * 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 org.apache.nifi.components.connector.ConnectorValueType;
+import org.apache.nifi.web.api.dto.AssetReferenceDTO;
+import org.apache.nifi.web.api.dto.ConfigurationStepConfigurationDTO;
+import org.apache.nifi.web.api.dto.ConnectorValueReferenceDTO;
+import org.apache.nifi.web.api.dto.PropertyGroupConfigurationDTO;
+
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Authorizes a proposed Connector configuration step and the resources 
referenced by its property values.
+ * Requires {@code WRITE} on the connector itself. In addition, referencing a 
Secret requires {@code READ} on
+ * the backing Parameter Provider, and referencing an Asset requires {@code 
READ} on the connector that owns
+ * the Asset, which enforces that the Asset belongs to the connector being 
configured.
+ */
+public final class AuthorizeConnectorConfigReferences {
+
+    private AuthorizeConnectorConfigReferences() {
+    }
+
+    /**
+     * Authorize a proposed Connector configuration step, including any 
resources it references.
+     *
+     * @param authorizer Authorizer used for determining results
+     * @param lookup Authorizable Lookup used to resolve the connector and any 
referenced Secrets and Assets
+     * @param connectorId the identifier of the connector whose configuration 
is being applied or verified
+     * @param configurationStep the proposed configuration step, whose 
property values are inspected for references
+     */
+    public static void authorize(
+            final Authorizer authorizer,
+            final AuthorizableLookup lookup,
+            final String connectorId,
+            final ConfigurationStepConfigurationDTO configurationStep
+    ) {
+        final NiFiUser user = NiFiUserUtils.getNiFiUser();
+
+        lookup.getConnector(connectorId).authorize(authorizer, 
RequestAction.WRITE, user);
+
+        if (configurationStep == null || 
configurationStep.getPropertyGroupConfigurations() == null) {
+            return;
+        }
+
+        for (final PropertyGroupConfigurationDTO propertyGroup : 
configurationStep.getPropertyGroupConfigurations()) {
+            final Map<String, ConnectorValueReferenceDTO> propertyValues = 
propertyGroup.getPropertyValues();
+            if (propertyValues == null) {
+                continue;
+            }
+
+            for (final ConnectorValueReferenceDTO valueReference : 
propertyValues.values()) {
+                authorizeValueReference(authorizer, lookup, connectorId, 
valueReference, user);
+            }
+        }
+    }
+
+    private static void authorizeValueReference(
+            final Authorizer authorizer,
+            final AuthorizableLookup lookup,
+            final String connectorId,
+            final ConnectorValueReferenceDTO valueReference,
+            final NiFiUser user
+    ) {
+        if (valueReference == null || valueReference.getValueType() == null) {
+            return;
+        }
+
+        final ConnectorValueType valueType;
+        try {
+            valueType = 
ConnectorValueType.valueOf(valueReference.getValueType());
+        } catch (final IllegalArgumentException e) {
+            throw new IllegalArgumentException("Unknown Connector Value Type: 
" + valueReference.getValueType());
+        }
+
+        switch (valueType) {
+            case STRING_LITERAL -> {
+                // String literals do not reference any externally authorized 
resource.
+            }
+            case ASSET_REFERENCE -> authorizeAssetReferences(authorizer, 
lookup, connectorId, valueReference.getAssetReferences(), user);
+            case SECRET_REFERENCE -> authorizeSecretReference(authorizer, 
lookup, valueReference, user);
+        }
+    }
+
+    private static void authorizeAssetReferences(
+            final Authorizer authorizer,
+            final AuthorizableLookup lookup,
+            final String connectorId,
+            final List<AssetReferenceDTO> assetReferences,
+            final NiFiUser user
+    ) {
+        if (assetReferences == null || assetReferences.isEmpty()) {
+            throw new IllegalArgumentException("Asset references must be 
specified when value type is ASSET_REFERENCE");
+        }
+
+        for (final AssetReferenceDTO assetReference : assetReferences) {
+            if (assetReference == null) {
+                throw new IllegalArgumentException("Asset reference must be 
specified");
+            }
+
+            final String assetId = assetReference.getId();
+            if (assetId == null || assetId.isEmpty()) {
+                throw new IllegalArgumentException("Asset reference identifier 
must be specified");
+            }
+
+            final Authorizable assetAuthorizable = 
lookup.getConnectorAsset(connectorId, assetId);
+            assetAuthorizable.authorize(authorizer, RequestAction.READ, user);
+        }
+    }
+
+    private static void authorizeSecretReference(
+            final Authorizer authorizer,
+            final AuthorizableLookup lookup,
+            final ConnectorValueReferenceDTO valueReference,
+            final NiFiUser user
+    ) {
+        final Authorizable secretProvider = lookup.getConnectorSecretProvider(
+                valueReference.getSecretProviderId(),
+                valueReference.getSecretProviderName(),
+                valueReference.getFullyQualifiedSecretName());
+        secretProvider.authorize(authorizer, RequestAction.READ, user);
+    }
+}
diff --git 
a/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/authorization/StandardAuthorizableLookup.java
 
b/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/authorization/StandardAuthorizableLookup.java
index ad3258a0818..ce67307d4ad 100644
--- 
a/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/authorization/StandardAuthorizableLookup.java
+++ 
b/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/authorization/StandardAuthorizableLookup.java
@@ -17,6 +17,7 @@
 package org.apache.nifi.authorization;
 
 import org.apache.commons.lang3.StringUtils;
+import org.apache.nifi.asset.Asset;
 import org.apache.nifi.authorization.resource.AccessPolicyAuthorizable;
 import org.apache.nifi.authorization.resource.Authorizable;
 import org.apache.nifi.authorization.resource.DataAuthorizable;
@@ -87,6 +88,8 @@ import java.util.stream.Collectors;
 @Component
 public class StandardAuthorizableLookup implements AuthorizableLookup {
 
+    private static final String SECRET_PROVIDER_NOT_FOUND_MESSAGE = "The 
Parameter Provider for the referenced Secret could not be found";
+
     private static final TenantAuthorizable TENANT_AUTHORIZABLE = new 
TenantAuthorizable();
 
     private static final Authorizable POLICIES_AUTHORIZABLE = new 
Authorizable() {
@@ -709,6 +712,65 @@ public class StandardAuthorizableLookup implements 
AuthorizableLookup {
         return connectorDAO.getConnector(connectorId, 
ConnectorSyncMode.LOCAL_ONLY);
     }
 
+    @Override
+    public Authorizable getConnectorAsset(final String connectorId, final 
String assetId) {
+        final Asset asset = connectorDAO.getAsset(assetId).orElseThrow(() -> 
new ResourceNotFoundException("Asset [%s] not found".formatted(assetId)));
+        if (connectorId.equals(asset.getOwnerIdentifier())) {
+            return getConnector(connectorId);
+        } else {
+            throw new ResourceNotFoundException("Asset [%s] not 
found".formatted(assetId));
+        }
+    }
+
+    @Override
+    public Authorizable getConnectorSecretProvider(final String 
secretProviderId, final String secretProviderName, final String 
fullyQualifiedSecretName) {
+        if (secretProviderId != null && !secretProviderId.isEmpty()) {
+            try {
+                return 
parameterProviderDAO.getParameterProvider(secretProviderId);
+            } catch (final ResourceNotFoundException e) {
+                throw new 
ResourceNotFoundException(SECRET_PROVIDER_NOT_FOUND_MESSAGE);
+            }
+        }
+
+        final String resolvedSecretProviderName = 
getResolvedSecretProviderName(secretProviderName, fullyQualifiedSecretName);
+        final List<ParameterProviderNode> matchingProviders = 
parameterProviderDAO.getParameterProviders().stream()
+                .filter(node -> 
resolvedSecretProviderName.equals(node.getName()))
+                .toList();
+
+        if (matchingProviders.isEmpty()) {
+            throw new 
ResourceNotFoundException(SECRET_PROVIDER_NOT_FOUND_MESSAGE);
+        }
+        if (matchingProviders.size() > 1) {
+            throw new IllegalArgumentException("Multiple Parameter Providers 
found [%s] Parameter Provider ID 
required".formatted(resolvedSecretProviderName));
+        }
+
+        return matchingProviders.getFirst();
+    }
+
+    private String getResolvedSecretProviderName(final String 
secretProviderName, final String fullyQualifiedSecretName) {
+        final String resolvedSecretProviderName;
+        if (secretProviderName == null || secretProviderName.isEmpty()) {
+            if (fullyQualifiedSecretName == null) {
+                resolvedSecretProviderName = null;
+            } else {
+                final int providerNameEndIndex = 
fullyQualifiedSecretName.indexOf('.');
+                if (providerNameEndIndex > 0) {
+                    resolvedSecretProviderName = 
fullyQualifiedSecretName.substring(0, providerNameEndIndex);
+                } else {
+                    resolvedSecretProviderName = null;
+                }
+            }
+        } else {
+            resolvedSecretProviderName = secretProviderName;
+        }
+
+        if (resolvedSecretProviderName == null) {
+            throw new 
ResourceNotFoundException(SECRET_PROVIDER_NOT_FOUND_MESSAGE);
+        }
+        return resolvedSecretProviderName;
+    }
+
+
     private Authorizable handleResourceTypeContainingOtherResourceType(final 
String resource, final ResourceType resourceType) {
         // get the resource type
         final String baseResource = StringUtils.substringAfter(resource, 
resourceType.getValue());
diff --git 
a/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ConnectorResource.java
 
b/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ConnectorResource.java
index 7836582db82..c0de37b3f76 100644
--- 
a/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ConnectorResource.java
+++ 
b/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ConnectorResource.java
@@ -47,6 +47,7 @@ import jakarta.ws.rs.core.StreamingOutput;
 import org.apache.commons.lang3.StringUtils;
 import org.apache.commons.lang3.exception.ExceptionUtils;
 import org.apache.nifi.asset.Asset;
+import org.apache.nifi.authorization.AuthorizeConnectorConfigReferences;
 import org.apache.nifi.authorization.Authorizer;
 import org.apache.nifi.authorization.RequestAction;
 import org.apache.nifi.authorization.resource.Authorizable;
@@ -1403,7 +1404,9 @@ public class ConnectorResource extends 
ApplicationResource {
                     @ApiResponse(responseCode = "409", description = "The 
request was valid but NiFi was not in the appropriate state to process it.")
             },
             security = {
-                    @SecurityRequirement(name = "Write - /connectors/{uuid}")
+                    @SecurityRequirement(name = "Write - /connectors/{uuid}"),
+                    @SecurityRequirement(name = "Read - any referenced 
Parameter Providers - /parameter-providers/{uuid}"),
+                    @SecurityRequirement(name = "Read - /connectors/{uuid} 
when referencing Assets owned by the connector")
             }
     )
     public Response updateConnectorConfigurationStep(
@@ -1458,10 +1461,7 @@ public class ConnectorResource extends 
ApplicationResource {
                 serviceFacade,
                 requestConfigurationStepEntity,
                 requestRevision,
-                lookup -> {
-                    final Authorizable connector = lookup.getConnector(id);
-                    connector.authorize(authorizer, RequestAction.WRITE, 
NiFiUserUtils.getNiFiUser());
-                },
+                lookup -> 
AuthorizeConnectorConfigReferences.authorize(authorizer, lookup, id, 
requestConfigurationStep),
                 () -> {
                     // Verify the connector exists and the configuration step 
exists
                     serviceFacade.getConnectorConfigurationStep(id, 
configurationStepName);
@@ -1507,7 +1507,9 @@ public class ConnectorResource extends 
ApplicationResource {
                     
"/connectors/{connectorId}/configuration-steps/{stepName}/verify-config/{requestId}.
 Once the request is completed, the client is expected to issue a DELETE 
request to " +
                     
"/connectors/{connectorId}/configuration-steps/{stepName}/verify-config/{requestId}.",
             security = {
-                    @SecurityRequirement(name = "Write - /connectors/{uuid}")
+                    @SecurityRequirement(name = "Write - /connectors/{uuid}"),
+                    @SecurityRequirement(name = "Read - any referenced 
Parameter Providers - /parameter-providers/{uuid}"),
+                    @SecurityRequirement(name = "Read - /connectors/{uuid} 
when referencing Assets owned by the connector")
             }
     )
     public Response submitConfigurationStepVerificationRequest(
@@ -1552,10 +1554,7 @@ public class ConnectorResource extends 
ApplicationResource {
         return withWriteLock(
                 serviceFacade,
                 requestEntity,
-                lookup -> {
-                    final Authorizable connector = lookup.getConnector(id);
-                    connector.authorize(authorizer, RequestAction.WRITE, 
NiFiUserUtils.getNiFiUser());
-                },
+                lookup -> 
AuthorizeConnectorConfigReferences.authorize(authorizer, lookup, id, 
requestDto.getConfigurationStep()),
                 () -> {
                     
serviceFacade.verifyCanVerifyConnectorConfigurationStep(id, 
configurationStepName);
                 },
diff --git 
a/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/java/org/apache/nifi/authorization/AuthorizeConnectorConfigReferencesTest.java
 
b/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/java/org/apache/nifi/authorization/AuthorizeConnectorConfigReferencesTest.java
new file mode 100644
index 00000000000..3a4b762c017
--- /dev/null
+++ 
b/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/java/org/apache/nifi/authorization/AuthorizeConnectorConfigReferencesTest.java
@@ -0,0 +1,224 @@
+/*
+ * 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.components.connector.ConnectorValueType;
+import org.apache.nifi.web.api.dto.AssetReferenceDTO;
+import org.apache.nifi.web.api.dto.ConfigurationStepConfigurationDTO;
+import org.apache.nifi.web.api.dto.ConnectorValueReferenceDTO;
+import org.apache.nifi.web.api.dto.PropertyGroupConfigurationDTO;
+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.List;
+import java.util.Map;
+
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.doThrow;
+import static org.mockito.Mockito.eq;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+@ExtendWith(MockitoExtension.class)
+class AuthorizeConnectorConfigReferencesTest {
+
+    private static final String CONNECTOR_ID = "connector-1";
+    private static final String SECRET_PROVIDER_ID = "parameter-provider-1";
+    private static final String SECRET_PROVIDER_NAME = "Vault Provider";
+    private static final String SECRET_NAME = "api-key";
+    private static final String FULLY_QUALIFIED_SECRET_NAME = 
SECRET_PROVIDER_NAME + "." + SECRET_NAME;
+    private static final String ASSET_ID = "asset-1";
+    private static final String PROPERTY_GROUP_NAME = "authentication";
+    private static final String PROPERTY_NAME = "password";
+
+    @Mock
+    private Authorizer authorizer;
+
+    @Mock
+    private AuthorizableLookup lookup;
+
+    @Mock
+    private Authorizable connectorAuthorizable;
+
+    @Mock
+    private Authorizable secretProviderAuthorizable;
+
+    @Mock
+    private Authorizable assetAuthorizable;
+
+    @Test
+    void testAuthorizesConnectorWriteWithNoReferences() {
+        
when(lookup.getConnector(eq(CONNECTOR_ID))).thenReturn(connectorAuthorizable);
+
+        AuthorizeConnectorConfigReferences.authorize(authorizer, lookup, 
CONNECTOR_ID, new ConfigurationStepConfigurationDTO());
+
+        verify(connectorAuthorizable).authorize(eq(authorizer), 
eq(RequestAction.WRITE), any());
+    }
+
+    @Test
+    void testAuthorizesSecretReferenceReadOnProvider() {
+        
when(lookup.getConnector(eq(CONNECTOR_ID))).thenReturn(connectorAuthorizable);
+        when(lookup.getConnectorSecretProvider(eq(SECRET_PROVIDER_ID), 
eq(SECRET_PROVIDER_NAME), eq(FULLY_QUALIFIED_SECRET_NAME)))
+                .thenReturn(secretProviderAuthorizable);
+
+        final ConfigurationStepConfigurationDTO configurationStep = 
configurationStepWithValue(secretReference());
+        AuthorizeConnectorConfigReferences.authorize(authorizer, lookup, 
CONNECTOR_ID, configurationStep);
+
+        verify(connectorAuthorizable).authorize(eq(authorizer), 
eq(RequestAction.WRITE), any());
+        verify(secretProviderAuthorizable).authorize(eq(authorizer), 
eq(RequestAction.READ), any());
+    }
+
+    @Test
+    void testAuthorizesAssetReferenceReadOnOwningConnector() {
+        
when(lookup.getConnector(eq(CONNECTOR_ID))).thenReturn(connectorAuthorizable);
+        when(lookup.getConnectorAsset(eq(CONNECTOR_ID), 
eq(ASSET_ID))).thenReturn(assetAuthorizable);
+
+        final ConfigurationStepConfigurationDTO configurationStep = 
configurationStepWithValue(assetReference());
+        AuthorizeConnectorConfigReferences.authorize(authorizer, lookup, 
CONNECTOR_ID, configurationStep);
+
+        verify(connectorAuthorizable).authorize(eq(authorizer), 
eq(RequestAction.WRITE), any());
+        verify(assetAuthorizable).authorize(eq(authorizer), 
eq(RequestAction.READ), any());
+    }
+
+    @Test
+    void testIgnoresStringLiterals() {
+        
when(lookup.getConnector(eq(CONNECTOR_ID))).thenReturn(connectorAuthorizable);
+
+        final ConnectorValueReferenceDTO stringLiteral = new 
ConnectorValueReferenceDTO();
+        
stringLiteral.setValueType(ConnectorValueType.STRING_LITERAL.toString());
+        stringLiteral.setValue("plain-text");
+
+        AuthorizeConnectorConfigReferences.authorize(authorizer, lookup, 
CONNECTOR_ID, configurationStepWithValue(stringLiteral));
+
+        verify(connectorAuthorizable).authorize(eq(authorizer), 
eq(RequestAction.WRITE), any());
+        verify(lookup, never()).getConnectorSecretProvider(any(), any(), 
any());
+        verify(lookup, never()).getConnectorAsset(any(), any());
+    }
+
+    @Test
+    void testConnectorWriteDeniedSkipsReferenceResolution() {
+        
when(lookup.getConnector(eq(CONNECTOR_ID))).thenReturn(connectorAuthorizable);
+        doThrow(new 
AccessDeniedException("denied")).when(connectorAuthorizable).authorize(eq(authorizer),
 eq(RequestAction.WRITE), any());
+
+        final ConfigurationStepConfigurationDTO configurationStep = 
configurationStepWithValue(secretReference());
+
+        assertThrows(AccessDeniedException.class,
+                () -> AuthorizeConnectorConfigReferences.authorize(authorizer, 
lookup, CONNECTOR_ID, configurationStep));
+
+        verify(lookup, never()).getConnectorSecretProvider(any(), any(), 
any());
+    }
+
+    @Test
+    void testSecretProviderReadDenied() {
+        
when(lookup.getConnector(eq(CONNECTOR_ID))).thenReturn(connectorAuthorizable);
+        when(lookup.getConnectorSecretProvider(eq(SECRET_PROVIDER_ID), 
eq(SECRET_PROVIDER_NAME), eq(FULLY_QUALIFIED_SECRET_NAME)))
+                .thenReturn(secretProviderAuthorizable);
+        doThrow(new 
AccessDeniedException("denied")).when(secretProviderAuthorizable).authorize(eq(authorizer),
 eq(RequestAction.READ), any());
+
+        final ConfigurationStepConfigurationDTO configurationStep = 
configurationStepWithValue(secretReference());
+
+        assertThrows(AccessDeniedException.class,
+                () -> AuthorizeConnectorConfigReferences.authorize(authorizer, 
lookup, CONNECTOR_ID, configurationStep));
+    }
+
+    @Test
+    void testUnknownValueTypeRejected() {
+        
when(lookup.getConnector(eq(CONNECTOR_ID))).thenReturn(connectorAuthorizable);
+
+        final ConnectorValueReferenceDTO unknown = new 
ConnectorValueReferenceDTO();
+        unknown.setValueType("NOT_A_REAL_TYPE");
+
+        final ConfigurationStepConfigurationDTO configurationStep = 
configurationStepWithValue(unknown);
+
+        assertThrows(IllegalArgumentException.class,
+                () -> AuthorizeConnectorConfigReferences.authorize(authorizer, 
lookup, CONNECTOR_ID, configurationStep));
+    }
+
+    @Test
+    void testRejectsNullAssetReferences() {
+        
when(lookup.getConnector(eq(CONNECTOR_ID))).thenReturn(connectorAuthorizable);
+
+        final ConnectorValueReferenceDTO valueReference = new 
ConnectorValueReferenceDTO();
+        
valueReference.setValueType(ConnectorValueType.ASSET_REFERENCE.toString());
+        valueReference.setAssetReferences(null);
+
+        assertThrows(IllegalArgumentException.class,
+                () -> AuthorizeConnectorConfigReferences.authorize(authorizer, 
lookup, CONNECTOR_ID, configurationStepWithValue(valueReference)));
+
+        verify(lookup, never()).getConnectorAsset(any(), any());
+    }
+
+    @Test
+    void testRejectsEmptyAssetReferences() {
+        
when(lookup.getConnector(eq(CONNECTOR_ID))).thenReturn(connectorAuthorizable);
+
+        final ConnectorValueReferenceDTO valueReference = new 
ConnectorValueReferenceDTO();
+        
valueReference.setValueType(ConnectorValueType.ASSET_REFERENCE.toString());
+        valueReference.setAssetReferences(List.of());
+
+        assertThrows(IllegalArgumentException.class,
+                () -> AuthorizeConnectorConfigReferences.authorize(authorizer, 
lookup, CONNECTOR_ID, configurationStepWithValue(valueReference)));
+
+        verify(lookup, never()).getConnectorAsset(any(), any());
+    }
+
+    @Test
+    void testRejectsNullAssetReferenceId() {
+        
when(lookup.getConnector(eq(CONNECTOR_ID))).thenReturn(connectorAuthorizable);
+
+        final ConnectorValueReferenceDTO valueReference = new 
ConnectorValueReferenceDTO();
+        
valueReference.setValueType(ConnectorValueType.ASSET_REFERENCE.toString());
+        valueReference.setAssetReferences(List.of(new AssetReferenceDTO()));
+
+        assertThrows(IllegalArgumentException.class,
+                () -> AuthorizeConnectorConfigReferences.authorize(authorizer, 
lookup, CONNECTOR_ID, configurationStepWithValue(valueReference)));
+
+        verify(lookup, never()).getConnectorAsset(any(), any());
+    }
+
+    private ConfigurationStepConfigurationDTO configurationStepWithValue(final 
ConnectorValueReferenceDTO valueReference) {
+        final PropertyGroupConfigurationDTO propertyGroup = new 
PropertyGroupConfigurationDTO();
+        propertyGroup.setPropertyGroupName(PROPERTY_GROUP_NAME);
+        propertyGroup.setPropertyValues(Map.of(PROPERTY_NAME, valueReference));
+
+        final ConfigurationStepConfigurationDTO configurationStep = new 
ConfigurationStepConfigurationDTO();
+        
configurationStep.setPropertyGroupConfigurations(List.of(propertyGroup));
+        return configurationStep;
+    }
+
+    private ConnectorValueReferenceDTO secretReference() {
+        final ConnectorValueReferenceDTO valueReference = new 
ConnectorValueReferenceDTO();
+        
valueReference.setValueType(ConnectorValueType.SECRET_REFERENCE.toString());
+        valueReference.setSecretProviderId(SECRET_PROVIDER_ID);
+        valueReference.setSecretProviderName(SECRET_PROVIDER_NAME);
+        valueReference.setSecretName(SECRET_NAME);
+        
valueReference.setFullyQualifiedSecretName(FULLY_QUALIFIED_SECRET_NAME);
+        return valueReference;
+    }
+
+    private ConnectorValueReferenceDTO assetReference() {
+        final ConnectorValueReferenceDTO valueReference = new 
ConnectorValueReferenceDTO();
+        
valueReference.setValueType(ConnectorValueType.ASSET_REFERENCE.toString());
+        valueReference.setAssetReferences(List.of(new 
AssetReferenceDTO(ASSET_ID)));
+        return valueReference;
+    }
+}
diff --git 
a/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/java/org/apache/nifi/authorization/StandardAuthorizableLookupTest.java
 
b/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/java/org/apache/nifi/authorization/StandardAuthorizableLookupTest.java
index b44bc1c13a7..1d8facc2745 100644
--- 
a/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/java/org/apache/nifi/authorization/StandardAuthorizableLookupTest.java
+++ 
b/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/java/org/apache/nifi/authorization/StandardAuthorizableLookupTest.java
@@ -16,32 +16,44 @@
  */
 package org.apache.nifi.authorization;
 
+import org.apache.nifi.asset.Asset;
 import org.apache.nifi.authorization.resource.AccessPolicyAuthorizable;
 import org.apache.nifi.authorization.resource.Authorizable;
 import org.apache.nifi.authorization.resource.DataAuthorizable;
 import org.apache.nifi.authorization.resource.DataTransferAuthorizable;
 import org.apache.nifi.authorization.resource.OperationAuthorizable;
 import org.apache.nifi.authorization.resource.ProvenanceDataAuthorizable;
+import org.apache.nifi.components.connector.ConnectorNode;
+import org.apache.nifi.components.connector.ConnectorSyncMode;
 import org.apache.nifi.connectable.Connectable;
 import org.apache.nifi.connectable.Connection;
 import org.apache.nifi.controller.FlowAnalysisRuleNode;
+import org.apache.nifi.controller.ParameterProviderNode;
 import org.apache.nifi.controller.ProcessorNode;
 import org.apache.nifi.groups.ProcessGroup;
 import org.apache.nifi.nar.ExtensionDiscoveringManager;
 import org.apache.nifi.nar.ExtensionManager;
 import org.apache.nifi.registry.flow.FlowRegistryClientNode;
+import org.apache.nifi.web.ResourceNotFoundException;
 import org.apache.nifi.web.controller.ControllerFacade;
 import org.apache.nifi.web.dao.ConnectionDAO;
+import org.apache.nifi.web.dao.ConnectorDAO;
 import org.apache.nifi.web.dao.ConnectorManagedComponentLookup;
 import org.apache.nifi.web.dao.FlowAnalysisRuleDAO;
 import org.apache.nifi.web.dao.FlowRegistryDAO;
+import org.apache.nifi.web.dao.ParameterProviderDAO;
 import org.apache.nifi.web.dao.ProcessGroupDAO;
 import org.apache.nifi.web.dao.ProcessorDAO;
 import org.junit.jupiter.api.Test;
 
+import java.util.Optional;
+import java.util.Set;
+
 import static org.junit.jupiter.api.Assertions.assertEquals;
 import static org.junit.jupiter.api.Assertions.assertInstanceOf;
 import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertSame;
+import static org.junit.jupiter.api.Assertions.assertThrows;
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.verify;
@@ -50,6 +62,10 @@ import static org.mockito.Mockito.when;
 public class StandardAuthorizableLookupTest {
 
     private static final String COMPONENT_ID = "id";
+    private static final String CONNECTOR_ID = "connector-1";
+    private static final String ASSET_ID = "asset-1";
+    private static final String SECRET_PROVIDER_ID = "parameter-provider-1";
+    private static final String SECRET_PROVIDER_NAME = "Vault Provider";
 
     @Test
     void testGetAuthorizableFromResource() {
@@ -179,6 +195,133 @@ public class StandardAuthorizableLookupTest {
         
verify(connectorManagedComponentLookup).getProcessGroup(eq(COMPONENT_ID));
     }
 
+    @Test
+    void testGetConnectorAssetResolvesOwnedAsset() {
+        final StandardAuthorizableLookup lookup = getLookup();
+        final ConnectorDAO connectorDAO = mock(ConnectorDAO.class);
+        lookup.setConnectorDAO(connectorDAO);
+
+        final Asset asset = mock(Asset.class);
+        when(asset.getOwnerIdentifier()).thenReturn(CONNECTOR_ID);
+        
when(connectorDAO.getAsset(eq(ASSET_ID))).thenReturn(Optional.of(asset));
+
+        final ConnectorNode connectorNode = mock(ConnectorNode.class);
+        when(connectorDAO.getConnector(eq(CONNECTOR_ID), 
eq(ConnectorSyncMode.LOCAL_ONLY))).thenReturn(connectorNode);
+
+        final Authorizable result = lookup.getConnectorAsset(CONNECTOR_ID, 
ASSET_ID);
+
+        assertSame(connectorNode, result);
+    }
+
+    @Test
+    void testGetConnectorAssetRejectsAssetOwnedByAnotherConnector() {
+        final StandardAuthorizableLookup lookup = getLookup();
+        final ConnectorDAO connectorDAO = mock(ConnectorDAO.class);
+        lookup.setConnectorDAO(connectorDAO);
+
+        final Asset asset = mock(Asset.class);
+        when(asset.getOwnerIdentifier()).thenReturn("another-connector");
+        
when(connectorDAO.getAsset(eq(ASSET_ID))).thenReturn(Optional.of(asset));
+
+        assertThrows(ResourceNotFoundException.class, () -> 
lookup.getConnectorAsset(CONNECTOR_ID, ASSET_ID));
+    }
+
+    @Test
+    void testGetConnectorAssetRejectsMissingAsset() {
+        final StandardAuthorizableLookup lookup = getLookup();
+        final ConnectorDAO connectorDAO = mock(ConnectorDAO.class);
+        lookup.setConnectorDAO(connectorDAO);
+
+        when(connectorDAO.getAsset(eq(ASSET_ID))).thenReturn(Optional.empty());
+
+        assertThrows(ResourceNotFoundException.class, () -> 
lookup.getConnectorAsset(CONNECTOR_ID, ASSET_ID));
+    }
+
+    @Test
+    void testGetConnectorSecretProviderResolvesById() {
+        final StandardAuthorizableLookup lookup = getLookup();
+        final ParameterProviderDAO parameterProviderDAO = 
mock(ParameterProviderDAO.class);
+        lookup.setParameterProviderDAO(parameterProviderDAO);
+
+        final ParameterProviderNode provider = 
mock(ParameterProviderNode.class);
+        
when(parameterProviderDAO.getParameterProvider(eq(SECRET_PROVIDER_ID))).thenReturn(provider);
+
+        final Authorizable result = 
lookup.getConnectorSecretProvider(SECRET_PROVIDER_ID, null, null);
+
+        assertSame(provider, result);
+    }
+
+    @Test
+    void testGetConnectorSecretProviderResolvesByName() {
+        final StandardAuthorizableLookup lookup = getLookup();
+        final ParameterProviderDAO parameterProviderDAO = 
mock(ParameterProviderDAO.class);
+        lookup.setParameterProviderDAO(parameterProviderDAO);
+
+        final ParameterProviderNode matchingProvider = 
mock(ParameterProviderNode.class);
+        when(matchingProvider.getName()).thenReturn(SECRET_PROVIDER_NAME);
+        final ParameterProviderNode otherProvider = 
mock(ParameterProviderNode.class);
+        when(otherProvider.getName()).thenReturn("Other Provider");
+        
when(parameterProviderDAO.getParameterProviders()).thenReturn(Set.of(matchingProvider,
 otherProvider));
+
+        final Authorizable result = lookup.getConnectorSecretProvider(null, 
SECRET_PROVIDER_NAME, null);
+
+        assertSame(matchingProvider, result);
+    }
+
+    @Test
+    void testGetConnectorSecretProviderResolvesByFullyQualifiedName() {
+        final StandardAuthorizableLookup lookup = getLookup();
+        final ParameterProviderDAO parameterProviderDAO = 
mock(ParameterProviderDAO.class);
+        lookup.setParameterProviderDAO(parameterProviderDAO);
+
+        final ParameterProviderNode matchingProvider = 
mock(ParameterProviderNode.class);
+        when(matchingProvider.getName()).thenReturn(SECRET_PROVIDER_NAME);
+        
when(parameterProviderDAO.getParameterProviders()).thenReturn(Set.of(matchingProvider));
+
+        final Authorizable result = lookup.getConnectorSecretProvider(null, 
null, SECRET_PROVIDER_NAME + ".api-key");
+
+        assertSame(matchingProvider, result);
+    }
+
+    @Test
+    void testGetConnectorSecretProviderFailsWhenUnresolved() {
+        final StandardAuthorizableLookup lookup = getLookup();
+        final ParameterProviderDAO parameterProviderDAO = 
mock(ParameterProviderDAO.class);
+        lookup.setParameterProviderDAO(parameterProviderDAO);
+        
when(parameterProviderDAO.getParameterProviders()).thenReturn(Set.of());
+
+        assertThrows(ResourceNotFoundException.class, () -> 
lookup.getConnectorSecretProvider(null, "Missing Provider", null));
+    }
+
+    @Test
+    void testGetConnectorSecretProviderMasksMissingProviderId() {
+        final StandardAuthorizableLookup lookup = getLookup();
+        final ParameterProviderDAO parameterProviderDAO = 
mock(ParameterProviderDAO.class);
+        lookup.setParameterProviderDAO(parameterProviderDAO);
+        when(parameterProviderDAO.getParameterProvider(eq(SECRET_PROVIDER_ID)))
+                .thenThrow(new ResourceNotFoundException("Unable to locate 
parameter provider with id '%s'.".formatted(SECRET_PROVIDER_ID)));
+
+        final ResourceNotFoundException exception = 
assertThrows(ResourceNotFoundException.class,
+                () -> lookup.getConnectorSecretProvider(SECRET_PROVIDER_ID, 
null, null));
+
+        assertEquals("The Parameter Provider for the referenced Secret could 
not be found", exception.getMessage());
+    }
+
+    @Test
+    void testGetConnectorSecretProviderFailsWhenMultipleProvidersShareName() {
+        final StandardAuthorizableLookup lookup = getLookup();
+        final ParameterProviderDAO parameterProviderDAO = 
mock(ParameterProviderDAO.class);
+        lookup.setParameterProviderDAO(parameterProviderDAO);
+
+        final ParameterProviderNode firstProvider = 
mock(ParameterProviderNode.class);
+        when(firstProvider.getName()).thenReturn(SECRET_PROVIDER_NAME);
+        final ParameterProviderNode secondProvider = 
mock(ParameterProviderNode.class);
+        when(secondProvider.getName()).thenReturn(SECRET_PROVIDER_NAME);
+        
when(parameterProviderDAO.getParameterProviders()).thenReturn(Set.of(firstProvider,
 secondProvider));
+
+        assertThrows(IllegalArgumentException.class, () -> 
lookup.getConnectorSecretProvider(null, SECRET_PROVIDER_NAME, null));
+    }
+
     private StandardAuthorizableLookup getLookup() {
         final ExtensionManager extensionManager = 
mock(ExtensionDiscoveringManager.class);
         final ControllerFacade controllerFacade = mock(ControllerFacade.class);
diff --git 
a/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/java/org/apache/nifi/web/api/TestConnectorResource.java
 
b/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/java/org/apache/nifi/web/api/TestConnectorResource.java
index b09de3509be..fb32e890f03 100644
--- 
a/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/java/org/apache/nifi/web/api/TestConnectorResource.java
+++ 
b/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/java/org/apache/nifi/web/api/TestConnectorResource.java
@@ -23,8 +23,11 @@ import jakarta.ws.rs.core.UriBuilder;
 import jakarta.ws.rs.core.UriInfo;
 import org.apache.nifi.asset.Asset;
 import org.apache.nifi.authorization.AccessDeniedException;
+import org.apache.nifi.authorization.AuthorizableLookup;
 import org.apache.nifi.authorization.AuthorizeAccess;
 import org.apache.nifi.authorization.Authorizer;
+import org.apache.nifi.authorization.RequestAction;
+import org.apache.nifi.authorization.resource.Authorizable;
 import org.apache.nifi.authorization.user.NiFiUser;
 import org.apache.nifi.authorization.user.NiFiUserDetails;
 import org.apache.nifi.authorization.user.StandardNiFiUser;
@@ -36,17 +39,22 @@ import org.apache.nifi.web.Revision;
 import org.apache.nifi.web.api.dto.AllowableValueDTO;
 import org.apache.nifi.web.api.dto.BacklogDTO;
 import org.apache.nifi.web.api.dto.ComponentStateDTO;
+import org.apache.nifi.web.api.dto.ConfigurationStepConfigurationDTO;
 import org.apache.nifi.web.api.dto.ConnectorDTO;
+import org.apache.nifi.web.api.dto.ConnectorValueReferenceDTO;
 import org.apache.nifi.web.api.dto.MigrationRequestDTO;
 import org.apache.nifi.web.api.dto.MigrationRequestLocalSourceDTO;
 import org.apache.nifi.web.api.dto.ParameterContextDTO;
 import org.apache.nifi.web.api.dto.ParameterDTO;
+import org.apache.nifi.web.api.dto.PropertyGroupConfigurationDTO;
 import org.apache.nifi.web.api.dto.RevisionDTO;
+import org.apache.nifi.web.api.dto.VerifyConnectorConfigStepRequestDTO;
 import org.apache.nifi.web.api.dto.flow.ProcessGroupFlowDTO;
 import org.apache.nifi.web.api.entity.AllowableValueEntity;
 import org.apache.nifi.web.api.entity.BacklogEntity;
 import org.apache.nifi.web.api.entity.BacklogRequestEntity;
 import org.apache.nifi.web.api.entity.ComponentStateEntity;
+import org.apache.nifi.web.api.entity.ConfigurationStepEntity;
 import org.apache.nifi.web.api.entity.ConnectorEntity;
 import org.apache.nifi.web.api.entity.ConnectorPropertyAllowableValuesEntity;
 import org.apache.nifi.web.api.entity.ConnectorRunStatusEntity;
@@ -57,6 +65,7 @@ import org.apache.nifi.web.api.entity.ParameterContextEntity;
 import org.apache.nifi.web.api.entity.ParameterEntity;
 import org.apache.nifi.web.api.entity.ProcessGroupFlowEntity;
 import org.apache.nifi.web.api.entity.SecretsEntity;
+import org.apache.nifi.web.api.entity.VerifyConnectorConfigStepRequestEntity;
 import org.apache.nifi.web.api.entity.VersionedFlowMigrationSourcesEntity;
 import org.apache.nifi.web.api.request.ClientIdParameter;
 import org.apache.nifi.web.api.request.LongParameter;
@@ -78,6 +87,7 @@ import java.net.URI;
 import java.nio.file.Files;
 import java.util.Arrays;
 import java.util.List;
+import java.util.Map;
 import java.util.Optional;
 import java.util.Set;
 import java.util.concurrent.CountDownLatch;
@@ -91,6 +101,7 @@ import static org.junit.jupiter.api.Assertions.fail;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyString;
 import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.doAnswer;
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.doThrow;
 import static org.mockito.Mockito.lenient;
@@ -139,6 +150,7 @@ public class TestConnectorResource {
     private static final String CONFIGURATION_STEP_NAME = "test-step";
     private static final String PROPERTY_GROUP_NAME = "test-group";
     private static final String PROPERTY_NAME = "test-property";
+    private static final String SECRET_PROVIDER_ID = "parameter-provider-1";
     private static final String PROCESS_GROUP_ID = "test-process-group-id";
     private static final String PROCESSOR_ID = "test-processor-id";
     private static final String CONTROLLER_SERVICE_ID = 
"test-controller-service-id";
@@ -543,6 +555,93 @@ public class TestConnectorResource {
         verify(serviceFacade, never()).getSecrets();
     }
 
+    private ConfigurationStepConfigurationDTO 
createConfigurationStepWithSecretReference() {
+        final ConnectorValueReferenceDTO secretReference = new 
ConnectorValueReferenceDTO();
+        secretReference.setValueType("SECRET_REFERENCE");
+        secretReference.setSecretProviderId(SECRET_PROVIDER_ID);
+        secretReference.setSecretName("api-key");
+
+        final PropertyGroupConfigurationDTO propertyGroup = new 
PropertyGroupConfigurationDTO();
+        propertyGroup.setPropertyGroupName(PROPERTY_GROUP_NAME);
+        propertyGroup.setPropertyValues(Map.of(PROPERTY_NAME, 
secretReference));
+
+        final ConfigurationStepConfigurationDTO configurationStep = new 
ConfigurationStepConfigurationDTO();
+        configurationStep.setConfigurationStepName(CONFIGURATION_STEP_NAME);
+        
configurationStep.setPropertyGroupConfigurations(List.of(propertyGroup));
+        return configurationStep;
+    }
+
+    private VerifyConnectorConfigStepRequestEntity 
createVerifyConfigStepRequestEntity() {
+        final VerifyConnectorConfigStepRequestDTO requestDto = new 
VerifyConnectorConfigStepRequestDTO();
+        requestDto.setConnectorId(CONNECTOR_ID);
+        requestDto.setConfigurationStepName(CONFIGURATION_STEP_NAME);
+        
requestDto.setConfigurationStep(createConfigurationStepWithSecretReference());
+
+        final VerifyConnectorConfigStepRequestEntity requestEntity = new 
VerifyConnectorConfigStepRequestEntity();
+        requestEntity.setRequest(requestDto);
+        return requestEntity;
+    }
+
+    private ConfigurationStepEntity createUpdateConfigurationStepEntity() {
+        final RevisionDTO revision = new RevisionDTO();
+        revision.setVersion(1L);
+        revision.setClientId("client-id");
+
+        final ConfigurationStepEntity entity = new ConfigurationStepEntity();
+        entity.setParentConnectorId(CONNECTOR_ID);
+        entity.setParentConnectorRevision(revision);
+        
entity.setConfigurationStep(createConfigurationStepWithSecretReference());
+        return entity;
+    }
+
+    private AuthorizableLookup wireDeniedSecretReferenceAuthorization() {
+        final AuthorizableLookup lookup = mock(AuthorizableLookup.class);
+
+        final Authorizable connector = mock(Authorizable.class);
+        when(lookup.getConnector(CONNECTOR_ID)).thenReturn(connector);
+
+        final Authorizable secretProvider = mock(Authorizable.class);
+        when(lookup.getConnectorSecretProvider(SECRET_PROVIDER_ID, null, 
null)).thenReturn(secretProvider);
+        doThrow(new AccessDeniedException("Not authorized to read the 
referenced secret's Parameter Provider"))
+                .when(secretProvider).authorize(any(), eq(RequestAction.READ), 
any());
+
+        doAnswer(invocation -> {
+            final AuthorizeAccess authorizeAccess = invocation.getArgument(0);
+            authorizeAccess.authorize(lookup);
+            return null;
+        }).when(serviceFacade).authorizeAccess(any(AuthorizeAccess.class));
+
+        return lookup;
+    }
+
+    @Test
+    public void 
testSubmitConfigStepVerificationAuthorizesReferencesBeforeVerification() {
+        authenticate();
+        final AuthorizableLookup lookup = 
wireDeniedSecretReferenceAuthorization();
+
+        final VerifyConnectorConfigStepRequestEntity requestEntity = 
createVerifyConfigStepRequestEntity();
+
+        assertThrows(AccessDeniedException.class,
+                () -> 
connectorResource.submitConfigurationStepVerificationRequest(CONNECTOR_ID, 
CONFIGURATION_STEP_NAME, requestEntity));
+
+        verify(lookup).getConnectorSecretProvider(SECRET_PROVIDER_ID, null, 
null);
+        verify(serviceFacade, 
never()).verifyCanVerifyConnectorConfigurationStep(anyString(), anyString());
+        verify(serviceFacade, 
never()).performConnectorConfigurationStepVerification(anyString(), 
anyString(), any());
+    }
+
+    @Test
+    public void testUpdateConfigStepAuthorizesReferencesBeforeUpdate() {
+        final AuthorizableLookup lookup = 
wireDeniedSecretReferenceAuthorization();
+
+        final ConfigurationStepEntity requestEntity = 
createUpdateConfigurationStepEntity();
+
+        assertThrows(AccessDeniedException.class,
+                () -> 
connectorResource.updateConnectorConfigurationStep(CONNECTOR_ID, 
CONFIGURATION_STEP_NAME, requestEntity));
+
+        verify(lookup).getConnectorSecretProvider(SECRET_PROVIDER_ID, null, 
null);
+        verify(serviceFacade, 
never()).updateConnectorConfigurationStep(any(Revision.class), anyString(), 
anyString(), any());
+    }
+
     @Test
     public void testGetFlow() {
         final ProcessGroupFlowEntity responseEntity = 
createProcessGroupFlowEntity();
diff --git 
a/nifi-system-tests/nifi-system-test-suite/src/test/java/org/apache/nifi/tests/system/connectors/ConnectorAssetsIT.java
 
b/nifi-system-tests/nifi-system-test-suite/src/test/java/org/apache/nifi/tests/system/connectors/ConnectorAssetsIT.java
index e02a3b3e5b0..b509a571475 100644
--- 
a/nifi-system-tests/nifi-system-test-suite/src/test/java/org/apache/nifi/tests/system/connectors/ConnectorAssetsIT.java
+++ 
b/nifi-system-tests/nifi-system-test-suite/src/test/java/org/apache/nifi/tests/system/connectors/ConnectorAssetsIT.java
@@ -171,6 +171,7 @@ public class ConnectorAssetsIT extends NiFiSystemIT {
         }
         assertNotNull(groupWithoutAsset);
 
+        configuredAssetValue.setValueType(null);
         configuredAssetValue.setAssetReferences(null);
         final Map<String, ConnectorValueReferenceDTO> 
propertyValuesWithoutAsset = groupWithoutAsset.getPropertyValues();
         propertyValuesWithoutAsset.put(propertyName, configuredAssetValue);

Reply via email to