-
Notifications
You must be signed in to change notification settings - Fork 3.4k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[RBAC] Rename managed role definitions, and move migration logic here (…
…#15087) * Rename managed role definitions, and move migration logic here * Fix naming capitalization
- Loading branch information
1 parent
c98727d
commit 818c326
Showing
8 changed files
with
189 additions
and
30 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -3,12 +3,15 @@ | |
|
||
from django.apps import apps as global_apps | ||
from django.db.models import ForeignKey | ||
from django.conf import settings | ||
from ansible_base.rbac.migrations._utils import give_permissions | ||
from ansible_base.rbac.management import create_dab_permissions | ||
|
||
from awx.main.fields import ImplicitRoleField | ||
from awx.main.constants import role_name_to_perm_mapping | ||
|
||
from ansible_base.rbac.permission_registry import permission_registry | ||
|
||
|
||
logger = logging.getLogger('awx.main.migrations._dab_rbac') | ||
|
||
|
@@ -194,7 +197,7 @@ def migrate_to_new_rbac(apps, schema_editor): | |
role_definition = managed_definitions[permissions] | ||
else: | ||
action = role.role_field.rsplit('_', 1)[0] # remove the _field ending of the name | ||
role_definition_name = f'{role.content_type.model}-{action}' | ||
role_definition_name = f'{role.content_type.model_class().__name__} {action.title()}' | ||
This comment has been minimized.
Sorry, something went wrong.
This comment has been minimized.
Sorry, something went wrong.
gundalow
Contributor
|
||
|
||
description = role_descriptions[role.role_field] | ||
if type(description) == dict: | ||
|
@@ -241,3 +244,100 @@ def migrate_to_new_rbac(apps, schema_editor): | |
ct += 1 | ||
if ct: | ||
logger.info(f'Migrated {ct} users to new system auditor flag') | ||
|
||
|
||
def get_or_create_managed(name, description, ct, permissions, RoleDefinition): | ||
role_definition, created = RoleDefinition.objects.get_or_create(name=name, defaults={'managed': True, 'description': description, 'content_type': ct}) | ||
role_definition.permissions.set(list(permissions)) | ||
|
||
if not role_definition.managed: | ||
role_definition.managed = True | ||
role_definition.save(update_fields=['managed']) | ||
|
||
if created: | ||
logger.info(f'Created RoleDefinition {role_definition.name} pk={role_definition} with {len(permissions)} permissions') | ||
|
||
return role_definition | ||
|
||
|
||
def setup_managed_role_definitions(apps, schema_editor): | ||
""" | ||
Idepotent method to create or sync the managed role definitions | ||
""" | ||
to_create = settings.ANSIBLE_BASE_ROLE_PRECREATE | ||
|
||
ContentType = apps.get_model('contenttypes', 'ContentType') | ||
Permission = apps.get_model('dab_rbac', 'DABPermission') | ||
RoleDefinition = apps.get_model('dab_rbac', 'RoleDefinition') | ||
Organization = apps.get_model(settings.ANSIBLE_BASE_ORGANIZATION_MODEL) | ||
org_ct = ContentType.objects.get_for_model(Organization) | ||
managed_role_definitions = [] | ||
|
||
org_perms = set() | ||
for cls in permission_registry._registry: | ||
ct = ContentType.objects.get_for_model(cls) | ||
object_perms = set(Permission.objects.filter(content_type=ct)) | ||
# Special case for InstanceGroup which has an organiation field, but is not an organization child object | ||
if cls._meta.model_name != 'instancegroup': | ||
org_perms.update(object_perms) | ||
|
||
if 'object_admin' in to_create and cls != Organization: | ||
indiv_perms = object_perms.copy() | ||
add_perms = [perm for perm in indiv_perms if perm.codename.startswith('add_')] | ||
if add_perms: | ||
for perm in add_perms: | ||
indiv_perms.remove(perm) | ||
|
||
managed_role_definitions.append( | ||
get_or_create_managed( | ||
to_create['object_admin'].format(cls=cls), f'Has all permissions to a single {cls._meta.verbose_name}', ct, indiv_perms, RoleDefinition | ||
) | ||
) | ||
|
||
if 'org_children' in to_create and cls != Organization: | ||
org_child_perms = object_perms.copy() | ||
org_child_perms.add(Permission.objects.get(codename='view_organization')) | ||
|
||
managed_role_definitions.append( | ||
get_or_create_managed( | ||
to_create['org_children'].format(cls=cls), | ||
f'Has all permissions to {cls._meta.verbose_name_plural} within an organization', | ||
org_ct, | ||
org_child_perms, | ||
RoleDefinition, | ||
) | ||
) | ||
|
||
if 'special' in to_create: | ||
special_perms = [] | ||
for perm in object_perms: | ||
if perm.codename.split('_')[0] not in ('add', 'change', 'update', 'delete', 'view'): | ||
special_perms.append(perm) | ||
for perm in special_perms: | ||
action = perm.codename.split('_')[0] | ||
view_perm = Permission.objects.get(content_type=ct, codename__startswith='view_') | ||
managed_role_definitions.append( | ||
get_or_create_managed( | ||
to_create['special'].format(cls=cls, action=action.title()), | ||
f'Has {action} permissions to a single {cls._meta.verbose_name}', | ||
ct, | ||
[perm, view_perm], | ||
RoleDefinition, | ||
) | ||
) | ||
|
||
if 'org_admin' in to_create: | ||
managed_role_definitions.append( | ||
get_or_create_managed( | ||
to_create['org_admin'].format(cls=Organization), | ||
'Has all permissions to a single organization and all objects inside of it', | ||
org_ct, | ||
org_perms, | ||
RoleDefinition, | ||
) | ||
) | ||
|
||
unexpected_role_definitions = RoleDefinition.objects.filter(managed=True).exclude(pk__in=[rd.pk for rd in managed_role_definitions]) | ||
for role_definition in unexpected_role_definitions: | ||
logger.info(f'Deleting old managed role definition {role_definition.name}, pk={role_definition.pk}') | ||
role_definition.delete() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
import pytest | ||
from django.apps import apps | ||
|
||
from awx.main.migrations._dab_rbac import setup_managed_role_definitions | ||
|
||
|
||
@pytest.fixture | ||
def managed_roles(): | ||
"Run the migration script to pre-create managed role definitions" | ||
setup_managed_role_definitions(apps, None) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,45 @@ | ||
import pytest | ||
from django.apps import apps | ||
from django.test.utils import override_settings | ||
|
||
from awx.main.migrations._dab_rbac import setup_managed_role_definitions | ||
|
||
from ansible_base.rbac.models import RoleDefinition | ||
|
||
INVENTORY_OBJ_PERMISSIONS = ['view_inventory', 'adhoc_inventory', 'use_inventory', 'change_inventory', 'delete_inventory', 'update_inventory'] | ||
|
||
|
||
@pytest.mark.django_db | ||
def test_managed_definitions_precreate(): | ||
with override_settings( | ||
ANSIBLE_BASE_ROLE_PRECREATE={ | ||
'object_admin': '{cls._meta.model_name}-admin', | ||
'org_admin': 'organization-admin', | ||
'org_children': 'organization-{cls._meta.model_name}-admin', | ||
'special': '{cls._meta.model_name}-{action}', | ||
} | ||
): | ||
setup_managed_role_definitions(apps, None) | ||
rd = RoleDefinition.objects.get(name='inventory-admin') | ||
assert rd.managed is True | ||
# add permissions do not go in the object-level admin | ||
assert set(rd.permissions.values_list('codename', flat=True)) == set(INVENTORY_OBJ_PERMISSIONS) | ||
|
||
# test org-level object admin permissions | ||
rd = RoleDefinition.objects.get(name='organization-inventory-admin') | ||
assert rd.managed is True | ||
assert set(rd.permissions.values_list('codename', flat=True)) == set(['add_inventory', 'view_organization'] + INVENTORY_OBJ_PERMISSIONS) | ||
|
||
|
||
@pytest.mark.django_db | ||
def test_managed_definitions_custom_obj_admin_name(): | ||
with override_settings( | ||
ANSIBLE_BASE_ROLE_PRECREATE={ | ||
'object_admin': 'foo-{cls._meta.model_name}-foo', | ||
} | ||
): | ||
setup_managed_role_definitions(apps, None) | ||
rd = RoleDefinition.objects.get(name='foo-inventory-foo') | ||
assert rd.managed is True | ||
# add permissions do not go in the object-level admin | ||
assert set(rd.permissions.values_list('codename', flat=True)) == set(INVENTORY_OBJ_PERMISSIONS) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
It seems like this is not working when migrating to the newest version. I get following error:
File "/var/lib/awx/venv/awx/lib64/python3.11/site-packages/awx/main/migrations/_dab_rbac.py", line 200, in migrate_to_new_rbac role_definition_name = f'{role.content_type.model_class().__name__} {action.title()}' ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ AttributeError: 'ContentType' object has no attribute 'model_class'
A quick google search points out that another call to apps.get_models might be necessary