diff --git a/moto/iam/models.py b/moto/iam/models.py
index a7e584284..18ed513b4 100644
--- a/moto/iam/models.py
+++ b/moto/iam/models.py
@@ -74,21 +74,13 @@ class ManagedPolicy(Policy):
is_attachable = True
- def attach_to_role(self, role):
+ def attach_to(self, obj):
self.attachment_count += 1
- role.managed_policies[self.name] = self
+ obj.managed_policies[self.name] = self
- def detach_from_role(self, role):
+ def detach_from(self, obj):
self.attachment_count -= 1
- del role.managed_policies[self.name]
-
- def attach_to_user(self, user):
- self.attachment_count += 1
- user.managed_policies[self.name] = self
-
- def detach_from_user(self, user):
- self.attachment_count -= 1
- del user.managed_policies[self.name]
+ del obj.managed_policies[self.name]
class AWSManagedPolicy(ManagedPolicy):
@@ -249,6 +241,7 @@ class Group(BaseModel):
)
self.users = []
+ self.managed_policies = {}
self.policies = {}
def get_cfn_attribute(self, attribute_name):
@@ -423,25 +416,47 @@ class IAMBackend(BaseBackend):
def attach_role_policy(self, policy_arn, role_name):
arns = dict((p.arn, p) for p in self.managed_policies.values())
policy = arns[policy_arn]
- policy.attach_to_role(self.get_role(role_name))
+ policy.attach_to(self.get_role(role_name))
def detach_role_policy(self, policy_arn, role_name):
arns = dict((p.arn, p) for p in self.managed_policies.values())
try:
policy = arns[policy_arn]
- policy.detach_from_role(self.get_role(role_name))
+ policy.detach_from(self.get_role(role_name))
except KeyError:
raise IAMNotFoundException("Policy {0} was not found.".format(policy_arn))
+ def attach_group_policy(self, policy_arn, group_name):
+ arns = dict((p.arn, p) for p in self.managed_policies.values())
+ try:
+ policy = arns[policy_arn]
+ except KeyError:
+ raise IAMNotFoundException("Policy {0} was not found.".format(policy_arn))
+ policy.attach_to(self.get_group(group_name))
+
+ def detach_group_policy(self, policy_arn, group_name):
+ arns = dict((p.arn, p) for p in self.managed_policies.values())
+ try:
+ policy = arns[policy_arn]
+ except KeyError:
+ raise IAMNotFoundException("Policy {0} was not found.".format(policy_arn))
+ policy.detach_from(self.get_group(group_name))
+
def attach_user_policy(self, policy_arn, user_name):
arns = dict((p.arn, p) for p in self.managed_policies.values())
- policy = arns[policy_arn]
- policy.attach_to_user(self.get_user(user_name))
+ try:
+ policy = arns[policy_arn]
+ except KeyError:
+ raise IAMNotFoundException("Policy {0} was not found.".format(policy_arn))
+ policy.attach_to(self.get_user(user_name))
def detach_user_policy(self, policy_arn, user_name):
arns = dict((p.arn, p) for p in self.managed_policies.values())
- policy = arns[policy_arn]
- policy.detach_from_user(self.get_user(user_name))
+ try:
+ policy = arns[policy_arn]
+ except KeyError:
+ raise IAMNotFoundException("Policy {0} was not found.".format(policy_arn))
+ policy.detach_from(self.get_user(user_name))
def create_policy(self, description, path, policy_document, policy_name):
policy = ManagedPolicy(
@@ -458,39 +473,15 @@ class IAMBackend(BaseBackend):
def list_attached_role_policies(self, role_name, marker=None, max_items=100, path_prefix='/'):
policies = self.get_role(role_name).managed_policies.values()
+ return self._filter_attached_policies(policies, marker, max_items, path_prefix)
- if path_prefix:
- policies = [p for p in policies if p.path.startswith(path_prefix)]
-
- policies = sorted(policies, key=lambda policy: policy.name)
- start_idx = int(marker) if marker else 0
-
- policies = policies[start_idx:start_idx + max_items]
-
- if len(policies) < max_items:
- marker = None
- else:
- marker = str(start_idx + max_items)
-
- return policies, marker
+ def list_attached_group_policies(self, group_name, marker=None, max_items=100, path_prefix='/'):
+ policies = self.get_group(group_name).managed_policies.values()
+ return self._filter_attached_policies(policies, marker, max_items, path_prefix)
def list_attached_user_policies(self, user_name, marker=None, max_items=100, path_prefix='/'):
policies = self.get_user(user_name).managed_policies.values()
-
- if path_prefix:
- policies = [p for p in policies if p.path.startswith(path_prefix)]
-
- policies = sorted(policies, key=lambda policy: policy.name)
- start_idx = int(marker) if marker else 0
-
- policies = policies[start_idx:start_idx + max_items]
-
- if len(policies) < max_items:
- marker = None
- else:
- marker = str(start_idx + max_items)
-
- return policies, marker
+ return self._filter_attached_policies(policies, marker, max_items, path_prefix)
def list_policies(self, marker, max_items, only_attached, path_prefix, scope):
policies = self.managed_policies.values()
@@ -504,6 +495,9 @@ class IAMBackend(BaseBackend):
policies = [p for p in policies if not isinstance(
p, AWSManagedPolicy)]
+ return self._filter_attached_policies(policies, marker, max_items, path_prefix)
+
+ def _filter_attached_policies(self, policies, marker, max_items, path_prefix):
if path_prefix:
policies = [p for p in policies if p.path.startswith(path_prefix)]
diff --git a/moto/iam/responses.py b/moto/iam/responses.py
index 13688869e..6ca49b830 100644
--- a/moto/iam/responses.py
+++ b/moto/iam/responses.py
@@ -20,6 +20,20 @@ class IamResponse(BaseResponse):
template = self.response_template(GENERIC_EMPTY_TEMPLATE)
return template.render(name="DetachRolePolicyResponse")
+ def attach_group_policy(self):
+ policy_arn = self._get_param('PolicyArn')
+ group_name = self._get_param('GroupName')
+ iam_backend.attach_group_policy(policy_arn, group_name)
+ template = self.response_template(ATTACH_GROUP_POLICY_TEMPLATE)
+ return template.render()
+
+ def detach_group_policy(self):
+ policy_arn = self._get_param('PolicyArn')
+ group_name = self._get_param('GroupName')
+ iam_backend.detach_group_policy(policy_arn, group_name)
+ template = self.response_template(DETACH_GROUP_POLICY_TEMPLATE)
+ return template.render()
+
def attach_user_policy(self):
policy_arn = self._get_param('PolicyArn')
user_name = self._get_param('UserName')
@@ -54,6 +68,17 @@ class IamResponse(BaseResponse):
template = self.response_template(LIST_ATTACHED_ROLE_POLICIES_TEMPLATE)
return template.render(policies=policies, marker=marker)
+ def list_attached_group_policies(self):
+ marker = self._get_param('Marker')
+ max_items = self._get_int_param('MaxItems', 100)
+ path_prefix = self._get_param('PathPrefix', '/')
+ group_name = self._get_param('GroupName')
+ policies, marker = iam_backend.list_attached_group_policies(
+ group_name, marker=marker, max_items=max_items,
+ path_prefix=path_prefix)
+ template = self.response_template(LIST_ATTACHED_GROUP_POLICIES_TEMPLATE)
+ return template.render(policies=policies, marker=marker)
+
def list_attached_user_policies(self):
marker = self._get_param('Marker')
max_items = self._get_int_param('MaxItems', 100)
@@ -520,6 +545,18 @@ DETACH_USER_POLICY_TEMPLATE = """
"""
+ATTACH_GROUP_POLICY_TEMPLATE = """
+
+ 7a62c49f-347e-4fc4-9331-6e8eEXAMPLE
+
+"""
+
+DETACH_GROUP_POLICY_TEMPLATE = """
+
+ 7a62c49f-347e-4fc4-9331-6e8eEXAMPLE
+
+"""
+
CREATE_POLICY_TEMPLATE = """
@@ -560,6 +597,28 @@ LIST_ATTACHED_ROLE_POLICIES_TEMPLATE = """
"""
+LIST_ATTACHED_GROUP_POLICIES_TEMPLATE = """
+
+ {% if marker is none %}
+ false
+ {% else %}
+ true
+ {{ marker }}
+ {% endif %}
+
+ {% for policy in policies %}
+
+ {{ policy.name }}
+ {{ policy.arn }}
+
+ {% endfor %}
+
+
+
+ 7a62c49f-347e-4fc4-9331-6e8eEXAMPLE
+
+"""
+
LIST_ATTACHED_USER_POLICIES_TEMPLATE = """
{% if marker is none %}
diff --git a/tests/test_iam/test_iam_groups.py b/tests/test_iam/test_iam_groups.py
index 9d5095884..49c7987f6 100644
--- a/tests/test_iam/test_iam_groups.py
+++ b/tests/test_iam/test_iam_groups.py
@@ -82,6 +82,26 @@ def test_put_group_policy():
conn.put_group_policy('my-group', 'my-policy', '{"some": "json"}')
+@mock_iam
+def test_attach_group_policies():
+ conn = boto3.client('iam', region_name='us-east-1')
+ conn.create_group(GroupName='my-group')
+ conn.list_attached_group_policies(GroupName='my-group')['AttachedPolicies'].should.be.empty
+ policy_arn = 'arn:aws:iam::aws:policy/service-role/AmazonElasticMapReduceforEC2Role'
+ conn.list_attached_group_policies(GroupName='my-group')['AttachedPolicies'].should.be.empty
+ conn.attach_group_policy(GroupName='my-group', PolicyArn=policy_arn)
+ conn.list_attached_group_policies(GroupName='my-group')['AttachedPolicies'].should.equal(
+ [
+ {
+ 'PolicyName': 'AmazonElasticMapReduceforEC2Role',
+ 'PolicyArn': policy_arn,
+ }
+ ])
+
+ conn.detach_group_policy(GroupName='my-group', PolicyArn=policy_arn)
+ conn.list_attached_group_policies(GroupName='my-group')['AttachedPolicies'].should.be.empty
+
+
@mock_iam_deprecated()
def test_get_group_policy():
conn = boto.connect_iam()
@@ -90,7 +110,8 @@ def test_get_group_policy():
conn.get_group_policy('my-group', 'my-policy')
conn.put_group_policy('my-group', 'my-policy', '{"some": "json"}')
- policy = conn.get_group_policy('my-group', 'my-policy')
+ conn.get_group_policy('my-group', 'my-policy')
+
@mock_iam_deprecated()
def test_get_all_group_policies():
@@ -107,6 +128,6 @@ def test_get_all_group_policies():
def test_list_group_policies():
conn = boto3.client('iam', region_name='us-east-1')
conn.create_group(GroupName='my-group')
- policies = conn.list_group_policies(GroupName='my-group')['PolicyNames'].should.be.empty
+ conn.list_group_policies(GroupName='my-group')['PolicyNames'].should.be.empty
conn.put_group_policy(GroupName='my-group', PolicyName='my-policy', PolicyDocument='{"some": "json"}')
- policies = conn.list_group_policies(GroupName='my-group')['PolicyNames'].should.equal(['my-policy'])
+ conn.list_group_policies(GroupName='my-group')['PolicyNames'].should.equal(['my-policy'])