summaryrefslogtreecommitdiffstats
path: root/tools/scripts
diff options
context:
space:
mode:
authorJeff Bradberry <jeff.bradberry@gmail.com>2024-05-07 22:18:55 +0200
committerJeff Bradberry <jeff.bradberry@gmail.com>2024-06-10 22:36:22 +0200
commitf613b76baa784fa0f22e5f1e9000659f935f3122 (patch)
treea073e474f88d3f85cbda2f3939696ea30d692a16 /tools/scripts
parentExclude the team grant false positives (diff)
downloadawx-f613b76baa784fa0f22e5f1e9000659f935f3122.tar.xz
awx-f613b76baa784fa0f22e5f1e9000659f935f3122.zip
Modify the role parent check logic to stay in the roles as much as possible
since the foreign keys to the roles from the resources can make us go wrong almost immediately.
Diffstat (limited to 'tools/scripts')
-rw-r--r--tools/scripts/ig-hotfix/role_check.py46
1 files changed, 35 insertions, 11 deletions
diff --git a/tools/scripts/ig-hotfix/role_check.py b/tools/scripts/ig-hotfix/role_check.py
index 32d38d2651..776bd74cd5 100644
--- a/tools/scripts/ig-hotfix/role_check.py
+++ b/tools/scripts/ig-hotfix/role_check.py
@@ -3,6 +3,7 @@ import json
import sys
from django.contrib.contenttypes.models import ContentType
+from django.db.models.fields.related_descriptors import ManyToManyDescriptor
from awx.main.fields import ImplicitRoleField
from awx.main.models.rbac import Role
@@ -14,6 +15,20 @@ crosslinked = defaultdict(lambda: defaultdict(dict))
orphaned_roles = []
+def resolve(obj, path):
+ fname, _, path = path.partition('.')
+ new_obj = getattr(obj, fname, None)
+ if new_obj is None:
+ return set()
+ if not path:
+ return {new_obj,}
+
+ if isinstance(new_obj, ManyToManyDescriptor):
+ return {x for o in new_obj.all() for x in resolve(o, path)}
+
+ return resolve(new_obj, path)
+
+
for ct in ContentType.objects.order_by('id'):
cls = ct.model_class()
if cls is None:
@@ -62,19 +77,28 @@ for r in Role.objects.exclude(role_field__startswith='system_').order_by('id'):
continue
# Check the resource's role field parents for consistency with Role.parents.all().
- # f._resolve_parent_roles() walks the f.parent_role list, splitting on dots and recursively
- # getting those resources as well, until we are down to just the Role ids at the end.
f = r.content_object._meta.get_field(r.role_field)
- parent_roles = f._resolve_parent_roles(r.content_object)
- minus = parent_roles - parents
- if minus:
- minus = [f"{x.content_type} {x.object_id} {x.role_field}" for x in Role.objects.filter(id__in=minus)]
- sys.stderr.write(f"Role id={r.id} is missing parents: {minus}\n")
- plus = parents - parent_roles
+ f_parent = set(f.parent_role) if isinstance(f.parent_role, list) else {f.parent_role,}
+ dotted = {x for p in f_parent if '.' in p for x in resolve(r.content_object, p)}
+ plus = set()
+ for p in r.parents.all():
+ if p.singleton_name:
+ if f'singleton:{p.singleton_name}' not in f_parent:
+ plus.add(p)
+ elif (p.content_type, p.role_field) == (team_ct, 'member_role'):
+ # Team has been granted this role; probably legitimate.
+ continue
+ elif (p.content_type, p.object_id) == (r.content_type, r.object_id):
+ if p.role_field not in f_parent:
+ plus.add(p)
+ elif p in dotted:
+ continue
+ else:
+ plus.add(p)
+
if plus:
- plus = [f"{x.content_type} {x.object_id} {x.role_field}" for x in Role.objects.filter(id__in=plus).exclude(content_type=team_ct, role_field='member_role')]
- if plus:
- sys.stderr.write(f"Role id={r.id} has excess parents: {plus}\n")
+ plus = [f"{x.content_type!r} {x.object_id} {x.role_field}" for x in plus]
+ sys.stderr.write(f"Role id={r.id} has cross-linked parents: {plus}\n")
rev = getattr(r.content_object, r.role_field, None)
if rev is None or r.id != rev.id: