diff --git a/apps/applications/serializers/application.py b/apps/applications/serializers/application.py index 8f6d103c0..f6bb60516 100644 --- a/apps/applications/serializers/application.py +++ b/apps/applications/serializers/application.py @@ -18,22 +18,28 @@ class ApplicationSerializerMixin(serializers.Serializer): attrs = MethodSerializer() def get_attrs_serializer(self): - serializer_class = None + default_serializer = serializers.Serializer(read_only=True) if isinstance(self.instance, models.Application): - instance_type = self.instance.type - serializer_class = type_serializer_classes_mapping.get(instance_type) + _type = self.instance.type + _category = self.instance.category else: - request = self.context['request'] - query_type = request.query_params.get('type') - query_category = request.query_params.get('category') - if query_type: - serializer_class = type_serializer_classes_mapping.get(query_type) - elif query_category: - serializer_class = category_serializer_classes_mapping.get(query_category) + _type = self.context['request'].query_params.get('type') + _category = self.context['request'].query_params.get('category') - if serializer_class is None: - serializer_class = serializers.Serializer - serializer = serializer_class() + if _type: + serializer_class = type_serializer_classes_mapping.get(_type) + elif _category: + serializer_class = category_serializer_classes_mapping.get(_category) + else: + serializer_class = default_serializer + + if not serializer_class: + serializer_class = default_serializer + + if isinstance(serializer_class, type): + serializer = serializer_class() + else: + serializer = serializer_class return serializer diff --git a/apps/terminal/serializers/storage.py b/apps/terminal/serializers/storage.py index dcac31c16..ab4a813c7 100644 --- a/apps/terminal/serializers/storage.py +++ b/apps/terminal/serializers/storage.py @@ -113,16 +113,25 @@ class ReplayStorageSerializer(serializers.ModelSerializer): return _meta def get_meta_serializer(self): - serializer_class = None - query_type = self.context['request'].query_params.get('type') - if query_type: - serializer_class = replay_storage_type_serializer_classes_mapping.get(query_type) + default_serializer = serializers.Serializer(read_only=True) + if isinstance(self.instance, ReplayStorage): - instance_type = self.instance.type - serializer_class = replay_storage_type_serializer_classes_mapping.get(instance_type) - if serializer_class is None: - serializer_class = serializers.Serializer - serializer = serializer_class() + _type = self.instance.type + else: + _type = self.context['request'].query_params.get('type') + + if _type: + serializer_class = replay_storage_type_serializer_classes_mapping.get(_type) + else: + serializer_class = default_serializer + + if not serializer_class: + serializer_class = default_serializer + + if isinstance(serializer_class, type): + serializer = serializer_class() + else: + serializer = serializer_class return serializer @@ -187,14 +196,23 @@ class CommandStorageSerializer(serializers.ModelSerializer): return _meta def get_meta_serializer(self): - serializer_class = None - query_type = self.context['request'].query_params.get('type') - if query_type: - serializer_class = command_storage_type_serializer_classes_mapping.get(query_type) + default_serializer = serializers.Serializer(read_only=True) + if isinstance(self.instance, CommandStorage): - instance_type = self.instance.type - serializer_class = command_storage_type_serializer_classes_mapping.get(instance_type) - if serializer_class is None: - serializer_class = serializers.Serializer - serializer = serializer_class() + _type = self.instance.type + else: + _type = self.context['request'].query_params.get('type') + + if _type: + serializer_class = command_storage_type_serializer_classes_mapping.get(_type) + else: + serializer_class = default_serializer + + if not serializer_class: + serializer_class = default_serializer + + if isinstance(serializer_class, type): + serializer = serializer_class() + else: + serializer = serializer_class return serializer diff --git a/apps/tickets/handler/apply_application.py b/apps/tickets/handler/apply_application.py index d9c72e2bf..83f62b417 100644 --- a/apps/tickets/handler/apply_application.py +++ b/apps/tickets/handler/apply_application.py @@ -88,12 +88,13 @@ class Handler(BaseHandler): apply_category = self.ticket.meta.get('apply_category') apply_type = self.ticket.meta.get('apply_type') + approve_permission_name = self.ticket.meta.get('approve_permission_name', '') approved_applications_id = self.ticket.meta.get('approve_applications', []) approve_system_users_id = self.ticket.meta.get('approve_system_users', []) approve_date_start = self.ticket.meta.get('approve_date_start') approve_date_expired = self.ticket.meta.get('approve_date_expired') - permission_name = _('Created by ticket ({}) ({})'.format( - self.ticket.title, str(self.ticket.id)[:4]) + permission_created_by = '{}:{}'.format( + str(self.ticket.__class__.__name__), str(self.ticket.id) ) permission_comment = _( 'Created by the ticket, ' @@ -101,18 +102,19 @@ class Handler(BaseHandler): 'ticket applicant: {}, ' 'ticket processor: {}, ' 'ticket ID: {}' - ''.format( - self.ticket.title, self.ticket.applicant_display, - self.ticket.processor_display, str(self.ticket.id) - ) + ).format( + self.ticket.title, + self.ticket.applicant_display, + self.ticket.processor_display, + str(self.ticket.id) ) permissions_data = { 'id': self.ticket.id, - 'name': str(permission_name), + 'name': approve_permission_name, 'category': apply_category, 'type': apply_type, 'comment': str(permission_comment), - 'created_by': '{}:{}'.format(str(self.__class__.__name__), str(self.ticket.id)), + 'created_by': permission_created_by, 'date_start': approve_date_start, 'date_expired': approve_date_expired, } diff --git a/apps/tickets/handler/apply_asset.py b/apps/tickets/handler/apply_asset.py index d01a5c6b1..71507e7ae 100644 --- a/apps/tickets/handler/apply_asset.py +++ b/apps/tickets/handler/apply_asset.py @@ -90,13 +90,14 @@ class Handler(BaseHandler): if asset_permission: return asset_permission + approve_permission_name = self.ticket.meta.get('approve_permission_name', ) approve_assets_id = self.ticket.meta.get('approve_assets', []) approve_system_users_id = self.ticket.meta.get('approve_system_users', []) approve_actions = self.ticket.meta.get('approve_actions', Action.NONE) approve_date_start = self.ticket.meta.get('approve_date_start') approve_date_expired = self.ticket.meta.get('approve_date_expired') - permission_name = _('Created by ticket ({}) ({})'.format( - self.ticket.title, str(self.ticket.id)[:4]) + permission_created_by = '{}:{}'.format( + str(self.ticket.__class__.__name__), str(self.ticket.id) ) permission_comment = _( 'Created by the ticket, ' @@ -104,18 +105,18 @@ class Handler(BaseHandler): 'ticket applicant: {}, ' 'ticket processor: {}, ' 'ticket ID: {}' - ''.format( - self.ticket.title, - self.ticket.applicant_display, - self.ticket.processor_display, - str(self.ticket.id) - ) + ).format( + self.ticket.title, + self.ticket.applicant_display, + self.ticket.processor_display, + str(self.ticket.id) ) + permission_data = { 'id': self.ticket.id, - 'name': str(permission_name), - 'comment': permission_comment, - 'created_by': '{}:{}'.format(str(self.__class__.__name__), str(self.ticket.id)), + 'name': approve_permission_name, + 'comment': str(permission_comment), + 'created_by': permission_created_by, 'actions': approve_actions, 'date_start': approve_date_start, 'date_expired': approve_date_expired, diff --git a/apps/tickets/handler/base.py b/apps/tickets/handler/base.py index f55b51a37..a0092a96a 100644 --- a/apps/tickets/handler/base.py +++ b/apps/tickets/handler/base.py @@ -86,17 +86,17 @@ class BaseHandler(object): {}: {}, {}: {}, {}: {}, - {}: {}, {}: {} '''.format( _('Ticket title'), self.ticket.title, _('Ticket type'), self.ticket.get_type_display(), + _('Ticket status'), self.ticket.get_status_display(), + _('Ticket action'), self.ticket.get_action_display(), _('Ticket applicant'), self.ticket.applicant_display, _('Ticket assignees'), self.ticket.assignees_display, - _('Ticket processor'), self.ticket.processor_display or _('No'), - _('Ticket action'), self.ticket.get_action_display(), - _('Ticket status'), self.ticket.get_status_display() ) + if self.ticket.status_closed: + basic_body += '''{}: {}'''.format(_('Ticket processor'), self.ticket.processor_display) body = self.body_html_format.format(_("Ticket basic info"), basic_body) return body diff --git a/apps/tickets/serializers/ticket/meta/meta.py b/apps/tickets/serializers/ticket/meta/meta.py index 1b3c071a8..b423805f4 100644 --- a/apps/tickets/serializers/ticket/meta/meta.py +++ b/apps/tickets/serializers/ticket/meta/meta.py @@ -29,5 +29,6 @@ type_serializer_classes_mapping = { const.TicketTypeChoices.login_confirm.value: { 'default': login_confirm.LoginConfirmSerializer, action_open: login_confirm.ApplySerializer, + action_approve: login_confirm.LoginConfirmSerializer(read_only=True), } } diff --git a/apps/tickets/serializers/ticket/meta/ticket_type/apply_application.py b/apps/tickets/serializers/ticket/meta/ticket_type/apply_application.py index 220c04199..4b5bb00e2 100644 --- a/apps/tickets/serializers/ticket/meta/ticket_type/apply_application.py +++ b/apps/tickets/serializers/ticket/meta/ticket_type/apply_application.py @@ -1,11 +1,13 @@ from rest_framework import serializers from django.utils.translation import ugettext_lazy as _ from django.db.models import Q +from perms.models import ApplicationPermission from applications.models import Application from applications.const import ApplicationCategoryChoices, ApplicationTypeChoices from assets.models import SystemUser from orgs.utils import tmp_to_org from tickets.models import Ticket +from .common import DefaultPermissionName __all__ = [ 'ApplyApplicationSerializer', 'ApplySerializer', 'ApproveSerializer', @@ -47,6 +49,9 @@ class ApplySerializer(serializers.Serializer): class ApproveSerializer(serializers.Serializer): # 审批信息 + approve_permission_name = serializers.CharField( + max_length=128, default=DefaultPermissionName(), label=_('Permission name') + ) approve_applications = serializers.ListField( required=True, child=serializers.UUIDField(), label=_('Approve applications'), allow_null=True @@ -72,6 +77,19 @@ class ApproveSerializer(serializers.Serializer): required=True, label=_('Date expired'), allow_null=True ) + def validate_approve_permission_name(self, permission_name): + if not isinstance(self.root.instance, Ticket): + return permission_name + + with tmp_to_org(self.root.instance.org_id): + already_exists = ApplicationPermission.objects.filter(name=permission_name).exists() + if not already_exists: + return permission_name + + raise serializers.ValidationError(_( + 'Permission named `{}` already exists'.format(permission_name) + )) + def validate_approve_applications(self, approve_applications): if not isinstance(self.root.instance, Ticket): return [] @@ -118,6 +136,9 @@ class ApplyApplicationSerializer(ApplySerializer, ApproveSerializer): return [] apply_application_group = value.get('apply_application_group', []) + if not apply_application_group: + return [] + apply_type = value.get('apply_type') queries = Q() for application in apply_application_group: @@ -133,8 +154,11 @@ class ApplyApplicationSerializer(ApplySerializer, ApproveSerializer): if not isinstance(self.root.instance, Ticket): return [] - apply_type = value.get('apply_type') apply_system_user_group = value.get('apply_system_user_group', []) + if not apply_system_user_group: + return [] + + apply_type = value.get('apply_type') protocol = SystemUser.get_protocol_by_application_type(apply_type) queries = Q() for system_user in apply_system_user_group: diff --git a/apps/tickets/serializers/ticket/meta/ticket_type/apply_asset.py b/apps/tickets/serializers/ticket/meta/ticket_type/apply_asset.py index ab613e19c..acdd217e1 100644 --- a/apps/tickets/serializers/ticket/meta/ticket_type/apply_asset.py +++ b/apps/tickets/serializers/ticket/meta/ticket_type/apply_asset.py @@ -2,9 +2,11 @@ from django.utils.translation import ugettext_lazy as _ from django.db.models import Q from rest_framework import serializers from perms.serializers import ActionsField +from perms.models import AssetPermission from assets.models import Asset, SystemUser from orgs.utils import tmp_to_org from tickets.models import Ticket +from .common import DefaultPermissionName __all__ = [ @@ -44,6 +46,9 @@ class ApplySerializer(serializers.Serializer): class ApproveSerializer(serializers.Serializer): # 审批信息 + approve_permission_name = serializers.CharField( + max_length=128, default=DefaultPermissionName(), label=_('Permission name') + ) approve_assets = serializers.ListField( required=True, allow_null=True, child=serializers.UUIDField(), label=_('Approve assets') ) @@ -76,6 +81,19 @@ class ApproveSerializer(serializers.Serializer): required=True, label=_('Date expired'), allow_null=True ) + def validate_approve_permission_name(self, permission_name): + if not isinstance(self.root.instance, Ticket): + return permission_name + + with tmp_to_org(self.root.instance.org_id): + already_exists = AssetPermission.objects.filter(name=permission_name).exists() + if not already_exists: + return permission_name + + raise serializers.ValidationError(_( + 'Permission named `{}` already exists'.format(permission_name) + )) + def validate_approve_assets(self, approve_assets): if not isinstance(self.root.instance, Ticket): return [] @@ -118,10 +136,13 @@ class ApplyAssetSerializer(ApplySerializer, ApproveSerializer): apply_ip_group = value.get('apply_ip_group', []) apply_hostname_group = value.get('apply_hostname_group', []) - queries = Q(ip__in=apply_ip_group) + queries = Q() + if apply_ip_group: + queries |= Q(ip__in=apply_ip_group) for hostname in apply_hostname_group: queries |= Q(hostname__icontains=hostname) - + if not queries: + return [] with tmp_to_org(self.root.instance.org_id): assets_id = Asset.objects.filter(queries).values_list('id', flat=True)[:5] assets_id = [str(asset_id) for asset_id in assets_id] @@ -132,6 +153,9 @@ class ApplyAssetSerializer(ApplySerializer, ApproveSerializer): return [] apply_system_user_group = value.get('apply_system_user_group', []) + if not apply_system_user_group: + return [] + queries = Q() for system_user in apply_system_user_group: queries |= Q(username__icontains=system_user) diff --git a/apps/tickets/serializers/ticket/meta/ticket_type/common.py b/apps/tickets/serializers/ticket/meta/ticket_type/common.py new file mode 100644 index 000000000..2d43f6a81 --- /dev/null +++ b/apps/tickets/serializers/ticket/meta/ticket_type/common.py @@ -0,0 +1,30 @@ +from django.utils.translation import ugettext as _ +from tickets.models import Ticket + + +__all__ = ['DefaultPermissionName', 'get_default_permission_name'] + + +def get_default_permission_name(ticket): + name = '' + if isinstance(ticket, Ticket): + name = _('Created by ticket ({}-{})').format(ticket.title, str(ticket.id)[:4]) + return name + + +class DefaultPermissionName(object): + default = None + + @staticmethod + def _construct_default_permission_name(serializer_field): + permission_name = '' + ticket = serializer_field.root.instance + if isinstance(ticket, Ticket): + permission_name = get_default_permission_name(ticket) + return permission_name + + def set_context(self, serializer_field): + self.default = self._construct_default_permission_name(serializer_field) + + def __call__(self): + return self.default diff --git a/apps/tickets/serializers/ticket/ticket.py b/apps/tickets/serializers/ticket/ticket.py index 1ca217adb..c28b9a9f1 100644 --- a/apps/tickets/serializers/ticket/ticket.py +++ b/apps/tickets/serializers/ticket/ticket.py @@ -38,31 +38,34 @@ class TicketSerializer(OrgResourceModelSerializerMixin): ] def get_meta_serializer(self): - request = self.context['request'] - default_serializer_class = serializers.Serializer + default_serializer = serializers.Serializer(read_only=True) if isinstance(self.instance, Ticket): _type = self.instance.type else: - _type = request.query_params.get('type') + _type = self.context['request'].query_params.get('type') - if not _type: - return default_serializer_class() + if _type: + action_serializer_classes_mapping = type_serializer_classes_mapping.get(_type) + if action_serializer_classes_mapping: + query_action = self.context['request'].query_params.get('action') + action = query_action if query_action else self.context['view'].action + serializer_class = action_serializer_classes_mapping.get(action) + if not serializer_class: + serializer_class = action_serializer_classes_mapping.get('default') + else: + serializer_class = default_serializer + else: + serializer_class = default_serializer - action_serializer_classes_mapping = type_serializer_classes_mapping.get(_type) - if not action_serializer_classes_mapping: - return default_serializer_class() + if not serializer_class: + serializer_class = default_serializer - query_action = request.query_params.get('action') - _action = query_action if query_action else self.context['view'].action - serializer_class = action_serializer_classes_mapping.get(_action) - if serializer_class: - return serializer_class() + if isinstance(serializer_class, type): + serializer = serializer_class() + else: + serializer = serializer_class - serializer_class = action_serializer_classes_mapping.get('default') - if serializer_class: - return serializer_class() - - return default_serializer_class() + return serializer class TicketDisplaySerializer(TicketSerializer): diff --git a/apps/tickets/utils.py b/apps/tickets/utils.py index 523d0f102..1bd789fda 100644 --- a/apps/tickets/utils.py +++ b/apps/tickets/utils.py @@ -10,6 +10,20 @@ from . import const logger = get_logger(__file__) +EMAIL_TEMPLATE = ''' +
+

+ {title} + + {ticket_detail_url_description} + +

+
+ {body} +
+
+''' + def send_ticket_applied_mail_to_assignees(ticket): if not ticket.assignees: @@ -18,22 +32,13 @@ def send_ticket_applied_mail_to_assignees(ticket): ) return - subject = _('New Ticket: {} ({})'.format(ticket.title, ticket.get_type_display())) - ticket_detail_url = urljoin( - settings.SITE_URL, const.TICKET_DETAIL_URL.format(id=str(ticket.id)) - ) - message = """ -
-

{title} {ticket_detail_url_description}

-
- {body} -
-
- """.format( - title=_('Your has a new ticket, from applicant - {}').format(str(ticket.applicant)), - ticket_detail_url=ticket_detail_url, - ticket_detail_url_description=_('click here to review'), - body=ticket.body.replace('\n', '
'), + ticket_detail_url = urljoin(settings.SITE_URL, const.TICKET_DETAIL_URL.format(id=str(ticket.id))) + subject = _('New Ticket - {} ({})').format(ticket.title, ticket.get_type_display()) + message = EMAIL_TEMPLATE.format( + title=_('Your has a new ticket, applicant - {}').format(str(ticket.applicant_display)), + ticket_detail_url=ticket_detail_url, + ticket_detail_url_description=_('click here to review'), + body=ticket.body.replace('\n', '
'), ) if settings.DEBUG: logger.debug(message) @@ -45,18 +50,15 @@ def send_ticket_processed_mail_to_applicant(ticket): if not ticket.applicant: logger.error("Not found applicant: {}({})".format(ticket.title, ticket.id)) return - subject = _('Ticket has processed: {} ({})').format(ticket.title, ticket.processor_display) - message = """ -
-

{title}

-
- {body} -
-
- """.format( - title=_('Your ticket has been ({}) processed').format(ticket.processor_display), - body=ticket.body.replace('\n', '
'), - ) + + ticket_detail_url = urljoin(settings.SITE_URL, const.TICKET_DETAIL_URL.format(id=str(ticket.id))) + subject = _('Ticket has processed - {} ({})').format(ticket.title, ticket.processor_display) + message = EMAIL_TEMPLATE.format( + title=_('Your ticket has been processed, processor - {}').format(ticket.processor_display), + ticket_detail_url=ticket_detail_url, + ticket_detail_url_description=_('click here to review'), + body=ticket.body.replace('\n', '
'), + ) if settings.DEBUG: logger.debug(message) recipient_list = [ticket.applicant.email]