diff --git a/moto/iam/models.py b/moto/iam/models.py
index 388984f51..2e9970785 100644
--- a/moto/iam/models.py
+++ b/moto/iam/models.py
@@ -6,6 +6,7 @@ from .utils import random_access_key, random_alphanumeric, random_resource_id
from datetime import datetime
import base64
+
class Role(object):
def __init__(self, role_id, name, assume_role_policy_document, path):
@@ -212,16 +213,16 @@ class User(object):
access_key_2_last_rotated = date_created.strftime(date_format)
return '{0},{1},{2},{3},{4},{5},not_supported,false,{6},{7},{8},{9},false,N/A,false,N/A'.format(self.name,
- self.arn,
- date_created.strftime(date_format),
- password_enabled,
- password_last_used,
- date_created.strftime(date_format),
- access_key_1_active,
- access_key_1_last_rotated,
- access_key_2_active,
- access_key_2_last_rotated
- )
+ self.arn,
+ date_created.strftime(date_format),
+ password_enabled,
+ password_last_used,
+ date_created.strftime(date_format),
+ access_key_1_active,
+ access_key_1_last_rotated,
+ access_key_2_active,
+ access_key_2_last_rotated
+ )
class IAMBackend(BaseBackend):
@@ -337,6 +338,18 @@ class IAMBackend(BaseBackend):
return group
+ def list_groups(self):
+ return self.groups.values()
+
+ def get_groups_for_user(self, user_name):
+ user = self.get_user(user_name)
+ groups = []
+ for group in self.list_groups():
+ if user in group.users:
+ groups.append(group)
+
+ return groups
+
def create_user(self, user_name, path='/'):
if user_name in self.users:
raise BotoServerError(409, 'Conflict')
diff --git a/moto/iam/responses.py b/moto/iam/responses.py
index 4ebfb74ec..be1601a83 100644
--- a/moto/iam/responses.py
+++ b/moto/iam/responses.py
@@ -131,6 +131,18 @@ class IamResponse(BaseResponse):
template = self.response_template(GET_GROUP_TEMPLATE)
return template.render(group=group)
+ def list_groups(self):
+ groups = iam_backend.list_groups()
+ template = self.response_template(LIST_GROUPS_TEMPLATE)
+ return template.render(groups=groups)
+
+ def list_groups_for_user(self):
+ user_name = self._get_param('UserName')
+
+ groups = iam_backend.get_groups_for_user(user_name)
+ template = self.response_template(LIST_GROUPS_FOR_USER_TEMPLATE)
+ return template.render(groups=groups)
+
def create_user(self):
user_name = self._get_param('UserName')
path = self._get_param('Path')
@@ -502,6 +514,45 @@ GET_GROUP_TEMPLATE = """
"""
+LIST_GROUPS_TEMPLATE = """
+
+
+ {% for group in groups %}
+
+ {{ group.path }}
+ {{ group.name }}
+ {{ group.id }}
+ arn:aws:iam::123456789012:group/{{ group.path }}
+
+ {% endfor %}
+
+ false
+
+
+ 7a62c49f-347e-4fc4-9331-6e8eEXAMPLE
+
+"""
+
+LIST_GROUPS_FOR_USER_TEMPLATE = """
+
+
+ {% for group in groups %}
+
+ {{ group.path }}
+ {{ group.name }}
+ {{ group.id }}
+ arn:aws:iam::123456789012:group/{{ group.path }}
+
+ {% endfor %}
+
+ false
+
+
+ 7a62c49f-347e-4fc4-9331-6e8eEXAMPLE
+
+"""
+
+
USER_TEMPLATE = """<{{ action }}UserResponse>
<{{ action }}UserResult>
@@ -640,4 +691,4 @@ LIST_INSTANCE_PROFILES_FOR_ROLE_TEMPLATE = """
6a8c3992-99f4-11e1-a4c3-27EXAMPLE804
-"""
\ No newline at end of file
+"""
diff --git a/tests/test_iam/test_iam.py b/tests/test_iam/test_iam.py
index ee77297b0..881369cae 100644
--- a/tests/test_iam/test_iam.py
+++ b/tests/test_iam/test_iam.py
@@ -1,7 +1,6 @@
from __future__ import unicode_literals
import boto
import sure # noqa
-import re
from nose.tools import assert_raises, assert_equals, assert_not_equals
from boto.exception import BotoServerError
@@ -62,6 +61,7 @@ def test_create_role_and_instance_profile():
conn.list_roles().roles[0].role_name.should.equal('my-role')
conn.list_instance_profiles().instance_profiles[0].instance_profile_name.should.equal("my-profile")
+
@mock_iam()
def test_list_instance_profiles_for_role():
conn = boto.connect_iam()
@@ -71,15 +71,15 @@ def test_list_instance_profiles_for_role():
profile_name_list = ['my-profile', 'my-profile2']
profile_path_list = ['my-path', 'my-path2']
- for profile_count in range(0,2):
+ for profile_count in range(0, 2):
conn.create_instance_profile(profile_name_list[profile_count], path=profile_path_list[profile_count])
- for profile_count in range(0,2):
+ for profile_count in range(0, 2):
conn.add_role_to_instance_profile(profile_name_list[profile_count], "my-role")
profile_dump = conn.list_instance_profiles_for_role(role_name="my-role")
profile_list = profile_dump['list_instance_profiles_for_role_response']['list_instance_profiles_for_role_result']['instance_profiles']
- for profile_count in range(0,len(profile_list)):
+ for profile_count in range(0, len(profile_list)):
profile_name_list.remove(profile_list[profile_count]["instance_profile_name"])
profile_path_list.remove(profile_list[profile_count]["path"])
profile_list[profile_count]["roles"]["member"]["role_name"].should.equal("my-role")
@@ -91,6 +91,7 @@ def test_list_instance_profiles_for_role():
profile_list = profile_dump2['list_instance_profiles_for_role_response']['list_instance_profiles_for_role_result']['instance_profiles']
len(profile_list).should.equal(0)
+
@mock_iam()
def test_list_role_policies():
conn = boto.connect_iam()
@@ -118,23 +119,6 @@ def test_update_assume_role_policy():
role.assume_role_policy_document.should.equal("my-policy")
-@mock_iam()
-def test_create_group():
- conn = boto.connect_iam()
- conn.create_group('my-group')
- with assert_raises(BotoServerError):
- conn.create_group('my-group')
-
-
-@mock_iam()
-def test_get_group():
- conn = boto.connect_iam()
- conn.create_group('my-group')
- conn.get_group('my-group')
- with assert_raises(BotoServerError):
- conn.get_group('not-group')
-
-
@mock_iam()
def test_create_user():
conn = boto.connect_iam()
@@ -163,31 +147,6 @@ def test_create_login_profile():
conn.create_login_profile('my-user', 'my-pass')
-@mock_iam()
-def test_add_user_to_group():
- conn = boto.connect_iam()
- with assert_raises(BotoServerError):
- conn.add_user_to_group('my-group', 'my-user')
- conn.create_group('my-group')
- with assert_raises(BotoServerError):
- conn.add_user_to_group('my-group', 'my-user')
- conn.create_user('my-user')
- conn.add_user_to_group('my-group', 'my-user')
-
-
-@mock_iam()
-def test_remove_user_from_group():
- conn = boto.connect_iam()
- with assert_raises(BotoServerError):
- conn.remove_user_from_group('my-group', 'my-user')
- conn.create_group('my-group')
- conn.create_user('my-user')
- with assert_raises(BotoServerError):
- conn.remove_user_from_group('my-group', 'my-user')
- conn.add_user_to_group('my-group', 'my-user')
- conn.remove_user_from_group('my-group', 'my-user')
-
-
@mock_iam()
def test_create_access_key():
conn = boto.connect_iam()
@@ -230,6 +189,7 @@ def test_delete_user():
conn.create_user('my-user')
conn.delete_user('my-user')
+
@mock_iam()
def test_generate_credential_report():
conn = boto.connect_iam()
@@ -238,6 +198,7 @@ def test_generate_credential_report():
result = conn.generate_credential_report()
result['generate_credential_report_response']['generate_credential_report_result']['state'].should.equal('COMPLETE')
+
@mock_iam()
def test_get_credential_report():
conn = boto.connect_iam()
@@ -249,4 +210,4 @@ def test_get_credential_report():
result = conn.generate_credential_report()
result = conn.get_credential_report()
report = base64.b64decode(result['get_credential_report_response']['get_credential_report_result']['content'].encode('ascii')).decode('ascii')
- report.should.match(r'.*my-user.*')
\ No newline at end of file
+ report.should.match(r'.*my-user.*')
diff --git a/tests/test_iam/test_iam_groups.py b/tests/test_iam/test_iam_groups.py
new file mode 100644
index 000000000..412484a70
--- /dev/null
+++ b/tests/test_iam/test_iam_groups.py
@@ -0,0 +1,72 @@
+from __future__ import unicode_literals
+import boto
+import sure # noqa
+
+from nose.tools import assert_raises
+from boto.exception import BotoServerError
+from moto import mock_iam
+
+
+@mock_iam()
+def test_create_group():
+ conn = boto.connect_iam()
+ conn.create_group('my-group')
+ with assert_raises(BotoServerError):
+ conn.create_group('my-group')
+
+
+@mock_iam()
+def test_get_group():
+ conn = boto.connect_iam()
+ conn.create_group('my-group')
+ conn.get_group('my-group')
+ with assert_raises(BotoServerError):
+ conn.get_group('not-group')
+
+
+@mock_iam()
+def test_get_all_groups():
+ conn = boto.connect_iam()
+ conn.create_group('my-group1')
+ conn.create_group('my-group2')
+ groups = conn.get_all_groups()['list_groups_response']['list_groups_result']['groups']
+ groups.should.have.length_of(2)
+
+
+@mock_iam()
+def test_add_user_to_group():
+ conn = boto.connect_iam()
+ with assert_raises(BotoServerError):
+ conn.add_user_to_group('my-group', 'my-user')
+ conn.create_group('my-group')
+ with assert_raises(BotoServerError):
+ conn.add_user_to_group('my-group', 'my-user')
+ conn.create_user('my-user')
+ conn.add_user_to_group('my-group', 'my-user')
+
+
+@mock_iam()
+def test_remove_user_from_group():
+ conn = boto.connect_iam()
+ with assert_raises(BotoServerError):
+ conn.remove_user_from_group('my-group', 'my-user')
+ conn.create_group('my-group')
+ conn.create_user('my-user')
+ with assert_raises(BotoServerError):
+ conn.remove_user_from_group('my-group', 'my-user')
+ conn.add_user_to_group('my-group', 'my-user')
+ conn.remove_user_from_group('my-group', 'my-user')
+
+
+@mock_iam()
+def test_get_groups_for_user():
+ conn = boto.connect_iam()
+ conn.create_group('my-group1')
+ conn.create_group('my-group2')
+ conn.create_group('other-group')
+ conn.create_user('my-user')
+ conn.add_user_to_group('my-group1', 'my-user')
+ conn.add_user_to_group('my-group2', 'my-user')
+
+ groups = conn.get_groups_for_user('my-user')['list_groups_for_user_response']['list_groups_for_user_result']['groups']
+ groups.should.have.length_of(2)