mirror of https://github.com/jumpserver/jumpserver
perf: 工单优化(审批人可以填写工单对应的授权规则名称) (#5468)
* perf: 工单优化(审批人可以填写工单对应的授权规则名称) * perf: 工单优化(优化推荐的资产、应用、系统用户等逻辑) * perf: 工单优化(优化工单邮件内容) * perf: MethodSerializer优化(优化当Serializer不需要时, 默认可以不传递对应字段) Co-authored-by: Bai <bugatti_it@163.com>pull/5482/head
parent
0842553f8a
commit
9126c7780d
|
@ -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
|
||||
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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,
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
|
@ -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):
|
||||
|
|
|
@ -10,6 +10,20 @@ from . import const
|
|||
|
||||
logger = get_logger(__file__)
|
||||
|
||||
EMAIL_TEMPLATE = '''
|
||||
<div>
|
||||
<p>
|
||||
{title}
|
||||
<a href={ticket_detail_url}>
|
||||
<strong>{ticket_detail_url_description}</strong>
|
||||
</a>
|
||||
</p>
|
||||
<div>
|
||||
{body}
|
||||
</div>
|
||||
</div>
|
||||
'''
|
||||
|
||||
|
||||
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 = """
|
||||
<div>
|
||||
<p>{title} <a href={ticket_detail_url}>{ticket_detail_url_description}</a></p>
|
||||
<div>
|
||||
{body}
|
||||
</div>
|
||||
</div>
|
||||
""".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', '<br/>'),
|
||||
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', '<br/>'),
|
||||
)
|
||||
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 = """
|
||||
<div>
|
||||
<p>{title}</p>
|
||||
<div>
|
||||
{body}
|
||||
</div>
|
||||
</div>
|
||||
""".format(
|
||||
title=_('Your ticket has been ({}) processed').format(ticket.processor_display),
|
||||
body=ticket.body.replace('\n', '<br/>'),
|
||||
)
|
||||
|
||||
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', '<br/>'),
|
||||
)
|
||||
if settings.DEBUG:
|
||||
logger.debug(message)
|
||||
recipient_list = [ticket.applicant.email]
|
||||
|
|
Loading…
Reference in New Issue