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.
This commit is contained in:
Akita Noek
2016-04-10 11:57:39 -04:00
parent 682552d9b0
commit 7d2e660749
4 changed files with 52 additions and 9 deletions

View File

@@ -17,10 +17,11 @@ from django.db.models.fields.related import (
ReverseManyRelatedObjectsDescriptor, ReverseManyRelatedObjectsDescriptor,
) )
from django.utils.encoding import smart_text from django.utils.encoding import smart_text
from django.utils.timezone import now
# AWX # 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'] __all__ = ['AutoOneToOneField', 'ImplicitRoleField']
@@ -65,8 +66,12 @@ def resolve_role_field(obj, field):
else: else:
return [] return []
if obj is None:
return []
if len(field_components) == 1: 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)))) raise Exception(smart_text('{} refers to a {}, not an ImplicitRoleField or Role'.format(field, type(obj))))
ret.append(obj) ret.append(obj)
else: else:
@@ -174,7 +179,10 @@ class ImplicitRoleField(models.ForeignKey):
role = getattr(instance, self.name, None) role = getattr(instance, self.name, None)
if role: if role:
return 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, name=self.role_name,
description=self.role_description description=self.role_description
) )
@@ -186,9 +194,16 @@ class ImplicitRoleField(models.ForeignKey):
role.save() role.save()
if self.permissions is not None: 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, role=role,
resource=instance, content_type=instance_content_type,
object_id=instance.id,
auto_generated=True auto_generated=True
) )
@@ -253,7 +268,20 @@ class ImplicitRoleField(models.ForeignKey):
parent_roles = set() parent_roles = set()
for path in paths: for path in paths:
if path.startswith("singleton:"): 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: else:
parents = resolve_role_field(instance, path) parents = resolve_role_field(instance, path)
for parent in parents: for parent in parents:

View File

@@ -12,6 +12,7 @@ class Migration(migrations.Migration):
] ]
operations = [ operations = [
migrations.RunPython(rbac.init_rbac_migration),
migrations.RunPython(rbac.migrate_users), migrations.RunPython(rbac.migrate_users),
migrations.RunPython(rbac.migrate_organization), migrations.RunPython(rbac.migrate_organization),
migrations.RunPython(rbac.migrate_team), migrations.RunPython(rbac.migrate_team),

View File

@@ -5,7 +5,7 @@ from django.db.models import Q
from django.utils.timezone import now from django.utils.timezone import now
from collections import defaultdict 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 import _old_access as old_access
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@@ -26,6 +26,10 @@ def log_migration(wrapped):
return wrapped(*args, **kwargs) return wrapped(*args, **kwargs)
return wrapper return wrapper
@log_migration
def init_rbac_migration(apps, schema_editor):
set_current_apps(apps)
@log_migration @log_migration
def migrate_users(apps, schema_editor): def migrate_users(apps, schema_editor):
User = apps.get_model('auth', "User") User = apps.get_model('auth', "User")

View File

@@ -20,6 +20,7 @@ import tempfile
from rest_framework.exceptions import ParseError, PermissionDenied from rest_framework.exceptions import ParseError, PermissionDenied
from django.utils.encoding import smart_str from django.utils.encoding import smart_str
from django.core.urlresolvers import reverse from django.core.urlresolvers import reverse
from django.apps import apps
# PyCrypto # PyCrypto
from Crypto.Cipher import AES 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_ansible_version', 'get_ssh_version', 'get_awx_version', 'update_scm_url',
'get_type_for_model', 'get_model_for_type', 'to_python_boolean', 'get_type_for_model', 'get_model_for_type', 'to_python_boolean',
'ignore_inventory_computed_fields', 'ignore_inventory_group_removal', '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): def get_object_or_400(klass, *args, **kwargs):
@@ -556,3 +558,11 @@ def getattrd(obj, name, default=NoDefaultProvided):
return default return default
raise 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