From be5f0414160ca254d9c6df5e13917d52969c269b Mon Sep 17 00:00:00 2001 From: Steve Pulec Date: Thu, 30 Apr 2015 19:32:53 -0400 Subject: [PATCH] Add IAM list_groups and list_groups_for_user. Closes #343. --- moto/iam/models.py | 33 +++++++++----- moto/iam/responses.py | 53 ++++++++++++++++++++++- tests/test_iam/test_iam.py | 55 ++++------------------- tests/test_iam/test_iam_groups.py | 72 +++++++++++++++++++++++++++++++ 4 files changed, 155 insertions(+), 58 deletions(-) create mode 100644 tests/test_iam/test_iam_groups.py 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)