diff --git a/moto/ssoadmin/models.py b/moto/ssoadmin/models.py index 3bb69e19a..4a5097525 100644 --- a/moto/ssoadmin/models.py +++ b/moto/ssoadmin/models.py @@ -180,6 +180,33 @@ class SSOAdminBackend(BaseBackend): ) return account_assignments + @paginate(PAGINATION_MODEL) # type: ignore[misc] + def list_account_assignments_for_principal( + self, + filter_: Dict[str, Any], + instance_arn: str, + principal_id: str, + principal_type: str, + ) -> List[Dict[str, Any]]: + return [ + { + "AccountId": account_assignment.target_id, + "PermissionSetArn": account_assignment.permission_set_arn, + "PrincipalId": account_assignment.principal_id, + "PrincipalType": account_assignment.principal_type, + } + for account_assignment in self.account_assignments + if all( + [ + filter_.get("AccountId", account_assignment.target_id) + == account_assignment.target_id, + principal_id == account_assignment.principal_id, + principal_type == account_assignment.principal_type, + instance_arn == account_assignment.instance_arn, + ] + ) + ] + def create_permission_set( self, name: str, diff --git a/moto/ssoadmin/responses.py b/moto/ssoadmin/responses.py index 8a684233e..e89ca0e89 100644 --- a/moto/ssoadmin/responses.py +++ b/moto/ssoadmin/responses.py @@ -69,6 +69,28 @@ class SSOAdminResponse(BaseResponse): ) return json.dumps({"AccountAssignments": assignments}) + def list_account_assignments_for_principal(self) -> str: + filter_ = self._get_param("Filter", {}) + instance_arn = self._get_param("InstanceArn") + max_results = self._get_param("MaxResults") + next_token = self._get_param("NextToken") + principal_id = self._get_param("PrincipalId") + principal_type = self._get_param("PrincipalType") + + ( + assignments, + next_token, + ) = self.ssoadmin_backend.list_account_assignments_for_principal( + filter_=filter_, + instance_arn=instance_arn, + max_results=max_results, + next_token=next_token, + principal_id=principal_id, + principal_type=principal_type, + ) + + return json.dumps(dict(AccountAssignments=assignments, NextToken=next_token)) + def create_permission_set(self) -> str: name = self._get_param("Name") description = self._get_param("Description") diff --git a/moto/ssoadmin/utils.py b/moto/ssoadmin/utils.py index 1b684206c..45fc457f2 100644 --- a/moto/ssoadmin/utils.py +++ b/moto/ssoadmin/utils.py @@ -6,4 +6,16 @@ PAGINATION_MODEL = { "result_key": "PermissionSets", "unique_attribute": "permission_set_arn", }, + "list_account_assignments_for_principal": { + "input_token": "next_token", + "limit_key": "max_results", + "limit_default": 100, + "result_key": "PermissionSets", + "unique_attribute": [ + "AccountId", + "PermissionSetArn", + "PrincipalId", + "PrincipalType", + ], + }, } diff --git a/tests/test_ssoadmin/test_ssoadmin.py b/tests/test_ssoadmin/test_ssoadmin.py index 1dddae081..ab1e88a17 100644 --- a/tests/test_ssoadmin/test_ssoadmin.py +++ b/tests/test_ssoadmin/test_ssoadmin.py @@ -9,6 +9,10 @@ from moto import mock_ssoadmin # See our Development Tips on writing tests for hints on how to write good tests: # http://docs.getmoto.org/en/latest/docs/contributing/development_tips/tests.html +DUMMY_PERMISSIONSET_ID = ( + "arn:aws:sso:::permissionSet/ins-eeeeffffgggghhhh/ps-hhhhkkkkppppoooo" +) +DUMMY_INSTANCE_ARN = "arn:aws:sso:::instance/ins-aaaabbbbccccdddd" @mock_ssoadmin @@ -186,6 +190,140 @@ def test_list_account_assignments(): ] +@mock_ssoadmin +def test_list_account_assignments_for_principal(): + client = boto3.client("sso-admin", region_name="us-west-2") + + id_1 = str(uuid4()) + id_2 = str(uuid4()) + + dummy_account_assignments = [ + { + "InstanceArn": DUMMY_INSTANCE_ARN, + "TargetId": "111111111111", + "TargetType": "AWS_ACCOUNT", + "PermissionSetArn": DUMMY_PERMISSIONSET_ID, + "PrincipalType": "USER", + "PrincipalId": id_1, + }, + { + "InstanceArn": DUMMY_INSTANCE_ARN, + "TargetId": "222222222222", + "TargetType": "AWS_ACCOUNT", + "PermissionSetArn": DUMMY_PERMISSIONSET_ID, + "PrincipalType": "USER", + "PrincipalId": id_2, + }, + { + "InstanceArn": DUMMY_INSTANCE_ARN, + "TargetId": "333333333333", + "TargetType": "AWS_ACCOUNT", + "PermissionSetArn": DUMMY_PERMISSIONSET_ID, + "PrincipalType": "USER", + "PrincipalId": id_2, + }, + { + "InstanceArn": DUMMY_INSTANCE_ARN, + "TargetId": "222222222222", + "TargetType": "AWS_ACCOUNT", + "PermissionSetArn": DUMMY_PERMISSIONSET_ID, + "PrincipalType": "GROUP", + "PrincipalId": id_2, + }, + ] + + # create the account assignments from above + for dummy_account_assignment in dummy_account_assignments: + client.create_account_assignment(**dummy_account_assignment) + + # check user 1 assignments in all accounts + response = client.list_account_assignments_for_principal( + InstanceArn=DUMMY_INSTANCE_ARN, PrincipalId=id_1, PrincipalType="USER" + ) + assert len(response["AccountAssignments"]) == 1 + assert response["AccountAssignments"][0]["PrincipalId"] == id_1 + + # check user 2 in a single account + response = client.list_account_assignments_for_principal( + Filter={"AccountId": "222222222222"}, + InstanceArn=DUMMY_INSTANCE_ARN, + PrincipalId=id_2, + PrincipalType="USER", + ) + assert len(response["AccountAssignments"]) == 1 + assert response["AccountAssignments"][0]["PrincipalId"] == id_2 + assert response["AccountAssignments"][0]["AccountId"] == "222222222222" + + # check group with id 2 is only returned + response = client.list_account_assignments_for_principal( + InstanceArn=DUMMY_INSTANCE_ARN, + PrincipalId=id_2, + PrincipalType="GROUP", + ) + assert len(response["AccountAssignments"]) == 1 + assert response["AccountAssignments"][0]["PrincipalId"] == id_2 + + # check empty response + response = client.list_account_assignments_for_principal( + InstanceArn=DUMMY_INSTANCE_ARN, + PrincipalId=str(uuid4()), + PrincipalType="USER", + ) + + assert len(response["AccountAssignments"]) == 0 + + +@mock_ssoadmin +def test_list_account_assignments_for_principal_pagination(): + client = boto3.client("sso-admin", region_name="us-east-2") + + user_id = str(uuid4()) + + dummy_account_assignments = [] + for x in range(3): + dummy_account_assignments.append( + { + "InstanceArn": DUMMY_INSTANCE_ARN, + "TargetId": str(x) * 12, + "TargetType": "AWS_ACCOUNT", + "PermissionSetArn": DUMMY_PERMISSIONSET_ID, + "PrincipalType": "USER", + "PrincipalId": user_id, + }, + ) + + for dummy_account_assignment in dummy_account_assignments: + client.create_account_assignment(**dummy_account_assignment) + + account_assignments = [] + + response = client.list_account_assignments_for_principal( + InstanceArn=DUMMY_INSTANCE_ARN, + PrincipalId=user_id, + PrincipalType="USER", + MaxResults=2, + ) + + assert len(response["AccountAssignments"]) == 2 + account_assignments.extend(response["AccountAssignments"]) + next_token = response["NextToken"] + + response = client.list_account_assignments_for_principal( + InstanceArn=DUMMY_INSTANCE_ARN, + PrincipalId=user_id, + PrincipalType="USER", + MaxResults=2, + NextToken=next_token, + ) + + assert len(response["AccountAssignments"]) == 1 + account_assignments.extend(response["AccountAssignments"]) + + assert set( + [account_assignment["AccountId"] for account_assignment in account_assignments] + ) == set(["000000000000", "111111111111", "222222222222"]) + + @mock_ssoadmin def test_create_permission_set(): client = boto3.client("sso-admin", region_name="ap-southeast-1")