From 7b4e7ec5b328d529178fbeb423a73ba5075da136 Mon Sep 17 00:00:00 2001 From: Akita Noek Date: Tue, 12 Apr 2016 11:39:14 -0400 Subject: [PATCH] Switch to explicitly stored implicit role parents Completes #1496 --- awx/main/fields.py | 42 +++++++------------ awx/main/migrations/0008_v300_rbac_changes.py | 1 + awx/main/models/rbac.py | 1 + 3 files changed, 17 insertions(+), 27 deletions(-) diff --git a/awx/main/fields.py b/awx/main/fields.py index 44aba25ccd..7e57c29c7d 100644 --- a/awx/main/fields.py +++ b/awx/main/fields.py @@ -1,6 +1,8 @@ # Copyright (c) 2015 Ansible, Inc. # All Rights Reserved. +import json + # Django from django.db.models.signals import ( pre_save, @@ -71,9 +73,9 @@ def resolve_role_field(obj, field): if len(field_components) == 1: Role_ = get_current_apps().get_model('main', 'Role') - if type(obj) is not ImplicitRoleDescriptor and type(obj) is not Role_: - raise Exception(smart_text('{} refers to a {}, not an ImplicitRoleField or Role'.format(field, type(obj)))) - ret.append(obj) + if type(obj) is not Role_: + raise Exception(smart_text('{} refers to a {}, not a Role'.format(field, type(obj)))) + ret.append(obj.id) else: if type(obj) is ManyRelatedObjectsDescriptor: for o in obj.all(): @@ -178,7 +180,7 @@ class ImplicitRoleField(models.ForeignKey): def _create_role_instance_if_not_exists(self, instance): role = getattr(instance, self.name, None) if role: - return role + return Role_ = get_current_apps().get_model('main', 'Role') role = Role_.objects.create( created=now(), @@ -226,38 +228,24 @@ class ImplicitRoleField(models.ForeignKey): for implicit_role_field in getattr(instance.__class__, '__implicit_role_fields'): implicit_role_field._create_role_instance_if_not_exists(instance) - original_parent_roles = dict() - if instance.pk: - original = instance.__class__.objects.get(pk=instance.pk) - for implicit_role_field in getattr(instance.__class__, '__implicit_role_fields'): - original_parent_roles[implicit_role_field.name] = implicit_role_field._resolve_parent_roles(original) - - setattr(instance, '__original_parent_roles', original_parent_roles) - - def _post_save(self, instance, created, *args, **kwargs): if created: for implicit_role_field in getattr(instance.__class__, '__implicit_role_fields'): implicit_role_field._patch_role_content_object_and_grant_permissions(instance) - original_parent_roles = getattr(instance, '__original_parent_roles') - - if created: - for implicit_role_field in getattr(instance.__class__, '__implicit_role_fields'): - original_parent_roles[implicit_role_field.name] = set() - - new_parent_roles = dict() - for implicit_role_field in getattr(instance.__class__, '__implicit_role_fields'): - new_parent_roles[implicit_role_field.name] = implicit_role_field._resolve_parent_roles(instance) - setattr(instance, '__original_parent_roles', new_parent_roles) - with batch_role_ancestor_rebuilding(): for implicit_role_field in getattr(instance.__class__, '__implicit_role_fields'): cur_role = getattr(instance, implicit_role_field.name) - original_parents = original_parent_roles[implicit_role_field.name] - new_parents = new_parent_roles[implicit_role_field.name] + original_parents = set(json.loads(cur_role.implicit_parents)) + new_parents = implicit_role_field._resolve_parent_roles(instance) cur_role.parents.remove(*list(original_parents - new_parents)) cur_role.parents.add(*list(new_parents - original_parents)) + new_parents_list = list(new_parents) + new_parents_list.sort() + new_parents_json = json.dumps(new_parents_list) + if cur_role.implicit_parents != new_parents_json: + cur_role.implicit_parents = new_parents_json + cur_role.save() def _resolve_parent_roles(self, instance): @@ -279,7 +267,7 @@ class ImplicitRoleField(models.ForeignKey): singleton_name=singleton_name, name=singleton_name, description=singleton_name) - parents = [role] + parents = [role.id] else: parents = resolve_role_field(instance, path) for parent in parents: diff --git a/awx/main/migrations/0008_v300_rbac_changes.py b/awx/main/migrations/0008_v300_rbac_changes.py index 75934a69ed..1ec3432c9e 100644 --- a/awx/main/migrations/0008_v300_rbac_changes.py +++ b/awx/main/migrations/0008_v300_rbac_changes.py @@ -64,6 +64,7 @@ class Migration(migrations.Migration): ('members', models.ManyToManyField(related_name='roles', to=settings.AUTH_USER_MODEL)), ('modified_by', models.ForeignKey(related_name="{u'class': 'role', u'app_label': 'main'}(class)s_modified+", on_delete=django.db.models.deletion.SET_NULL, default=None, editable=False, to=settings.AUTH_USER_MODEL, null=True)), ('parents', models.ManyToManyField(related_name='children', to='main.Role')), + ('implicit_parents', models.ManyToManyField(related_name='implicit_children', to='main.Role')), ('tags', taggit.managers.TaggableManager(to='taggit.Tag', through='taggit.TaggedItem', blank=True, help_text='A comma-separated list of tags.', verbose_name='Tags')), ], options={ diff --git a/awx/main/models/rbac.py b/awx/main/models/rbac.py index a8b2b58210..91e055f6eb 100644 --- a/awx/main/models/rbac.py +++ b/awx/main/models/rbac.py @@ -83,6 +83,7 @@ class Role(CommonModelNameNotUnique): singleton_name = models.TextField(null=True, default=None, db_index=True, unique=True) parents = models.ManyToManyField('Role', related_name='children') + implicit_parents = models.TextField(null=False, default='[]') ancestors = models.ManyToManyField('Role', related_name='descendents') # auto-generated by `rebuild_role_ancestor_list` members = models.ManyToManyField('auth.User', related_name='roles') content_type = models.ForeignKey(ContentType, null=True, default=None)