SecretsManager - deal with partial ARNs (#6127)
This commit is contained in:
parent
5c2fc55ea1
commit
638171a9e1
@ -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):
|
||||
|
@ -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):
|
||||
|
@ -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():
|
||||
|
Loading…
x
Reference in New Issue
Block a user