From b8ba7980a025d029c47b21b4e1f19c7aaae25bac Mon Sep 17 00:00:00 2001 From: Travis Truman Date: Tue, 21 May 2019 12:44:06 -0400 Subject: [PATCH] =?UTF-8?q?Adding=20support=20for=20specifying=20a=20Permi?= =?UTF-8?q?ssionsBoundary=20ARN=20in=20calls=20to=20i=E2=80=A6=20(#2182)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Adding support for specifying a PermissionsBoundary ARN in calls to iam.create_role Closes #2181 * Correcting whitespace error * Adding support for Role PermissionsBoundary to be returned from calls to list_roles * Raise ClientError when a bad permissions boundary ARN is supplied --- moto/iam/models.py | 14 +++++++++++--- moto/iam/responses.py | 16 +++++++++++++++- tests/test_iam/test_iam.py | 21 +++++++++++++++++++-- 3 files changed, 45 insertions(+), 6 deletions(-) diff --git a/moto/iam/models.py b/moto/iam/models.py index e36b41459..095bbab29 100644 --- a/moto/iam/models.py +++ b/moto/iam/models.py @@ -9,6 +9,7 @@ from cryptography import x509 from cryptography.hazmat.backends import default_backend import pytz +from moto.core.exceptions import RESTError from moto.core import BaseBackend, BaseModel from moto.core.utils import iso_8601_datetime_without_milliseconds @@ -131,7 +132,7 @@ class InlinePolicy(Policy): class Role(BaseModel): - def __init__(self, role_id, name, assume_role_policy_document, path): + def __init__(self, role_id, name, assume_role_policy_document, path, permissions_boundary): self.id = role_id self.name = name self.assume_role_policy_document = assume_role_policy_document @@ -141,6 +142,7 @@ class Role(BaseModel): self.create_date = datetime.now(pytz.utc) self.tags = {} self.description = "" + self.permissions_boundary = permissions_boundary @classmethod def create_from_cloudformation_json(cls, resource_name, cloudformation_json, region_name): @@ -150,6 +152,7 @@ class Role(BaseModel): role_name=resource_name, assume_role_policy_document=properties['AssumeRolePolicyDocument'], path=properties.get('Path', '/'), + permissions_boundary=properties.get('PermissionsBoundary', '') ) policies = properties.get('Policies', []) @@ -470,6 +473,8 @@ class IAMBackend(BaseBackend): self.managed_policies = self._init_managed_policies() self.account_aliases = [] self.saml_providers = {} + self.policy_arn_regex = re.compile( + r'^arn:aws:iam::[0-9]*:policy/.*$') super(IAMBackend, self).__init__() def _init_managed_policies(self): @@ -587,9 +592,12 @@ class IAMBackend(BaseBackend): return policies, marker - def create_role(self, role_name, assume_role_policy_document, path): + def create_role(self, role_name, assume_role_policy_document, path, permissions_boundary): role_id = random_resource_id() - role = Role(role_id, role_name, assume_role_policy_document, path) + if permissions_boundary and not self.policy_arn_regex.match(permissions_boundary): + raise RESTError('InvalidParameterValue', 'Value ({}) for parameter PermissionsBoundary is invalid.'.format(permissions_boundary)) + + role = Role(role_id, role_name, assume_role_policy_document, path, permissions_boundary) self.roles[role_id] = role return role diff --git a/moto/iam/responses.py b/moto/iam/responses.py index e5b4c9070..8d2a557cb 100644 --- a/moto/iam/responses.py +++ b/moto/iam/responses.py @@ -175,9 +175,11 @@ class IamResponse(BaseResponse): path = self._get_param('Path') assume_role_policy_document = self._get_param( 'AssumeRolePolicyDocument') + permissions_boundary = self._get_param( + 'PermissionsBoundary') role = iam_backend.create_role( - role_name, assume_role_policy_document, path) + role_name, assume_role_policy_document, path, permissions_boundary) template = self.response_template(CREATE_ROLE_TEMPLATE) return template.render(role=role) @@ -1000,6 +1002,12 @@ CREATE_ROLE_TEMPLATE = """