SecretsManager - deal with partial ARNs (#6127)

This commit is contained in:
Bert Blommers 2023-03-25 11:00:17 -01:00 committed by GitHub
parent 5c2fc55ea1
commit 638171a9e1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 50 additions and 23 deletions

View File

@ -16,7 +16,7 @@ from .exceptions import (
InvalidRequestException,
ClientError,
)
from .utils import random_password, secret_arn, get_secret_name_from_arn
from .utils import random_password, secret_arn, get_secret_name_from_partial_arn
from .list_secrets.filters import (
filter_all,
tag_key,
@ -176,25 +176,40 @@ class FakeSecret:
class SecretsStore(dict):
# Parameters to this dictionary can be three possible values:
# names, full ARNs, and partial ARNs
# Every retrieval method should check which type of input it receives
def __setitem__(self, key, value):
new_key = get_secret_name_from_arn(key)
super().__setitem__(new_key, value)
super().__setitem__(key, value)
def __getitem__(self, key):
new_key = get_secret_name_from_arn(key)
return super().__getitem__(new_key)
for secret in dict.values(self):
if secret.arn == key or secret.name == key:
return secret
name = get_secret_name_from_partial_arn(key)
return super().__getitem__(name)
def __contains__(self, key):
new_key = get_secret_name_from_arn(key)
return dict.__contains__(self, new_key)
for secret in dict.values(self):
if secret.arn == key or secret.name == key:
return True
name = get_secret_name_from_partial_arn(key)
return dict.__contains__(self, name)
def get(self, key, *args, **kwargs):
new_key = get_secret_name_from_arn(key)
return super().get(new_key, *args, **kwargs)
for secret in dict.values(self):
if secret.arn == key or secret.name == key:
return secret
name = get_secret_name_from_partial_arn(key)
return super().get(name, *args, **kwargs)
def pop(self, key, *args, **kwargs):
new_key = get_secret_name_from_arn(key)
return super().pop(new_key, *args, **kwargs)
for secret in dict.values(self):
if secret.arn == key or secret.name == key:
key = secret.name
name = get_secret_name_from_partial_arn(key)
return super().pop(name, *args, **kwargs)
class SecretsManagerBackend(BaseBackend):

View File

@ -68,17 +68,20 @@ def secret_arn(account_id, region, secret_id):
)
def get_secret_name_from_arn(secret_id):
# can fetch by both arn and by name
# but we are storing via name
# so we need to change the arn to name
# if it starts with arn then the secret id is arn
if secret_id.startswith("arn:aws:secretsmanager:"):
def get_secret_name_from_partial_arn(partial_arn: str) -> str:
# We can retrieve a secret either using a full ARN, or using a partial ARN
# name: testsecret
# full ARN: arn:aws:secretsmanager:us-west-2:123456789012:secret:testsecret-xxxxxx
# partial ARN: arn:aws:secretsmanager:us-west-2:123456789012:secret:testsecret
#
# This method only deals with partial ARN's, and will return the name: testsecret
#
# If you were to pass in full url, this method will return 'testsecret-xxxxxx' - which has no meaning on it's own
if partial_arn.startswith("arn:aws:secretsmanager:"):
# split the arn by colon
# then get the last value which is the name appended with a random string
# then remove the random string
secret_id = "-".join(secret_id.split(":")[-1].split("-")[:-1])
return secret_id
return partial_arn.split(":")[-1]
return partial_arn
def _exclude_characters(password, exclude_characters):

View File

@ -547,17 +547,26 @@ def test_describe_secret():
@mock_secretsmanager
def test_describe_secret_with_arn():
@pytest.mark.parametrize("name", ["testsecret", "test-secret"])
def test_describe_secret_with_arn(name):
conn = boto3.client("secretsmanager", region_name="us-west-2")
results = conn.create_secret(Name="test-secret", SecretString="foosecret")
results = conn.create_secret(Name=name, SecretString="foosecret")
secret_description = conn.describe_secret(SecretId=results["ARN"])
assert secret_description # Returned dict is not empty
secret_description["Name"].should.equal("test-secret")
secret_description["Name"].should.equal(name)
secret_description["ARN"].should.equal(results["ARN"])
conn.list_secrets()["SecretList"][0]["ARN"].should.equal(results["ARN"])
# We can also supply a partial ARN
partial_arn = f"arn:aws:secretsmanager:us-west-2:{ACCOUNT_ID}:secret:{name}"
resp = conn.get_secret_value(SecretId=partial_arn)
assert resp["Name"] == name
resp = conn.describe_secret(SecretId=partial_arn)
assert resp["Name"] == name
@mock_secretsmanager
def test_describe_secret_with_KmsKeyId():