CF support for IAM:ManagedPolicy (#3933)

This commit is contained in:
Bert Blommers 2021-08-28 07:32:10 +01:00 committed by GitHub
parent 6d0bcba791
commit 6b960e0d4f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 300 additions and 5 deletions

View File

@ -253,7 +253,7 @@ class PolicyVersion(object):
return iso_8601_datetime_with_milliseconds(self.create_date)
class ManagedPolicy(Policy):
class ManagedPolicy(Policy, CloudFormationModel):
"""Managed policy."""
is_attachable = True
@ -312,6 +312,47 @@ class ManagedPolicy(Policy):
"supplementaryConfiguration": {},
}
@staticmethod
def cloudformation_name_type():
return None # Resource never gets named after by template PolicyName!
@staticmethod
def cloudformation_type():
return "AWS::IAM::ManagedPolicy"
@classmethod
def create_from_cloudformation_json(
cls, resource_physical_name, cloudformation_json, region_name
):
properties = cloudformation_json.get("Properties", {})
policy_document = json.dumps(properties.get("PolicyDocument"))
name = properties.get("ManagedPolicyName", resource_physical_name)
description = properties.get("Description")
path = properties.get("Path")
group_names = properties.get("Groups", [])
user_names = properties.get("Users", [])
role_names = properties.get("Roles", [])
policy = iam_backend.create_policy(
description=description,
path=path,
policy_document=policy_document,
policy_name=name,
)
for group_name in group_names:
iam_backend.attach_group_policy(
group_name=group_name, policy_arn=policy.arn
)
for user_name in user_names:
iam_backend.attach_user_policy(user_name=user_name, policy_arn=policy.arn)
for role_name in role_names:
iam_backend.attach_role_policy(role_name=role_name, policy_arn=policy.arn)
return policy
@property
def physical_resource_id(self):
return self.arn
class AWSManagedPolicy(ManagedPolicy):
"""AWS-managed policy."""

View File

@ -125,7 +125,7 @@ class IamResponse(BaseResponse):
entity_groups = []
entity_users = []
if entity == "User":
if not entity or entity == "User":
users = iam_backend.list_users(path_prefix, marker, max_items)
if users:
for user in users:
@ -133,7 +133,7 @@ class IamResponse(BaseResponse):
if p == policy_arn:
entity_users.append(user.name)
elif entity == "Role":
if not entity or entity == "Role":
roles, _ = iam_backend.list_roles(path_prefix, marker, max_items)
if roles:
for role in roles:
@ -141,7 +141,7 @@ class IamResponse(BaseResponse):
if p == policy_arn:
entity_roles.append(role.name)
elif entity == "Group":
if not entity or entity == "Group":
groups = iam_backend.list_groups()
if groups:
for group in groups:
@ -149,7 +149,7 @@ class IamResponse(BaseResponse):
if p == policy_arn:
entity_groups.append(group.name)
elif entity == "LocalManagedPolicy" or entity == "AWSManagedPolicy":
if entity == "LocalManagedPolicy" or entity == "AWSManagedPolicy":
users = iam_backend.list_users(path_prefix, marker, max_items)
if users:
for user in users:

View File

@ -2357,18 +2357,24 @@ def test_list_entities_for_policy():
EntityFilter="Role",
)
assert response["PolicyRoles"] == [{"RoleName": "my-role"}]
response["PolicyGroups"].should.equal([])
response["PolicyUsers"].should.equal([])
response = conn.list_entities_for_policy(
PolicyArn="arn:aws:iam::{}:policy/testPolicy".format(ACCOUNT_ID),
EntityFilter="User",
)
assert response["PolicyUsers"] == [{"UserName": "testUser"}]
response["PolicyGroups"].should.equal([])
response["PolicyRoles"].should.equal([])
response = conn.list_entities_for_policy(
PolicyArn="arn:aws:iam::{}:policy/testPolicy".format(ACCOUNT_ID),
EntityFilter="Group",
)
assert response["PolicyGroups"] == [{"GroupName": "testGroup"}]
response["PolicyRoles"].should.equal([])
response["PolicyUsers"].should.equal([])
response = conn.list_entities_for_policy(
PolicyArn="arn:aws:iam::{}:policy/testPolicy".format(ACCOUNT_ID),
@ -2378,6 +2384,14 @@ def test_list_entities_for_policy():
assert response["PolicyUsers"] == [{"UserName": "testUser"}]
assert response["PolicyRoles"] == [{"RoleName": "my-role"}]
# Return everything when no entity is specified
response = conn.list_entities_for_policy(
PolicyArn="arn:aws:iam::{}:policy/testPolicy".format(ACCOUNT_ID)
)
response["PolicyGroups"].should.equal([{"GroupName": "testGroup"}])
response["PolicyUsers"].should.equal([{"UserName": "testUser"}])
response["PolicyRoles"].should.equal([{"RoleName": "my-role"}])
@mock_iam()
def test_create_role_no_path():

View File

@ -6,6 +6,7 @@ import pytest
from botocore.exceptions import ClientError
from moto import mock_iam, mock_cloudformation, mock_s3, mock_sts
from moto.core import ACCOUNT_ID
# AWS::IAM::User Tests
@mock_iam
@ -281,6 +282,245 @@ Outputs:
output_user_arn.should.equal(user_description["Arn"])
# AWS::IAM::ManagedPolicy Tests
@mock_iam
@mock_cloudformation
def test_iam_cloudformation_create_managed_policy():
iam_client = boto3.client("iam", region_name="us-east-1")
cf_client = boto3.client("cloudformation", region_name="us-east-1")
stack_name = "MyStack"
template = """
Resources:
ThePolicy:
Type: AWS::IAM::ManagedPolicy
Properties:
PolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Action: s3:*
Resource: '*'
""".strip()
cf_client.create_stack(StackName=stack_name, TemplateBody=template)
provisioned_resource = cf_client.list_stack_resources(StackName=stack_name)[
"StackResourceSummaries"
][0]
logical_resource_id = provisioned_resource["LogicalResourceId"]
logical_resource_id.should.equal("ThePolicy")
policy_arn = provisioned_resource["PhysicalResourceId"]
policy_arn.should.match(
"arn:aws:iam::{}:policy/MyStack-ThePolicy-[A-Z0-9]+".format(ACCOUNT_ID)
)
expected_name = policy_arn.split("/")[1]
response = iam_client.list_entities_for_policy(PolicyArn=policy_arn)
response.should.have.key("PolicyGroups").equal([])
response.should.have.key("PolicyUsers").equal([])
response.should.have.key("PolicyRoles").equal([])
policy = iam_client.get_policy(PolicyArn=policy_arn)["Policy"]
policy.should.have.key("Arn").equal(policy_arn)
policy.should.have.key("PolicyName").equal(expected_name)
policy.should.have.key("Description").equal("")
policy.should.have.key("Path").equal("/")
@mock_iam
@mock_cloudformation
def test_iam_cloudformation_create_managed_policy_with_additional_properties():
iam_client = boto3.client("iam", region_name="us-east-1")
cf_client = boto3.client("cloudformation", region_name="us-east-1")
stack_name = "MyStack"
name = "FancyManagedPolicy"
desc = "Custom managed policy with name"
template = """
Resources:
ThePolicy:
Type: AWS::IAM::ManagedPolicy
Properties:
Description: {0}
Path: /
ManagedPolicyName: {1}
PolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Action: s3:*
Resource: '*'
""".strip().format(
desc, name
)
cf_client.create_stack(StackName=stack_name, TemplateBody=template)
provisioned_resource = cf_client.list_stack_resources(StackName=stack_name)[
"StackResourceSummaries"
][0]
logical_resource_id = provisioned_resource["LogicalResourceId"]
logical_resource_id.should.equal("ThePolicy")
policy_arn = provisioned_resource["PhysicalResourceId"]
policy_arn.should.equal("arn:aws:iam::{}:policy/{}".format(ACCOUNT_ID, name))
policy = iam_client.get_policy(PolicyArn=policy_arn)["Policy"]
policy.should.have.key("Arn").equal(policy_arn)
policy.should.have.key("Path").equal("/")
policy.should.have.key("Description").equal(desc)
policy.should.have.key("PolicyName").equal(name)
@mock_iam
@mock_cloudformation
def test_iam_cloudformation_create_managed_policy_attached_to_a_group():
iam_client = boto3.client("iam", region_name="us-east-1")
group_name = "MyGroup"
iam_client.create_group(GroupName=group_name)
cf_client = boto3.client("cloudformation", region_name="us-east-1")
stack_name = "MyStack"
desc = "Custom managed policy"
template = """
Resources:
ThePolicy:
Type: AWS::IAM::ManagedPolicy
Properties:
Description: {0}
Path: /
PolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Action: s3:*
Resource: '*'
Groups:
- {1}
""".strip().format(
desc, group_name
)
cf_client.create_stack(StackName=stack_name, TemplateBody=template)
provisioned_resource = cf_client.list_stack_resources(StackName=stack_name)[
"StackResourceSummaries"
][0]
logical_resource_id = provisioned_resource["LogicalResourceId"]
logical_resource_id.should.equal("ThePolicy")
policy_arn = provisioned_resource["PhysicalResourceId"]
policy_arn.should.match(
"rn:aws:iam::{}:policy/MyStack-ThePolicy-[A-Z0-9]+".format(ACCOUNT_ID)
)
response = iam_client.list_entities_for_policy(PolicyArn=policy_arn)
response.should.have.key("PolicyGroups").equal([{"GroupName": group_name}])
response.should.have.key("PolicyUsers").equal([])
response.should.have.key("PolicyRoles").equal([])
@mock_iam
@mock_cloudformation
def test_iam_cloudformation_create_managed_policy_attached_to_a_user():
iam_client = boto3.client("iam", region_name="us-east-1")
user_name = "MyUser"
iam_client.create_user(UserName=user_name)
cf_client = boto3.client("cloudformation", region_name="us-east-1")
stack_name = "MyStack"
desc = "Custom managed policy"
template = """
Resources:
ThePolicy:
Type: AWS::IAM::ManagedPolicy
Properties:
Description: {0}
Path: /
PolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Action: s3:*
Resource: '*'
Users:
- {1}
""".strip().format(
desc, user_name
)
cf_client.create_stack(StackName=stack_name, TemplateBody=template)
provisioned_resource = cf_client.list_stack_resources(StackName=stack_name)[
"StackResourceSummaries"
][0]
logical_resource_id = provisioned_resource["LogicalResourceId"]
logical_resource_id.should.equal("ThePolicy")
policy_arn = provisioned_resource["PhysicalResourceId"]
policy_arn.should.match(
"rn:aws:iam::{}:policy/MyStack-ThePolicy-[A-Z0-9]+".format(ACCOUNT_ID)
)
response = iam_client.list_entities_for_policy(PolicyArn=policy_arn)
response.should.have.key("PolicyGroups").equal([])
response.should.have.key("PolicyUsers").equal([{"UserName": user_name}])
response.should.have.key("PolicyRoles").equal([])
@mock_iam
@mock_cloudformation
def test_iam_cloudformation_create_managed_policy_attached_to_a_role():
iam_client = boto3.client("iam", region_name="us-east-1")
role_name = "MyRole"
iam_client.create_role(RoleName=role_name, AssumeRolePolicyDocument="some policy")
cf_client = boto3.client("cloudformation", region_name="us-east-1")
stack_name = "MyStack"
desc = "Custom managed policy"
template = """
Resources:
ThePolicy:
Type: AWS::IAM::ManagedPolicy
Properties:
Description: {0}
Path: /
PolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Action: s3:*
Resource: '*'
Roles:
- {1}
""".strip().format(
desc, role_name
)
cf_client.create_stack(StackName=stack_name, TemplateBody=template)
provisioned_resource = cf_client.list_stack_resources(StackName=stack_name)[
"StackResourceSummaries"
][0]
logical_resource_id = provisioned_resource["LogicalResourceId"]
logical_resource_id.should.equal("ThePolicy")
policy_arn = provisioned_resource["PhysicalResourceId"]
policy_arn.should.match(
"rn:aws:iam::{}:policy/MyStack-ThePolicy-[A-Z0-9]+".format(ACCOUNT_ID)
)
response = iam_client.list_entities_for_policy(PolicyArn=policy_arn)
response.should.have.key("PolicyGroups").equal([])
response.should.have.key("PolicyUsers").equal([])
response.should.have.key("PolicyRoles").equal([{"RoleName": role_name}])
# AWS::IAM::Policy Tests
@mock_s3
@mock_iam