mirror of
https://github.com/ansible/awx.git
synced 2026-07-01 11:28:02 -02:30
Migrate nested team memberships to direct team memberships (#7005)
* migrate team on team users add setting to prevent team on team cases. remove tests that should fail now * adjust tests for disallowing team on teams * use RoleUserAssignment to retrieve users * assign users with RoleUserAssignment instead * fix broken test * move methods out to utils file. add tests * add missed positional arg * test old rbac system also consolidates * fix test
This commit is contained in:
18
awx/main/migrations/0203_remove_team_of_teams.py
Normal file
18
awx/main/migrations/0203_remove_team_of_teams.py
Normal file
@@ -0,0 +1,18 @@
|
||||
import logging
|
||||
|
||||
from django.db import migrations
|
||||
|
||||
from awx.main.migrations._dab_rbac import consolidate_indirect_user_roles
|
||||
|
||||
logger = logging.getLogger('awx.main.migrations')
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('main', '0202_convert_controller_role_definitions'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RunPython(consolidate_indirect_user_roles, migrations.RunPython.noop),
|
||||
]
|
||||
@@ -1,5 +1,6 @@
|
||||
import json
|
||||
import logging
|
||||
from collections import defaultdict
|
||||
|
||||
from django.apps import apps as global_apps
|
||||
from django.db.models import ForeignKey
|
||||
@@ -403,3 +404,115 @@ def setup_managed_role_definitions(apps, schema_editor):
|
||||
for role_definition in unexpected_role_definitions:
|
||||
logger.info(f'Deleting old managed role definition {role_definition.name}, pk={role_definition.pk}')
|
||||
role_definition.delete()
|
||||
|
||||
|
||||
def get_team_to_team_relationships(apps, team_member_role):
|
||||
"""
|
||||
Find all team-to-team relationships where one team is a member of another.
|
||||
Returns a dict mapping parent_team_id -> [child_team_id, ...]
|
||||
"""
|
||||
team_to_team_relationships = defaultdict(list)
|
||||
|
||||
# Find all team assignments with the Team Member role
|
||||
RoleTeamAssignment = apps.get_model('dab_rbac', 'RoleTeamAssignment')
|
||||
team_assignments = RoleTeamAssignment.objects.filter(role_definition=team_member_role).select_related('team')
|
||||
|
||||
for assignment in team_assignments:
|
||||
parent_team_id = int(assignment.object_id)
|
||||
child_team_id = assignment.team.id
|
||||
team_to_team_relationships[parent_team_id].append(child_team_id)
|
||||
|
||||
return team_to_team_relationships
|
||||
|
||||
|
||||
def get_all_user_members_of_team(apps, team_member_role, team_id, team_to_team_map, visited=None):
|
||||
"""
|
||||
Recursively find all users who are members of a team, including through nested teams.
|
||||
"""
|
||||
if visited is None:
|
||||
visited = set()
|
||||
|
||||
if team_id in visited:
|
||||
return set() # Avoid infinite recursion
|
||||
|
||||
visited.add(team_id)
|
||||
all_users = set()
|
||||
|
||||
# Get direct user assignments to this team
|
||||
RoleUserAssignment = apps.get_model('dab_rbac', 'RoleUserAssignment')
|
||||
user_assignments = RoleUserAssignment.objects.filter(role_definition=team_member_role, object_id=team_id).select_related('user')
|
||||
|
||||
for assignment in user_assignments:
|
||||
all_users.add(assignment.user)
|
||||
|
||||
# Get team-to-team assignments and recursively find their users
|
||||
child_team_ids = team_to_team_map.get(team_id, [])
|
||||
for child_team_id in child_team_ids:
|
||||
nested_users = get_all_user_members_of_team(apps, team_member_role, child_team_id, team_to_team_map, visited.copy())
|
||||
all_users.update(nested_users)
|
||||
|
||||
return all_users
|
||||
|
||||
|
||||
def remove_team_to_team_assignment(apps, team_member_role, parent_team_id, child_team_id):
|
||||
"""
|
||||
Remove team-to-team memberships.
|
||||
"""
|
||||
Team = apps.get_model('main', 'Team')
|
||||
RoleTeamAssignment = apps.get_model('dab_rbac', 'RoleTeamAssignment')
|
||||
|
||||
parent_team = Team.objects.get(id=parent_team_id)
|
||||
child_team = Team.objects.get(id=child_team_id)
|
||||
|
||||
# Remove all team-to-team RoleTeamAssignments
|
||||
RoleTeamAssignment.objects.filter(role_definition=team_member_role, object_id=parent_team_id, team=child_team).delete()
|
||||
|
||||
# Check mirroring Team model for children under member_role
|
||||
parent_team.member_role.children.filter(object_id=child_team_id).delete()
|
||||
|
||||
|
||||
def consolidate_indirect_user_roles(apps, schema_editor):
|
||||
"""
|
||||
A user should have a member role for every team they were indirectly
|
||||
a member of. ex. Team A is a member of Team B. All users in Team A
|
||||
previously were only members of Team A. They should now be members of
|
||||
Team A and Team B.
|
||||
"""
|
||||
|
||||
# get models for membership on teams
|
||||
RoleDefinition = apps.get_model('dab_rbac', 'RoleDefinition')
|
||||
Team = apps.get_model('main', 'Team')
|
||||
|
||||
team_member_role = RoleDefinition.objects.get(name='Team Member')
|
||||
|
||||
team_to_team_map = get_team_to_team_relationships(apps, team_member_role)
|
||||
|
||||
if not team_to_team_map:
|
||||
return # No team-to-team relationships to consolidate
|
||||
|
||||
# Get content type for Team - needed for give_permissions
|
||||
try:
|
||||
from django.contrib.contenttypes.models import ContentType
|
||||
|
||||
team_content_type = ContentType.objects.get_for_model(Team)
|
||||
except ImportError:
|
||||
# Fallback if ContentType is not available
|
||||
ContentType = apps.get_model('contenttypes', 'ContentType')
|
||||
team_content_type = ContentType.objects.get_for_model(Team)
|
||||
|
||||
# Get all users who should be direct members of a team
|
||||
for parent_team_id, child_team_ids in team_to_team_map.items():
|
||||
all_users = get_all_user_members_of_team(apps, team_member_role, parent_team_id, team_to_team_map)
|
||||
|
||||
# Create direct RoleUserAssignments for all users
|
||||
if all_users:
|
||||
give_permissions(apps=apps, rd=team_member_role, users=list(all_users), object_id=parent_team_id, content_type_id=team_content_type.id)
|
||||
|
||||
# Mirror assignments to Team model
|
||||
parent_team = Team.objects.get(id=parent_team_id)
|
||||
for user in all_users:
|
||||
parent_team.member_role.members.add(user.id)
|
||||
|
||||
# Remove all team-to-team assignments for parent team
|
||||
for child_team_id in child_team_ids:
|
||||
remove_team_to_team_assignment(apps, team_member_role, parent_team_id, child_team_id)
|
||||
|
||||
Reference in New Issue
Block a user