This is an automated email from the ASF dual-hosted git repository.
rfellows pushed a commit to branch NIFI-15258
in repository https://gitbox.apache.org/repos/asf/nifi.git
The following commit(s) were added to refs/heads/NIFI-15258 by this push:
new d2c8b77c51 NIFI-15343: Adding an endpoint to return available secrets
to the con… (#10645)
d2c8b77c51 is described below
commit d2c8b77c51451d5c122dab8bc98c9bdfba0bf3d1
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 aee15d1c45..963ec8e401 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 2d0c650bb5..6a6e575039 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;
@@ -294,6 +296,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;
@@ -391,6 +394,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;
@@ -7765,4 +7769,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 7baadec9c5..9f46284f1b 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 cd748204af..a8ca5ab8c3 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 e3bd3f67dd..65b16e4e69 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
@@ -39,6 +39,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;
@@ -105,6 +107,7 @@ import
org.apache.nifi.web.api.entity.ClearBulletinsResultEntity;
import org.apache.nifi.web.api.entity.ClearBulletinsForGroupResultsEntity;
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;
@@ -1700,4 +1703,77 @@ public class StandardNiFiServiceFacadeTest {
serviceFacade.clearBulletinsForComponents(processGroupId,
fromTimestamp, emptyComponentIds);
});
}
+
+ @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();