mirror of
https://github.com/ansible/awx.git
synced 2026-05-19 14:57:39 -02:30
Job template migration and migration tests
This commit is contained in:
1693
awx/main/migrations/_old_access.py
Normal file
1693
awx/main/migrations/_old_access.py
Normal file
File diff suppressed because it is too large
Load Diff
@@ -1,4 +1,5 @@
|
|||||||
from collections import defaultdict
|
from collections import defaultdict
|
||||||
|
import _old_access as old_access
|
||||||
|
|
||||||
def migrate_users(apps, schema_editor):
|
def migrate_users(apps, schema_editor):
|
||||||
migrations = list()
|
migrations = list()
|
||||||
@@ -52,7 +53,7 @@ def migrate_inventory(apps, schema_editor):
|
|||||||
|
|
||||||
for inventory in Inventory.objects.all():
|
for inventory in Inventory.objects.all():
|
||||||
teams, users = [], []
|
teams, users = [], []
|
||||||
for perm in Permission.objects.filter(inventory=inventory):
|
for perm in Permission.objects.filter(inventory=inventory, active=True):
|
||||||
role = None
|
role = None
|
||||||
execrole = None
|
execrole = None
|
||||||
if perm.permission_type == 'admin':
|
if perm.permission_type == 'admin':
|
||||||
@@ -64,6 +65,10 @@ def migrate_inventory(apps, schema_editor):
|
|||||||
elif perm.permission_type == 'write':
|
elif perm.permission_type == 'write':
|
||||||
role = inventory.updater_role
|
role = inventory.updater_role
|
||||||
pass
|
pass
|
||||||
|
elif perm.permission_type == 'check':
|
||||||
|
pass
|
||||||
|
elif perm.permission_type == 'run':
|
||||||
|
pass
|
||||||
else:
|
else:
|
||||||
raise Exception('Unhandled permission type for inventory: %s' % perm.permission_type)
|
raise Exception('Unhandled permission type for inventory: %s' % perm.permission_type)
|
||||||
if perm.run_ad_hoc_commands:
|
if perm.run_ad_hoc_commands:
|
||||||
@@ -122,14 +127,92 @@ def migrate_projects(apps, schema_editor):
|
|||||||
project.member_role.members.add(user)
|
project.member_role.members.add(user)
|
||||||
migrations[project.name]['users'].add(user)
|
migrations[project.name]['users'].add(user)
|
||||||
|
|
||||||
for perm in Permission.objects.filter(project=project):
|
for perm in Permission.objects.filter(project=project, active=True):
|
||||||
# All perms at this level just imply a user or team can read
|
# All perms at this level just imply a user or team can read
|
||||||
if perm.team:
|
if perm.team:
|
||||||
team.member_role.children.add(project.member_role)
|
perm.team.member_role.children.add(project.member_role)
|
||||||
migrations[project.name]['teams'].add(team)
|
migrations[project.name]['teams'].add(perm.team)
|
||||||
|
|
||||||
if perm.user:
|
if perm.user:
|
||||||
project.member_role.members.add(perm.user)
|
project.member_role.members.add(perm.user)
|
||||||
migrations[project.name]['users'].add(perm.user)
|
migrations[project.name]['users'].add(perm.user)
|
||||||
|
|
||||||
return migrations
|
return migrations
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def migrate_job_templates(apps, schema_editor):
|
||||||
|
'''
|
||||||
|
NOTE: This must be run after orgs, inventory, projects, credential, and
|
||||||
|
users have been migrated
|
||||||
|
'''
|
||||||
|
|
||||||
|
|
||||||
|
'''
|
||||||
|
I can see job templates when:
|
||||||
|
X I am a superuser.
|
||||||
|
- I can read the inventory, project and credential (which means I am an
|
||||||
|
org admin or member of a team with access to all of the above).
|
||||||
|
- I have permission explicitly granted to check/deploy with the inventory
|
||||||
|
and project.
|
||||||
|
|
||||||
|
|
||||||
|
#This does not mean I would be able to launch a job from the template or
|
||||||
|
#edit the template.
|
||||||
|
- access.py can_read for JobTemplate enforces that you can only
|
||||||
|
see it if you can launch it, so the above imply launch too
|
||||||
|
'''
|
||||||
|
|
||||||
|
|
||||||
|
'''
|
||||||
|
Tower administrators, organization administrators, and project
|
||||||
|
administrators, within a project under their purview, may create and modify
|
||||||
|
new job templates for that project.
|
||||||
|
|
||||||
|
When editing a job template, they may select among the inventory groups and
|
||||||
|
credentials in the organization for which they have usage permissions, or
|
||||||
|
they may leave either blank to be selected at runtime.
|
||||||
|
|
||||||
|
Additionally, they may specify one or more users/teams that have execution
|
||||||
|
permission for that job template, among the users/teams that are a member
|
||||||
|
of that project.
|
||||||
|
|
||||||
|
That execution permission is valid irrespective of any explicit permissions
|
||||||
|
the user has or has not been granted to the inventory group or credential
|
||||||
|
specified in the job template.
|
||||||
|
|
||||||
|
'''
|
||||||
|
|
||||||
|
migrations = defaultdict(lambda: defaultdict(set))
|
||||||
|
|
||||||
|
User = apps.get_model('auth', 'User')
|
||||||
|
JobTemplate = apps.get_model('main', 'JobTemplate')
|
||||||
|
Team = apps.get_model('main', 'Team')
|
||||||
|
Permission = apps.get_model('main', 'Permission')
|
||||||
|
|
||||||
|
for jt in JobTemplate.objects.all():
|
||||||
|
for team in Team.objects.all():
|
||||||
|
if Permission.objects.filter(
|
||||||
|
team=team,
|
||||||
|
inventory=jt.inventory,
|
||||||
|
project=jt.project,
|
||||||
|
active=True,
|
||||||
|
permission_type__in=['create', 'check', 'run'] if jt.job_type == 'check' else ['create', 'run']
|
||||||
|
):
|
||||||
|
team.member_role.children.add(jt.executor_role);
|
||||||
|
migrations[jt.name]['teams'].add(team)
|
||||||
|
|
||||||
|
|
||||||
|
for user in User.objects.all():
|
||||||
|
if jt.accessible_by(user, {'execute': True}):
|
||||||
|
# If the job template is already accessible by the user, because they
|
||||||
|
# are a sytem, organization, or project admin, then don't add an explicit
|
||||||
|
# role entry for them
|
||||||
|
continue
|
||||||
|
|
||||||
|
if old_access.check_user_access(user, jt.__class__, 'start', jt, False):
|
||||||
|
jt.executor_role.members.add(user)
|
||||||
|
migrations[jt.name]['users'].add(user)
|
||||||
|
|
||||||
|
|
||||||
|
return migrations
|
||||||
|
|||||||
@@ -195,9 +195,8 @@ class JobTemplate(UnifiedJobTemplate, JobOptions, ResourceMixin):
|
|||||||
)
|
)
|
||||||
executor_role = ImplicitRoleField(
|
executor_role = ImplicitRoleField(
|
||||||
role_name='Job Template Executor',
|
role_name='Job Template Executor',
|
||||||
parent_role='project.auditor_role',
|
|
||||||
resource_field='resource',
|
resource_field='resource',
|
||||||
permissions = {'execute': True}
|
permissions = {'read': True, 'execute': True}
|
||||||
)
|
)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ from awx.main.models.inventory import (
|
|||||||
Group,
|
Group,
|
||||||
)
|
)
|
||||||
from awx.main.models.projects import Project
|
from awx.main.models.projects import Project
|
||||||
|
from awx.main.models.jobs import JobTemplate
|
||||||
from awx.main.models.organization import (
|
from awx.main.models.organization import (
|
||||||
Organization,
|
Organization,
|
||||||
Team,
|
Team,
|
||||||
@@ -23,6 +24,28 @@ def user():
|
|||||||
return user
|
return user
|
||||||
return u
|
return u
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def check_jobtemplate(project, inventory, credential):
|
||||||
|
return \
|
||||||
|
JobTemplate.objects.create(
|
||||||
|
job_type='check',
|
||||||
|
project=project,
|
||||||
|
inventory=inventory,
|
||||||
|
credential=credential,
|
||||||
|
name='check-job-template'
|
||||||
|
)
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def deploy_jobtemplate(project, inventory, credential):
|
||||||
|
return \
|
||||||
|
JobTemplate.objects.create(
|
||||||
|
job_type='run',
|
||||||
|
project=project,
|
||||||
|
inventory=inventory,
|
||||||
|
credential=credential,
|
||||||
|
name='deploy-job-template'
|
||||||
|
)
|
||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
def team(organization):
|
def team(organization):
|
||||||
return Team.objects.create(organization=organization, name='test-team')
|
return Team.objects.create(organization=organization, name='test-team')
|
||||||
|
|||||||
133
awx/main/tests/functional/test_rbac_job_templates.py
Normal file
133
awx/main/tests/functional/test_rbac_job_templates.py
Normal file
@@ -0,0 +1,133 @@
|
|||||||
|
import pytest
|
||||||
|
|
||||||
|
from awx.main.migrations import _rbac as rbac
|
||||||
|
from awx.main.models import Permission
|
||||||
|
from django.apps import apps
|
||||||
|
|
||||||
|
@pytest.mark.django_db
|
||||||
|
def test_job_template_migration_check(deploy_jobtemplate, check_jobtemplate, user):
|
||||||
|
admin = user('admin', is_superuser=True)
|
||||||
|
joe = user('joe')
|
||||||
|
|
||||||
|
|
||||||
|
check_jobtemplate.project.organizations.all()[0].users.add(joe)
|
||||||
|
|
||||||
|
Permission(user=joe, inventory=check_jobtemplate.inventory, permission_type='read').save()
|
||||||
|
Permission(user=joe, inventory=check_jobtemplate.inventory,
|
||||||
|
project=check_jobtemplate.project, permission_type='check').save()
|
||||||
|
|
||||||
|
rbac.migrate_users(apps, None)
|
||||||
|
rbac.migrate_organization(apps, None)
|
||||||
|
rbac.migrate_projects(apps, None)
|
||||||
|
rbac.migrate_inventory(apps, None)
|
||||||
|
|
||||||
|
assert check_jobtemplate.project.accessible_by(joe, {'read': True})
|
||||||
|
assert check_jobtemplate.accessible_by(admin, {'execute': True}) is True
|
||||||
|
assert check_jobtemplate.accessible_by(joe, {'execute': True}) is False
|
||||||
|
|
||||||
|
migrations = rbac.migrate_job_templates(apps, None)
|
||||||
|
|
||||||
|
assert len(migrations[check_jobtemplate.name]['users']) == 1
|
||||||
|
assert check_jobtemplate.accessible_by(admin, {'execute': True}) is True
|
||||||
|
assert check_jobtemplate.accessible_by(joe, {'execute': True}) is True
|
||||||
|
assert deploy_jobtemplate.accessible_by(admin, {'execute': True}) is True
|
||||||
|
assert deploy_jobtemplate.accessible_by(joe, {'execute': True}) is False
|
||||||
|
|
||||||
|
@pytest.mark.django_db
|
||||||
|
def test_job_template_migration_deploy(deploy_jobtemplate, check_jobtemplate, user):
|
||||||
|
admin = user('admin', is_superuser=True)
|
||||||
|
joe = user('joe')
|
||||||
|
|
||||||
|
|
||||||
|
deploy_jobtemplate.project.organizations.all()[0].users.add(joe)
|
||||||
|
|
||||||
|
Permission(user=joe, inventory=deploy_jobtemplate.inventory, permission_type='read').save()
|
||||||
|
Permission(user=joe, inventory=deploy_jobtemplate.inventory,
|
||||||
|
project=deploy_jobtemplate.project, permission_type='run').save()
|
||||||
|
|
||||||
|
rbac.migrate_users(apps, None)
|
||||||
|
rbac.migrate_organization(apps, None)
|
||||||
|
rbac.migrate_projects(apps, None)
|
||||||
|
rbac.migrate_inventory(apps, None)
|
||||||
|
|
||||||
|
assert deploy_jobtemplate.project.accessible_by(joe, {'read': True})
|
||||||
|
assert deploy_jobtemplate.accessible_by(admin, {'execute': True}) is True
|
||||||
|
assert deploy_jobtemplate.accessible_by(joe, {'execute': True}) is False
|
||||||
|
|
||||||
|
migrations = rbac.migrate_job_templates(apps, None)
|
||||||
|
|
||||||
|
assert len(migrations[deploy_jobtemplate.name]['users']) == 1
|
||||||
|
assert deploy_jobtemplate.accessible_by(admin, {'execute': True}) is True
|
||||||
|
assert deploy_jobtemplate.accessible_by(joe, {'execute': True}) is True
|
||||||
|
assert check_jobtemplate.accessible_by(admin, {'execute': True}) is True
|
||||||
|
assert check_jobtemplate.accessible_by(joe, {'execute': True}) is True
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.django_db
|
||||||
|
def test_job_template_team_migration_check(deploy_jobtemplate, check_jobtemplate, organization, team, user):
|
||||||
|
admin = user('admin', is_superuser=True)
|
||||||
|
joe = user('joe')
|
||||||
|
team.users.add(joe)
|
||||||
|
team.organization = organization
|
||||||
|
team.save()
|
||||||
|
|
||||||
|
check_jobtemplate.project.organizations.all()[0].users.add(joe)
|
||||||
|
|
||||||
|
Permission(team=team, inventory=check_jobtemplate.inventory, permission_type='read').save()
|
||||||
|
Permission(team=team, inventory=check_jobtemplate.inventory,
|
||||||
|
project=check_jobtemplate.project, permission_type='check').save()
|
||||||
|
|
||||||
|
rbac.migrate_users(apps, None)
|
||||||
|
rbac.migrate_team(apps, None)
|
||||||
|
rbac.migrate_organization(apps, None)
|
||||||
|
rbac.migrate_projects(apps, None)
|
||||||
|
rbac.migrate_inventory(apps, None)
|
||||||
|
|
||||||
|
assert check_jobtemplate.project.accessible_by(joe, {'read': True})
|
||||||
|
assert check_jobtemplate.accessible_by(admin, {'execute': True}) is True
|
||||||
|
assert check_jobtemplate.accessible_by(joe, {'execute': True}) is False
|
||||||
|
|
||||||
|
migrations = rbac.migrate_job_templates(apps, None)
|
||||||
|
|
||||||
|
assert len(migrations[check_jobtemplate.name]['users']) == 0
|
||||||
|
assert len(migrations[check_jobtemplate.name]['teams']) == 1
|
||||||
|
assert check_jobtemplate.accessible_by(admin, {'execute': True}) is True
|
||||||
|
assert check_jobtemplate.accessible_by(joe, {'execute': True}) is True
|
||||||
|
|
||||||
|
assert deploy_jobtemplate.accessible_by(admin, {'execute': True}) is True
|
||||||
|
assert deploy_jobtemplate.accessible_by(joe, {'execute': True}) is False
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.django_db
|
||||||
|
def test_job_template_team_deploy_migration(deploy_jobtemplate, check_jobtemplate, organization, team, user):
|
||||||
|
admin = user('admin', is_superuser=True)
|
||||||
|
joe = user('joe')
|
||||||
|
team.users.add(joe)
|
||||||
|
team.organization = organization
|
||||||
|
team.save()
|
||||||
|
|
||||||
|
deploy_jobtemplate.project.organizations.all()[0].users.add(joe)
|
||||||
|
|
||||||
|
Permission(team=team, inventory=deploy_jobtemplate.inventory, permission_type='read').save()
|
||||||
|
Permission(team=team, inventory=deploy_jobtemplate.inventory,
|
||||||
|
project=deploy_jobtemplate.project, permission_type='run').save()
|
||||||
|
|
||||||
|
rbac.migrate_users(apps, None)
|
||||||
|
rbac.migrate_team(apps, None)
|
||||||
|
rbac.migrate_organization(apps, None)
|
||||||
|
rbac.migrate_projects(apps, None)
|
||||||
|
rbac.migrate_inventory(apps, None)
|
||||||
|
|
||||||
|
assert deploy_jobtemplate.project.accessible_by(joe, {'read': True})
|
||||||
|
assert deploy_jobtemplate.accessible_by(admin, {'execute': True}) is True
|
||||||
|
assert deploy_jobtemplate.accessible_by(joe, {'execute': True}) is False
|
||||||
|
|
||||||
|
migrations = rbac.migrate_job_templates(apps, None)
|
||||||
|
|
||||||
|
assert len(migrations[deploy_jobtemplate.name]['users']) == 0
|
||||||
|
assert len(migrations[deploy_jobtemplate.name]['teams']) == 1
|
||||||
|
assert deploy_jobtemplate.accessible_by(admin, {'execute': True}) is True
|
||||||
|
assert deploy_jobtemplate.accessible_by(joe, {'execute': True}) is True
|
||||||
|
|
||||||
|
assert check_jobtemplate.accessible_by(admin, {'execute': True}) is True
|
||||||
|
assert check_jobtemplate.accessible_by(joe, {'execute': True}) is True
|
||||||
Reference in New Issue
Block a user