Skip to content

Commit c55be0d

Browse files
authored
NIFI-15556 Add support for Parameter Tags to AWS Secrets Manager Parameter Provider (#10859)
Signed-off-by: David Handermann <exceptionfactory@apache.org>
1 parent 1c61b54 commit c55be0d

File tree

2 files changed

+266
-49
lines changed

2 files changed

+266
-49
lines changed

nifi-extension-bundles/nifi-aws-bundle/nifi-aws-parameter-providers/src/main/java/org/apache/nifi/parameter/aws/AwsSecretsManagerParameterProvider.java

Lines changed: 66 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
import org.apache.nifi.parameter.AbstractParameterProvider;
3232
import org.apache.nifi.parameter.Parameter;
3333
import org.apache.nifi.parameter.ParameterGroup;
34+
import org.apache.nifi.parameter.ParameterTag;
3435
import org.apache.nifi.parameter.VerifiableParameterProvider;
3536
import org.apache.nifi.processor.util.StandardValidators;
3637
import org.apache.nifi.processors.aws.credentials.provider.AwsCredentialsProviderService;
@@ -45,23 +46,24 @@
4546
import software.amazon.awssdk.regions.Region;
4647
import software.amazon.awssdk.retries.DefaultRetryStrategy;
4748
import software.amazon.awssdk.services.secretsmanager.SecretsManagerClient;
49+
import software.amazon.awssdk.services.secretsmanager.model.DescribeSecretRequest;
50+
import software.amazon.awssdk.services.secretsmanager.model.DescribeSecretResponse;
4851
import software.amazon.awssdk.services.secretsmanager.model.GetSecretValueRequest;
4952
import software.amazon.awssdk.services.secretsmanager.model.GetSecretValueResponse;
5053
import software.amazon.awssdk.services.secretsmanager.model.ListSecretsRequest;
5154
import software.amazon.awssdk.services.secretsmanager.model.ListSecretsResponse;
5255
import software.amazon.awssdk.services.secretsmanager.model.ResourceNotFoundException;
5356
import software.amazon.awssdk.services.secretsmanager.model.SecretListEntry;
5457
import software.amazon.awssdk.services.secretsmanager.model.SecretsManagerException;
58+
import software.amazon.awssdk.services.secretsmanager.model.Tag;
5559

5660
import java.time.Duration;
5761
import java.util.ArrayList;
58-
import java.util.Arrays;
5962
import java.util.Comparator;
60-
import java.util.HashSet;
63+
import java.util.HashMap;
6164
import java.util.List;
6265
import java.util.Map;
6366
import java.util.Optional;
64-
import java.util.Set;
6567
import java.util.regex.Pattern;
6668
import javax.net.ssl.KeyManager;
6769
import javax.net.ssl.TrustManager;
@@ -77,7 +79,7 @@
7779
"key/value pairs in the secret mapping to Parameters in the group.")
7880
public class AwsSecretsManagerParameterProvider extends AbstractParameterProvider implements VerifiableParameterProvider {
7981
enum ListingStrategy implements DescribedValue {
80-
ENUMERATION("Enumerate Secret Names", "Requires a set of secret names to fetch. AWS actions required: GetSecretValue."),
82+
ENUMERATION("Enumerate Secret Names", "Requires a set of secret names to fetch. AWS actions required: DescribeSecret and GetSecretValue."),
8183

8284
PATTERN("Match Pattern", "Requires a regular expression pattern to match secret names. AWS actions required: ListSecrets and GetSecretValue.");
8385

@@ -196,10 +198,16 @@ public List<ParameterGroup> fetchParameters(final ConfigurationContext context)
196198

197199
// Fetch either by pattern or by enumerated list. See description of SECRET_LISTING_STRATEGY for more details.
198200
final ListingStrategy listingStrategy = context.getProperty(SECRET_LISTING_STRATEGY).asAllowableValue(ListingStrategy.class);
199-
final Set<String> fetchSecretNames = new HashSet<>();
201+
final Map<String, List<Tag>> secretNameToTags = new HashMap<>();
202+
200203
if (listingStrategy == ListingStrategy.ENUMERATION) {
201204
final String secretNames = context.getProperty(SECRET_NAMES).getValue();
202-
fetchSecretNames.addAll(Arrays.asList(secretNames.split(",")));
205+
for (final String secretName : secretNames.split(",")) {
206+
final String trimmedName = secretName.trim();
207+
// For enumeration strategy, we need to call DescribeSecret to get tags
208+
final List<Tag> tags = describeSecretTags(secretsManager, trimmedName);
209+
secretNameToTags.put(trimmedName, tags);
210+
}
203211
} else {
204212
final Pattern secretNamePattern = Pattern.compile(context.getProperty(SECRET_NAME_PATTERN).getValue());
205213

@@ -212,7 +220,9 @@ public List<ParameterGroup> fetchParameters(final ConfigurationContext context)
212220
getLogger().debug("Secret [{}] does not match the secret name pattern {}", secretName, secretNamePattern);
213221
continue;
214222
}
215-
fetchSecretNames.add(secretName);
223+
// ListSecrets response includes tags, so we can use them directly
224+
final List<Tag> tags = entry.hasTags() ? entry.tags() : List.of();
225+
secretNameToTags.put(secretName, tags);
216226
}
217227
final String nextToken = listSecretsResponse.nextToken();
218228
if (nextToken == null) {
@@ -223,8 +233,10 @@ public List<ParameterGroup> fetchParameters(final ConfigurationContext context)
223233
}
224234
}
225235

226-
for (final String secretName : fetchSecretNames) {
227-
final List<ParameterGroup> secretParameterGroups = fetchSecret(secretsManager, secretName);
236+
for (final Map.Entry<String, List<Tag>> entry : secretNameToTags.entrySet()) {
237+
final String secretName = entry.getKey();
238+
final List<ParameterTag> parameterTags = convertTags(entry.getValue());
239+
final List<ParameterGroup> secretParameterGroups = fetchSecret(secretsManager, secretName, parameterTags);
228240
groups.addAll(secretParameterGroups);
229241
}
230242
return groups;
@@ -257,7 +269,8 @@ public List<ConfigVerificationResult> verify(final ConfigurationContext context,
257269
return results;
258270
}
259271

260-
private List<ParameterGroup> fetchSecret(final SecretsManagerClient secretsManager, final String secretName) {
272+
private List<ParameterGroup> fetchSecret(final SecretsManagerClient secretsManager, final String secretName,
273+
final List<ParameterTag> tags) {
261274
final List<ParameterGroup> groups = new ArrayList<>();
262275

263276
final List<Parameter> parameters = new ArrayList<>();
@@ -289,7 +302,7 @@ private List<ParameterGroup> fetchSecret(final SecretsManagerClient secretsManag
289302
}
290303
final String parameterValue = valueNode.asText();
291304

292-
parameters.add(createParameter(parameterName, parameterValue));
305+
parameters.add(createParameter(parameterName, parameterValue, tags));
293306
}
294307

295308
groups.add(new ParameterGroup(secretName, parameters));
@@ -302,14 +315,55 @@ private List<ParameterGroup> fetchSecret(final SecretsManagerClient secretsManag
302315
}
303316
}
304317

305-
private Parameter createParameter(final String parameterName, final String parameterValue) {
318+
private Parameter createParameter(final String parameterName, final String parameterValue,
319+
final List<ParameterTag> tags) {
306320
return new Parameter.Builder()
307321
.name(parameterName)
308322
.value(parameterValue)
309323
.provided(true)
324+
.tags(tags)
310325
.build();
311326
}
312327

328+
/**
329+
* Retrieves tags for a secret using DescribeSecret API.
330+
* This is needed for the ENUMERATION strategy since GetSecretValue does not return tags.
331+
*
332+
* @param secretsManager the Secrets Manager client
333+
* @param secretName the name of the secret
334+
* @return list of tags associated with the secret
335+
*/
336+
private List<Tag> describeSecretTags(final SecretsManagerClient secretsManager, final String secretName) {
337+
try {
338+
final DescribeSecretRequest describeRequest = DescribeSecretRequest.builder()
339+
.secretId(secretName)
340+
.build();
341+
final DescribeSecretResponse describeResponse = secretsManager.describeSecret(describeRequest);
342+
return describeResponse.hasTags() ? describeResponse.tags() : List.of();
343+
} catch (final ResourceNotFoundException e) {
344+
getLogger().debug("Secret [{}] not found when describing for tags", secretName);
345+
return List.of();
346+
} catch (final SecretsManagerException e) {
347+
getLogger().warn("Error describing secret [{}] for tags: {}", secretName, e.getMessage());
348+
return List.of();
349+
}
350+
}
351+
352+
/**
353+
* Converts AWS Secrets Manager Tags to NiFi ParameterTags.
354+
*
355+
* @param awsTags the AWS tags to convert
356+
* @return list of NiFi ParameterTag objects
357+
*/
358+
private List<ParameterTag> convertTags(final List<Tag> awsTags) {
359+
if (awsTags == null || awsTags.isEmpty()) {
360+
return List.of();
361+
}
362+
return awsTags.stream()
363+
.map(tag -> new ParameterTag(tag.key(), tag.value()))
364+
.toList();
365+
}
366+
313367
private ClientOverrideConfiguration createConfiguration() {
314368
return ClientOverrideConfiguration.builder()
315369
.retryStrategy(DefaultRetryStrategy.doNotRetry())

0 commit comments

Comments
 (0)