Refactor of accessible_objects for polymorphic model

query simplification, with break-out into an intermediary
state that can be used in access methods
This commit is contained in:
AlanCoding
2016-12-20 09:55:08 -05:00
parent 8650b69c9e
commit b2d0871a5e
5 changed files with 110 additions and 68 deletions

View File

@@ -1825,25 +1825,22 @@ class JobEventAccess(BaseAccess):
class UnifiedJobTemplateAccess(BaseAccess):
'''
I can see a unified job template whenever I can see the same project,
inventory source or job template. Unified job templates do not include
projects without SCM configured or inventory sources without a cloud
source.
inventory source, WFJT, or job template. Unified job templates do not include
inventory sources without a cloud source.
'''
model = UnifiedJobTemplate
def get_queryset(self):
qs = self.model.objects.all()
project_qs = self.user.get_queryset(Project).filter(scm_type__in=[s[0] for s in Project.SCM_TYPE_CHOICES])
inventory_source_qs = self.user.get_queryset(InventorySource).filter(source__in=CLOUD_INVENTORY_SOURCES)
job_template_qs = self.user.get_queryset(JobTemplate)
system_job_template_qs = self.user.get_queryset(SystemJobTemplate)
workflow_job_template_qs = self.user.get_queryset(WorkflowJobTemplate)
qs = qs.filter(Q(Project___in=project_qs) |
Q(InventorySource___in=inventory_source_qs) |
Q(JobTemplate___in=job_template_qs) |
Q(systemjobtemplate__in=system_job_template_qs) |
Q(workflowjobtemplate__in=workflow_job_template_qs))
if self.user.is_superuser or self.user.is_system_auditor:
qs = self.model.objects.all()
else:
qs = self.model.objects.filter(
Q(pk__in=self.model.accessible_pk_qs(self.user, 'read_role')) |
Q(inventorysource__inventory__id__in=Inventory._accessible_pk_qs(
Inventory, self.user, 'read_role')))
qs = qs.exclude(inventorysource__source="")
qs = qs.select_related(
'created_by',
'modified_by',
@@ -1882,25 +1879,25 @@ class UnifiedJobAccess(BaseAccess):
model = UnifiedJob
def get_queryset(self):
qs = self.model.objects.all()
project_update_qs = self.user.get_queryset(ProjectUpdate)
inventory_update_qs = self.user.get_queryset(InventoryUpdate).filter(source__in=CLOUD_INVENTORY_SOURCES)
job_qs = self.user.get_queryset(Job)
ad_hoc_command_qs = self.user.get_queryset(AdHocCommand)
system_job_qs = self.user.get_queryset(SystemJob)
workflow_job_qs = self.user.get_queryset(WorkflowJob)
qs = qs.filter(Q(ProjectUpdate___in=project_update_qs) |
Q(InventoryUpdate___in=inventory_update_qs) |
Q(Job___in=job_qs) |
Q(AdHocCommand___in=ad_hoc_command_qs) |
Q(SystemJob___in=system_job_qs) |
Q(WorkflowJob___in=workflow_job_qs))
qs = qs.select_related(
if self.user.is_superuser or self.user.is_system_auditor:
qs = self.model.objects.all()
else:
inv_pk_qs = Inventory._accessible_pk_qs(Inventory, self.user, 'read_role')
inv_update_qs = InventoryUpdate.objects.filter(inventory_source__inventory__id__in=inv_pk_qs)
ad_hoc_command_qs = AdHocCommand.objects.filter(inventory__id__in=inv_pk_qs)
org_auditor_qs = Organization.objects.filter(
Q(admin_role__members=self.user) | Q(auditor_role__members=self.user))
qs = self.model.objects.filter(
Q(unified_job_template__id__in=UnifiedJobTemplate.accessible_pk_qs(self.user, 'read_role')) |
Q(inventoryupdate__in=inv_update_qs) |
Q(adhoccommand__in=ad_hoc_command_qs) |
Q(job__inventory__organization__in=org_auditor_qs) |
Q(job__project__organization__in=org_auditor_qs)
)
qs = qs.prefetch_related(
'created_by',
'modified_by',
'unified_job_node__workflow_job',
)
qs = qs.prefetch_related(
'unified_job_template',
)
@@ -1922,6 +1919,9 @@ class UnifiedJobAccess(BaseAccess):
# 'job_template__credential',
# 'job_template__cloud_credential',
#)
# Maybe we can do these, like:
# 'projectupdate__project',
# 'inventoryupdate__inventory'
return qs.all()

View File

@@ -3,6 +3,7 @@ import json
# Django
from django.db import models
from django.db.models import Q
from django.contrib.contenttypes.models import ContentType
from django.contrib.auth.models import User # noqa
@@ -37,8 +38,9 @@ class ResourceMixin(models.Model):
'''
return ResourceMixin._accessible_objects(cls, accessor, role_field)
@staticmethod
def _accessible_objects(cls, accessor, role_field):
def _accessible_pk_qs(cls, accessor, role_field, content_types=None):
if type(accessor) == User:
ancestor_roles = accessor.roles.all()
elif type(accessor) == Role:
@@ -47,14 +49,24 @@ class ResourceMixin(models.Model):
accessor_type = ContentType.objects.get_for_model(accessor)
ancestor_roles = Role.objects.filter(content_type__pk=accessor_type.id,
object_id=accessor.id)
qs = cls.objects.filter(pk__in =
RoleAncestorEntry.objects.filter(
ancestor__in=ancestor_roles,
content_type_id = ContentType.objects.get_for_model(cls).id,
role_field = role_field
).values_list('object_id').distinct()
)
return qs
if content_types is not None:
return RoleAncestorEntry.objects.filter(
ancestor__in = ancestor_roles,
content_type_id__in = content_types,
role_field = role_field
).values_list('object_id').distinct()
return RoleAncestorEntry.objects.filter(
ancestor__in = ancestor_roles,
content_type_id = ContentType.objects.get_for_model(cls).id,
role_field = role_field
).values_list('object_id').distinct()
@staticmethod
def _accessible_objects(cls, accessor, role_field):
return cls.objects.filter(pk__in = ResourceMixin._accessible_pk_qs(cls, accessor, role_field))
def get_permissions(self, accessor):

View File

@@ -20,6 +20,7 @@ from django.utils.translation import ugettext_lazy as _
from django.utils.timezone import now
from django.utils.encoding import smart_text
from django.apps import apps
from django.contrib.contenttypes.models import ContentType
# Django-Polymorphic
from polymorphic import PolymorphicModel
@@ -30,6 +31,7 @@ from djcelery.models import TaskMeta
# AWX
from awx.main.models.base import * # noqa
from awx.main.models.schedules import Schedule
from awx.main.models.mixins import ResourceMixin
from awx.main.utils import (
decrypt_field, _inventory_updates,
copy_model_by_class, copy_m2m_relationships
@@ -166,6 +168,22 @@ class UnifiedJobTemplate(PolymorphicModel, CommonModelNameNotUnique, Notificatio
else:
return super(UnifiedJobTemplate, self).unique_error_message(model_class, unique_check)
@classmethod
def accessible_pk_qs(cls, accessor, role_field):
'''
A re-implementation of accessible pk queryset for the "normal" unified JTs.
Does not return inventory sources or system JTs, these should
be handled inside of get_queryset where it is utilized.
'''
# Algorithmic option, if we don't want hardcoded class names
# ujt_name_list = [c.__name__.lower() for c in cls.__subclasses__()]
# ujt_name_list.remove('inventorysource')
subclass_content_types = ContentType.objects.filter(
model__in=['project', 'jobtemplate', 'systemjobtemplate', 'workflowjobtemplate']
).values_list('id', flat=True)
return ResourceMixin._accessible_pk_qs(cls, accessor, role_field, content_types=subclass_content_types)
def _perform_unique_checks(self, unique_checks):
# Handle the list of unique fields returned above. Replace with an
# appropriate error message for the remaining field(s) in the unique