Merge pull request #1480 from anoek/migration

Migration fixes
This commit is contained in:
Akita Noek 2016-04-12 16:33:54 -04:00
commit d6d8390f8c
8 changed files with 211 additions and 94 deletions

View File

@ -1,6 +1,8 @@
# Copyright (c) 2015 Ansible, Inc.
# All Rights Reserved.
import json
# Django
from django.db.models.signals import (
pre_save,
@ -17,10 +19,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 batch_role_ancestor_rebuilding
from awx.main.utils import get_current_apps
__all__ = ['AutoOneToOneField', 'ImplicitRoleField']
@ -65,10 +68,14 @@ 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:
raise Exception(smart_text('{} refers to a {}, not an ImplicitRoleField or Role'.format(field, type(obj))))
ret.append(obj)
Role_ = get_current_apps().get_model('main', 'Role')
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():
@ -97,6 +104,14 @@ class ImplicitRoleField(models.ForeignKey):
kwargs.setdefault('null', 'True')
super(ImplicitRoleField, self).__init__(*args, **kwargs)
def deconstruct(self):
name, path, args, kwargs = super(ImplicitRoleField, self).deconstruct()
kwargs['role_name'] = self.role_name
kwargs['role_description'] = self.role_description
kwargs['permissions'] = self.permissions
kwargs['parent_role'] = self.parent_role
return name, path, args, kwargs
def contribute_to_class(self, cls, name):
super(ImplicitRoleField, self).contribute_to_class(cls, name)
setattr(cls, self.name, ImplicitRoleDescriptor(self))
@ -165,8 +180,11 @@ class ImplicitRoleField(models.ForeignKey):
def _create_role_instance_if_not_exists(self, instance):
role = getattr(instance, self.name, None)
if role:
return role
role = Role.objects.create(
return
Role_ = get_current_apps().get_model('main', 'Role')
role = Role_.objects.create(
created=now(),
modified=now(),
name=self.role_name,
description=self.role_description
)
@ -178,9 +196,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
)
@ -203,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):
@ -245,7 +256,18 @@ 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.id]
else:
parents = resolve_role_field(instance, path)
for parent in parents:

View File

@ -7,7 +7,6 @@ from django.conf import settings
import taggit.managers
import awx.main.fields
class Migration(migrations.Migration):
dependencies = [
@ -38,6 +37,16 @@ class Migration(migrations.Migration):
'projects',
'deprecated_projects',
),
migrations.AddField(
model_name='project',
name='organization',
field=models.ForeignKey(related_name='projects', to='main.Organization', blank=True, null=True),
),
migrations.AlterField(
model_name='team',
name='deprecated_projects',
field=models.ManyToManyField(related_name='deprecated_teams', to='main.Project', blank=True),
),
migrations.CreateModel(
name='Role',
@ -55,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={
@ -86,146 +96,149 @@ class Migration(migrations.Migration):
'verbose_name_plural': 'permissions',
},
),
migrations.AddField(
model_name='credential',
name='auditor_role',
field=awx.main.fields.ImplicitRoleField(related_name='+', role_description=b'Auditor of the credential', parent_role=[b'singleton:System Auditor'], to='main.Role', role_name=b'Credential Auditor', null=b'True', permissions={b'read': True}),
),
migrations.AddField(
model_name='credential',
name='owner_role',
field=awx.main.fields.ImplicitRoleField(related_name='+', to='main.Role', null=b'True'),
),
migrations.AddField(
model_name='credential',
name='auditor_role',
field=awx.main.fields.ImplicitRoleField(related_name='+', to='main.Role', null=b'True'),
field=awx.main.fields.ImplicitRoleField(related_name='+', role_description=b'Owner of the credential', parent_role=[b'singleton:System Administrator'], to='main.Role', role_name=b'Credential Owner', null=b'True', permissions={b'all': True}),
),
migrations.AddField(
model_name='credential',
name='usage_role',
field=awx.main.fields.ImplicitRoleField(related_name='+', to='main.Role', null=b'True'),
field=awx.main.fields.ImplicitRoleField(related_name='+', role_description=b'May use this credential, but not read sensitive portions or modify it', parent_role=None, to='main.Role', role_name=b'Credential User', null=b'True', permissions={b'use': True}),
),
migrations.AddField(
model_name='custominventoryscript',
name='admin_role',
field=awx.main.fields.ImplicitRoleField(related_name='+', role_description=b'May manage this inventory', parent_role=b'organization.admin_role', to='main.Role', role_name=b'CustomInventory Administrator', null=b'True', permissions={b'all': True}),
),
migrations.AddField(
model_name='custominventoryscript',
name='auditor_role',
field=awx.main.fields.ImplicitRoleField(related_name='+', role_description=b'May view but not modify this inventory', parent_role=b'organization.auditor_role', to='main.Role', role_name=b'CustomInventory Auditor', null=b'True', permissions={b'read': True}),
),
migrations.AddField(
model_name='custominventoryscript',
name='member_role',
field=awx.main.fields.ImplicitRoleField(related_name='+', role_description=b'May view but not modify this inventory', parent_role=b'organization.member_role', to='main.Role', role_name=b'CustomInventory Member', null=b'True', permissions={b'read': True}),
),
migrations.AddField(
model_name='group',
name='admin_role',
field=awx.main.fields.ImplicitRoleField(related_name='+', to='main.Role', null=b'True'),
field=awx.main.fields.ImplicitRoleField(related_name='+', role_description=b'', parent_role=[b'inventory.admin_role', b'parents.admin_role'], to='main.Role', role_name=b'Inventory Group Administrator', null=b'True', permissions={b'all': True}),
),
migrations.AddField(
model_name='group',
name='auditor_role',
field=awx.main.fields.ImplicitRoleField(related_name='+', to='main.Role', null=b'True'),
field=awx.main.fields.ImplicitRoleField(related_name='+', role_description=b'', parent_role=[b'inventory.auditor_role', b'parents.auditor_role'], to='main.Role', role_name=b'Inventory Group Auditor', null=b'True', permissions={b'read': True}),
),
migrations.AddField(
model_name='group',
name='executor_role',
field=awx.main.fields.ImplicitRoleField(related_name='+', to='main.Role', null=b'True'),
field=awx.main.fields.ImplicitRoleField(related_name='+', role_description=b'', parent_role=[b'inventory.executor_role', b'parents.executor_role'], to='main.Role', role_name=b'Inventory Group Executor', null=b'True', permissions={b'read': True, b'execute': True}),
),
migrations.AddField(
model_name='group',
name='updater_role',
field=awx.main.fields.ImplicitRoleField(related_name='+', to='main.Role', null=b'True'),
field=awx.main.fields.ImplicitRoleField(related_name='+', role_description=b'', parent_role=[b'inventory.updater_role', b'parents.updater_role'], to='main.Role', role_name=b'Inventory Group Updater', null=b'True', permissions={b'read': True, b'write': True, b'create': True, b'use': True}),
),
migrations.AddField(
model_name='inventory',
name='admin_role',
field=awx.main.fields.ImplicitRoleField(related_name='+', to='main.Role', null=b'True'),
field=awx.main.fields.ImplicitRoleField(related_name='+', role_description=b'May manage this inventory', parent_role=b'organization.admin_role', to='main.Role', role_name=b'Inventory Administrator', null=b'True', permissions={b'all': True}),
),
migrations.AddField(
model_name='inventory',
name='auditor_role',
field=awx.main.fields.ImplicitRoleField(related_name='+', to='main.Role', null=b'True'),
field=awx.main.fields.ImplicitRoleField(related_name='+', role_description=b'May view but not modify this inventory', parent_role=b'organization.auditor_role', to='main.Role', role_name=b'Inventory Auditor', null=b'True', permissions={b'read': True}),
),
migrations.AddField(
model_name='inventory',
name='executor_role',
field=awx.main.fields.ImplicitRoleField(related_name='+', to='main.Role', null=b'True'),
field=awx.main.fields.ImplicitRoleField(related_name='+', role_description=b'May execute jobs against this inventory', parent_role=None, to='main.Role', role_name=b'Inventory Executor', null=b'True', permissions={b'read': True, b'execute': True}),
),
migrations.AddField(
model_name='inventory',
name='updater_role',
field=awx.main.fields.ImplicitRoleField(related_name='+', to='main.Role', null=b'True'),
field=awx.main.fields.ImplicitRoleField(related_name='+', role_description=b'May update the inventory', parent_role=None, to='main.Role', role_name=b'Inventory Updater', null=b'True', permissions={b'read': True, b'update': True}),
),
migrations.AddField(
model_name='inventory',
name='usage_role',
field=awx.main.fields.ImplicitRoleField(related_name='+', to='main.Role', null=b'True'),
),
migrations.AddField(
model_name='custominventoryscript',
name='admin_role',
field=awx.main.fields.ImplicitRoleField(related_name='+', to='main.Role', null=b'True'),
),
migrations.AddField(
model_name='custominventoryscript',
name='auditor_role',
field=awx.main.fields.ImplicitRoleField(related_name='+', to='main.Role', null=b'True'),
),
migrations.AddField(
model_name='custominventoryscript',
name='member_role',
field=awx.main.fields.ImplicitRoleField(related_name='+', to='main.Role', null=b'True'),
field=awx.main.fields.ImplicitRoleField(related_name='+', role_description=b'May use this inventory, but not read sensitive portions or modify it', parent_role=None, to='main.Role', role_name=b'Inventory User', null=b'True', permissions={b'use': True}),
),
migrations.AddField(
model_name='jobtemplate',
name='admin_role',
field=awx.main.fields.ImplicitRoleField(related_name='+', to='main.Role', null=b'True'),
field=awx.main.fields.ImplicitRoleField(related_name='+', role_description=b'Full access to all settings', parent_role=b'project.admin_role', to='main.Role', role_name=b'Job Template Administrator', null=b'True', permissions={b'all': True}),
),
migrations.AddField(
model_name='jobtemplate',
name='auditor_role',
field=awx.main.fields.ImplicitRoleField(related_name='+', to='main.Role', null=b'True'),
field=awx.main.fields.ImplicitRoleField(related_name='+', role_description=b'Read-only access to all settings', parent_role=b'project.auditor_role', to='main.Role', role_name=b'Job Template Auditor', null=b'True', permissions={b'read': True}),
),
migrations.AddField(
model_name='jobtemplate',
name='executor_role',
field=awx.main.fields.ImplicitRoleField(related_name='+', to='main.Role', null=b'True'),
field=awx.main.fields.ImplicitRoleField(related_name='+', role_description=b'May run the job template', parent_role=None, to='main.Role', role_name=b'Job Template Runner', null=b'True', permissions={b'read': True, b'execute': True}),
),
migrations.AddField(
model_name='organization',
name='admin_role',
field=awx.main.fields.ImplicitRoleField(related_name='+', to='main.Role', null=b'True'),
field=awx.main.fields.ImplicitRoleField(related_name='+', role_description=b'May manage all aspects of this organization', parent_role=b'singleton:System Administrator', to='main.Role', role_name=b'Organization Administrator', null=b'True', permissions={b'write': True, b'use': True, b'scm_update': True, b'execute': True, b'read': True, b'create': True, b'update': True, b'delete': True}),
),
migrations.AddField(
model_name='organization',
name='auditor_role',
field=awx.main.fields.ImplicitRoleField(related_name='+', to='main.Role', null=b'True'),
field=awx.main.fields.ImplicitRoleField(related_name='+', role_description=b'May read all settings associated with this organization', parent_role=b'singleton:System Auditor', to='main.Role', role_name=b'Organization Auditor', null=b'True', permissions={b'read': True}),
),
migrations.AddField(
model_name='organization',
name='member_role',
field=awx.main.fields.ImplicitRoleField(related_name='+', to='main.Role', null=b'True'),
field=awx.main.fields.ImplicitRoleField(related_name='+', role_description=b'A member of this organization', parent_role=b'admin_role', to='main.Role', role_name=b'Organization Member', null=b'True', permissions={b'read': True}),
),
migrations.AddField(
model_name='project',
name='admin_role',
field=awx.main.fields.ImplicitRoleField(related_name='+', to='main.Role', null=b'True'),
field=awx.main.fields.ImplicitRoleField(related_name='+', role_description=b'May manage this project', parent_role=[b'organization.admin_role', b'singleton:System Administrator'], to='main.Role', role_name=b'Project Administrator', null=b'True', permissions={b'all': True}),
),
migrations.AddField(
model_name='project',
name='auditor_role',
field=awx.main.fields.ImplicitRoleField(related_name='+', to='main.Role', null=b'True'),
field=awx.main.fields.ImplicitRoleField(related_name='+', role_description=b'May read all settings associated with this project', parent_role=[b'organization.auditor_role', b'singleton:System Auditor'], to='main.Role', role_name=b'Project Auditor', null=b'True', permissions={b'read': True}),
),
migrations.AddField(
model_name='project',
name='member_role',
field=awx.main.fields.ImplicitRoleField(related_name='+', to='main.Role', null=b'True'),
field=awx.main.fields.ImplicitRoleField(related_name='+', role_description=b'Implies membership within this project', parent_role=None, to='main.Role', role_name=b'Project Member', null=b'True', permissions={b'read': True}),
),
migrations.AddField(
model_name='project',
name='scm_update_role',
field=awx.main.fields.ImplicitRoleField(related_name='+', to='main.Role', null=b'True'),
field=awx.main.fields.ImplicitRoleField(related_name='+', role_description=b'May update this project from the source control management system', parent_role=b'admin_role', to='main.Role', role_name=b'Project Updater', null=b'True', permissions={b'scm_update': True}),
),
migrations.AddField(
model_name='team',
name='admin_role',
field=awx.main.fields.ImplicitRoleField(related_name='+', to='main.Role', null=b'True'),
field=awx.main.fields.ImplicitRoleField(related_name='+', role_description=b'May manage this team', parent_role=b'organization.admin_role', to='main.Role', role_name=b'Team Administrator', null=b'True', permissions={b'write': True, b'use': True, b'scm_update': True, b'execute': True, b'read': True, b'create': True, b'update': True, b'delete': True}),
),
migrations.AddField(
model_name='team',
name='auditor_role',
field=awx.main.fields.ImplicitRoleField(related_name='+', to='main.Role', null=b'True'),
field=awx.main.fields.ImplicitRoleField(related_name='+', role_description=b'May read all settings associated with this team', parent_role=b'organization.auditor_role', to='main.Role', role_name=b'Team Auditor', null=b'True', permissions={b'read': True}),
),
migrations.AddField(
model_name='team',
name='member_role',
field=awx.main.fields.ImplicitRoleField(related_name='+', to='main.Role', null=b'True'),
field=awx.main.fields.ImplicitRoleField(related_name='+', role_description=b'A member of this team', parent_role=b'admin_role', to='main.Role', role_name=b'Team Member', null=b'True', permissions={b'read': True}),
),
migrations.AlterIndexTogether(
name='rolepermission',
index_together=set([('content_type', 'object_id')]),
@ -240,11 +253,6 @@ class Migration(migrations.Migration):
name='deprecated_projects',
field=models.ManyToManyField(related_name='deprecated_organizations', to='main.Project', blank=True),
),
migrations.AddField(
model_name='project',
name='organization',
field=models.ForeignKey(related_name='projects', to='main.Organization', blank=True, null=True),
),
migrations.RenameField(
'Credential',
'team',

View File

@ -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),

View File

@ -2,11 +2,12 @@ import logging
from django.utils.encoding import smart_text
from django.db.models import Q
from django.utils.timezone import now
from collections import defaultdict
from awx.main.utils import getattrd
import _old_access as old_access
from awx.main.utils import getattrd, set_current_apps
import _old_access as old_access
logger = logging.getLogger(__name__)
def log_migration(wrapped):
@ -25,39 +26,62 @@ 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")
Role = apps.get_model('main', "Role")
RolePermission = apps.get_model('main', "RolePermission")
ContentType = apps.get_model('contenttypes', "ContentType")
user_content_type = ContentType.objects.get_for_model(User)
for user in User.objects.iterator():
user.save()
try:
Role.objects.get(content_type=ContentType.objects.get_for_model(User), object_id=user.id)
Role.objects.get(content_type=user_content_type, object_id=user.id)
logger.info(smart_text(u"found existing role for user: {}".format(user.username)))
except Role.DoesNotExist:
role = Role.objects.create(
created=now(),
modified=now(),
singleton_name = smart_text(u'{}-admin_role'.format(user.username)),
content_object = user,
content_type = user_content_type,
object_id = user.id
)
role.members.add(user)
RolePermission.objects.create(
created=now(),
modified=now(),
role = role,
resource = user,
content_type = user_content_type,
object_id = user.id,
create=1, read=1, write=1, delete=1, update=1,
execute=1, scm_update=1, use=1,
)
logger.info(smart_text(u"migrating to new role for user: {}".format(user.username)))
if user.is_superuser:
Role.singleton('System Administrator').members.add(user)
if Role.objects.filter(singleton_name='System Administrator').exists():
sa_role = Role.objects.get(singleton_name='System Administrator')
else:
sa_role = Role.objects.create(
created=now(),
modified=now(),
singleton_name='System Administrator',
name='System Administrator'
)
sa_role.members.add(user)
logger.warning(smart_text(u"added superuser: {}".format(user.username)))
@log_migration
def migrate_organization(apps, schema_editor):
Organization = apps.get_model('main', "Organization")
for org in Organization.objects.iterator():
org.save() # force creates missing roles
for admin in org.deprecated_admins.all():
org.admin_role.members.add(admin)
logger.info(smart_text(u"added admin: {}, {}".format(org.name, admin.username)))
@ -69,6 +93,7 @@ def migrate_organization(apps, schema_editor):
def migrate_team(apps, schema_editor):
Team = apps.get_model('main', 'Team')
for t in Team.objects.iterator():
t.save()
for user in t.deprecated_users.all():
t.member_role.members.add(user)
logger.info(smart_text(u"team: {}, added user: {}".format(t.name, user.username)))
@ -110,7 +135,7 @@ def _discover_credentials(instances, cred, orgfunc):
orgs[orgfunc(inst)].append(inst)
if len(orgs) == 1:
_update_credential_parents(instances[0].inventory.organization, cred)
_update_credential_parents(orgfunc(instances[0]), cred)
else:
for pos, org in enumerate(orgs):
if pos == 0:
@ -135,9 +160,14 @@ def migrate_credential(apps, schema_editor):
Credential = apps.get_model('main', "Credential")
JobTemplate = apps.get_model('main', 'JobTemplate')
Project = apps.get_model('main', 'Project')
Role = apps.get_model('main', 'Role')
User = apps.get_model('auth', 'User')
InventorySource = apps.get_model('main', 'InventorySource')
ContentType = apps.get_model('contenttypes', "ContentType")
user_content_type = ContentType.objects.get_for_model(User)
for cred in Credential.objects.iterator():
cred.save()
results = (JobTemplate.objects.filter(Q(credential=cred) | Q(cloud_credential=cred)).all() or
InventorySource.objects.filter(credential=cred).all())
if results:
@ -164,7 +194,8 @@ def migrate_credential(apps, schema_editor):
cred.save()
logger.info(smart_text(u"added Credential(name={}, kind={}, host={}) at user level".format(cred.name, cred.kind, cred.host)))
elif cred.deprecated_user is not None:
cred.deprecated_user.admin_role.children.add(cred.owner_role)
user_admin_role = Role.objects.get(content_type=user_content_type, object_id=cred.deprecated_user.id)
user_admin_role.children.add(cred.owner_role)
cred.deprecated_user, cred.deprecated_team = None, None
cred.save()
logger.info(smart_text(u"added Credential(name={}, kind={}, host={}) at user level".format(cred.name, cred.kind, cred.host, )))
@ -191,6 +222,7 @@ def migrate_inventory(apps, schema_editor):
return None
for inventory in Inventory.objects.iterator():
inventory.save()
for perm in Permission.objects.filter(inventory=inventory):
role = None
execrole = None
@ -238,16 +270,18 @@ def migrate_projects(apps, schema_editor):
# Migrate projects to single organizations, duplicating as necessary
for project in Project.objects.iterator():
project.save()
original_project_name = project.name
project_orgs = project.deprecated_organizations.distinct().all()
if len(project_orgs) > 1:
if len(project_orgs) >= 1:
first_org = None
for org in project_orgs:
if first_org is None:
# For the first org, re-use our existing Project object, so don't do the below duplication effort
first_org = org
project.name = smart_text(u'{} - {}'.format(first_org.name, original_project_name))
if len(project_orgs) > 1:
project.name = smart_text(u'{} - {}'.format(first_org.name, original_project_name))
project.organization = first_org
project.save()
else:
@ -349,6 +383,7 @@ def migrate_job_templates(apps, schema_editor):
Permission = apps.get_model('main', 'Permission')
for jt in JobTemplate.objects.iterator():
jt.save()
permission = Permission.objects.filter(
inventory=jt.inventory,
project=jt.project,

View File

@ -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)

View File

@ -91,6 +91,46 @@ def test_project_migration():
assert o2.projects.all()[0].jobtemplates.count() == 1
assert o3.projects.all()[0].jobtemplates.count() == 0
@pytest.mark.django_db
def test_single_org_project_migration(organization):
project = Project.objects.create(name='my project',
description="description",
organization=None)
organization.deprecated_projects.add(project)
assert project.organization is None
rbac.migrate_projects(apps, None)
project = Project.objects.get(id=project.id)
assert project.organization.id == organization.id
@pytest.mark.django_db
def test_no_org_project_migration(organization):
project = Project.objects.create(name='my project',
description="description",
organization=None)
assert project.organization is None
rbac.migrate_projects(apps, None)
assert project.organization is None
@pytest.mark.django_db
def test_multi_org_project_migration():
org1 = Organization.objects.create(name="org1", description="org1 desc")
org2 = Organization.objects.create(name="org2", description="org2 desc")
project = Project.objects.create(name='my project',
description="description",
organization=None)
assert Project.objects.all().count() == 1
assert Project.objects.filter(organization=org1).count() == 0
assert Project.objects.filter(organization=org2).count() == 0
project.deprecated_organizations.add(org1)
project.deprecated_organizations.add(org2)
assert project.organization is None
rbac.migrate_projects(apps, None)
assert Project.objects.filter(organization=org1).count() == 1
assert Project.objects.filter(organization=org2).count() == 1
@pytest.mark.django_db
def test_project_user_project(user_project, project, user):
u = user('owner')

View File

@ -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

View File

@ -3,7 +3,7 @@ DJANGO_SETTINGS_MODULE = awx.settings.development
python_paths = awx/lib/site-packages
site_dirs = awx/lib/site-packages
python_files = *.py
addopts = --reuse-db
addopts = --reuse-db --nomigrations
markers =
ac: access control test
license_feature: ensure license features are accessible or not depending on license