SecretsManager - deal with partial ARNs (#6127)
This commit is contained in:
parent
5c2fc55ea1
commit
638171a9e1
@ -16,7 +16,7 @@ from .exceptions import (
|
|||||||
InvalidRequestException,
|
InvalidRequestException,
|
||||||
ClientError,
|
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 (
|
from .list_secrets.filters import (
|
||||||
filter_all,
|
filter_all,
|
||||||
tag_key,
|
tag_key,
|
||||||
@ -176,25 +176,40 @@ class FakeSecret:
|
|||||||
|
|
||||||
|
|
||||||
class SecretsStore(dict):
|
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):
|
def __setitem__(self, key, value):
|
||||||
new_key = get_secret_name_from_arn(key)
|
super().__setitem__(key, value)
|
||||||
super().__setitem__(new_key, value)
|
|
||||||
|
|
||||||
def __getitem__(self, key):
|
def __getitem__(self, key):
|
||||||
new_key = get_secret_name_from_arn(key)
|
for secret in dict.values(self):
|
||||||
return super().__getitem__(new_key)
|
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):
|
def __contains__(self, key):
|
||||||
new_key = get_secret_name_from_arn(key)
|
for secret in dict.values(self):
|
||||||
return dict.__contains__(self, new_key)
|
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):
|
def get(self, key, *args, **kwargs):
|
||||||
new_key = get_secret_name_from_arn(key)
|
for secret in dict.values(self):
|
||||||
return super().get(new_key, *args, **kwargs)
|
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):
|
def pop(self, key, *args, **kwargs):
|
||||||
new_key = get_secret_name_from_arn(key)
|
for secret in dict.values(self):
|
||||||
return super().pop(new_key, *args, **kwargs)
|
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):
|
class SecretsManagerBackend(BaseBackend):
|
||||||
|
@ -68,17 +68,20 @@ def secret_arn(account_id, region, secret_id):
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def get_secret_name_from_arn(secret_id):
|
def get_secret_name_from_partial_arn(partial_arn: str) -> str:
|
||||||
# can fetch by both arn and by name
|
# We can retrieve a secret either using a full ARN, or using a partial ARN
|
||||||
# but we are storing via name
|
# name: testsecret
|
||||||
# so we need to change the arn to name
|
# full ARN: arn:aws:secretsmanager:us-west-2:123456789012:secret:testsecret-xxxxxx
|
||||||
# if it starts with arn then the secret id is arn
|
# partial ARN: arn:aws:secretsmanager:us-west-2:123456789012:secret:testsecret
|
||||||
if secret_id.startswith("arn:aws:secretsmanager:"):
|
#
|
||||||
|
# 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
|
# split the arn by colon
|
||||||
# then get the last value which is the name appended with a random string
|
# then get the last value which is the name appended with a random string
|
||||||
# then remove the random string
|
return partial_arn.split(":")[-1]
|
||||||
secret_id = "-".join(secret_id.split(":")[-1].split("-")[:-1])
|
return partial_arn
|
||||||
return secret_id
|
|
||||||
|
|
||||||
|
|
||||||
def _exclude_characters(password, exclude_characters):
|
def _exclude_characters(password, exclude_characters):
|
||||||
|
@ -547,17 +547,26 @@ def test_describe_secret():
|
|||||||
|
|
||||||
|
|
||||||
@mock_secretsmanager
|
@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")
|
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"])
|
secret_description = conn.describe_secret(SecretId=results["ARN"])
|
||||||
|
|
||||||
assert secret_description # Returned dict is not empty
|
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"])
|
secret_description["ARN"].should.equal(results["ARN"])
|
||||||
conn.list_secrets()["SecretList"][0]["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
|
@mock_secretsmanager
|
||||||
def test_describe_secret_with_KmsKeyId():
|
def test_describe_secret_with_KmsKeyId():
|
||||||
|
Loading…
x
Reference in New Issue
Block a user