Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

SSO-admin: Add basic inline policy crud operations #7182

Merged
merged 1 commit into from
Jan 3, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 7 additions & 3 deletions moto/ssoadmin/exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@
from moto.core.exceptions import JsonRESTError


class ResourceNotFound(JsonRESTError):
def __init__(self) -> None:
super().__init__("ResourceNotFound", "Account not found")
class ResourceNotFoundException(JsonRESTError):
def __init__(self, message: str = "Account not found") -> None:
super().__init__(
error_type="ResourceNotFoundException",
message=message,
code="ResourceNotFoundException",
)
37 changes: 34 additions & 3 deletions moto/ssoadmin/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
from moto.moto_api._internal import mock_random as random
from moto.utilities.paginator import paginate

from .exceptions import ResourceNotFound
from .exceptions import ResourceNotFoundException
from .utils import PAGINATION_MODEL


Expand Down Expand Up @@ -59,6 +59,7 @@ def __init__(
self.relay_state = relay_state
self.tags = tags
self.created_date = unix_time()
self.inline_policy = ""

def to_json(self, include_creation_date: bool = False) -> Dict[str, Any]:
summary: Dict[str, Any] = {
Expand Down Expand Up @@ -155,7 +156,7 @@ def _find_account(
and principal_id_match
):
return account
raise ResourceNotFound
raise ResourceNotFoundException

def list_account_assignments(
self, instance_arn: str, account_id: str, permission_set_arn: str
Expand Down Expand Up @@ -275,7 +276,10 @@ def _find_permission_set(
)
if instance_arn_match and permission_set_match:
return permission_set
raise ResourceNotFound
ps_id = permission_set_arn.split("/")[-1]
raise ResourceNotFoundException(
message=f"Could not find PermissionSet with id {ps_id}"
)

@paginate(pagination_model=PAGINATION_MODEL) # type: ignore[misc]
def list_permission_sets(self, instance_arn: str) -> List[PermissionSet]:
Expand All @@ -285,5 +289,32 @@ def list_permission_sets(self, instance_arn: str) -> List[PermissionSet]:
permission_sets.append(permission_set)
return permission_sets

def put_inline_policy_to_permission_set(
self, instance_arn: str, permission_set_arn: str, inline_policy: str
) -> None:
permission_set = self._find_permission_set(
instance_arn,
permission_set_arn,
)
permission_set.inline_policy = inline_policy

def get_inline_policy_for_permission_set(
self, instance_arn: str, permission_set_arn: str
) -> str:
permission_set = self._find_permission_set(
instance_arn,
permission_set_arn,
)
return permission_set.inline_policy

def delete_inline_policy_from_permission_set(
self, instance_arn: str, permission_set_arn: str
) -> None:
permission_set = self._find_permission_set(
instance_arn,
permission_set_arn,
)
permission_set.inline_policy = ""


ssoadmin_backends = BackendDict(SSOAdminBackend, "sso")
29 changes: 29 additions & 0 deletions moto/ssoadmin/responses.py
Original file line number Diff line number Diff line change
Expand Up @@ -160,3 +160,32 @@ def list_permission_sets(self) -> str:
if next_token:
response["NextToken"] = next_token
return json.dumps(response)

def put_inline_policy_to_permission_set(self) -> str:
instance_arn = self._get_param("InstanceArn")
permission_set_arn = self._get_param("PermissionSetArn")
inline_policy = self._get_param("InlinePolicy")
self.ssoadmin_backend.put_inline_policy_to_permission_set(
instance_arn=instance_arn,
permission_set_arn=permission_set_arn,
inline_policy=inline_policy,
)
return json.dumps({})

def get_inline_policy_for_permission_set(self) -> str:
instance_arn = self._get_param("InstanceArn")
permission_set_arn = self._get_param("PermissionSetArn")
inline_policy = self.ssoadmin_backend.get_inline_policy_for_permission_set(
instance_arn=instance_arn,
permission_set_arn=permission_set_arn,
)
return json.dumps({"InlinePolicy": inline_policy})

def delete_inline_policy_from_permission_set(self) -> str:
instance_arn = self._get_param("InstanceArn")
permission_set_arn = self._get_param("PermissionSetArn")
self.ssoadmin_backend.delete_inline_policy_from_permission_set(
instance_arn=instance_arn,
permission_set_arn=permission_set_arn,
)
return json.dumps({})
10 changes: 5 additions & 5 deletions tests/test_ssoadmin/test_ssoadmin.py
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,7 @@ def test_delete_account_assignment_unknown():
PrincipalId=principal_id,
)
err = exc.value.response["Error"]
assert err["Code"] == "ResourceNotFound"
assert err["Code"] == "ResourceNotFoundException"


@mock_ssoadmin
Expand Down Expand Up @@ -391,7 +391,7 @@ def test_update_permission_set_unknown():
RelayState="https://console.aws.amazon.com/s3",
)
err = exc.value.response["Error"]
assert err["Code"] == "ResourceNotFound"
assert err["Code"] == "ResourceNotFoundException"


@mock_ssoadmin
Expand Down Expand Up @@ -428,7 +428,7 @@ def test_describe_permission_set_unknown():
PermissionSetArn="arn:aws:sso:::permissionSet/ins-eeeeffffgggghhhh/ps-hhhhkkkkppppoooo",
)
err = exc.value.response["Error"]
assert err["Code"] == "ResourceNotFound"
assert err["Code"] == "ResourceNotFoundException"


@mock_ssoadmin
Expand All @@ -451,7 +451,7 @@ def test_delete_permission_set():
PermissionSetArn=permission_set["PermissionSetArn"],
)
err = exc.value.response["Error"]
assert err["Code"] == "ResourceNotFound"
assert err["Code"] == "ResourceNotFoundException"


@mock_ssoadmin
Expand All @@ -464,7 +464,7 @@ def test_delete_permission_set_unknown():
PermissionSetArn="arn:aws:sso:::permissionSet/ins-eeeeffffgggghhhh/ps-hhhhkkkkppppoooo",
)
err = exc.value.response["Error"]
assert err["Code"] == "ResourceNotFound"
assert err["Code"] == "ResourceNotFoundException"


@mock_ssoadmin
Expand Down
132 changes: 132 additions & 0 deletions tests/test_ssoadmin/test_ssoadmin_policies.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
import json

import boto3
import pytest
from botocore.exceptions import ClientError

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"


def create_permissionset(client) -> str:
"""Helper function to create a dummy permission set and returns the arn."""

response = client.create_permission_set(
Name="test-permission-set",
InstanceArn=DUMMY_INSTANCE_ARN,
Description="test permission set",
)

return response["PermissionSet"]["PermissionSetArn"]


@mock_ssoadmin
def test_put_inline_policy_to_permission_set():
"""
Tests putting and getting an inline policy to a permission set.
"""
client = boto3.client("sso-admin", region_name="us-east-1")

permission_set_arn = create_permissionset(client)
dummy_policy = {
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": "s3:GetObject",
"Resource": "arn:aws:s3:::your-bucket-name/*",
}
],
}

# Happy path
response = client.put_inline_policy_to_permission_set(
InstanceArn=DUMMY_INSTANCE_ARN,
PermissionSetArn=permission_set_arn,
InlinePolicy=json.dumps(dummy_policy),
)

response = client.get_inline_policy_for_permission_set(
InstanceArn=DUMMY_INSTANCE_ARN,
PermissionSetArn=permission_set_arn,
)

assert response["InlinePolicy"] == json.dumps(dummy_policy)

# Invalid permission set arn
not_create_ps_arn = (
"arn:aws:sso:::permissionSet/ins-eeeeffffgggghhhh/ps-hhhhkkkkppppoxyz"
)
with pytest.raises(ClientError) as e:
client.put_inline_policy_to_permission_set(
InstanceArn=DUMMY_INSTANCE_ARN,
PermissionSetArn=not_create_ps_arn,
InlinePolicy=json.dumps(dummy_policy),
)
err = e.value.response["Error"]
assert err["Code"] == "ResourceNotFoundException"
assert err["Message"] == "Could not find PermissionSet with id ps-hhhhkkkkppppoxyz"


@mock_ssoadmin
def test_get_inline_policy_to_permission_set_no_policy():
client = boto3.client("sso-admin", region_name="us-east-1")

permission_set_arn = create_permissionset(client)

response = client.get_inline_policy_for_permission_set(
InstanceArn=DUMMY_INSTANCE_ARN,
PermissionSetArn=permission_set_arn,
)

assert response["InlinePolicy"] == ""


@mock_ssoadmin
def test_delete_inline_policy_to_permissionset():
client = boto3.client("sso-admin", region_name="us-east-1")

permission_set_arn = create_permissionset(client)

dummy_policy = {
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": "s3:GetObject",
"Resource": "arn:aws:s3:::your-bucket-name/*",
}
],
}

client.put_inline_policy_to_permission_set(
InstanceArn=DUMMY_INSTANCE_ARN,
PermissionSetArn=permission_set_arn,
InlinePolicy=json.dumps(dummy_policy),
)

response = client.get_inline_policy_for_permission_set(
InstanceArn=DUMMY_INSTANCE_ARN,
PermissionSetArn=permission_set_arn,
)

assert response["InlinePolicy"] == json.dumps(dummy_policy)

client.delete_inline_policy_from_permission_set(
InstanceArn=DUMMY_INSTANCE_ARN,
PermissionSetArn=permission_set_arn,
)

response = client.get_inline_policy_for_permission_set(
InstanceArn=DUMMY_INSTANCE_ARN,
PermissionSetArn=permission_set_arn,
)

assert response["InlinePolicy"] == ""