diff --git a/awx/api/serializers.py b/awx/api/serializers.py index 1e499b5b37..05491a829f 100644 --- a/awx/api/serializers.py +++ b/awx/api/serializers.py @@ -2544,6 +2544,7 @@ class LabelSerializer(BaseSerializer): return res class ScheduleSerializer(BaseSerializer): + show_capabilities = ['edit', 'delete'] class Meta: model = Schedule diff --git a/awx/main/access.py b/awx/main/access.py index cc624265f2..a00acb6214 100644 --- a/awx/main/access.py +++ b/awx/main/access.py @@ -222,8 +222,8 @@ class BaseAccess(object): def get_user_capabilities(self, obj, method_list): user_capabilities = {} + # Custom ordering to loop through methods so we can reuse earlier calcs for display_method in ['edit', 'delete', 'start', 'schedule', 'copy', 'adhoc']: - # Custom ordering of methods used so we can reuse earlier calcs if display_method not in method_list: continue @@ -233,54 +233,38 @@ class BaseAccess(object): continue # Aliases for going form UI language to API language - # speedups in certain cases by deferring to earlier property if display_method == 'edit': method = 'change' elif display_method == 'copy': method = 'add' - elif display_method == 'schedule' and 'edit' in user_capabilities: - user_capabilities['schedule'] = user_capabilities['edit'] - continue - elif display_method == 'delete' and not isinstance(obj, (User, UnifiedJob)): - user_capabilities['delete'] = user_capabilities['edit'] - continue elif display_method == 'adhoc': method = 'run_ad_hoc_commands' else: method = display_method + # Shortcuts in certain cases by deferring to earlier property + if display_method == 'schedule' and 'edit' in user_capabilities: + user_capabilities['schedule'] = user_capabilities['edit'] + continue + elif display_method == 'delete' and not isinstance(obj, (User, UnifiedJob)): + user_capabilities['delete'] = user_capabilities['edit'] + continue + # Preprocessing before the access method is called data = None - if method == 'add': - data = {} - - access_instance = self - obj_check = obj - if isinstance(obj, (Group, Host)): - if method == 'start': - if obj.inventory_source: - obj_check = obj.inventory_source - else: - user_capabilities[method] = False - continue - else: - obj_check = obj.inventory - access_class = access_registry.get(type(obj_check), [])[0] - access_instance = access_class(self.user) if isinstance(obj, JobTemplate): data = {'reference_obj': obj} + elif method == 'add': + data = {} - # try: - access_method = getattr(access_instance, "can_%s" % method) + # Compute permission + access_method = getattr(self, "can_%s" % method) if method in ['change']: # 3 args - user_capabilities[display_method] = access_method(obj_check, data) + user_capabilities[display_method] = access_method(obj, data) elif method in ['delete', 'start', 'run_ad_hoc_commands']: # 2 args - user_capabilities[display_method] = access_method(obj_check) + user_capabilities[display_method] = access_method(obj) elif method in ['add']: # 2 args with data user_capabilities[display_method] = access_method(data) - # except Exception as exc: - # user_capabilities[display_method] = False - # print(exc) return user_capabilities @@ -603,6 +587,12 @@ class GroupAccess(BaseAccess): "active_jobs": active_jobs}) return True + def can_start(self, obj): + # Used as another alias to inventory_source start access + if obj and obj.inventory_source: + return self.user.can_access(InventorySource, 'start', obj.inventory_source) + return False + class InventorySourceAccess(BaseAccess): ''' I can see inventory sources whenever I can see their group or inventory. @@ -938,7 +928,9 @@ class JobTemplateAccess(BaseAccess): Users who are able to create deploy jobs can also run normal and check (dry run) jobs. ''' if not data: # So the browseable API will work - return True + return ( + Project.accessible_objects(self.user, 'use_role').exists() or + Inventory.accessible_objects(self.user, 'use_role').exists()) # if reference_obj is provided, determine if it can be coppied reference_obj = data.pop('reference_obj', None) diff --git a/awx/main/utils.py b/awx/main/utils.py index f65698f052..df30faf2f3 100644 --- a/awx/main/utils.py +++ b/awx/main/utils.py @@ -416,6 +416,9 @@ def cache_list_capabilities(page, role_types, model, user): ''' page_ids = [obj.id for obj in page] id_lists = {} + for obj in page: + obj.capabilities_cache = {} + for role_type in role_types: # Role name translation to UI names for methods display_method = role_type @@ -423,14 +426,15 @@ def cache_list_capabilities(page, role_types, model, user): display_method = 'edit' elif role_type in ['execute', 'update']: display_method = 'start' + # Query for union of page objects & role accessible_objects - id_lists[display_method] = model.accessible_objects( - user, '%s_role' % role_type).filter(pk__in=page_ids).values_list('pk', flat=True) - # Save data item-by-item - for obj in page: - obj.capabilities_cache = {display_method: False for display_method in id_lists.keys()} - for display_method, id_list in id_lists.iteritems(): - if obj.pk in id_list: + ids_with_role = set(model.accessible_objects( + user, '%s_role' % role_type).filter(pk__in=page_ids).values_list('pk', flat=True)) + + # Save data item-by-item + for obj in page: + obj.capabilities_cache[display_method] = False + if obj.pk in ids_with_role: obj.capabilities_cache[display_method] = True