Merge pull request #1224 from JackDanger/jack/implement-attached-group-policies
Implement IAM attached group policies
This commit is contained in:
		
						commit
						0c07c4467a
					
				@ -74,21 +74,13 @@ class ManagedPolicy(Policy):
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    is_attachable = True
 | 
					    is_attachable = True
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def attach_to_role(self, role):
 | 
					    def attach_to(self, obj):
 | 
				
			||||||
        self.attachment_count += 1
 | 
					        self.attachment_count += 1
 | 
				
			||||||
        role.managed_policies[self.name] = self
 | 
					        obj.managed_policies[self.name] = self
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def detach_from_role(self, role):
 | 
					    def detach_from(self, obj):
 | 
				
			||||||
        self.attachment_count -= 1
 | 
					        self.attachment_count -= 1
 | 
				
			||||||
        del role.managed_policies[self.name]
 | 
					        del obj.managed_policies[self.name]
 | 
				
			||||||
 | 
					 | 
				
			||||||
    def attach_to_user(self, user):
 | 
					 | 
				
			||||||
        self.attachment_count += 1
 | 
					 | 
				
			||||||
        user.managed_policies[self.name] = self
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def detach_from_user(self, user):
 | 
					 | 
				
			||||||
        self.attachment_count -= 1
 | 
					 | 
				
			||||||
        del user.managed_policies[self.name]
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class AWSManagedPolicy(ManagedPolicy):
 | 
					class AWSManagedPolicy(ManagedPolicy):
 | 
				
			||||||
@ -249,6 +241,7 @@ class Group(BaseModel):
 | 
				
			|||||||
        )
 | 
					        )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        self.users = []
 | 
					        self.users = []
 | 
				
			||||||
 | 
					        self.managed_policies = {}
 | 
				
			||||||
        self.policies = {}
 | 
					        self.policies = {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def get_cfn_attribute(self, attribute_name):
 | 
					    def get_cfn_attribute(self, attribute_name):
 | 
				
			||||||
@ -423,25 +416,47 @@ class IAMBackend(BaseBackend):
 | 
				
			|||||||
    def attach_role_policy(self, policy_arn, role_name):
 | 
					    def attach_role_policy(self, policy_arn, role_name):
 | 
				
			||||||
        arns = dict((p.arn, p) for p in self.managed_policies.values())
 | 
					        arns = dict((p.arn, p) for p in self.managed_policies.values())
 | 
				
			||||||
        policy = arns[policy_arn]
 | 
					        policy = arns[policy_arn]
 | 
				
			||||||
        policy.attach_to_role(self.get_role(role_name))
 | 
					        policy.attach_to(self.get_role(role_name))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def detach_role_policy(self, policy_arn, role_name):
 | 
					    def detach_role_policy(self, policy_arn, role_name):
 | 
				
			||||||
        arns = dict((p.arn, p) for p in self.managed_policies.values())
 | 
					        arns = dict((p.arn, p) for p in self.managed_policies.values())
 | 
				
			||||||
        try:
 | 
					        try:
 | 
				
			||||||
            policy = arns[policy_arn]
 | 
					            policy = arns[policy_arn]
 | 
				
			||||||
            policy.detach_from_role(self.get_role(role_name))
 | 
					            policy.detach_from(self.get_role(role_name))
 | 
				
			||||||
        except KeyError:
 | 
					        except KeyError:
 | 
				
			||||||
            raise IAMNotFoundException("Policy {0} was not found.".format(policy_arn))
 | 
					            raise IAMNotFoundException("Policy {0} was not found.".format(policy_arn))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def attach_group_policy(self, policy_arn, group_name):
 | 
				
			||||||
 | 
					        arns = dict((p.arn, p) for p in self.managed_policies.values())
 | 
				
			||||||
 | 
					        try:
 | 
				
			||||||
 | 
					            policy = arns[policy_arn]
 | 
				
			||||||
 | 
					        except KeyError:
 | 
				
			||||||
 | 
					            raise IAMNotFoundException("Policy {0} was not found.".format(policy_arn))
 | 
				
			||||||
 | 
					        policy.attach_to(self.get_group(group_name))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def detach_group_policy(self, policy_arn, group_name):
 | 
				
			||||||
 | 
					        arns = dict((p.arn, p) for p in self.managed_policies.values())
 | 
				
			||||||
 | 
					        try:
 | 
				
			||||||
 | 
					            policy = arns[policy_arn]
 | 
				
			||||||
 | 
					        except KeyError:
 | 
				
			||||||
 | 
					            raise IAMNotFoundException("Policy {0} was not found.".format(policy_arn))
 | 
				
			||||||
 | 
					        policy.detach_from(self.get_group(group_name))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def attach_user_policy(self, policy_arn, user_name):
 | 
					    def attach_user_policy(self, policy_arn, user_name):
 | 
				
			||||||
        arns = dict((p.arn, p) for p in self.managed_policies.values())
 | 
					        arns = dict((p.arn, p) for p in self.managed_policies.values())
 | 
				
			||||||
 | 
					        try:
 | 
				
			||||||
            policy = arns[policy_arn]
 | 
					            policy = arns[policy_arn]
 | 
				
			||||||
        policy.attach_to_user(self.get_user(user_name))
 | 
					        except KeyError:
 | 
				
			||||||
 | 
					            raise IAMNotFoundException("Policy {0} was not found.".format(policy_arn))
 | 
				
			||||||
 | 
					        policy.attach_to(self.get_user(user_name))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def detach_user_policy(self, policy_arn, user_name):
 | 
					    def detach_user_policy(self, policy_arn, user_name):
 | 
				
			||||||
        arns = dict((p.arn, p) for p in self.managed_policies.values())
 | 
					        arns = dict((p.arn, p) for p in self.managed_policies.values())
 | 
				
			||||||
 | 
					        try:
 | 
				
			||||||
            policy = arns[policy_arn]
 | 
					            policy = arns[policy_arn]
 | 
				
			||||||
        policy.detach_from_user(self.get_user(user_name))
 | 
					        except KeyError:
 | 
				
			||||||
 | 
					            raise IAMNotFoundException("Policy {0} was not found.".format(policy_arn))
 | 
				
			||||||
 | 
					        policy.detach_from(self.get_user(user_name))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def create_policy(self, description, path, policy_document, policy_name):
 | 
					    def create_policy(self, description, path, policy_document, policy_name):
 | 
				
			||||||
        policy = ManagedPolicy(
 | 
					        policy = ManagedPolicy(
 | 
				
			||||||
@ -458,39 +473,15 @@ class IAMBackend(BaseBackend):
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    def list_attached_role_policies(self, role_name, marker=None, max_items=100, path_prefix='/'):
 | 
					    def list_attached_role_policies(self, role_name, marker=None, max_items=100, path_prefix='/'):
 | 
				
			||||||
        policies = self.get_role(role_name).managed_policies.values()
 | 
					        policies = self.get_role(role_name).managed_policies.values()
 | 
				
			||||||
 | 
					        return self._filter_attached_policies(policies, marker, max_items, path_prefix)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if path_prefix:
 | 
					    def list_attached_group_policies(self, group_name, marker=None, max_items=100, path_prefix='/'):
 | 
				
			||||||
            policies = [p for p in policies if p.path.startswith(path_prefix)]
 | 
					        policies = self.get_group(group_name).managed_policies.values()
 | 
				
			||||||
 | 
					        return self._filter_attached_policies(policies, marker, max_items, path_prefix)
 | 
				
			||||||
        policies = sorted(policies, key=lambda policy: policy.name)
 | 
					 | 
				
			||||||
        start_idx = int(marker) if marker else 0
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        policies = policies[start_idx:start_idx + max_items]
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        if len(policies) < max_items:
 | 
					 | 
				
			||||||
            marker = None
 | 
					 | 
				
			||||||
        else:
 | 
					 | 
				
			||||||
            marker = str(start_idx + max_items)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        return policies, marker
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def list_attached_user_policies(self, user_name, marker=None, max_items=100, path_prefix='/'):
 | 
					    def list_attached_user_policies(self, user_name, marker=None, max_items=100, path_prefix='/'):
 | 
				
			||||||
        policies = self.get_user(user_name).managed_policies.values()
 | 
					        policies = self.get_user(user_name).managed_policies.values()
 | 
				
			||||||
 | 
					        return self._filter_attached_policies(policies, marker, max_items, path_prefix)
 | 
				
			||||||
        if path_prefix:
 | 
					 | 
				
			||||||
            policies = [p for p in policies if p.path.startswith(path_prefix)]
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        policies = sorted(policies, key=lambda policy: policy.name)
 | 
					 | 
				
			||||||
        start_idx = int(marker) if marker else 0
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        policies = policies[start_idx:start_idx + max_items]
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        if len(policies) < max_items:
 | 
					 | 
				
			||||||
            marker = None
 | 
					 | 
				
			||||||
        else:
 | 
					 | 
				
			||||||
            marker = str(start_idx + max_items)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        return policies, marker
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def list_policies(self, marker, max_items, only_attached, path_prefix, scope):
 | 
					    def list_policies(self, marker, max_items, only_attached, path_prefix, scope):
 | 
				
			||||||
        policies = self.managed_policies.values()
 | 
					        policies = self.managed_policies.values()
 | 
				
			||||||
@ -504,6 +495,9 @@ class IAMBackend(BaseBackend):
 | 
				
			|||||||
            policies = [p for p in policies if not isinstance(
 | 
					            policies = [p for p in policies if not isinstance(
 | 
				
			||||||
                p, AWSManagedPolicy)]
 | 
					                p, AWSManagedPolicy)]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return self._filter_attached_policies(policies, marker, max_items, path_prefix)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def _filter_attached_policies(self, policies, marker, max_items, path_prefix):
 | 
				
			||||||
        if path_prefix:
 | 
					        if path_prefix:
 | 
				
			||||||
            policies = [p for p in policies if p.path.startswith(path_prefix)]
 | 
					            policies = [p for p in policies if p.path.startswith(path_prefix)]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -20,6 +20,20 @@ class IamResponse(BaseResponse):
 | 
				
			|||||||
        template = self.response_template(GENERIC_EMPTY_TEMPLATE)
 | 
					        template = self.response_template(GENERIC_EMPTY_TEMPLATE)
 | 
				
			||||||
        return template.render(name="DetachRolePolicyResponse")
 | 
					        return template.render(name="DetachRolePolicyResponse")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def attach_group_policy(self):
 | 
				
			||||||
 | 
					        policy_arn = self._get_param('PolicyArn')
 | 
				
			||||||
 | 
					        group_name = self._get_param('GroupName')
 | 
				
			||||||
 | 
					        iam_backend.attach_group_policy(policy_arn, group_name)
 | 
				
			||||||
 | 
					        template = self.response_template(ATTACH_GROUP_POLICY_TEMPLATE)
 | 
				
			||||||
 | 
					        return template.render()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def detach_group_policy(self):
 | 
				
			||||||
 | 
					        policy_arn = self._get_param('PolicyArn')
 | 
				
			||||||
 | 
					        group_name = self._get_param('GroupName')
 | 
				
			||||||
 | 
					        iam_backend.detach_group_policy(policy_arn, group_name)
 | 
				
			||||||
 | 
					        template = self.response_template(DETACH_GROUP_POLICY_TEMPLATE)
 | 
				
			||||||
 | 
					        return template.render()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def attach_user_policy(self):
 | 
					    def attach_user_policy(self):
 | 
				
			||||||
        policy_arn = self._get_param('PolicyArn')
 | 
					        policy_arn = self._get_param('PolicyArn')
 | 
				
			||||||
        user_name = self._get_param('UserName')
 | 
					        user_name = self._get_param('UserName')
 | 
				
			||||||
@ -54,6 +68,17 @@ class IamResponse(BaseResponse):
 | 
				
			|||||||
        template = self.response_template(LIST_ATTACHED_ROLE_POLICIES_TEMPLATE)
 | 
					        template = self.response_template(LIST_ATTACHED_ROLE_POLICIES_TEMPLATE)
 | 
				
			||||||
        return template.render(policies=policies, marker=marker)
 | 
					        return template.render(policies=policies, marker=marker)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def list_attached_group_policies(self):
 | 
				
			||||||
 | 
					        marker = self._get_param('Marker')
 | 
				
			||||||
 | 
					        max_items = self._get_int_param('MaxItems', 100)
 | 
				
			||||||
 | 
					        path_prefix = self._get_param('PathPrefix', '/')
 | 
				
			||||||
 | 
					        group_name = self._get_param('GroupName')
 | 
				
			||||||
 | 
					        policies, marker = iam_backend.list_attached_group_policies(
 | 
				
			||||||
 | 
					            group_name, marker=marker, max_items=max_items,
 | 
				
			||||||
 | 
					            path_prefix=path_prefix)
 | 
				
			||||||
 | 
					        template = self.response_template(LIST_ATTACHED_GROUP_POLICIES_TEMPLATE)
 | 
				
			||||||
 | 
					        return template.render(policies=policies, marker=marker)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def list_attached_user_policies(self):
 | 
					    def list_attached_user_policies(self):
 | 
				
			||||||
        marker = self._get_param('Marker')
 | 
					        marker = self._get_param('Marker')
 | 
				
			||||||
        max_items = self._get_int_param('MaxItems', 100)
 | 
					        max_items = self._get_int_param('MaxItems', 100)
 | 
				
			||||||
@ -520,6 +545,18 @@ DETACH_USER_POLICY_TEMPLATE = """<DetachUserPolicyResponse>
 | 
				
			|||||||
  </ResponseMetadata>
 | 
					  </ResponseMetadata>
 | 
				
			||||||
</DetachUserPolicyResponse>"""
 | 
					</DetachUserPolicyResponse>"""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					ATTACH_GROUP_POLICY_TEMPLATE = """<AttachGroupPolicyResponse>
 | 
				
			||||||
 | 
					  <ResponseMetadata>
 | 
				
			||||||
 | 
					    <RequestId>7a62c49f-347e-4fc4-9331-6e8eEXAMPLE</RequestId>
 | 
				
			||||||
 | 
					  </ResponseMetadata>
 | 
				
			||||||
 | 
					</AttachGroupPolicyResponse>"""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					DETACH_GROUP_POLICY_TEMPLATE = """<DetachGroupPolicyResponse>
 | 
				
			||||||
 | 
					  <ResponseMetadata>
 | 
				
			||||||
 | 
					    <RequestId>7a62c49f-347e-4fc4-9331-6e8eEXAMPLE</RequestId>
 | 
				
			||||||
 | 
					  </ResponseMetadata>
 | 
				
			||||||
 | 
					</DetachGroupPolicyResponse>"""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
CREATE_POLICY_TEMPLATE = """<CreatePolicyResponse>
 | 
					CREATE_POLICY_TEMPLATE = """<CreatePolicyResponse>
 | 
				
			||||||
  <CreatePolicyResult>
 | 
					  <CreatePolicyResult>
 | 
				
			||||||
    <Policy>
 | 
					    <Policy>
 | 
				
			||||||
@ -560,6 +597,28 @@ LIST_ATTACHED_ROLE_POLICIES_TEMPLATE = """<ListAttachedRolePoliciesResponse>
 | 
				
			|||||||
  </ResponseMetadata>
 | 
					  </ResponseMetadata>
 | 
				
			||||||
</ListAttachedRolePoliciesResponse>"""
 | 
					</ListAttachedRolePoliciesResponse>"""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					LIST_ATTACHED_GROUP_POLICIES_TEMPLATE = """<ListAttachedGroupPoliciesResponse>
 | 
				
			||||||
 | 
					  <ListAttachedGroupPoliciesResult>
 | 
				
			||||||
 | 
					    {% if marker is none %}
 | 
				
			||||||
 | 
					    <IsTruncated>false</IsTruncated>
 | 
				
			||||||
 | 
					    {% else %}
 | 
				
			||||||
 | 
					    <IsTruncated>true</IsTruncated>
 | 
				
			||||||
 | 
					    <Marker>{{ marker }}</Marker>
 | 
				
			||||||
 | 
					    {% endif %}
 | 
				
			||||||
 | 
					    <AttachedPolicies>
 | 
				
			||||||
 | 
					      {% for policy in policies %}
 | 
				
			||||||
 | 
					      <member>
 | 
				
			||||||
 | 
					        <PolicyName>{{ policy.name }}</PolicyName>
 | 
				
			||||||
 | 
					        <PolicyArn>{{ policy.arn }}</PolicyArn>
 | 
				
			||||||
 | 
					      </member>
 | 
				
			||||||
 | 
					      {% endfor %}
 | 
				
			||||||
 | 
					    </AttachedPolicies>
 | 
				
			||||||
 | 
					  </ListAttachedGroupPoliciesResult>
 | 
				
			||||||
 | 
					  <ResponseMetadata>
 | 
				
			||||||
 | 
					    <RequestId>7a62c49f-347e-4fc4-9331-6e8eEXAMPLE</RequestId>
 | 
				
			||||||
 | 
					  </ResponseMetadata>
 | 
				
			||||||
 | 
					</ListAttachedGroupPoliciesResponse>"""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
LIST_ATTACHED_USER_POLICIES_TEMPLATE = """<ListAttachedUserPoliciesResponse>
 | 
					LIST_ATTACHED_USER_POLICIES_TEMPLATE = """<ListAttachedUserPoliciesResponse>
 | 
				
			||||||
  <ListAttachedUserPoliciesResult>
 | 
					  <ListAttachedUserPoliciesResult>
 | 
				
			||||||
    {% if marker is none %}
 | 
					    {% if marker is none %}
 | 
				
			||||||
 | 
				
			|||||||
@ -82,6 +82,26 @@ def test_put_group_policy():
 | 
				
			|||||||
    conn.put_group_policy('my-group', 'my-policy', '{"some": "json"}')
 | 
					    conn.put_group_policy('my-group', 'my-policy', '{"some": "json"}')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@mock_iam
 | 
				
			||||||
 | 
					def test_attach_group_policies():
 | 
				
			||||||
 | 
					    conn = boto3.client('iam', region_name='us-east-1')
 | 
				
			||||||
 | 
					    conn.create_group(GroupName='my-group')
 | 
				
			||||||
 | 
					    conn.list_attached_group_policies(GroupName='my-group')['AttachedPolicies'].should.be.empty
 | 
				
			||||||
 | 
					    policy_arn = 'arn:aws:iam::aws:policy/service-role/AmazonElasticMapReduceforEC2Role'
 | 
				
			||||||
 | 
					    conn.list_attached_group_policies(GroupName='my-group')['AttachedPolicies'].should.be.empty
 | 
				
			||||||
 | 
					    conn.attach_group_policy(GroupName='my-group', PolicyArn=policy_arn)
 | 
				
			||||||
 | 
					    conn.list_attached_group_policies(GroupName='my-group')['AttachedPolicies'].should.equal(
 | 
				
			||||||
 | 
					        [
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                'PolicyName': 'AmazonElasticMapReduceforEC2Role',
 | 
				
			||||||
 | 
					                'PolicyArn': policy_arn,
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        ])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    conn.detach_group_policy(GroupName='my-group', PolicyArn=policy_arn)
 | 
				
			||||||
 | 
					    conn.list_attached_group_policies(GroupName='my-group')['AttachedPolicies'].should.be.empty
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@mock_iam_deprecated()
 | 
					@mock_iam_deprecated()
 | 
				
			||||||
def test_get_group_policy():
 | 
					def test_get_group_policy():
 | 
				
			||||||
    conn = boto.connect_iam()
 | 
					    conn = boto.connect_iam()
 | 
				
			||||||
@ -90,7 +110,8 @@ def test_get_group_policy():
 | 
				
			|||||||
        conn.get_group_policy('my-group', 'my-policy')
 | 
					        conn.get_group_policy('my-group', 'my-policy')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    conn.put_group_policy('my-group', 'my-policy', '{"some": "json"}')
 | 
					    conn.put_group_policy('my-group', 'my-policy', '{"some": "json"}')
 | 
				
			||||||
    policy = conn.get_group_policy('my-group', 'my-policy')
 | 
					    conn.get_group_policy('my-group', 'my-policy')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@mock_iam_deprecated()
 | 
					@mock_iam_deprecated()
 | 
				
			||||||
def test_get_all_group_policies():
 | 
					def test_get_all_group_policies():
 | 
				
			||||||
@ -107,6 +128,6 @@ def test_get_all_group_policies():
 | 
				
			|||||||
def test_list_group_policies():
 | 
					def test_list_group_policies():
 | 
				
			||||||
    conn = boto3.client('iam', region_name='us-east-1')
 | 
					    conn = boto3.client('iam', region_name='us-east-1')
 | 
				
			||||||
    conn.create_group(GroupName='my-group')
 | 
					    conn.create_group(GroupName='my-group')
 | 
				
			||||||
    policies = conn.list_group_policies(GroupName='my-group')['PolicyNames'].should.be.empty
 | 
					    conn.list_group_policies(GroupName='my-group')['PolicyNames'].should.be.empty
 | 
				
			||||||
    conn.put_group_policy(GroupName='my-group', PolicyName='my-policy', PolicyDocument='{"some": "json"}')
 | 
					    conn.put_group_policy(GroupName='my-group', PolicyName='my-policy', PolicyDocument='{"some": "json"}')
 | 
				
			||||||
    policies = conn.list_group_policies(GroupName='my-group')['PolicyNames'].should.equal(['my-policy'])
 | 
					    conn.list_group_policies(GroupName='my-group')['PolicyNames'].should.equal(['my-policy'])
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user