From 3cd373f1f9d9bc0ed0443ad10c84fc9123e57920 Mon Sep 17 00:00:00 2001 From: acsbendi Date: Sat, 29 Jun 2019 18:15:01 +0200 Subject: [PATCH 1/4] Created failing tests. --- tests/test_iam/test_iam.py | 62 +++++++++++++++++++++++++++++++++++++- 1 file changed, 61 insertions(+), 1 deletion(-) diff --git a/tests/test_iam/test_iam.py b/tests/test_iam/test_iam.py index 3e1c5914f..608b08c2f 100644 --- a/tests/test_iam/test_iam.py +++ b/tests/test_iam/test_iam.py @@ -306,6 +306,7 @@ def test_create_policy_versions(): SetAsDefault=True) version.get('PolicyVersion').get('Document').should.equal({'some': 'policy'}) version.get('PolicyVersion').get('VersionId').should.equal("v2") + version.get('PolicyVersion').get('IsDefaultVersion').should.be.ok conn.delete_policy_version( PolicyArn="arn:aws:iam::123456789012:policy/TestCreatePolicyVersion", VersionId="v1") @@ -313,6 +314,47 @@ def test_create_policy_versions(): PolicyArn="arn:aws:iam::123456789012:policy/TestCreatePolicyVersion", PolicyDocument='{"some":"policy"}') version.get('PolicyVersion').get('VersionId').should.equal("v3") + version.get('PolicyVersion').get('IsDefaultVersion').shouldnt.be.ok + + +@mock_iam +def test_create_many_policy_versions(): + conn = boto3.client('iam', region_name='us-east-1') + conn.create_policy( + PolicyName="TestCreateManyPolicyVersions", + PolicyDocument='{"some":"policy"}') + for _ in range(0, 4): + conn.create_policy_version( + PolicyArn="arn:aws:iam::123456789012:policy/TestCreateManyPolicyVersions", + PolicyDocument='{"some":"policy"}') + with assert_raises(ClientError): + conn.create_policy_version( + PolicyArn="arn:aws:iam::123456789012:policy/TestCreateManyPolicyVersions", + PolicyDocument='{"some":"policy"}') + + +@mock_iam +def test_set_default_policy_version(): + conn = boto3.client('iam', region_name='us-east-1') + conn.create_policy( + PolicyName="TestSetDefaultPolicyVersion", + PolicyDocument='{"first":"policy"}') + conn.create_policy_version( + PolicyArn="arn:aws:iam::123456789012:policy/TestSetDefaultPolicyVersion", + PolicyDocument='{"second":"policy"}', + SetAsDefault=True) + conn.create_policy_version( + PolicyArn="arn:aws:iam::123456789012:policy/TestSetDefaultPolicyVersion", + PolicyDocument='{"third":"policy"}', + SetAsDefault=True) + versions = conn.list_policy_versions( + PolicyArn="arn:aws:iam::123456789012:policy/TestSetDefaultPolicyVersion") + versions.get('Versions')[0].get('Document').should.equal({'first': 'policy'}) + versions.get('Versions')[0].get('IsDefaultVersion').shouldnt.be.ok + versions.get('Versions')[1].get('Document').should.equal({'second': 'policy'}) + versions.get('Versions')[1].get('IsDefaultVersion').shouldnt.be.ok + versions.get('Versions')[2].get('Document').should.equal({'third': 'policy'}) + versions.get('Versions')[2].get('IsDefaultVersion').should.be.ok @mock_iam @@ -354,6 +396,7 @@ def test_get_policy_version(): PolicyArn="arn:aws:iam::123456789012:policy/TestGetPolicyVersion", VersionId=version.get('PolicyVersion').get('VersionId')) retrieved.get('PolicyVersion').get('Document').should.equal({'some': 'policy'}) + retrieved.get('PolicyVersion').get('IsDefaultVersion').shouldnt.be.ok @mock_iam @@ -400,6 +443,7 @@ def test_list_policy_versions(): versions = conn.list_policy_versions( PolicyArn="arn:aws:iam::123456789012:policy/TestListPolicyVersions") versions.get('Versions')[0].get('VersionId').should.equal('v1') + versions.get('Versions')[0].get('IsDefaultVersion').should.be.ok conn.create_policy_version( PolicyArn="arn:aws:iam::123456789012:policy/TestListPolicyVersions", @@ -409,9 +453,10 @@ def test_list_policy_versions(): PolicyDocument='{"third":"policy"}') versions = conn.list_policy_versions( PolicyArn="arn:aws:iam::123456789012:policy/TestListPolicyVersions") - print(versions.get('Versions')) versions.get('Versions')[1].get('Document').should.equal({'second': 'policy'}) + versions.get('Versions')[1].get('IsDefaultVersion').shouldnt.be.ok versions.get('Versions')[2].get('Document').should.equal({'third': 'policy'}) + versions.get('Versions')[2].get('IsDefaultVersion').shouldnt.be.ok @mock_iam @@ -435,6 +480,21 @@ def test_delete_policy_version(): len(versions.get('Versions')).should.equal(1) +@mock_iam +def test_delete_default_policy_version(): + conn = boto3.client('iam', region_name='us-east-1') + conn.create_policy( + PolicyName="TestDeletePolicyVersion", + PolicyDocument='{"first":"policy"}') + conn.create_policy_version( + PolicyArn="arn:aws:iam::123456789012:policy/TestDeletePolicyVersion", + PolicyDocument='{"second":"policy"}') + with assert_raises(ClientError): + conn.delete_policy_version( + PolicyArn="arn:aws:iam::123456789012:policy/TestDeletePolicyVersion", + VersionId='v1') + + @mock_iam_deprecated() def test_create_user(): conn = boto.connect_iam() From ed01ceddc8a2b9fe816879646170793b16c2db85 Mon Sep 17 00:00:00 2001 From: acsbendi Date: Sat, 29 Jun 2019 18:29:18 +0200 Subject: [PATCH 2/4] Fixed IsDefaultVersion value returned with an uppercase first letter. --- moto/iam/responses.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/moto/iam/responses.py b/moto/iam/responses.py index 05624101a..a0bcb0b56 100644 --- a/moto/iam/responses.py +++ b/moto/iam/responses.py @@ -1144,7 +1144,7 @@ CREATE_POLICY_VERSION_TEMPLATE = """ {{ policy_version.document }} - {{ policy_version.is_default }} + {{ policy_version.is_default | lower }} {{ policy_version.version_id }} {{ policy_version.created_iso_8601 }} From 6f5948af33d2a68604da067182a91c3dbba54602 Mon Sep 17 00:00:00 2001 From: acsbendi Date: Sat, 29 Jun 2019 18:55:19 +0200 Subject: [PATCH 3/4] Fixed is_default is not reset on old default version. --- moto/iam/models.py | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/moto/iam/models.py b/moto/iam/models.py index 86eec73f0..5166071f8 100644 --- a/moto/iam/models.py +++ b/moto/iam/models.py @@ -66,6 +66,13 @@ class Policy(BaseModel): self.create_date = create_date if create_date is not None else datetime.utcnow() self.update_date = update_date if update_date is not None else datetime.utcnow() + def update_default_version(self, new_default_version_id): + for version in self.versions: + if version.version_id == self.default_version_id: + version.is_default = False + break + self.default_version_id = new_default_version_id + @property def created_iso_8601(self): return iso_8601_datetime_with_milliseconds(self.create_date) @@ -760,12 +767,13 @@ class IAMBackend(BaseBackend): policy = self.get_policy(policy_arn) if not policy: raise IAMNotFoundException("Policy not found") + set_as_default = (set_as_default == "true") # convert it to python bool version = PolicyVersion(policy_arn, policy_document, set_as_default) policy.versions.append(version) version.version_id = 'v{0}'.format(policy.next_version_num) policy.next_version_num += 1 if set_as_default: - policy.default_version_id = version.version_id + policy.update_default_version(version.version_id) return version def get_policy_version(self, policy_arn, version_id): @@ -788,8 +796,8 @@ class IAMBackend(BaseBackend): if not policy: raise IAMNotFoundException("Policy not found") if version_id == policy.default_version_id: - raise IAMConflictException( - "Cannot delete the default version of a policy") + raise IAMConflictException(code="DeleteConflict", + message="Cannot delete the default version of a policy.") for i, v in enumerate(policy.versions): if v.version_id == version_id: del policy.versions[i] From c799b1a122d2521fc1e8762aa1d2feb7d658f76e Mon Sep 17 00:00:00 2001 From: acsbendi Date: Sat, 29 Jun 2019 19:01:43 +0200 Subject: [PATCH 4/4] Fixed being able to create more than 5 policy versions. --- moto/iam/exceptions.py | 8 ++++++++ moto/iam/models.py | 6 ++++-- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/moto/iam/exceptions.py b/moto/iam/exceptions.py index 5b13277da..a6a50ac9a 100644 --- a/moto/iam/exceptions.py +++ b/moto/iam/exceptions.py @@ -26,6 +26,14 @@ class IAMReportNotPresentException(RESTError): "ReportNotPresent", message) +class IAMLimitExceededException(RESTError): + code = 400 + + def __init__(self, message): + super(IAMLimitExceededException, self).__init__( + "LimitExceeded", message) + + class MalformedCertificate(RESTError): code = 400 diff --git a/moto/iam/models.py b/moto/iam/models.py index 5166071f8..8e2c7ea46 100644 --- a/moto/iam/models.py +++ b/moto/iam/models.py @@ -13,8 +13,8 @@ from moto.core import BaseBackend, BaseModel from moto.core.utils import iso_8601_datetime_without_milliseconds, iso_8601_datetime_with_milliseconds from .aws_managed_policies import aws_managed_policies_data -from .exceptions import IAMNotFoundException, IAMConflictException, IAMReportNotPresentException, MalformedCertificate, \ - DuplicateTags, TagKeyTooBig, InvalidTagCharacters, TooManyTags, TagValueTooBig +from .exceptions import IAMNotFoundException, IAMConflictException, IAMReportNotPresentException, IAMLimitExceededException, \ + MalformedCertificate, DuplicateTags, TagKeyTooBig, InvalidTagCharacters, TooManyTags, TagValueTooBig from .utils import random_access_key, random_alphanumeric, random_resource_id, random_policy_id ACCOUNT_ID = 123456789012 @@ -767,6 +767,8 @@ class IAMBackend(BaseBackend): policy = self.get_policy(policy_arn) if not policy: raise IAMNotFoundException("Policy not found") + if len(policy.versions) >= 5: + raise IAMLimitExceededException("A managed policy can have up to 5 versions. Before you create a new version, you must delete an existing version.") set_as_default = (set_as_default == "true") # convert it to python bool version = PolicyVersion(policy_arn, policy_document, set_as_default) policy.versions.append(version)