From 29406ed74e3c8d450c98ca10610f134c8d151ac8 Mon Sep 17 00:00:00 2001 From: George Lungley <54810125+glungley@users.noreply.github.com> Date: Wed, 1 Dec 2021 15:33:52 +0000 Subject: [PATCH] Resolves #4644 - Add negative filter support in secretsmanager (#4645) --- moto/secretsmanager/list_secrets/filters.py | 80 +++++++++---------- .../test_secretsmanager/test_list_secrets.py | 18 +++++ 2 files changed, 58 insertions(+), 40 deletions(-) diff --git a/moto/secretsmanager/list_secrets/filters.py b/moto/secretsmanager/list_secrets/filters.py index c888ebe64..bf13cafdf 100644 --- a/moto/secretsmanager/list_secrets/filters.py +++ b/moto/secretsmanager/list_secrets/filters.py @@ -1,44 +1,44 @@ -def _matcher(pattern, str): +def name(secret, names): + return _matcher(names, [secret.name]) + + +def description(secret, descriptions): + return _matcher(descriptions, [secret.description]) + + +def tag_key(secret, tag_keys): + return _matcher(tag_keys, [tag["Key"] for tag in secret.tags]) + + +def tag_value(secret, tag_values): + return _matcher(tag_values, [tag["Value"] for tag in secret.tags]) + + +def all(secret, values): + attributes = ( + [secret.name, secret.description] + + [tag["Key"] for tag in secret.tags] + + [tag["Value"] for tag in secret.tags] + ) + + return _matcher(values, attributes) + + +def _matcher(patterns, strings): + for pattern in [p for p in patterns if p.startswith("!")]: + for string in strings: + if _match_pattern(pattern[1:], string): + return False + + for pattern in [p for p in patterns if not p.startswith("!")]: + for string in strings: + if _match_pattern(pattern, string): + return True + return False + + +def _match_pattern(pattern, str): for word in pattern.split(" "): if word not in str: return False return True - - -def name(secret, names): - for n in names: - if _matcher(n, secret.name): - return True - return False - - -def description(secret, descriptions): - for d in descriptions: - if _matcher(d, secret.description): - return True - return False - - -def tag_key(secret, tag_keys): - for k in tag_keys: - for tag in secret.tags: - if _matcher(k, tag["Key"]): - return True - return False - - -def tag_value(secret, tag_values): - for v in tag_values: - for tag in secret.tags: - if _matcher(v, tag["Value"]): - return True - return False - - -def all(secret, values): - return ( - name(secret, values) - or description(secret, values) - or tag_key(secret, values) - or tag_value(secret, values) - ) diff --git a/tests/test_secretsmanager/test_list_secrets.py b/tests/test_secretsmanager/test_list_secrets.py index 8bf9552b1..7031d64d2 100644 --- a/tests/test_secretsmanager/test_list_secrets.py +++ b/tests/test_secretsmanager/test_list_secrets.py @@ -244,3 +244,21 @@ def test_with_filter_with_value_with_multiple_words(): secret_names = list(map(lambda s: s["Name"], secrets["SecretList"])) assert secret_names == ["foo", "bar"] + + +@mock_secretsmanager +def test_with_filter_with_negation(): + conn = boto_client() + + conn.create_secret(Name="foo", SecretString="secret", Description="one two") + conn.create_secret(Name="bar", SecretString="secret", Description="one and two") + conn.create_secret(Name="baz", SecretString="secret", Description="one") + conn.create_secret(Name="qux", SecretString="secret", Description="two") + conn.create_secret(Name="none", SecretString="secret", Description="unrelated") + + secrets = conn.list_secrets( + Filters=[{"Key": "description", "Values": ["one", "!two"]}] + ) + + secret_names = list(map(lambda s: s["Name"], secrets["SecretList"])) + assert secret_names == ["baz"]