This is an automated email from the ASF dual-hosted git repository.

markap14 pushed a commit to branch NIFI-15258
in repository https://gitbox.apache.org/repos/asf/nifi.git

commit 3a33552408ae7d82857e7ea4ce21e40f85b0e197
Author: Matt Gilman <[email protected]>
AuthorDate: Wed Dec 17 14:28:56 2025 -0500

    NIFI-15343: Adding an endpoint to return available secrets to the con… 
(#10645)
    
    * NIFI-15343: Adding an endpoint to return available secrets to the 
connector configuration wizard.
    
    * NIFI-15343: Adding providerId to SecretDTO.
    
    * NIFI-15343: Skipping ParameterProviders that are invalid or validating 
when fetching secrets.
    
    * NIFI-15343: Adding fully qualified name to SecretDTO.
    - Fixing rebase issue.
    
    This closes #10645
---
 .../org/apache/nifi/web/api/dto/SecretDTO.java     | 89 ++++++++++++++++++++++
 .../apache/nifi/web/api/entity/SecretsEntity.java  | 43 +++++++++++
 .../secrets/ParameterProviderSecretsManager.java   |  6 ++
 .../TestParameterProviderSecretsManager.java       | 32 ++++++++
 .../org/apache/nifi/web/NiFiServiceFacade.java     | 13 ++++
 .../apache/nifi/web/StandardNiFiServiceFacade.java | 23 ++++++
 .../org/apache/nifi/web/api/ConnectorResource.java | 48 ++++++++++++
 .../org/apache/nifi/web/api/dto/DtoFactory.java    | 23 ++++++
 .../org/apache/nifi/web/api/dto/EntityFactory.java |  7 ++
 .../nifi/web/controller/ControllerFacade.java      | 10 +++
 .../nifi/web/StandardNiFiServiceFacadeTest.java    | 76 ++++++++++++++++++
 .../apache/nifi/web/api/TestConnectorResource.java | 27 +++++++
 12 files changed, 397 insertions(+)

diff --git 
a/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/dto/SecretDTO.java
 
b/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/dto/SecretDTO.java
new file mode 100644
index 0000000000..dbbf0f36a0
--- /dev/null
+++ 
b/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/dto/SecretDTO.java
@@ -0,0 +1,89 @@
+/*
+ * 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.web.api.dto;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import jakarta.xml.bind.annotation.XmlType;
+
+/**
+ * DTO representing a secret's metadata. Note: The actual secret value is 
never exposed via the REST API.
+ */
+@XmlType(name = "secret")
+public class SecretDTO {
+
+    private String providerId;
+    private String providerName;
+    private String groupName;
+    private String name;
+    private String fullyQualifiedName;
+    private String description;
+
+    @Schema(description = "The identifier of the secret provider that manages 
this secret.")
+    public String getProviderId() {
+        return providerId;
+    }
+
+    public void setProviderId(final String providerId) {
+        this.providerId = providerId;
+    }
+
+    @Schema(description = "The name of the secret provider that manages this 
secret.")
+    public String getProviderName() {
+        return providerName;
+    }
+
+    public void setProviderName(final String providerName) {
+        this.providerName = providerName;
+    }
+
+    @Schema(description = "The name of the group this secret belongs to.")
+    public String getGroupName() {
+        return groupName;
+    }
+
+    public void setGroupName(final String groupName) {
+        this.groupName = groupName;
+    }
+
+    @Schema(description = "The name of the secret.")
+    public String getName() {
+        return name;
+    }
+
+    public void setName(final String name) {
+        this.name = name;
+    }
+
+    @Schema(description = "The fully qualified name of the secret, including 
the group name.")
+    public String getFullyQualifiedName() {
+        return fullyQualifiedName;
+    }
+
+    public void setFullyQualifiedName(final String fullyQualifiedName) {
+        this.fullyQualifiedName = fullyQualifiedName;
+    }
+
+    @Schema(description = "A description of the secret.")
+    public String getDescription() {
+        return description;
+    }
+
+    public void setDescription(final String description) {
+        this.description = description;
+    }
+}
+
diff --git 
a/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/entity/SecretsEntity.java
 
b/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/entity/SecretsEntity.java
new file mode 100644
index 0000000000..0706dde05c
--- /dev/null
+++ 
b/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/entity/SecretsEntity.java
@@ -0,0 +1,43 @@
+/*
+ * 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.web.api.entity;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import jakarta.xml.bind.annotation.XmlRootElement;
+import org.apache.nifi.web.api.dto.SecretDTO;
+
+import java.util.List;
+
+/**
+ * A serialized representation of this class can be placed in the entity body 
of a response to the API.
+ * This particular entity holds a list of secrets.
+ */
+@XmlRootElement(name = "secretsEntity")
+public class SecretsEntity extends Entity {
+
+    private List<SecretDTO> secrets;
+
+    @Schema(description = "The list of secrets available from all secret 
providers.")
+    public List<SecretDTO> getSecrets() {
+        return secrets;
+    }
+
+    public void setSecrets(final List<SecretDTO> secrets) {
+        this.secrets = secrets;
+    }
+}
+
diff --git 
a/nifi-framework-bundle/nifi-framework/nifi-framework-components/src/main/java/org/apache/nifi/components/connector/secrets/ParameterProviderSecretsManager.java
 
b/nifi-framework-bundle/nifi-framework/nifi-framework-components/src/main/java/org/apache/nifi/components/connector/secrets/ParameterProviderSecretsManager.java
index d4ba733b08..0fe3715555 100644
--- 
a/nifi-framework-bundle/nifi-framework/nifi-framework-components/src/main/java/org/apache/nifi/components/connector/secrets/ParameterProviderSecretsManager.java
+++ 
b/nifi-framework-bundle/nifi-framework/nifi-framework-components/src/main/java/org/apache/nifi/components/connector/secrets/ParameterProviderSecretsManager.java
@@ -22,6 +22,8 @@ import org.apache.nifi.components.connector.SecretReference;
 import org.apache.nifi.controller.ParameterProviderNode;
 import org.apache.nifi.controller.flow.FlowManager;
 
+import org.apache.nifi.components.validation.ValidationStatus;
+
 import java.util.ArrayList;
 import java.util.Comparator;
 import java.util.HashMap;
@@ -60,6 +62,10 @@ public class ParameterProviderSecretsManager implements 
SecretsManager {
     public Set<SecretProvider> getSecretProviders() {
         final Set<SecretProvider> providers = new HashSet<>();
         for (final ParameterProviderNode parameterProviderNode : 
flowManager.getAllParameterProviders()) {
+            if (parameterProviderNode.getValidationStatus() != 
ValidationStatus.VALID) {
+                continue;
+            }
+
             providers.add(new 
ParameterProviderSecretProvider(parameterProviderNode));
         }
 
diff --git 
a/nifi-framework-bundle/nifi-framework/nifi-framework-components/src/test/java/org/apache/nifi/components/connector/secrets/TestParameterProviderSecretsManager.java
 
b/nifi-framework-bundle/nifi-framework/nifi-framework-components/src/test/java/org/apache/nifi/components/connector/secrets/TestParameterProviderSecretsManager.java
index b193239ed3..2ccb772f09 100644
--- 
a/nifi-framework-bundle/nifi-framework/nifi-framework-components/src/test/java/org/apache/nifi/components/connector/secrets/TestParameterProviderSecretsManager.java
+++ 
b/nifi-framework-bundle/nifi-framework/nifi-framework-components/src/test/java/org/apache/nifi/components/connector/secrets/TestParameterProviderSecretsManager.java
@@ -18,6 +18,7 @@ package org.apache.nifi.components.connector.secrets;
 
 import org.apache.nifi.components.connector.Secret;
 import org.apache.nifi.components.connector.SecretReference;
+import org.apache.nifi.components.validation.ValidationStatus;
 import org.apache.nifi.controller.ParameterProviderNode;
 import org.apache.nifi.controller.flow.FlowManager;
 import org.apache.nifi.parameter.Parameter;
@@ -85,9 +86,15 @@ public class TestParameterProviderSecretsManager {
     }
 
     private ParameterProviderNode createMockedParameterProviderNode(final 
String id, final String name, final String groupName, final Parameter... 
parameters) {
+        return createMockedParameterProviderNode(id, name, groupName, 
ValidationStatus.VALID, parameters);
+    }
+
+    private ParameterProviderNode createMockedParameterProviderNode(final 
String id, final String name, final String groupName,
+            final ValidationStatus validationStatus, final Parameter... 
parameters) {
         final ParameterProviderNode node = mock(ParameterProviderNode.class);
         when(node.getIdentifier()).thenReturn(id);
         when(node.getName()).thenReturn(name);
+        when(node.getValidationStatus()).thenReturn(validationStatus);
 
         final List<Parameter> parameterList = List.of(parameters);
         final ParameterGroup group = new ParameterGroup(groupName, 
parameterList);
@@ -319,5 +326,30 @@ public class TestParameterProviderSecretsManager {
         assertTrue(result.isPresent());
         assertEquals(PROVIDER_1_NAME, result.get().getProviderName());
     }
+
+    @Test
+    public void testGetSecretProvidersFiltersOutInvalidProviders() {
+        final FlowManager flowManager = mock(FlowManager.class);
+        final ParameterProviderNode validProvider = 
createMockedParameterProviderNode("valid-id", "Valid Provider", "Group",
+            ValidationStatus.VALID, createParameter("secret", "description", 
"value"));
+        final ParameterProviderNode invalidProvider = 
createMockedParameterProviderNode("invalid-id", "Invalid Provider", "Group",
+            ValidationStatus.INVALID, createParameter("secret2", 
"description2", "value2"));
+        final ParameterProviderNode validatingProvider = 
createMockedParameterProviderNode("validating-id", "Validating Provider", 
"Group",
+            ValidationStatus.VALIDATING, createParameter("secret3", 
"description3", "value3"));
+
+        final Set<ParameterProviderNode> allProviders = new HashSet<>();
+        allProviders.add(validProvider);
+        allProviders.add(invalidProvider);
+        allProviders.add(validatingProvider);
+        when(flowManager.getAllParameterProviders()).thenReturn(allProviders);
+
+        final ParameterProviderSecretsManager manager = new 
ParameterProviderSecretsManager();
+        manager.initialize(new 
StandardSecretsManagerInitializationContext(flowManager));
+
+        final Set<SecretProvider> secretProviders = 
manager.getSecretProviders();
+
+        assertEquals(1, secretProviders.size());
+        assertEquals("valid-id", 
secretProviders.iterator().next().getProviderId());
+    }
 }
 
diff --git 
a/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/NiFiServiceFacade.java
 
b/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/NiFiServiceFacade.java
index 7fcef99ee2..7e8a53c8a1 100644
--- 
a/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/NiFiServiceFacade.java
+++ 
b/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/NiFiServiceFacade.java
@@ -161,6 +161,7 @@ import org.apache.nifi.web.api.entity.StatusHistoryEntity;
 import org.apache.nifi.web.api.entity.TenantsEntity;
 import org.apache.nifi.web.api.entity.UserEntity;
 import org.apache.nifi.web.api.entity.UserGroupEntity;
+import org.apache.nifi.web.api.entity.SecretsEntity;
 import org.apache.nifi.web.api.entity.VersionControlComponentMappingEntity;
 import org.apache.nifi.web.api.entity.VersionControlInformationEntity;
 import org.apache.nifi.web.api.entity.VersionedFlowEntity;
@@ -3197,4 +3198,16 @@ public interface NiFiServiceFacade {
      * @return the list of listen Ports accessible to the current user
      */
     Set<ListenPortDTO> getListenPorts(NiFiUser user);
+
+    // ----------------------------------------
+    // Secrets methods
+    // ----------------------------------------
+
+    /**
+     * Gets all secrets available from all secret providers. Note: The actual 
secret values are not included
+     * in the response for security reasons; only metadata is returned.
+     *
+     * @return the secrets entity containing metadata for all available secrets
+     */
+    SecretsEntity getSecrets();
 }
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 6852c9ee7b..6fb12571f2 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
@@ -80,6 +80,8 @@ import org.apache.nifi.components.ValidationResult;
 import org.apache.nifi.components.Validator;
 import org.apache.nifi.components.connector.ConnectorNode;
 import org.apache.nifi.components.connector.ConnectorUpdateContext;
+import org.apache.nifi.components.connector.Secret;
+import org.apache.nifi.components.connector.secrets.AuthorizableSecret;
 import org.apache.nifi.components.state.Scope;
 import org.apache.nifi.components.state.StateMap;
 import org.apache.nifi.components.validation.ValidationState;
@@ -295,6 +297,7 @@ import org.apache.nifi.web.api.dto.ReportingTaskDTO;
 import org.apache.nifi.web.api.dto.RequiredPermissionDTO;
 import org.apache.nifi.web.api.dto.ResourceDTO;
 import org.apache.nifi.web.api.dto.RevisionDTO;
+import org.apache.nifi.web.api.dto.SecretDTO;
 import org.apache.nifi.web.api.dto.SnippetDTO;
 import org.apache.nifi.web.api.dto.SystemDiagnosticsDTO;
 import org.apache.nifi.web.api.dto.TenantDTO;
@@ -392,6 +395,7 @@ import org.apache.nifi.web.api.entity.ConnectorEntity;
 import org.apache.nifi.web.api.entity.ConfigurationStepEntity;
 import org.apache.nifi.web.api.entity.ConfigurationStepNamesEntity;
 import org.apache.nifi.web.api.entity.ScheduleComponentsEntity;
+import org.apache.nifi.web.api.entity.SecretsEntity;
 import org.apache.nifi.web.api.entity.SnippetEntity;
 import org.apache.nifi.web.api.entity.StartVersionControlRequestEntity;
 import org.apache.nifi.web.api.entity.StatusHistoryEntity;
@@ -7773,4 +7777,23 @@ public class StandardNiFiServiceFacade implements 
NiFiServiceFacade {
         final ProcessGroup processGroup = 
processGroupDAO.getProcessGroup(groupId);
         return getComponents.apply(processGroup);
     }
+
+    @Override
+    public SecretsEntity getSecrets() {
+        final NiFiUser user = NiFiUserUtils.getNiFiUser();
+        final List<Secret> secrets = controllerFacade.getAllSecrets();
+        final List<SecretDTO> secretDtos = secrets.stream()
+                .filter(secret -> isSecretAuthorized(secret, user))
+                .map(dtoFactory::createSecretDto)
+                .toList();
+        return entityFactory.createSecretsEntity(secretDtos);
+    }
+
+    private boolean isSecretAuthorized(final Secret secret, final NiFiUser 
user) {
+        if (secret instanceof AuthorizableSecret authorizableSecret) {
+            final AuthorizationResult result = 
authorizableSecret.checkAuthorization(authorizer, RequestAction.READ, user);
+            return Result.Approved.equals(result.getResult());
+        }
+        return true;
+    }
 }
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 ffc3fc264d..e5ad85ea1a 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
@@ -65,6 +65,7 @@ import 
org.apache.nifi.web.api.entity.ConnectorRunStatusEntity;
 import org.apache.nifi.web.api.entity.ProcessGroupFlowEntity;
 import org.apache.nifi.web.api.entity.ProcessGroupStatusEntity;
 import org.apache.nifi.web.api.entity.SearchResultsEntity;
+import org.apache.nifi.web.api.entity.SecretsEntity;
 import org.apache.nifi.web.api.concurrent.AsyncRequestManager;
 import org.apache.nifi.web.api.concurrent.AsynchronousWebRequest;
 import org.apache.nifi.web.api.concurrent.RequestManager;
@@ -313,6 +314,53 @@ public class ConnectorResource extends ApplicationResource 
{
         return generateOkResponse(entity).build();
     }
 
+    /**
+     * Gets all available secrets from the SecretsManager for configuring a 
specific connector.
+     *
+     * @param id The id of the connector being configured
+     * @return A secretsEntity containing metadata for all available secrets.
+     */
+    @GET
+    @Consumes(MediaType.WILDCARD)
+    @Produces(MediaType.APPLICATION_JSON)
+    @Path("/{id}/secrets")
+    @Operation(
+            summary = "Gets all secrets available for configuring a connector",
+            description = "Returns metadata for all secrets available from all 
secret providers. "
+                    + "This endpoint is used when configuring a connector to 
discover available secrets. "
+                    + "Note: Actual secret values are not included in the 
response for security reasons.",
+            responses = {
+                    @ApiResponse(responseCode = "200", content = 
@Content(schema = @Schema(implementation = SecretsEntity.class))),
+                    @ApiResponse(responseCode = "400", description = "NiFi was 
unable to complete the request because it was invalid. The request should not 
be retried without modification."),
+                    @ApiResponse(responseCode = "401", description = "Client 
could not be authenticated."),
+                    @ApiResponse(responseCode = "403", description = "Client 
is not authorized to make this request."),
+                    @ApiResponse(responseCode = "404", description = "The 
specified resource could not be found."),
+                    @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}")
+            }
+    )
+    public Response getSecrets(
+            @Parameter(
+                    description = "The connector id.",
+                    required = true
+            )
+            @PathParam("id") final String id) {
+
+        // NOTE: fetching secrets is handled by the node that receives the 
request and does not need to be replicated
+        // Secrets are sourced from ParameterProviders which should have 
consistent configuration across the cluster
+
+        // authorize access - require write permission on the specific 
connector since this is used for configuration
+        serviceFacade.authorizeAccess(lookup -> {
+            final Authorizable connector = lookup.getConnector(id);
+            connector.authorize(authorizer, RequestAction.WRITE, 
NiFiUserUtils.getNiFiUser());
+        });
+
+        final SecretsEntity entity = serviceFacade.getSecrets();
+        return generateOkResponse(entity).build();
+    }
+
     /**
      * Updates the specified connector.
      *
diff --git 
a/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/dto/DtoFactory.java
 
b/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/dto/DtoFactory.java
index 4d26d4d7ab..e48a59325b 100644
--- 
a/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/dto/DtoFactory.java
+++ 
b/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/dto/DtoFactory.java
@@ -80,6 +80,7 @@ import 
org.apache.nifi.components.connector.ConnectorPropertyGroup;
 import org.apache.nifi.components.connector.ConnectorValueReference;
 import org.apache.nifi.components.connector.FrameworkFlowContext;
 import org.apache.nifi.components.connector.NamedStepConfiguration;
+import org.apache.nifi.components.connector.Secret;
 import org.apache.nifi.components.connector.SecretReference;
 import org.apache.nifi.components.connector.StepConfiguration;
 import org.apache.nifi.components.connector.StringLiteralValue;
@@ -5436,4 +5437,26 @@ public final class DtoFactory {
         return dto;
     }
 
+    /**
+     * Creates a SecretDTO from the specified Secret. Note: The secret value 
is intentionally not included
+     * in the DTO for security reasons.
+     *
+     * @param secret the secret
+     * @return the DTO containing only the secret's metadata
+     */
+    public SecretDTO createSecretDto(final Secret secret) {
+        if (secret == null) {
+            return null;
+        }
+
+        final SecretDTO dto = new SecretDTO();
+        dto.setProviderId(secret.getProviderId());
+        dto.setProviderName(secret.getProviderName());
+        dto.setGroupName(secret.getGroupName());
+        dto.setName(secret.getName());
+        dto.setFullyQualifiedName(secret.getFullyQualifiedName());
+        dto.setDescription(secret.getDescription());
+        return dto;
+    }
+
 }
diff --git 
a/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/dto/EntityFactory.java
 
b/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/dto/EntityFactory.java
index 144f7805fa..9d51b58e65 100644
--- 
a/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/dto/EntityFactory.java
+++ 
b/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/dto/EntityFactory.java
@@ -83,6 +83,7 @@ import 
org.apache.nifi.web.api.entity.RemoteProcessGroupEntity;
 import org.apache.nifi.web.api.entity.RemoteProcessGroupPortEntity;
 import org.apache.nifi.web.api.entity.RemoteProcessGroupStatusEntity;
 import org.apache.nifi.web.api.entity.RemoteProcessGroupStatusSnapshotEntity;
+import org.apache.nifi.web.api.entity.SecretsEntity;
 import org.apache.nifi.web.api.entity.ReportingTaskEntity;
 import org.apache.nifi.web.api.entity.SnippetEntity;
 import org.apache.nifi.web.api.entity.StatusHistoryEntity;
@@ -921,4 +922,10 @@ public final class EntityFactory {
         entity.setAllowableValues(allowableValues);
         return entity;
     }
+
+    public SecretsEntity createSecretsEntity(final List<SecretDTO> secrets) {
+        final SecretsEntity entity = new SecretsEntity();
+        entity.setSecrets(secrets);
+        return entity;
+    }
 }
diff --git 
a/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/controller/ControllerFacade.java
 
b/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/controller/ControllerFacade.java
index c75e827375..63611f6a1f 100644
--- 
a/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/controller/ControllerFacade.java
+++ 
b/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/controller/ControllerFacade.java
@@ -46,6 +46,7 @@ import org.apache.nifi.components.RequiredPermission;
 import org.apache.nifi.components.listen.ListenComponent;
 import org.apache.nifi.components.connector.Connector;
 import org.apache.nifi.components.connector.ConnectorNode;
+import org.apache.nifi.components.connector.Secret;
 import org.apache.nifi.connectable.Connectable;
 import org.apache.nifi.connectable.Connection;
 import org.apache.nifi.connectable.Port;
@@ -2004,6 +2005,15 @@ public class ControllerFacade implements Authorizable {
         return new StandardVersionedReportingTaskImporter(flowController);
     }
 
+    /**
+     * Gets all secrets from the SecretsManager.
+     *
+     * @return list of all secrets available from all secret providers
+     */
+    public List<Secret> getAllSecrets() {
+        return 
flowController.getConnectorRepository().getSecretsManager().getAllSecrets();
+    }
+
     /*
      * setters
      */
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 e5270f871b..016e5220ad 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
@@ -38,6 +38,8 @@ import 
org.apache.nifi.authorization.user.StandardNiFiUser.Builder;
 import org.apache.nifi.components.PropertyDescriptor;
 import org.apache.nifi.components.connector.ConnectorNode;
 import org.apache.nifi.components.connector.FrameworkFlowContext;
+import org.apache.nifi.components.connector.Secret;
+import org.apache.nifi.components.connector.secrets.AuthorizableSecret;
 import org.apache.nifi.controller.Counter;
 import org.apache.nifi.controller.FlowController;
 import org.apache.nifi.controller.ProcessorNode;
@@ -104,6 +106,7 @@ import 
org.apache.nifi.web.api.entity.ClearBulletinsForGroupResultsEntity;
 import org.apache.nifi.web.api.entity.ClearBulletinsResultEntity;
 import org.apache.nifi.web.api.entity.CopyRequestEntity;
 import org.apache.nifi.web.api.entity.CopyResponseEntity;
+import org.apache.nifi.web.api.entity.SecretsEntity;
 import org.apache.nifi.web.api.entity.ProcessGroupEntity;
 import org.apache.nifi.web.api.entity.StatusHistoryEntity;
 import org.apache.nifi.web.api.entity.TenantEntity;
@@ -1703,4 +1706,77 @@ public class StandardNiFiServiceFacadeTest {
         assertNotNull(result);
         assertEquals(0, result.getBulletinsCleared());
     }
+
+    @Test
+    public void testGetSecretsFiltersUnauthorizedSecrets() {
+        final Authentication authentication = new NiFiAuthenticationToken(new 
NiFiUserDetails(new Builder().identity(USER_1).build()));
+        SecurityContextHolder.getContext().setAuthentication(authentication);
+
+        final ControllerFacade controllerFacade = mock(ControllerFacade.class);
+        serviceFacade.setControllerFacade(controllerFacade);
+
+        final AuthorizableSecret authorizedSecret = 
mock(AuthorizableSecret.class);
+        when(authorizedSecret.getProviderName()).thenReturn("provider1");
+        when(authorizedSecret.getGroupName()).thenReturn("group1");
+        when(authorizedSecret.getName()).thenReturn("authorized-secret");
+        when(authorizedSecret.getDescription()).thenReturn("An authorized 
secret");
+        when(authorizedSecret.checkAuthorization(any(Authorizer.class), any(), 
any())).thenReturn(AuthorizationResult.approved());
+
+        final AuthorizableSecret unauthorizedSecret = 
mock(AuthorizableSecret.class);
+        when(unauthorizedSecret.getProviderName()).thenReturn("provider2");
+        when(unauthorizedSecret.getGroupName()).thenReturn("group2");
+        when(unauthorizedSecret.getName()).thenReturn("unauthorized-secret");
+        when(unauthorizedSecret.getDescription()).thenReturn("An unauthorized 
secret");
+        when(unauthorizedSecret.checkAuthorization(any(Authorizer.class), 
any(), any())).thenReturn(AuthorizationResult.denied());
+
+        
when(controllerFacade.getAllSecrets()).thenReturn(List.of(authorizedSecret, 
unauthorizedSecret));
+
+        final SecretsEntity result = serviceFacade.getSecrets();
+
+        assertNotNull(result);
+        assertNotNull(result.getSecrets());
+        assertEquals(1, result.getSecrets().size());
+        assertEquals("authorized-secret", 
result.getSecrets().get(0).getName());
+    }
+
+    @Test
+    public void testGetSecretsWithNonAuthorizableSecrets() {
+        final Authentication authentication = new NiFiAuthenticationToken(new 
NiFiUserDetails(new Builder().identity(USER_1).build()));
+        SecurityContextHolder.getContext().setAuthentication(authentication);
+
+        final ControllerFacade controllerFacade = mock(ControllerFacade.class);
+        serviceFacade.setControllerFacade(controllerFacade);
+
+        final Secret nonAuthorizableSecret = mock(Secret.class);
+        when(nonAuthorizableSecret.getProviderName()).thenReturn("provider1");
+        when(nonAuthorizableSecret.getGroupName()).thenReturn("group1");
+        
when(nonAuthorizableSecret.getName()).thenReturn("non-authorizable-secret");
+        when(nonAuthorizableSecret.getDescription()).thenReturn("A 
non-authorizable secret");
+
+        
when(controllerFacade.getAllSecrets()).thenReturn(List.of(nonAuthorizableSecret));
+
+        final SecretsEntity result = serviceFacade.getSecrets();
+
+        assertNotNull(result);
+        assertNotNull(result.getSecrets());
+        assertEquals(1, result.getSecrets().size());
+        assertEquals("non-authorizable-secret", 
result.getSecrets().get(0).getName());
+    }
+
+    @Test
+    public void testGetSecretsWithEmptyList() {
+        final Authentication authentication = new NiFiAuthenticationToken(new 
NiFiUserDetails(new Builder().identity(USER_1).build()));
+        SecurityContextHolder.getContext().setAuthentication(authentication);
+
+        final ControllerFacade controllerFacade = mock(ControllerFacade.class);
+        serviceFacade.setControllerFacade(controllerFacade);
+
+        when(controllerFacade.getAllSecrets()).thenReturn(List.of());
+
+        final SecretsEntity result = serviceFacade.getSecrets();
+
+        assertNotNull(result);
+        assertNotNull(result.getSecrets());
+        assertTrue(result.getSecrets().isEmpty());
+    }
 }
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 e1093d266d..df21a6935e 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
@@ -35,6 +35,7 @@ import org.apache.nifi.web.api.entity.AllowableValueEntity;
 import org.apache.nifi.web.api.entity.ConnectorEntity;
 import org.apache.nifi.web.api.entity.ConnectorPropertyAllowableValuesEntity;
 import org.apache.nifi.web.api.entity.ConnectorRunStatusEntity;
+import org.apache.nifi.web.api.entity.SecretsEntity;
 import org.apache.nifi.web.api.request.ClientIdParameter;
 import org.apache.nifi.web.api.request.LongParameter;
 import org.junit.jupiter.api.BeforeEach;
@@ -326,6 +327,32 @@ public class TestConnectorResource {
         verify(serviceFacade, 
never()).getConnectorPropertyAllowableValues(anyString(), anyString(), 
anyString(), anyString(), any());
     }
 
+    @Test
+    public void testGetSecrets() {
+        final SecretsEntity responseEntity = new SecretsEntity();
+        responseEntity.setSecrets(List.of());
+
+        when(serviceFacade.getSecrets()).thenReturn(responseEntity);
+
+        try (Response response = connectorResource.getSecrets(CONNECTOR_ID)) {
+            assertEquals(200, response.getStatus());
+            assertEquals(responseEntity, response.getEntity());
+        }
+
+        verify(serviceFacade).authorizeAccess(any(AuthorizeAccess.class));
+        verify(serviceFacade).getSecrets();
+    }
+
+    @Test
+    public void testGetSecretsNotAuthorized() {
+        
doThrow(AccessDeniedException.class).when(serviceFacade).authorizeAccess(any(AuthorizeAccess.class));
+
+        assertThrows(AccessDeniedException.class, () -> 
connectorResource.getSecrets(CONNECTOR_ID));
+
+        verify(serviceFacade).authorizeAccess(any(AuthorizeAccess.class));
+        verify(serviceFacade, never()).getSecrets();
+    }
+
     private ConnectorEntity createConnectorEntity() {
         final ConnectorEntity entity = new ConnectorEntity();
 

Reply via email to