diff --git a/moto/iam/models.py b/moto/iam/models.py index e949cf173..aaa199b9e 100644 --- a/moto/iam/models.py +++ b/moto/iam/models.py @@ -1,7 +1,9 @@ from __future__ import unicode_literals -from moto.core import BaseBackend -from .utils import random_resource_id +from boto.exception import BotoServerError +from moto.core import BaseBackend +from .utils import random_access_key, random_alhpnumeric, random_resource_id +from datetime import datetime class Role(object): @@ -65,12 +67,83 @@ class Certificate(object): return self.name +class AccessKey(object): + def __init__(self, user_name): + self.user_name = user_name + self.access_key_id = random_access_key() + self.secret_access_key = random_alhpnumeric(32) + self.status = 'Active' + self.create_date = datetime.strftime( + datetime.utcnow(), + "%Y-%m-%d-%H-%M-%S" + ) + + +class Group(object): + def __init__(self, name, path='/'): + self.name = name + self.id = random_resource_id() + self.path = path + self.created = datetime.strftime( + datetime.utcnow(), + "%Y-%m-%d-%H-%M-%S" + ) + + self.users = [] + + +class User(object): + def __init__(self, name, path='/'): + self.name = name + self.id = random_resource_id() + self.path = path + self.created = datetime.strftime( + datetime.utcnow(), + "%Y-%m-%d-%H-%M-%S" + ) + + self.policies = {} + self.access_keys = [] + + def get_policy(self, policy_name): + policy_json = None + try: + policy_json = self.policies[policy_name] + except: + raise BotoServerError(404, 'Not Found') + + return { + 'policy_name': policy_name, + 'policy_document': policy_json, + 'user_name': self.name, + } + + def put_policy(self, policy_name, policy_json): + self.policies[policy_name] = policy_json + + def delete_policy(self, policy_name): + if policy_name not in self.policies: + raise BotoServerError(404, 'Not Found') + + del self.policies[policy_name] + + def create_access_key(self): + access_key = AccessKey(self.name) + self.access_keys.append(access_key) + return access_key + + def get_all_access_keys(self): + return self.access_keys + + class IAMBackend(BaseBackend): def __init__(self): self.instance_profiles = {} self.roles = {} self.certificates = {} + self.groups = {} + self.users = {} super(IAMBackend, self).__init__() def create_role(self, role_name, assume_role_policy_document, path, policies): @@ -125,4 +198,105 @@ class IAMBackend(BaseBackend): if name == cert.cert_name: return cert + def create_group(self, group_name, path='/'): + if group_name in self.groups: + raise BotoServerError(409, 'Conflict') + + group = Group(group_name, path) + self.groups[group_name] = group + return group + + def get_group(self, group_name, marker=None, max_items=None): + + group = None + try: + group = self.groups[group_name] + except KeyError: + raise BotoServerError(404, 'Not Found') + + return group + + def create_user(self, user_name, path='/'): + if user_name in self.users: + raise BotoServerError(409, 'Conflict') + + user = User(user_name, path) + self.users[user_name] = user + return user + + def add_user_to_group(self, group_name, user_name): + group = None + user = None + + try: + group = self.groups[group_name] + user = self.users[user_name] + except KeyError: + raise BotoServerError(404, 'Not Found') + + group.users.append(user) + + def remove_user_from_group(self, group_name, user_name): + group = None + user = None + + try: + group = self.groups[group_name] + user = self.users[user_name] + group.users.remove(user) + except (KeyError, ValueError): + raise BotoServerError(404, 'Not Found') + + def get_user_policy(self, user_name, policy_name): + policy = None + try: + user = self.users[user_name] + policy = user.get_policy(policy_name) + except KeyError: + raise BotoServerError(404, 'Not Found') + + return policy + + def put_user_policy(self, user_name, policy_name, policy_json): + try: + user = self.users[user_name] + user.put_policy(policy_name, policy_json) + except KeyError: + raise BotoServerError(404, 'Not Found') + + def delete_user_policy(self, user_name, policy_name): + try: + user = self.users[user_name] + user.delete_policy(policy_name) + except KeyError: + raise BotoServerError(404, 'Not Found') + + def create_access_key(self, user_name=None): + key = None + try: + user = self.users[user_name] + key = user.create_access_key() + except KeyError: + raise BotoServerError(404, 'Not Found') + + print('username={}'.format(key.user_name)) + return key + + def get_all_access_keys(self, user_name, marker=None, max_items=None): + keys = None + try: + user = self.users[user_name] + keys = user.get_all_access_keys() + except KeyError: + raise BotoServerError(404, 'Not Found') + + return keys + + def delete_user(self, user_name): + try: + del self.users[user_name] + except KeyError: + raise BotoServerError(404, 'Not Found') + + iam_backend = IAMBackend() diff --git a/moto/iam/responses.py b/moto/iam/responses.py index 35f1ad09b..bac627361 100644 --- a/moto/iam/responses.py +++ b/moto/iam/responses.py @@ -83,6 +83,100 @@ class IamResponse(BaseResponse): template = Template(GET_SERVER_CERTIFICATE_TEMPLATE) return template.render(certificate=cert) + def create_group(self): + group_name = self._get_param('GroupName') + path = self._get_param('Path') + + group = iam_backend.create_group(group_name, path) + template = Template(CREATE_GROUP_TEMPLATE) + return template.render(group=group) + + def get_group(self): + group_name = self._get_param('GroupName') + + group = iam_backend.get_group(group_name) + template = Template(GET_GROUP_TEMPLATE) + return template.render(group=group) + + def create_user(self): + user_name = self._get_param('UserName') + path = self._get_param('Path') + + user = iam_backend.create_user(user_name, path) + template = Template(USER_TEMPLATE) + return template.render(action='Create', user=user) + + def add_user_to_group(self): + group_name = self._get_param('GroupName') + user_name = self._get_param('UserName') + + iam_backend.add_user_to_group(group_name, user_name) + template = Template(GENERIC_EMPTY_TEMPLATE) + return template.render(name='AddUserToGroup') + + def remove_user_from_group(self): + group_name = self._get_param('GroupName') + user_name = self._get_param('UserName') + + iam_backend.remove_user_from_group(group_name, user_name) + template = Template(GENERIC_EMPTY_TEMPLATE) + return template.render(name='RemoveUserFromGroup') + + def get_user_policy(self): + user_name = self._get_param('UserName') + policy_name = self._get_param('PolicyName') + + policy_document = iam_backend.get_user_policy(user_name, policy_name) + template = Template(GET_USER_POLICY_TEMPLATE) + return template.render( + user_name=user_name, + policy_name=policy_name, + policy_document=policy_document + ) + + def put_user_policy(self): + user_name = self._get_param('UserName') + policy_name = self._get_param('PolicyName') + policy_document = self._get_param('PolicyDocument') + + iam_backend.put_user_policy(user_name, policy_name, policy_document) + template = Template(GENERIC_EMPTY_TEMPLATE) + return template.render(name='PutUserPolicy') + + def delete_user_policy(self): + user_name = self._get_param('UserName') + policy_name = self._get_param('PolicyName') + + iam_backend.delete_user_policy(user_name, policy_name) + template = Template(GENERIC_EMPTY_TEMPLATE) + return template.render(name='DeleteUserPolicy') + + def create_access_key(self): + user_name = self._get_param('UserName') + + key = iam_backend.create_access_key(user_name) + template = Template(CREATE_ACCESS_KEY_TEMPLATE) + return template.render(key=key) + + def list_access_keys(self): + user_name = self._get_param('UserName') + + keys = iam_backend.get_all_access_keys(user_name) + template = Template(LIST_ACCESS_KEYS_TEMPLATE) + return template.render(user_name=user_name, keys=keys) + + def delete_user(self): + user_name = self._get_param('UserName') + iam_backend.delete_user(user_name) + template = Template(GENERIC_EMPTY_TEMPLATE) + return template.render(name='DeleteUser') + + +GENERIC_EMPTY_TEMPLATE = """<{{ name }}Response> + + 7a62c49f-347e-4fc4-9331-6e8eEXAMPLE + +""" CREATE_INSTANCE_PROFILE_TEMPLATE = """ @@ -275,3 +369,106 @@ GET_SERVER_CERTIFICATE_TEMPLATE = """ """ +CREATE_GROUP_TEMPLATE = """ + + + {{ group.path }} + {{ group.name }} + {{ group.id }} + arn:aws:iam::123456789012:group/{{ group.path }} + + + + 7a62c49f-347e-4fc4-9331-6e8eEXAMPLE + +""" + +GET_GROUP_TEMPLATE = """ + + + {{ group.path }} + {{ group.name }} + {{ group.id }} + arn:aws:iam::123456789012:group/{{ group.path }} + + + {% for user in group.users %} + + {{ user.path }} + {{ user.name }} + {{ user.id }} + + arn:aws:iam::123456789012:user/{{ user.path }}/{{ user.name}} + + + {% endfor %} + + false + + + 7a62c49f-347e-4fc4-9331-6e8eEXAMPLE + +""" + +USER_TEMPLATE = """<{{ action }}UserResponse> + <{{ action }}UserResult> + + {{ user.path }} + {{ user.name }} + {{ user.id }} + arn:aws:iam::123456789012:user/{{ user.path }}/{{ user.name }} + + + + + 7a62c49f-347e-4fc4-9331-6e8eEXAMPLE + +""" + +GET_USER_POLICY_TEMPLATE = """ + + {{ user_name }} + {{ policy_name }} + + {{ policy_document }} + + + + 7a62c49f-347e-4fc4-9331-6e8eEXAMPLE + +""" + +CREATE_ACCESS_KEY_TEMPLATE = """ + + + {{ key.user_name }} + {{ key.access_key_id }} + {{ key.status }} + + {{ key.secret_access_key }} + + + + + 7a62c49f-347e-4fc4-9331-6e8eEXAMPLE + +""" + +LIST_ACCESS_KEYS_TEMPLATE = """ + + {{ user_name }} + + {% for key in keys %} + + {{ user_name }} + {{ key.access_key_id }} + {{ key.status }} + + {% endfor %} + + false + + + 7a62c49f-347e-4fc4-9331-6e8eEXAMPLE + +""" diff --git a/moto/iam/utils.py b/moto/iam/utils.py index 684548f60..671de8f47 100644 --- a/moto/iam/utils.py +++ b/moto/iam/utils.py @@ -4,8 +4,24 @@ import string import six +def random_alhpnumeric(length): + return ''.join(unicode( + random.choice( + string.ascii_letters + string.digits + )) for _ in range(length) + ) + + def random_resource_id(): size = 20 chars = list(range(10)) + list(string.ascii_lowercase) return ''.join(six.text_type(random.choice(chars)) for x in range(size)) + + +def random_access_key(): + return ''.join(unicode( + random.choice( + string.ascii_uppercase + string.digits + )) for _ in range(16) + ) diff --git a/tests/test_iam/test_iam.py b/tests/test_iam/test_iam.py index c2d8c2480..91359cf72 100644 --- a/tests/test_iam/test_iam.py +++ b/tests/test_iam/test_iam.py @@ -1,8 +1,10 @@ from __future__ import unicode_literals import boto - import sure # noqa +from nose.tools import assert_raises, assert_equals, assert_not_equals +from boto.exception import BotoServerError + from moto import mock_iam @@ -58,3 +60,80 @@ 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_create_group(): + conn = boto.connect_iam() + conn.create_group('my-group') + 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') + assert_raises(BotoServerError, conn.get_group, 'not-group') + + +@mock_iam() +def test_create_user(): + conn = boto.connect_iam() + conn.create_user('my-user') + assert_raises(BotoServerError, conn.create_user, 'my-user') + + +@mock_iam() +def test_add_user_to_group(): + conn = boto.connect_iam() + assert_raises(BotoServerError, conn.add_user_to_group, 'my-group', 'my-user') + conn.create_group('my-group') + 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() + assert_raises(BotoServerError, conn.remove_user_from_group, 'my-group', 'my-user') + conn.create_group('my-group') + conn.create_user('my-user') + 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() + assert_raises(BotoServerError, conn.create_access_key, 'my-user') + conn.create_user('my-user') + conn.create_access_key('my-user') + + +@mock_iam() +def test_get_all_access_keys(): + conn = boto.connect_iam() + conn.create_user('my-user') + response = conn.get_all_access_keys('my-user') + assert_equals( + response['list_access_keys_response']['list_access_keys_result']['access_key_metadata'], + [] + ) + conn.create_access_key('my-user') + response = conn.get_all_access_keys('my-user') + assert_not_equals( + response['list_access_keys_response']['list_access_keys_result']['access_key_metadata'], + [] + ) + + + +@mock_iam() +def test_delete_user(): + conn = boto.connect_iam() + assert_raises(BotoServerError, conn.delete_user, 'my-user') + conn.create_user('my-user') + conn.delete_user('my-user')