From 7d2e66074981770d27d08901286993f5e292bdb3 Mon Sep 17 00:00:00 2001 From: Akita Noek Date: Sun, 10 Apr 2016 11:57:39 -0400 Subject: [PATCH] Make use of 'current' apps so RBAC ImplicitRoleField can work during migrations While a migration is taking place, we can't juse use normal model references like Role and RolePermission, nor can we use generic foreign keys without manually referring to the content type and object id fields. --- awx/main/fields.py | 42 +++++++++++++++---- .../migrations/0009_v300_rbac_migrations.py | 1 + awx/main/migrations/_rbac.py | 6 ++- awx/main/utils.py | 12 +++++- 4 files changed, 52 insertions(+), 9 deletions(-) diff --git a/awx/main/fields.py b/awx/main/fields.py index 650edb55e0..90f94b734c 100644 --- a/awx/main/fields.py +++ b/awx/main/fields.py @@ -17,10 +17,11 @@ from django.db.models.fields.related import ( ReverseManyRelatedObjectsDescriptor, ) from django.utils.encoding import smart_text +from django.utils.timezone import now # AWX -from awx.main.models.rbac import RolePermission, Role, batch_role_ancestor_rebuilding - +from awx.main.models.rbac import Role, batch_role_ancestor_rebuilding +from awx.main.utils import get_current_apps __all__ = ['AutoOneToOneField', 'ImplicitRoleField'] @@ -65,8 +66,12 @@ def resolve_role_field(obj, field): else: return [] + if obj is None: + return [] + if len(field_components) == 1: - if type(obj) is not ImplicitRoleDescriptor and type(obj) is not Role: + 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) else: @@ -174,7 +179,10 @@ class ImplicitRoleField(models.ForeignKey): role = getattr(instance, self.name, None) if role: return role - role = Role.objects.create( + Role_ = get_current_apps().get_model('main', 'Role') + role = Role_.objects.create( + created=now(), + modified=now(), name=self.role_name, description=self.role_description ) @@ -186,9 +194,16 @@ class ImplicitRoleField(models.ForeignKey): role.save() if self.permissions is not None: - permissions = RolePermission( + RolePermission_ = get_current_apps().get_model('main', 'RolePermission') + ContentType = get_current_apps().get_model('contenttypes', "ContentType") + instance_content_type = ContentType.objects.get_for_model(instance) + + permissions = RolePermission_( + created=now(), + modified=now(), role=role, - resource=instance, + content_type=instance_content_type, + object_id=instance.id, auto_generated=True ) @@ -253,7 +268,20 @@ class ImplicitRoleField(models.ForeignKey): parent_roles = set() for path in paths: if path.startswith("singleton:"): - parents = [Role.singleton(path[10:])] + singleton_name = path[10:] + Role_ = get_current_apps().get_model('main', 'Role') + qs = Role_.objects.filter(singleton_name=singleton_name) + if qs.count() >= 1: + role = qs[0] + else: + role = Role_.objects.create( + created=now(), + modified=now(), + singleton_name=singleton_name, + name=singleton_name, + description=singleton_name + ) + parents = [role] else: parents = resolve_role_field(instance, path) for parent in parents: diff --git a/awx/main/migrations/0009_v300_rbac_migrations.py b/awx/main/migrations/0009_v300_rbac_migrations.py index b652c1067a..9c7f7d8dd7 100644 --- a/awx/main/migrations/0009_v300_rbac_migrations.py +++ b/awx/main/migrations/0009_v300_rbac_migrations.py @@ -12,6 +12,7 @@ class Migration(migrations.Migration): ] operations = [ + migrations.RunPython(rbac.init_rbac_migration), migrations.RunPython(rbac.migrate_users), migrations.RunPython(rbac.migrate_organization), migrations.RunPython(rbac.migrate_team), diff --git a/awx/main/migrations/_rbac.py b/awx/main/migrations/_rbac.py index 85f2d3fd7d..de09d06fd1 100644 --- a/awx/main/migrations/_rbac.py +++ b/awx/main/migrations/_rbac.py @@ -5,7 +5,7 @@ from django.db.models import Q from django.utils.timezone import now from collections import defaultdict -from awx.main.utils import getattrd +from awx.main.utils import getattrd, set_current_apps import _old_access as old_access logger = logging.getLogger(__name__) @@ -26,6 +26,10 @@ def log_migration(wrapped): return wrapped(*args, **kwargs) return wrapper +@log_migration +def init_rbac_migration(apps, schema_editor): + set_current_apps(apps) + @log_migration def migrate_users(apps, schema_editor): User = apps.get_model('auth', "User") diff --git a/awx/main/utils.py b/awx/main/utils.py index dea2155597..f1d85f72b2 100644 --- a/awx/main/utils.py +++ b/awx/main/utils.py @@ -20,6 +20,7 @@ import tempfile from rest_framework.exceptions import ParseError, PermissionDenied from django.utils.encoding import smart_str from django.core.urlresolvers import reverse +from django.apps import apps # PyCrypto from Crypto.Cipher import AES @@ -30,7 +31,8 @@ __all__ = ['get_object_or_400', 'get_object_or_403', 'camelcase_to_underscore', 'get_ansible_version', 'get_ssh_version', 'get_awx_version', 'update_scm_url', 'get_type_for_model', 'get_model_for_type', 'to_python_boolean', 'ignore_inventory_computed_fields', 'ignore_inventory_group_removal', - '_inventory_updates', 'get_pk_from_dict', 'getattrd', 'NoDefaultProvided'] + '_inventory_updates', 'get_pk_from_dict', 'getattrd', 'NoDefaultProvided', + 'get_current_apps', 'set_current_apps'] def get_object_or_400(klass, *args, **kwargs): @@ -556,3 +558,11 @@ def getattrd(obj, name, default=NoDefaultProvided): return default raise +current_apps = apps +def set_current_apps(apps): + global current_apps + current_apps = apps + +def get_current_apps(): + global current_apps + return current_apps