SSOAdmin: Add customer managed policy functionality (#7186)
This commit is contained in:
parent
3ae1b62590
commit
c2139a450b
@ -70,6 +70,7 @@ class PermissionSet(BaseModel):
|
|||||||
self.created_date = unix_time()
|
self.created_date = unix_time()
|
||||||
self.inline_policy = ""
|
self.inline_policy = ""
|
||||||
self.managed_policies: List[ManagedPolicy] = list()
|
self.managed_policies: List[ManagedPolicy] = list()
|
||||||
|
self.customer_managed_policies: List[CustomerManagedPolicy] = list()
|
||||||
self.total_managed_policies_attached = (
|
self.total_managed_policies_attached = (
|
||||||
0 # this will also include customer managed policies
|
0 # this will also include customer managed policies
|
||||||
)
|
)
|
||||||
@ -107,6 +108,17 @@ class ManagedPolicy(BaseModel):
|
|||||||
return self.arn == other.arn
|
return self.arn == other.arn
|
||||||
|
|
||||||
|
|
||||||
|
class CustomerManagedPolicy(BaseModel):
|
||||||
|
def __init__(self, name: str, path: str = "/"):
|
||||||
|
self.name = name
|
||||||
|
self.path = path
|
||||||
|
|
||||||
|
def __eq__(self, other: Any) -> bool:
|
||||||
|
if not isinstance(other, CustomerManagedPolicy):
|
||||||
|
return False
|
||||||
|
return f"{self.path}{self.name}" == f"{other.path}{other.name}"
|
||||||
|
|
||||||
|
|
||||||
class SSOAdminBackend(BaseBackend):
|
class SSOAdminBackend(BaseBackend):
|
||||||
"""Implementation of SSOAdmin APIs."""
|
"""Implementation of SSOAdmin APIs."""
|
||||||
|
|
||||||
@ -424,5 +436,81 @@ class SSOAdminBackend(BaseBackend):
|
|||||||
instance_arn, permission_set_arn, managed_policy_arn
|
instance_arn, permission_set_arn, managed_policy_arn
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def attach_customer_managed_policy_reference_to_permission_set(
|
||||||
|
self,
|
||||||
|
instance_arn: str,
|
||||||
|
permission_set_arn: str,
|
||||||
|
customer_managed_policy_reference: Dict[str, str],
|
||||||
|
) -> None:
|
||||||
|
permissionset = self._find_permission_set(
|
||||||
|
permission_set_arn=permission_set_arn, instance_arn=instance_arn
|
||||||
|
)
|
||||||
|
|
||||||
|
name = customer_managed_policy_reference["Name"]
|
||||||
|
path = customer_managed_policy_reference.get("Path", "/") # default path is "/"
|
||||||
|
customer_managed_policy = CustomerManagedPolicy(name=name, path=path)
|
||||||
|
|
||||||
|
if customer_managed_policy in permissionset.customer_managed_policies:
|
||||||
|
raise ConflictException(
|
||||||
|
f"Given customer managed policy with name: {name} and path {path} already attached"
|
||||||
|
)
|
||||||
|
|
||||||
|
if (
|
||||||
|
permissionset.total_managed_policies_attached
|
||||||
|
>= MAX_MANAGED_POLICIES_PER_PERMISSION_SET
|
||||||
|
):
|
||||||
|
raise ServiceQuotaExceededException(
|
||||||
|
f"Cannot attach managed policy: number of attached managed policies is already at maximum {MAX_MANAGED_POLICIES_PER_PERMISSION_SET}"
|
||||||
|
)
|
||||||
|
|
||||||
|
permissionset.customer_managed_policies.append(customer_managed_policy)
|
||||||
|
permissionset.total_managed_policies_attached += 1
|
||||||
|
|
||||||
|
@paginate(pagination_model=PAGINATION_MODEL) # type: ignore[misc]
|
||||||
|
def list_customer_managed_policy_references_in_permission_set(
|
||||||
|
self, instance_arn: str, permission_set_arn: str
|
||||||
|
) -> List[CustomerManagedPolicy]:
|
||||||
|
permissionset = self._find_permission_set(
|
||||||
|
permission_set_arn=permission_set_arn, instance_arn=instance_arn
|
||||||
|
)
|
||||||
|
return permissionset.customer_managed_policies
|
||||||
|
|
||||||
|
def _detach_customer_managed_policy_from_permissionset(
|
||||||
|
self,
|
||||||
|
instance_arn: str,
|
||||||
|
permission_set_arn: str,
|
||||||
|
customer_managed_policy_reference: Dict[str, str],
|
||||||
|
) -> None:
|
||||||
|
permissionset = self._find_permission_set(
|
||||||
|
permission_set_arn=permission_set_arn, instance_arn=instance_arn
|
||||||
|
)
|
||||||
|
path: str = customer_managed_policy_reference.get("Path", "/")
|
||||||
|
name: str = customer_managed_policy_reference["Name"]
|
||||||
|
|
||||||
|
for customer_managed_policy in permissionset.customer_managed_policies:
|
||||||
|
if (
|
||||||
|
customer_managed_policy.name == name
|
||||||
|
and customer_managed_policy.path == path
|
||||||
|
):
|
||||||
|
permissionset.customer_managed_policies.remove(customer_managed_policy)
|
||||||
|
permissionset.total_managed_policies_attached -= 1
|
||||||
|
return
|
||||||
|
|
||||||
|
raise ResourceNotFoundException(
|
||||||
|
f"Given managed policy with name: {name} and path {path} does not exist on PermissionSet"
|
||||||
|
)
|
||||||
|
|
||||||
|
def detach_customer_managed_policy_reference_from_permission_set(
|
||||||
|
self,
|
||||||
|
instance_arn: str,
|
||||||
|
permission_set_arn: str,
|
||||||
|
customer_managed_policy_reference: Dict[str, str],
|
||||||
|
) -> None:
|
||||||
|
self._detach_customer_managed_policy_from_permissionset(
|
||||||
|
instance_arn=instance_arn,
|
||||||
|
permission_set_arn=permission_set_arn,
|
||||||
|
customer_managed_policy_reference=customer_managed_policy_reference,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
ssoadmin_backends = BackendDict(SSOAdminBackend, "sso")
|
ssoadmin_backends = BackendDict(SSOAdminBackend, "sso")
|
||||||
|
@ -244,3 +244,59 @@ class SSOAdminResponse(BaseResponse):
|
|||||||
managed_policy_arn=managed_policy_arn,
|
managed_policy_arn=managed_policy_arn,
|
||||||
)
|
)
|
||||||
return json.dumps({})
|
return json.dumps({})
|
||||||
|
|
||||||
|
def attach_customer_managed_policy_reference_to_permission_set(self) -> str:
|
||||||
|
instance_arn = self._get_param("InstanceArn")
|
||||||
|
permission_set_arn = self._get_param("PermissionSetArn")
|
||||||
|
customer_managed_policy_reference = self._get_param(
|
||||||
|
"CustomerManagedPolicyReference"
|
||||||
|
)
|
||||||
|
self.ssoadmin_backend.attach_customer_managed_policy_reference_to_permission_set(
|
||||||
|
instance_arn=instance_arn,
|
||||||
|
permission_set_arn=permission_set_arn,
|
||||||
|
customer_managed_policy_reference=customer_managed_policy_reference,
|
||||||
|
)
|
||||||
|
return json.dumps({})
|
||||||
|
|
||||||
|
def list_customer_managed_policy_references_in_permission_set(self) -> str:
|
||||||
|
instance_arn = self._get_param("InstanceArn")
|
||||||
|
permission_set_arn = self._get_param("PermissionSetArn")
|
||||||
|
max_results = self._get_int_param("MaxResults")
|
||||||
|
next_token = self._get_param("NextToken")
|
||||||
|
|
||||||
|
(
|
||||||
|
customer_managed_policy_references,
|
||||||
|
next_token,
|
||||||
|
) = self.ssoadmin_backend.list_customer_managed_policy_references_in_permission_set(
|
||||||
|
instance_arn=instance_arn,
|
||||||
|
permission_set_arn=permission_set_arn,
|
||||||
|
max_results=max_results,
|
||||||
|
next_token=next_token,
|
||||||
|
)
|
||||||
|
|
||||||
|
customer_managed_policy_references_response = [
|
||||||
|
{
|
||||||
|
"Name": customer_managed_policy_reference.name,
|
||||||
|
"Path": customer_managed_policy_reference.path,
|
||||||
|
}
|
||||||
|
for customer_managed_policy_reference in customer_managed_policy_references
|
||||||
|
]
|
||||||
|
return json.dumps(
|
||||||
|
{
|
||||||
|
"CustomerManagedPolicyReferences": customer_managed_policy_references_response,
|
||||||
|
"NextToken": next_token,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
def detach_customer_managed_policy_reference_from_permission_set(self) -> str:
|
||||||
|
instance_arn = self._get_param("InstanceArn")
|
||||||
|
permission_set_arn = self._get_param("PermissionSetArn")
|
||||||
|
customer_managed_policy_reference = self._get_param(
|
||||||
|
"CustomerManagedPolicyReference"
|
||||||
|
)
|
||||||
|
self.ssoadmin_backend.detach_customer_managed_policy_reference_from_permission_set(
|
||||||
|
instance_arn=instance_arn,
|
||||||
|
permission_set_arn=permission_set_arn,
|
||||||
|
customer_managed_policy_reference=customer_managed_policy_reference,
|
||||||
|
)
|
||||||
|
return json.dumps({})
|
||||||
|
@ -37,4 +37,11 @@ PAGINATION_MODEL = {
|
|||||||
"result_key": "AttachedManagedPolicies",
|
"result_key": "AttachedManagedPolicies",
|
||||||
"unique_attribute": ["arn"],
|
"unique_attribute": ["arn"],
|
||||||
},
|
},
|
||||||
|
"list_customer_managed_policy_references_in_permission_set": {
|
||||||
|
"input_token": "next_token",
|
||||||
|
"limit_key": "max_results",
|
||||||
|
"limit_default": 100,
|
||||||
|
"result_key": "CustomerManagedPolicyReferences",
|
||||||
|
"unique_attribute": ["name", "path"],
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
@ -328,3 +328,153 @@ def test_detach_managed_policy_from_permission_set():
|
|||||||
)
|
)
|
||||||
|
|
||||||
assert len(response["AttachedManagedPolicies"]) == 0
|
assert len(response["AttachedManagedPolicies"]) == 0
|
||||||
|
|
||||||
|
|
||||||
|
@mock_ssoadmin
|
||||||
|
def test_attach_customer_managed_policy_reference_to_permission_set():
|
||||||
|
client = boto3.client("sso-admin", region_name="us-east-1")
|
||||||
|
permission_set_arn = create_permissionset(client)
|
||||||
|
|
||||||
|
policy_name = "test-policy"
|
||||||
|
policy_path = "/test-path/"
|
||||||
|
|
||||||
|
client.attach_customer_managed_policy_reference_to_permission_set(
|
||||||
|
InstanceArn=DUMMY_INSTANCE_ARN,
|
||||||
|
PermissionSetArn=permission_set_arn,
|
||||||
|
CustomerManagedPolicyReference={
|
||||||
|
"Name": policy_name,
|
||||||
|
"Path": policy_path,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
response = client.list_customer_managed_policy_references_in_permission_set(
|
||||||
|
InstanceArn=DUMMY_INSTANCE_ARN,
|
||||||
|
PermissionSetArn=permission_set_arn,
|
||||||
|
)
|
||||||
|
|
||||||
|
assert len(response["CustomerManagedPolicyReferences"]) == 1
|
||||||
|
assert response["CustomerManagedPolicyReferences"][0]["Name"] == policy_name
|
||||||
|
assert response["CustomerManagedPolicyReferences"][0]["Path"] == policy_path
|
||||||
|
|
||||||
|
# test for customer managed policy that is already attached
|
||||||
|
with pytest.raises(ClientError) as e:
|
||||||
|
client.attach_customer_managed_policy_reference_to_permission_set(
|
||||||
|
InstanceArn=DUMMY_INSTANCE_ARN,
|
||||||
|
PermissionSetArn=permission_set_arn,
|
||||||
|
CustomerManagedPolicyReference={
|
||||||
|
"Name": policy_name,
|
||||||
|
"Path": policy_path,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
err = e.value.response["Error"]
|
||||||
|
assert err["Code"] == "ConflictException"
|
||||||
|
assert (
|
||||||
|
err["Message"]
|
||||||
|
== f"Given customer managed policy with name: {policy_name} and path {policy_path} already attached"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@mock_ssoadmin
|
||||||
|
def test_list_customer_managed_policy_references_in_permission_set():
|
||||||
|
"""
|
||||||
|
Tests listing customer managed policies including pagination.
|
||||||
|
"""
|
||||||
|
client = boto3.client("sso-admin", region_name="us-east-1")
|
||||||
|
permission_set_arn = create_permissionset(client)
|
||||||
|
|
||||||
|
policy_name = "test-policy-"
|
||||||
|
|
||||||
|
# attach 3 customer managed policies
|
||||||
|
for idx in range(3):
|
||||||
|
client.attach_customer_managed_policy_reference_to_permission_set(
|
||||||
|
InstanceArn=DUMMY_INSTANCE_ARN,
|
||||||
|
PermissionSetArn=permission_set_arn,
|
||||||
|
CustomerManagedPolicyReference={"Name": f"{policy_name}{idx}"},
|
||||||
|
)
|
||||||
|
|
||||||
|
response = client.list_customer_managed_policy_references_in_permission_set(
|
||||||
|
InstanceArn=DUMMY_INSTANCE_ARN,
|
||||||
|
PermissionSetArn=permission_set_arn,
|
||||||
|
MaxResults=2,
|
||||||
|
)
|
||||||
|
|
||||||
|
customer_managed_policy_names = []
|
||||||
|
|
||||||
|
assert len(response["CustomerManagedPolicyReferences"]) == 2
|
||||||
|
next_token = response["NextToken"]
|
||||||
|
for name in response["CustomerManagedPolicyReferences"]:
|
||||||
|
customer_managed_policy_names.append(name["Name"])
|
||||||
|
|
||||||
|
response = client.list_customer_managed_policy_references_in_permission_set(
|
||||||
|
InstanceArn=DUMMY_INSTANCE_ARN,
|
||||||
|
PermissionSetArn=permission_set_arn,
|
||||||
|
MaxResults=2,
|
||||||
|
NextToken=next_token,
|
||||||
|
)
|
||||||
|
for name in response["CustomerManagedPolicyReferences"]:
|
||||||
|
customer_managed_policy_names.append(name["Name"])
|
||||||
|
|
||||||
|
assert len(response["CustomerManagedPolicyReferences"]) == 1
|
||||||
|
|
||||||
|
# ensure the 3 unique customer managed policies were returned
|
||||||
|
assert len(set(customer_managed_policy_names)) == 3
|
||||||
|
|
||||||
|
|
||||||
|
@mock_ssoadmin
|
||||||
|
def test_detach_customer_managed_policy_reference_from_permission_set():
|
||||||
|
client = boto3.client("sso-admin", region_name="us-east-1")
|
||||||
|
permission_set_arn = create_permissionset(client)
|
||||||
|
|
||||||
|
# trying to detach a policy that doesn't exist yet
|
||||||
|
with pytest.raises(ClientError) as e:
|
||||||
|
client.detach_customer_managed_policy_reference_from_permission_set(
|
||||||
|
InstanceArn=DUMMY_INSTANCE_ARN,
|
||||||
|
PermissionSetArn=permission_set_arn,
|
||||||
|
CustomerManagedPolicyReference={
|
||||||
|
"Name": "test-policy",
|
||||||
|
},
|
||||||
|
)
|
||||||
|
err = e.value.response["Error"]
|
||||||
|
assert err["Code"] == "ResourceNotFoundException"
|
||||||
|
assert (
|
||||||
|
err["Message"]
|
||||||
|
== "Given managed policy with name: test-policy and path / does not exist on PermissionSet"
|
||||||
|
)
|
||||||
|
|
||||||
|
# attach a policy
|
||||||
|
client.attach_customer_managed_policy_reference_to_permission_set(
|
||||||
|
InstanceArn=DUMMY_INSTANCE_ARN,
|
||||||
|
PermissionSetArn=permission_set_arn,
|
||||||
|
CustomerManagedPolicyReference={
|
||||||
|
"Name": "test-policy",
|
||||||
|
"Path": "/some-path/",
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
# try to detach the policy but default path (should fail)
|
||||||
|
with pytest.raises(ClientError) as e:
|
||||||
|
client.detach_customer_managed_policy_reference_from_permission_set(
|
||||||
|
InstanceArn=DUMMY_INSTANCE_ARN,
|
||||||
|
PermissionSetArn=permission_set_arn,
|
||||||
|
CustomerManagedPolicyReference={
|
||||||
|
"Name": "test-policy",
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
# detach the policy
|
||||||
|
client.detach_customer_managed_policy_reference_from_permission_set(
|
||||||
|
InstanceArn=DUMMY_INSTANCE_ARN,
|
||||||
|
PermissionSetArn=permission_set_arn,
|
||||||
|
CustomerManagedPolicyReference={
|
||||||
|
"Name": "test-policy",
|
||||||
|
"Path": "/some-path/",
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
# ensure policy is detached
|
||||||
|
response = client.list_customer_managed_policy_references_in_permission_set(
|
||||||
|
InstanceArn=DUMMY_INSTANCE_ARN,
|
||||||
|
PermissionSetArn=permission_set_arn,
|
||||||
|
)
|
||||||
|
|
||||||
|
assert len(response["CustomerManagedPolicyReferences"]) == 0
|
||||||
|
Loading…
Reference in New Issue
Block a user