From 198a0db808f0423fd71f493987d09a172c766fd3 Mon Sep 17 00:00:00 2001 From: Wayne Witzel III Date: Fri, 28 Sep 2018 16:41:43 -0400 Subject: [PATCH] move organization views to their own file --- awx/api/generics.py | 23 ++- awx/api/views/__init__.py | 225 +++------------------------- awx/api/views/organization.py | 266 ++++++++++++++++++++++++++++++++++ 3 files changed, 305 insertions(+), 209 deletions(-) create mode 100644 awx/api/views/organization.py diff --git a/awx/api/generics.py b/awx/api/generics.py index 01d63ff252..b4745d1f4f 100644 --- a/awx/api/generics.py +++ b/awx/api/generics.py @@ -6,7 +6,7 @@ import inspect import logging import time import six -import urllib +import urllib # Django from django.conf import settings @@ -56,7 +56,7 @@ __all__ = ['APIView', 'GenericAPIView', 'ListAPIView', 'SimpleListAPIView', 'ParentMixin', 'DeleteLastUnattachLabelMixin', 'SubListAttachDetachAPIView', - 'CopyAPIView'] + 'CopyAPIView', 'BaseUsersList',] logger = logging.getLogger('awx.api.generics') analytics_logger = logging.getLogger('awx.analytics.performance') @@ -990,3 +990,22 @@ class CopyAPIView(GenericAPIView): serializer = self._get_copy_return_serializer(new_obj) headers = {'Location': new_obj.get_absolute_url(request=request)} return Response(serializer.data, status=status.HTTP_201_CREATED, headers=headers) + + +class BaseUsersList(SubListCreateAttachDetachAPIView): + def post(self, request, *args, **kwargs): + ret = super(BaseUsersList, self).post( request, *args, **kwargs) + if ret.status_code != 201: + return ret + try: + if ret.data is not None and request.data.get('is_system_auditor', False): + # This is a faux-field that just maps to checking the system + # auditor role member list.. unfortunately this means we can't + # set it on creation, and thus needs to be set here. + user = User.objects.get(id=ret.data['id']) + user.is_system_auditor = request.data['is_system_auditor'] + ret.data['is_system_auditor'] = request.data['is_system_auditor'] + except AttributeError as exc: + print(exc) + pass + return ret diff --git a/awx/api/views/__init__.py b/awx/api/views/__init__.py index 68da3df44a..7fad317e85 100644 --- a/awx/api/views/__init__.py +++ b/awx/api/views/__init__.py @@ -101,6 +101,24 @@ from awx.api.views.mixin import ( RelatedJobsPreventDeleteMixin, OrganizationCountsMixin, ) +from awx.api.views.organization import ( # noqa + OrganizationList, + OrganizationDetail, + OrganizationInventoriesList, + OrganizationUsersList, + OrganizationAdminsList, + OrganizationProjectsList, + OrganizationWorkflowJobTemplatesList, + OrganizationTeamsList, + OrganizationActivityStreamList, + OrganizationNotificationTemplatesList, + OrganizationNotificationTemplatesAnyList, + OrganizationNotificationTemplatesErrorList, + OrganizationNotificationTemplatesSuccessList, + OrganizationInstanceGroupsList, + OrganizationAccessList, + OrganizationObjectRolesList, +) logger = logging.getLogger('awx.api.views') @@ -744,213 +762,6 @@ class AuthView(APIView): return Response(data) -class OrganizationList(OrganizationCountsMixin, ListCreateAPIView): - - model = Organization - serializer_class = OrganizationSerializer - - def get_queryset(self): - qs = Organization.accessible_objects(self.request.user, 'read_role') - qs = qs.select_related('admin_role', 'auditor_role', 'member_role', 'read_role') - qs = qs.prefetch_related('created_by', 'modified_by') - return qs - - def create(self, request, *args, **kwargs): - """Create a new organzation. - - If there is already an organization and the license of this - instance does not permit multiple organizations, then raise - LicenseForbids. - """ - # Sanity check: If the multiple organizations feature is disallowed - # by the license, then we are only willing to create this organization - # if no organizations exist in the system. - if (not feature_enabled('multiple_organizations') and - self.model.objects.exists()): - raise LicenseForbids(_('Your license only permits a single ' - 'organization to exist.')) - - # Okay, create the organization as usual. - return super(OrganizationList, self).create(request, *args, **kwargs) - - -class OrganizationDetail(RelatedJobsPreventDeleteMixin, RetrieveUpdateDestroyAPIView): - - model = Organization - serializer_class = OrganizationSerializer - - def get_serializer_context(self, *args, **kwargs): - full_context = super(OrganizationDetail, self).get_serializer_context(*args, **kwargs) - - if not hasattr(self, 'kwargs') or 'pk' not in self.kwargs: - return full_context - org_id = int(self.kwargs['pk']) - - org_counts = {} - access_kwargs = {'accessor': self.request.user, 'role_field': 'read_role'} - direct_counts = Organization.objects.filter(id=org_id).annotate( - users=Count('member_role__members', distinct=True), - admins=Count('admin_role__members', distinct=True) - ).values('users', 'admins') - - if not direct_counts: - return full_context - - org_counts = direct_counts[0] - org_counts['inventories'] = Inventory.accessible_objects(**access_kwargs).filter( - organization__id=org_id).count() - org_counts['teams'] = Team.accessible_objects(**access_kwargs).filter( - organization__id=org_id).count() - org_counts['projects'] = Project.accessible_objects(**access_kwargs).filter( - organization__id=org_id).count() - org_counts['job_templates'] = JobTemplate.accessible_objects(**access_kwargs).filter( - project__organization__id=org_id).count() - - full_context['related_field_counts'] = {} - full_context['related_field_counts'][org_id] = org_counts - - return full_context - - -class OrganizationInventoriesList(SubListAPIView): - - model = Inventory - serializer_class = InventorySerializer - parent_model = Organization - relationship = 'inventories' - - -class BaseUsersList(SubListCreateAttachDetachAPIView): - def post(self, request, *args, **kwargs): - ret = super(BaseUsersList, self).post( request, *args, **kwargs) - if ret.status_code != 201: - return ret - try: - if ret.data is not None and request.data.get('is_system_auditor', False): - # This is a faux-field that just maps to checking the system - # auditor role member list.. unfortunately this means we can't - # set it on creation, and thus needs to be set here. - user = User.objects.get(id=ret.data['id']) - user.is_system_auditor = request.data['is_system_auditor'] - ret.data['is_system_auditor'] = request.data['is_system_auditor'] - except AttributeError as exc: - print(exc) - pass - return ret - - -class OrganizationUsersList(BaseUsersList): - - model = User - serializer_class = UserSerializer - parent_model = Organization - relationship = 'member_role.members' - - -class OrganizationAdminsList(BaseUsersList): - - model = User - serializer_class = UserSerializer - parent_model = Organization - relationship = 'admin_role.members' - - -class OrganizationProjectsList(SubListCreateAttachDetachAPIView): - - model = Project - serializer_class = ProjectSerializer - parent_model = Organization - relationship = 'projects' - parent_key = 'organization' - - -class OrganizationWorkflowJobTemplatesList(SubListCreateAttachDetachAPIView): - - model = WorkflowJobTemplate - serializer_class = WorkflowJobTemplateSerializer - parent_model = Organization - relationship = 'workflows' - parent_key = 'organization' - - -class OrganizationTeamsList(SubListCreateAttachDetachAPIView): - - model = Team - serializer_class = TeamSerializer - parent_model = Organization - relationship = 'teams' - parent_key = 'organization' - - -class OrganizationActivityStreamList(ActivityStreamEnforcementMixin, SubListAPIView): - - model = ActivityStream - serializer_class = ActivityStreamSerializer - parent_model = Organization - relationship = 'activitystream_set' - search_fields = ('changes',) - - -class OrganizationNotificationTemplatesList(SubListCreateAttachDetachAPIView): - - model = NotificationTemplate - serializer_class = NotificationTemplateSerializer - parent_model = Organization - relationship = 'notification_templates' - parent_key = 'organization' - - -class OrganizationNotificationTemplatesAnyList(SubListCreateAttachDetachAPIView): - - model = NotificationTemplate - serializer_class = NotificationTemplateSerializer - parent_model = Organization - relationship = 'notification_templates_any' - - -class OrganizationNotificationTemplatesErrorList(SubListCreateAttachDetachAPIView): - - model = NotificationTemplate - serializer_class = NotificationTemplateSerializer - parent_model = Organization - relationship = 'notification_templates_error' - - -class OrganizationNotificationTemplatesSuccessList(SubListCreateAttachDetachAPIView): - - model = NotificationTemplate - serializer_class = NotificationTemplateSerializer - parent_model = Organization - relationship = 'notification_templates_success' - - -class OrganizationInstanceGroupsList(SubListAttachDetachAPIView): - - model = InstanceGroup - serializer_class = InstanceGroupSerializer - parent_model = Organization - relationship = 'instance_groups' - - -class OrganizationAccessList(ResourceAccessList): - - model = User # needs to be User for AccessLists's - parent_model = Organization - - -class OrganizationObjectRolesList(SubListAPIView): - - model = Role - serializer_class = RoleSerializer - parent_model = Organization - search_fields = ('role_field', 'content_type__model',) - - def get_queryset(self): - po = self.get_parent_object() - content_type = ContentType.objects.get_for_model(self.parent_model) - return Role.objects.filter(content_type=content_type, object_id=po.pk) - - class TeamList(ListCreateAPIView): model = Team diff --git a/awx/api/views/organization.py b/awx/api/views/organization.py new file mode 100644 index 0000000000..f2870214a0 --- /dev/null +++ b/awx/api/views/organization.py @@ -0,0 +1,266 @@ +# Copyright (c) 2018 Red Hat, Inc. +# All Rights Reserved. + +# Python +import logging + +# Django +from django.db.models import Count +from django.contrib.contenttypes.models import ContentType +from django.utils.translation import ugettext_lazy as _ + +# AWX +from awx.conf.license import ( + feature_enabled, + LicenseForbids, +) +from awx.main.models import ( + ActivityStream, + Inventory, + Project, + JobTemplate, + WorkflowJobTemplate, + Organization, + NotificationTemplate, + Role, + User, + Team, + InstanceGroup, +) +from awx.api.generics import ( + ListCreateAPIView, + RetrieveUpdateDestroyAPIView, + SubListAPIView, + SubListCreateAttachDetachAPIView, + SubListAttachDetachAPIView, + ResourceAccessList, + BaseUsersList, +) + +from awx.api.serializers import ( + OrganizationSerializer, + InventorySerializer, + ProjectSerializer, + UserSerializer, + TeamSerializer, + ActivityStreamSerializer, + RoleSerializer, + NotificationTemplateSerializer, + WorkflowJobTemplateSerializer, + InstanceGroupSerializer, +) +from awx.api.views.mixin import ( + ActivityStreamEnforcementMixin, + RelatedJobsPreventDeleteMixin, + OrganizationCountsMixin, +) + +logger = logging.getLogger('awx.api.views.organization') + + +class OrganizationList(OrganizationCountsMixin, ListCreateAPIView): + + model = Organization + serializer_class = OrganizationSerializer + + def get_queryset(self): + qs = Organization.accessible_objects(self.request.user, 'read_role') + qs = qs.select_related('admin_role', 'auditor_role', 'member_role', 'read_role') + qs = qs.prefetch_related('created_by', 'modified_by') + return qs + + def create(self, request, *args, **kwargs): + """Create a new organzation. + + If there is already an organization and the license of this + instance does not permit multiple organizations, then raise + LicenseForbids. + """ + # Sanity check: If the multiple organizations feature is disallowed + # by the license, then we are only willing to create this organization + # if no organizations exist in the system. + if (not feature_enabled('multiple_organizations') and + self.model.objects.exists()): + raise LicenseForbids(_('Your license only permits a single ' + 'organization to exist.')) + + # Okay, create the organization as usual. + return super(OrganizationList, self).create(request, *args, **kwargs) + + +class OrganizationDetail(RelatedJobsPreventDeleteMixin, RetrieveUpdateDestroyAPIView): + + model = Organization + serializer_class = OrganizationSerializer + + def get_serializer_context(self, *args, **kwargs): + full_context = super(OrganizationDetail, self).get_serializer_context(*args, **kwargs) + + if not hasattr(self, 'kwargs') or 'pk' not in self.kwargs: + return full_context + org_id = int(self.kwargs['pk']) + + org_counts = {} + access_kwargs = {'accessor': self.request.user, 'role_field': 'read_role'} + direct_counts = Organization.objects.filter(id=org_id).annotate( + users=Count('member_role__members', distinct=True), + admins=Count('admin_role__members', distinct=True) + ).values('users', 'admins') + + if not direct_counts: + return full_context + + org_counts = direct_counts[0] + org_counts['inventories'] = Inventory.accessible_objects(**access_kwargs).filter( + organization__id=org_id).count() + org_counts['teams'] = Team.accessible_objects(**access_kwargs).filter( + organization__id=org_id).count() + org_counts['projects'] = Project.accessible_objects(**access_kwargs).filter( + organization__id=org_id).count() + org_counts['job_templates'] = JobTemplate.accessible_objects(**access_kwargs).filter( + project__organization__id=org_id).count() + + full_context['related_field_counts'] = {} + full_context['related_field_counts'][org_id] = org_counts + + return full_context + + +class OrganizationInventoriesList(SubListAPIView): + + model = Inventory + serializer_class = InventorySerializer + parent_model = Organization + relationship = 'inventories' + + +class BaseUsersList(SubListCreateAttachDetachAPIView): + def post(self, request, *args, **kwargs): + ret = super(BaseUsersList, self).post( request, *args, **kwargs) + if ret.status_code != 201: + return ret + try: + if ret.data is not None and request.data.get('is_system_auditor', False): + # This is a faux-field that just maps to checking the system + # auditor role member list.. unfortunately this means we can't + # set it on creation, and thus needs to be set here. + user = User.objects.get(id=ret.data['id']) + user.is_system_auditor = request.data['is_system_auditor'] + ret.data['is_system_auditor'] = request.data['is_system_auditor'] + except AttributeError as exc: + print(exc) + pass + return ret + + +class OrganizationUsersList(BaseUsersList): + + model = User + serializer_class = UserSerializer + parent_model = Organization + relationship = 'member_role.members' + + +class OrganizationAdminsList(BaseUsersList): + + model = User + serializer_class = UserSerializer + parent_model = Organization + relationship = 'admin_role.members' + + +class OrganizationProjectsList(SubListCreateAttachDetachAPIView): + + model = Project + serializer_class = ProjectSerializer + parent_model = Organization + relationship = 'projects' + parent_key = 'organization' + + +class OrganizationWorkflowJobTemplatesList(SubListCreateAttachDetachAPIView): + + model = WorkflowJobTemplate + serializer_class = WorkflowJobTemplateSerializer + parent_model = Organization + relationship = 'workflows' + parent_key = 'organization' + + +class OrganizationTeamsList(SubListCreateAttachDetachAPIView): + + model = Team + serializer_class = TeamSerializer + parent_model = Organization + relationship = 'teams' + parent_key = 'organization' + + +class OrganizationActivityStreamList(ActivityStreamEnforcementMixin, SubListAPIView): + + model = ActivityStream + serializer_class = ActivityStreamSerializer + parent_model = Organization + relationship = 'activitystream_set' + search_fields = ('changes',) + + +class OrganizationNotificationTemplatesList(SubListCreateAttachDetachAPIView): + + model = NotificationTemplate + serializer_class = NotificationTemplateSerializer + parent_model = Organization + relationship = 'notification_templates' + parent_key = 'organization' + + +class OrganizationNotificationTemplatesAnyList(SubListCreateAttachDetachAPIView): + + model = NotificationTemplate + serializer_class = NotificationTemplateSerializer + parent_model = Organization + relationship = 'notification_templates_any' + + +class OrganizationNotificationTemplatesErrorList(SubListCreateAttachDetachAPIView): + + model = NotificationTemplate + serializer_class = NotificationTemplateSerializer + parent_model = Organization + relationship = 'notification_templates_error' + + +class OrganizationNotificationTemplatesSuccessList(SubListCreateAttachDetachAPIView): + + model = NotificationTemplate + serializer_class = NotificationTemplateSerializer + parent_model = Organization + relationship = 'notification_templates_success' + + +class OrganizationInstanceGroupsList(SubListAttachDetachAPIView): + + model = InstanceGroup + serializer_class = InstanceGroupSerializer + parent_model = Organization + relationship = 'instance_groups' + + +class OrganizationAccessList(ResourceAccessList): + + model = User # needs to be User for AccessLists's + parent_model = Organization + + +class OrganizationObjectRolesList(SubListAPIView): + + model = Role + serializer_class = RoleSerializer + parent_model = Organization + search_fields = ('role_field', 'content_type__model',) + + def get_queryset(self): + po = self.get_parent_object() + content_type = ContentType.objects.get_for_model(self.parent_model) + return Role.objects.filter(content_type=content_type, object_id=po.pk) +