diff --git a/awx/main/access.py b/awx/main/access.py index 6f621cab46..bf74f2a491 100644 --- a/awx/main/access.py +++ b/awx/main/access.py @@ -2523,6 +2523,14 @@ class RoleAccess(BaseAccess): if not check_user_access(self.user, sub_obj_resource.__class__, 'read', sub_obj_resource): return False + # Being a user in the member_role or admin_role of an organization grants + # administrators of that Organization the ability to edit that user. To prevent + # unwanted escalations lets ensure that the Organization administartor has the abilty + # to admin the user being added to the role. + if isinstance(obj.content_object, Organization) and obj.role_field in ['member_role', 'admin_role']: + if not UserAccess(self.user).can_admin(sub_obj, None): + return False + if isinstance(obj.content_object, ResourceMixin) and \ self.user in obj.content_object.admin_role: return True diff --git a/awx/main/tests/functional/test_rbac_role.py b/awx/main/tests/functional/test_rbac_role.py index 96484f9fee..438a72182b 100644 --- a/awx/main/tests/functional/test_rbac_role.py +++ b/awx/main/tests/functional/test_rbac_role.py @@ -50,3 +50,15 @@ def test_visible_roles(admin_user, system_auditor, rando, organization, project) assert rando not in project.admin_role assert access.can_read(project.admin_role) assert project.admin_role in Role.visible_roles(rando) + + +@pytest.mark.django_db +def test_org_user_role_attach(user, organization): + admin = user('admin') + nonmember = user('nonmember') + + organization.admin_role.members.add(admin) + + access = RoleAccess(admin) + assert not access.can_attach(organization.member_role, nonmember, 'members', None) + assert not access.can_attach(organization.admin_role, nonmember, 'members', None)