diff --git a/awx/main/access.py b/awx/main/access.py index cec0b7c2bf..7e2289df42 100644 --- a/awx/main/access.py +++ b/awx/main/access.py @@ -773,7 +773,9 @@ class JobTemplateAccess(BaseAccess): inventory_pk = get_pk_from_dict(data, 'inventory') inventory = Inventory.objects.filter(id=inventory_pk) if not inventory.exists() and not data.get('ask_inventory_on_launch', False): - return False # Does this make sense? Maybe should check read access + return False + if inventory.exists() and self.user not in inventory[0].use_role: + return False project_pk = get_pk_from_dict(data, 'project') if 'job_type' in data and data['job_type'] == PERM_INVENTORY_SCAN: @@ -786,10 +788,8 @@ class JobTemplateAccess(BaseAccess): # If the user has admin access to the project (as an org admin), should # be able to proceed without additional checks. project = get_object_or_400(Project, pk=project_pk) - if self.user in project.admin_role: - return True - return self.user in project.admin_role and self.user in inventory.read_role + return self.user in project.use_role def can_start(self, obj, validate_license=True): # Check license. diff --git a/awx/main/migrations/0017_v300_prompting_migrations.py b/awx/main/migrations/0017_v300_prompting_migrations.py index c5a1df0eb9..6bec778956 100644 --- a/awx/main/migrations/0017_v300_prompting_migrations.py +++ b/awx/main/migrations/0017_v300_prompting_migrations.py @@ -1,6 +1,7 @@ # -*- coding: utf-8 -*- from __future__ import unicode_literals +from awx.main.migrations import _rbac as rbac from awx.main.migrations import _ask_for_variables as ask_for_variables from awx.main.migrations import _migration_utils as migration_utils from django.db import migrations @@ -15,4 +16,5 @@ class Migration(migrations.Migration): operations = [ migrations.RunPython(migration_utils.set_current_apps_for_migrations), migrations.RunPython(ask_for_variables.migrate_credential), + migrations.RunPython(rbac.rebuild_role_hierarchy), ] diff --git a/awx/main/migrations/_rbac.py b/awx/main/migrations/_rbac.py index e16b3575d3..7eeb95579d 100644 --- a/awx/main/migrations/_rbac.py +++ b/awx/main/migrations/_rbac.py @@ -215,7 +215,7 @@ def migrate_inventory(apps, schema_editor): Inventory = apps.get_model('main', 'Inventory') Permission = apps.get_model('main', 'Permission') - def role_from_permission(): + def role_from_permission(perm): if perm.permission_type == 'admin': return inventory.admin_role elif perm.permission_type == 'read': @@ -233,7 +233,7 @@ def migrate_inventory(apps, schema_editor): role = None execrole = None - role = role_from_permission() + role = role_from_permission(perm) if role is None: raise Exception(smart_text(u'Unhandled permission type for inventory: {}'.format( perm.permission_type))) @@ -320,24 +320,30 @@ def migrate_projects(apps, schema_editor): logger.warn(smart_text(u'adding Project({}) admin: {}'.format(project.name, project.created_by.username))) for team in project.deprecated_teams.all(): - team.member_role.children.add(project.use_role) + team.member_role.children.add(project.read_role) logger.info(smart_text(u'adding Team({}) access for Project({})'.format(team.name, project.name))) - if project.organization is not None: - for user in project.organization.deprecated_users.all(): - project.use_role.members.add(user) - logger.info(smart_text(u'adding Organization({}) member access to Project({})'.format(project.organization.name, project.name))) - for perm in Permission.objects.filter(project=project): - # All perms at this level just imply a user or team can read + if perm.permission_type == 'create': + role = project.use_role + else: + role = project.read_role + if perm.team: - perm.team.member_role.children.add(project.use_role) + perm.team.member_role.children.add(role) logger.info(smart_text(u'adding Team({}) access for Project({})'.format(perm.team.name, project.name))) if perm.user: - project.use_role.members.add(perm.user) + role.members.add(perm.user) logger.info(smart_text(u'adding User({}) access for Project({})'.format(perm.user.username, project.name))) + if project.organization is not None: + for user in project.organization.deprecated_users.all(): + if not (project.use_role.members.filter(pk=user.id).exists() or project.admin_role.members.filter(pk=user.id).exists()): + project.read_role.members.add(user) + logger.info(smart_text(u'adding Organization({}) member access to Project({})'.format(project.organization.name, project.name))) + + @log_migration def migrate_job_templates(apps, schema_editor): @@ -403,7 +409,7 @@ def migrate_job_templates(apps, schema_editor): team_create_permissions = set( jt_permission_qs - .filter(permission_type__in=['create'] if jt.job_type == 'check' else ['create']) + .filter(permission_type__in=['create']) .values_list('team__id', flat=True) ) team_run_permissions = set( @@ -413,12 +419,12 @@ def migrate_job_templates(apps, schema_editor): ) user_create_permissions = set( jt_permission_qs - .filter(permission_type__in=['create'] if jt.job_type == 'check' else ['run']) + .filter(permission_type__in=['create']) .values_list('user__id', flat=True) ) user_run_permissions = set( jt_permission_qs - .filter(permission_type__in=['check', 'run'] if jt.job_type == 'check' else ['create']) + .filter(permission_type__in=['check', 'run'] if jt.job_type == 'check' else ['run']) .values_list('user__id', flat=True) ) @@ -446,17 +452,20 @@ def migrate_job_templates(apps, schema_editor): logger.info(smart_text(u'transfering execute access on JobTemplate({}) to Team({})'.format(jt.name, team.name))) for user in User.objects.filter(id__in=user_create_permissions).iterator(): + cred = jt.credential or jt.cloud_credential if (jt.inventory.id in user_inv_permissions[user.id] or any([jt.inventory.id in team_inv_permissions[team.id] for team in user.deprecated_teams.all()])) and \ - ((not jt.credential and not jt.cloud_credential) or - Credential.objects.filter(Q(deprecated_user=user) | Q(deprecated_team__deprecated_users=user), jobtemplates=jt).exists()): + (not cred or cred.deprecated_user == user or + (cred.deprecated_team and cred.deprecated_team.deprecated_users.filter(pk=user.id).exists())): jt.admin_role.members.add(user) logger.info(smart_text(u'transfering admin access on JobTemplate({}) to User({})'.format(jt.name, user.username))) for user in User.objects.filter(id__in=user_run_permissions).iterator(): + cred = jt.credential or jt.cloud_credential + if (jt.inventory.id in user_inv_permissions[user.id] or any([jt.inventory.id in team_inv_permissions[team.id] for team in user.deprecated_teams.all()])) and \ - ((not jt.credential and not jt.cloud_credential) or - Credential.objects.filter(Q(deprecated_user=user) | Q(deprecated_team__deprecated_users=user), jobtemplates=jt).exists()): + (not cred or cred.deprecated_user == user or + (cred.deprecated_team and cred.deprecated_team.deprecated_users.filter(pk=user.id).exists())): jt.execute_role.members.add(user) logger.info(smart_text(u'transfering execute access on JobTemplate({}) to User({})'.format(jt.name, user.username))) @@ -468,8 +477,6 @@ def rebuild_role_hierarchy(apps, schema_editor): start = time() roots = Role.objects \ .all() \ - .exclude(pk__in=Role.parents.through.objects.all() - .values_list('from_role_id', flat=True).distinct()) \ .values_list('id', flat=True) stop = time() logger.info('Found %d roots in %f seconds, rebuilding ancestry map' % (len(roots), stop - start))