reactor: 重构命令&录像存储模块的Serializer及相关模块 (#5392)

* reactor: 重构命令&录像存储模块的Serializer及相关模块


Co-authored-by: Bai <bugatti_it@163.com>
pull/5406/head
fit2bot 2021-01-12 18:06:42 +08:00 committed by GitHub
parent b3f359d47b
commit 86a055638c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
34 changed files with 636 additions and 551 deletions

View File

@ -18,15 +18,19 @@ class ApplicationSerializerMixin(serializers.Serializer):
attrs = MethodSerializer()
def get_attrs_serializer(self):
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)
serializer_class = None
if isinstance(self.instance, models.Application):
instance_type = self.instance.type
serializer_class = type_serializer_classes_mapping.get(instance_type)
else:
serializer_class = None
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)
if serializer_class is None:
serializer_class = serializers.Serializer
serializer = serializer_class()

View File

@ -6,4 +6,4 @@ __all__ = ['CloudSerializer']
class CloudSerializer(serializers.Serializer):
cluster = serializers.CharField(max_length=1024, label=_('Cluster'))
cluster = serializers.CharField(max_length=1024, label=_('Cluster'), allow_null=True)

View File

@ -8,9 +8,8 @@ __all__ = ['DBSerializer']
class DBSerializer(serializers.Serializer):
host = serializers.CharField(max_length=128, label=_('Host'))
port = serializers.IntegerField(label=_('Port'))
# 添加allow_null=True兼容之前数据库中database字段为None的情况
host = serializers.CharField(max_length=128, label=_('Host'), allow_null=True)
port = serializers.IntegerField(label=_('Port'), allow_null=True)
database = serializers.CharField(
max_length=128, required=True, allow_null=True, label=_('Database')
)

View File

@ -29,8 +29,12 @@ class CharPrimaryKeyRelatedField(serializers.PrimaryKeyRelatedField):
class RemoteAppSerializer(serializers.Serializer):
asset_info = serializers.SerializerMethodField()
asset = CharPrimaryKeyRelatedField(queryset=Asset.objects, required=False, label=_("Asset"))
path = serializers.CharField(max_length=128, label=_('Application path'))
asset = CharPrimaryKeyRelatedField(
queryset=Asset.objects, required=False, label=_("Asset"), allow_null=True
)
path = serializers.CharField(
max_length=128, label=_('Application path'), allow_null=True
)
@staticmethod
def get_asset_info(obj):

View File

@ -11,15 +11,16 @@ class ChromeSerializer(RemoteAppSerializer):
CHROME_PATH = 'C:\Program Files (x86)\Google\Chrome\Application\chrome.exe'
path = serializers.CharField(
max_length=128, label=_('Application path'), default=CHROME_PATH
max_length=128, label=_('Application path'), default=CHROME_PATH, allow_null=True,
)
chrome_target = serializers.CharField(
max_length=128, allow_blank=True, required=False, label=_('Target URL')
max_length=128, allow_blank=True, required=False, label=_('Target URL'), allow_null=True,
)
chrome_username = serializers.CharField(
max_length=128, allow_blank=True, required=False, label=_('Username')
max_length=128, allow_blank=True, required=False, label=_('Username'), allow_null=True,
)
chrome_password = serializers.CharField(
max_length=128, allow_blank=True, required=False, write_only=True, label=_('Password')
max_length=128, allow_blank=True, required=False, write_only=True, label=_('Password'),
allow_null=True
)

View File

@ -10,14 +10,18 @@ __all__ = ['CustomSerializer']
class CustomSerializer(RemoteAppSerializer):
custom_cmdline = serializers.CharField(
max_length=128, allow_blank=True, required=False, label=_('Operating parameter')
max_length=128, allow_blank=True, required=False, label=_('Operating parameter'),
allow_null=True,
)
custom_target = serializers.CharField(
max_length=128, allow_blank=True, required=False, label=_('Target url')
max_length=128, allow_blank=True, required=False, label=_('Target url'),
allow_null=True,
)
custom_username = serializers.CharField(
max_length=128, allow_blank=True, required=False, label=_('Username')
max_length=128, allow_blank=True, required=False, label=_('Username'),
allow_null=True,
)
custom_password = serializers.CharField(
max_length=128, allow_blank=True, required=False, write_only=True, label=_('Password')
max_length=128, allow_blank=True, required=False, write_only=True, label=_('Password'),
allow_null=True,
)

View File

@ -8,7 +8,7 @@ __all__ = ['MySQLSerializer']
class MySQLSerializer(DBSerializer):
port = serializers.IntegerField(default=3306, label=_('Port'))
port = serializers.IntegerField(default=3306, label=_('Port'), allow_null=True)

View File

@ -11,20 +11,26 @@ class MySQLWorkbenchSerializer(RemoteAppSerializer):
MYSQL_WORKBENCH_PATH = 'C:\Program Files\MySQL\MySQL Workbench 8.0 CE\MySQLWorkbench.exe'
path = serializers.CharField(
max_length=128, label=_('Application path'), default=MYSQL_WORKBENCH_PATH
max_length=128, label=_('Application path'), default=MYSQL_WORKBENCH_PATH,
allow_null=True,
)
mysql_workbench_ip = serializers.CharField(
max_length=128, allow_blank=True, required=False, label=_('IP')
max_length=128, allow_blank=True, required=False, label=_('IP'),
allow_null=True,
)
mysql_workbench_port = serializers.IntegerField(
required=False, label=_('Port')
required=False, label=_('Port'),
allow_null=True,
)
mysql_workbench_name = serializers.CharField(
max_length=128, allow_blank=True, required=False, label=_('Database')
max_length=128, allow_blank=True, required=False, label=_('Database'),
allow_null=True,
)
mysql_workbench_username = serializers.CharField(
max_length=128, allow_blank=True, required=False, label=_('Username')
max_length=128, allow_blank=True, required=False, label=_('Username'),
allow_null=True,
)
mysql_workbench_password = serializers.CharField(
max_length=128, allow_blank=True, required=False, write_only=True, label=_('Password')
max_length=128, allow_blank=True, required=False, write_only=True, label=_('Password'),
allow_null=True,
)

View File

@ -8,5 +8,5 @@ __all__ = ['OracleSerializer']
class OracleSerializer(DBSerializer):
port = serializers.IntegerField(default=1521, label=_('Port'))
port = serializers.IntegerField(default=1521, label=_('Port'), allow_null=True)

View File

@ -8,5 +8,5 @@ __all__ = ['PostgreSerializer']
class PostgreSerializer(DBSerializer):
port = serializers.IntegerField(default=5432, label=_('Port'))
port = serializers.IntegerField(default=5432, label=_('Port'), allow_null=True)

View File

@ -15,14 +15,18 @@ class VMwareClientSerializer(RemoteAppSerializer):
VMWARE_CLIENT_PATH = ''.join(PATH.split())
path = serializers.CharField(
max_length=128, label=_('Application path'), default=VMWARE_CLIENT_PATH
max_length=128, label=_('Application path'), default=VMWARE_CLIENT_PATH,
allow_null=True
)
vmware_target = serializers.CharField(
max_length=128, allow_blank=True, required=False, label=_('Target URL')
max_length=128, allow_blank=True, required=False, label=_('Target URL'),
allow_null=True
)
vmware_username = serializers.CharField(
max_length=128, allow_blank=True, required=False, label=_('Username')
max_length=128, allow_blank=True, required=False, label=_('Username'),
allow_null=True
)
vmware_password = serializers.CharField(
max_length=128, allow_blank=True, required=False, write_only=True, label=_('Password')
max_length=128, allow_blank=True, required=False, write_only=True, label=_('Password'),
allow_null=True
)

View File

@ -208,12 +208,11 @@ class SystemUser(BaseUser):
def get_protocol_by_application_type(cls, app_type):
from applications.const import ApplicationTypeChoices
if app_type in ApplicationTypeChoices.remote_app_types():
return cls.PROTOCOL_RDP
protocol = None
other_types = [*ApplicationTypeChoices.db_types(), *ApplicationTypeChoices.cloud_types()]
if app_type in other_types and app_type in cls.APPLICATION_CATEGORY_PROTOCOLS:
protocol = cls.PROTOCOL_RDP
else:
protocol = app_type
return protocol
if protocol in cls.APPLICATION_CATEGORY_PROTOCOLS:
return protocol
class Meta:
ordering = ['name']

View File

@ -1,12 +1,11 @@
# -*- coding: utf-8 -*-
#
import copy
from rest_framework import serializers
__all__ = [
'ReadableHiddenField', 'CustomMetaDictField',
'ReadableHiddenField',
]
@ -24,87 +23,3 @@ class ReadableHiddenField(serializers.HiddenField):
if hasattr(value, 'id'):
return getattr(value, 'id')
return value
#
# OtherField
# ----------
# TODO: DELETE 替换完成后删除
class CustomMetaDictField(serializers.DictField):
"""
In use:
RemoteApp params field
CommandStorage meta field
ReplayStorage meta field
"""
type_fields_map = {}
default_type = None
convert_key_remove_type_prefix = False
convert_key_to_upper = False
def filter_attribute(self, attribute, instance):
fields = self.type_fields_map.get(instance.type, [])
for field in fields:
if field.get('write_only', False):
attribute.pop(field['name'], None)
return attribute
def get_attribute(self, instance):
"""
序列化时调用
"""
attribute = super().get_attribute(instance)
attribute = self.filter_attribute(attribute, instance)
return attribute
def convert_value_key_remove_type_prefix(self, dictionary, value):
if not self.convert_key_remove_type_prefix:
return value
tp = dictionary.get('type')
prefix = '{}_'.format(tp)
convert_value = {}
for k, v in value.items():
if k.lower().startswith(prefix):
k = k.lower().split(prefix, 1)[1]
convert_value[k] = v
return convert_value
def convert_value_key_to_upper(self, value):
if not self.convert_key_to_upper:
return value
convert_value = {k.upper(): v for k, v in value.items()}
return convert_value
def convert_value_key(self, dictionary, value):
value = self.convert_value_key_remove_type_prefix(dictionary, value)
value = self.convert_value_key_to_upper(value)
return value
def filter_value_key(self, dictionary, value):
tp = dictionary.get('type')
fields = self.type_fields_map.get(tp, [])
fields_names = [field['name'] for field in fields]
filter_value = {k: v for k, v in value.items() if k in fields_names}
return filter_value
@staticmethod
def strip_value(value):
new_value = {}
for k, v in value.items():
if isinstance(v, str):
v = v.strip()
new_value[k] = v
return new_value
def get_value(self, dictionary):
"""
反序列化时调用
"""
value = super().get_value(dictionary)
value = self.convert_value_key(dictionary, value)
value = self.filter_value_key(dictionary, value)
value = self.strip_value(value)
return value

View File

@ -1,3 +1,4 @@
import copy
from rest_framework import serializers
from rest_framework.serializers import Serializer
from rest_framework.serializers import ModelSerializer
@ -38,10 +39,10 @@ class MethodSerializer(serializers.Serializer):
@cached_property
def serializer(self) -> serializers.Serializer:
method = getattr(self.parent, self.method_name)
return method()
def get_fields(self):
return self.serializer.get_fields()
_serializer = method()
# 设置serializer的parent值否则在serializer实例中获取parent会出现断层
setattr(_serializer, 'parent', self.parent)
return _serializer
@cached_property
def fields(self):
@ -50,10 +51,16 @@ class MethodSerializer(serializers.Serializer):
这样在调用 field.parent , 才会达到预期的结果
比如: serializers.SerializerMethodField
"""
fields = BindingDict(self.serializer)
for key, value in self.get_fields().items():
fields[key] = value
return fields
return self.serializer.fields
def run_validation(self, data=serializers.empty):
return self.serializer.run_validation(data)
def to_representation(self, instance):
return self.serializer.to_representation(instance)
def get_initial(self):
return self.serializer.get_initial()
# Other Serializer

View File

@ -20,10 +20,10 @@ class BaseStorageViewSetMixin:
def destroy(self, request, *args, **kwargs):
instance = self.get_object()
if instance.in_defaults():
if instance.type_null_or_server:
data = {'msg': _('Deleting the default storage is not allowed')}
return Response(data=data, status=status.HTTP_400_BAD_REQUEST)
if instance.is_using():
if instance.is_use():
data = {'msg': _('Cannot delete storage that is being used')}
return Response(data=data, status=status.HTTP_400_BAD_REQUEST)
return super().destroy(request, *args, **kwargs)
@ -67,11 +67,9 @@ class BaseStorageTestConnectiveMixin:
return Response(data)
class CommandStorageTestConnectiveApi(BaseStorageTestConnectiveMixin,
generics.RetrieveAPIView):
class CommandStorageTestConnectiveApi(BaseStorageTestConnectiveMixin, generics.RetrieveAPIView):
queryset = CommandStorage.objects.all()
class ReplayStorageTestConnectiveApi(BaseStorageTestConnectiveMixin,
generics.RetrieveAPIView):
class ReplayStorageTestConnectiveApi(BaseStorageTestConnectiveMixin, generics.RetrieveAPIView):
queryset = ReplayStorage.objects.all()

View File

@ -148,7 +148,5 @@ class TerminalConfig(APIView):
permission_classes = (IsAppUser,)
def get(self, request):
user = request.user
terminal = user.terminal
configs = terminal.config
return Response(configs, status=200)
config = request.user.terminal.config
return Response(config, status=200)

View File

@ -8,4 +8,4 @@ class TerminalConfig(AppConfig):
def ready(self):
from . import signals_handler
return super().ready()
return super().ready()

View File

@ -3,7 +3,6 @@ from django.conf import settings
from django.utils.functional import LazyObject
from .command.serializers import SessionCommandSerializer
from ..const import COMMAND_STORAGE_TYPE_SERVER
TYPE_ENGINE_MAPPING = {
@ -30,13 +29,12 @@ def get_terminal_command_storages():
from ..models import CommandStorage
storage_list = {}
for s in CommandStorage.objects.all():
tp = s.type
if tp == COMMAND_STORAGE_TYPE_SERVER:
if s.type_server:
storage = get_command_storage()
else:
if not TYPE_ENGINE_MAPPING.get(tp):
if not TYPE_ENGINE_MAPPING.get(s.type):
continue
engine_class = import_module(TYPE_ENGINE_MAPPING[tp])
engine_class = import_module(TYPE_ENGINE_MAPPING[s.type])
storage = engine_class.CommandStore(s.config)
storage_list[s.name] = storage
return storage_list

View File

@ -1,117 +1,31 @@
# -*- coding: utf-8 -*-
#
ASSETS_CACHE_KEY = "terminal__session__assets"
USERS_CACHE_KEY = "terminal__session__users"
SYSTEM_USER_CACHE_KEY = "terminal__session__system_users"
# Replay Storage
REPLAY_STORAGE_TYPE_NULL = 'null'
REPLAY_STORAGE_TYPE_SERVER = 'server'
REPLAY_STORAGE_TYPE_S3 = 's3'
REPLAY_STORAGE_TYPE_CEPH = 'ceph'
REPLAY_STORAGE_TYPE_SWIFT = 'swift'
REPLAY_STORAGE_TYPE_OSS = 'oss'
REPLAY_STORAGE_TYPE_AZURE = 'azure'
REPLAY_STORAGE_TYPE_EMPTY_FIELDS = []
REPLAY_STORAGE_TYPE_S3_FIELDS = [
{'name': 'BUCKET'},
{'name': 'ACCESS_KEY', 'write_only': True},
{'name': 'SECRET_KEY', 'write_only': True},
{'name': 'ENDPOINT'}
]
REPLAY_STORAGE_TYPE_CEPH_FIELDS = [
{'name': 'BUCKET'},
{'name': 'ACCESS_KEY', 'write_only': True},
{'name': 'SECRET_KEY', 'write_only': True},
{'name': 'ENDPOINT'}
]
REPLAY_STORAGE_TYPE_SWIFT_FIELDS = [
{'name': 'BUCKET'},
{'name': 'ACCESS_KEY', 'write_only': True},
{'name': 'SECRET_KEY', 'write_only': True},
{'name': 'REGION'},
{'name': 'ENDPOINT'},
{'name': 'PROTOCOL'},
]
REPLAY_STORAGE_TYPE_OSS_FIELDS = [
{'name': 'BUCKET'},
{'name': 'ACCESS_KEY', 'write_only': True},
{'name': 'SECRET_KEY', 'write_only': True},
{'name': 'ENDPOINT'}
]
REPLAY_STORAGE_TYPE_AZURE_FIELDS = [
{'name': 'CONTAINER_NAME'},
{'name': 'ACCOUNT_NAME'},
{'name': 'ACCOUNT_KEY', 'write_only': True},
{'name': 'ENDPOINT_SUFFIX'}
]
REPLAY_STORAGE_TYPE_FIELDS_MAP = {
REPLAY_STORAGE_TYPE_NULL: REPLAY_STORAGE_TYPE_EMPTY_FIELDS,
REPLAY_STORAGE_TYPE_SERVER: REPLAY_STORAGE_TYPE_EMPTY_FIELDS,
REPLAY_STORAGE_TYPE_S3: REPLAY_STORAGE_TYPE_S3_FIELDS,
REPLAY_STORAGE_TYPE_CEPH: REPLAY_STORAGE_TYPE_CEPH_FIELDS,
REPLAY_STORAGE_TYPE_SWIFT: REPLAY_STORAGE_TYPE_SWIFT_FIELDS,
REPLAY_STORAGE_TYPE_OSS: REPLAY_STORAGE_TYPE_OSS_FIELDS,
REPLAY_STORAGE_TYPE_AZURE: REPLAY_STORAGE_TYPE_AZURE_FIELDS
}
REPLAY_STORAGE_TYPE_CHOICES_DEFAULT = [
(REPLAY_STORAGE_TYPE_NULL, 'Null'),
(REPLAY_STORAGE_TYPE_SERVER, 'Server'),
]
REPLAY_STORAGE_TYPE_CHOICES_EXTENDS = [
(REPLAY_STORAGE_TYPE_S3, 'S3'),
(REPLAY_STORAGE_TYPE_CEPH, 'Ceph'),
(REPLAY_STORAGE_TYPE_SWIFT, 'Swift'),
(REPLAY_STORAGE_TYPE_OSS, 'OSS'),
(REPLAY_STORAGE_TYPE_AZURE, 'Azure')
]
REPLAY_STORAGE_TYPE_CHOICES = REPLAY_STORAGE_TYPE_CHOICES_DEFAULT + \
REPLAY_STORAGE_TYPE_CHOICES_EXTENDS
# Command Storage
COMMAND_STORAGE_TYPE_NULL = 'null'
COMMAND_STORAGE_TYPE_SERVER = 'server'
COMMAND_STORAGE_TYPE_ES = 'es'
COMMAND_STORAGE_TYPE_EMPTY_FIELDS = []
COMMAND_STORAGE_TYPE_ES_FIELDS = [
{'name': 'HOSTS'},
{'name': 'INDEX'},
{'name': 'DOC_TYPE'}
]
COMMAND_STORAGE_TYPE_FIELDS_MAP = {
COMMAND_STORAGE_TYPE_NULL: COMMAND_STORAGE_TYPE_EMPTY_FIELDS,
COMMAND_STORAGE_TYPE_SERVER: COMMAND_STORAGE_TYPE_EMPTY_FIELDS,
COMMAND_STORAGE_TYPE_ES: COMMAND_STORAGE_TYPE_ES_FIELDS,
}
COMMAND_STORAGE_TYPE_CHOICES_DEFAULT = [
(COMMAND_STORAGE_TYPE_NULL, 'Null'),
(COMMAND_STORAGE_TYPE_SERVER, 'Server'),
]
COMMAND_STORAGE_TYPE_CHOICES_EXTENDS = [
(COMMAND_STORAGE_TYPE_ES, 'Elasticsearch')
]
COMMAND_STORAGE_TYPE_CHOICES = COMMAND_STORAGE_TYPE_CHOICES_DEFAULT + \
COMMAND_STORAGE_TYPE_CHOICES_EXTENDS
from django.db.models import TextChoices
from django.utils.translation import ugettext_lazy as _
# Replay & Command Storage Choices
# --------------------------------
class ReplayStorageTypeChoices(TextChoices):
null = 'null', 'Null',
server = 'server', 'Server'
s3 = 's3', 'S3'
ceph = 'ceph', 'Ceph'
swift = 'swift', 'Swift'
oss = 'oss', 'OSS'
azure = 'azure', 'Azure'
class CommandStorageTypeChoices(TextChoices):
null = 'null', 'Null',
server = 'server', 'Server'
es = 'es', 'Elasticsearch'
# Component Status Choices
# ------------------------
class ComponentStatusChoices(TextChoices):
critical = 'critical', _('Critical')

View File

@ -2,102 +2,113 @@ from __future__ import unicode_literals
import os
import jms_storage
from django.db import models
from django.utils.translation import ugettext_lazy as _
from django.conf import settings
from common.mixins import CommonModelMixin
from common.fields.model import EncryptJsonDictTextField
from .. import const
from .terminal import Terminal
from .. import const
class CommandStorage(CommonModelMixin):
TYPE_CHOICES = const.COMMAND_STORAGE_TYPE_CHOICES
TYPE_DEFAULTS = dict(const.REPLAY_STORAGE_TYPE_CHOICES_DEFAULT).keys()
TYPE_SERVER = const.COMMAND_STORAGE_TYPE_SERVER
name = models.CharField(max_length=128, verbose_name=_("Name"), unique=True)
type = models.CharField(
max_length=16, choices=TYPE_CHOICES, verbose_name=_('Type'),
default=TYPE_SERVER
max_length=16, choices=const.CommandStorageTypeChoices.choices,
default=const.CommandStorageTypeChoices.server.value, verbose_name=_('Type'),
)
meta = EncryptJsonDictTextField(default={})
comment = models.TextField(
max_length=128, default='', blank=True, verbose_name=_('Comment')
)
comment = models.TextField(max_length=128, default='', blank=True, verbose_name=_('Comment'))
def __str__(self):
return self.name
@property
def type_null(self):
return self.type == const.CommandStorageTypeChoices.null.value
@property
def type_server(self):
return self.type == const.CommandStorageTypeChoices.server.value
@property
def type_null_or_server(self):
return self.type_null or self.type_server
@property
def config(self):
config = self.meta
config.update({'TYPE': self.type})
return config
def in_defaults(self):
return self.type in self.TYPE_DEFAULTS
def is_valid(self):
if self.in_defaults():
if self.type_null_or_server:
return True
storage = jms_storage.get_log_storage(self.config)
return storage.ping()
def is_using(self):
def is_use(self):
return Terminal.objects.filter(command_storage=self.name).exists()
class ReplayStorage(CommonModelMixin):
TYPE_CHOICES = const.REPLAY_STORAGE_TYPE_CHOICES
TYPE_SERVER = const.REPLAY_STORAGE_TYPE_SERVER
TYPE_DEFAULTS = dict(const.REPLAY_STORAGE_TYPE_CHOICES_DEFAULT).keys()
name = models.CharField(max_length=128, verbose_name=_("Name"), unique=True)
type = models.CharField(
max_length=16, choices=TYPE_CHOICES, verbose_name=_('Type'),
default=TYPE_SERVER
max_length=16, choices=const.ReplayStorageTypeChoices.choices,
default=const.ReplayStorageTypeChoices.server.value, verbose_name=_('Type')
)
meta = EncryptJsonDictTextField(default={})
comment = models.TextField(
max_length=128, default='', blank=True, verbose_name=_('Comment')
)
comment = models.TextField(max_length=128, default='', blank=True, verbose_name=_('Comment'))
def __str__(self):
return self.name
def convert_type(self):
s3_type_list = [const.REPLAY_STORAGE_TYPE_CEPH]
tp = self.type
if tp in s3_type_list:
tp = const.REPLAY_STORAGE_TYPE_S3
return tp
@property
def type_null(self):
return self.type == const.ReplayStorageTypeChoices.null.value
def get_extra_config(self):
extra_config = {'TYPE': self.convert_type()}
if self.type == const.REPLAY_STORAGE_TYPE_SWIFT:
extra_config.update({'signer': 'S3SignerType'})
return extra_config
@property
def type_server(self):
return self.type == const.ReplayStorageTypeChoices.server.value
@property
def type_null_or_server(self):
return self.type_null or self.type_server
@property
def type_swift(self):
return self.type == const.ReplayStorageTypeChoices.swift.value
@property
def type_ceph(self):
return self.type == const.ReplayStorageTypeChoices.ceph.value
@property
def config(self):
config = self.meta
extra_config = self.get_extra_config()
config.update(extra_config)
return config
_config = {}
def in_defaults(self):
return self.type in self.TYPE_DEFAULTS
# add type config
if self.type_ceph:
_type = const.ReplayStorageTypeChoices.s3.value
else:
_type = self.type
_config.update({'TYPE': _type})
# add special config
if self.type_swift:
_config.update({'signer': 'S3SignerType'})
# add meta config
_config.update(self.meta)
return _config
def is_valid(self):
if self.in_defaults():
if self.type_null_or_server:
return True
storage = jms_storage.get_object_storage(self.config)
target = 'tests.py'
src = os.path.join(settings.BASE_DIR, 'common', target)
return storage.is_valid(src, target)
def is_using(self):
def is_use(self):
return Terminal.objects.filter(replay_storage=self.name).exists()

View File

@ -2,74 +2,197 @@
#
import copy
from rest_framework import serializers
from common.drf.fields import CustomMetaDictField
from urllib.parse import urlparse
from django.utils.translation import ugettext_lazy as _
from django.db.models import TextChoices
from common.drf.serializers import MethodSerializer
from ..models import ReplayStorage, CommandStorage
from .. import const
class ReplayStorageMetaDictField(CustomMetaDictField):
type_fields_map = const.REPLAY_STORAGE_TYPE_FIELDS_MAP
default_type = const.REPLAY_STORAGE_TYPE_SERVER
convert_key_remove_type_prefix = True
convert_key_to_upper = True
# Replay storage serializers
# --------------------------
class BaseStorageSerializerMixin:
type_fields_map = None
def process_meta(self, instance, validated_data):
new_meta = copy.deepcopy(validated_data.get('meta', {}))
tp = validated_data.get('type', '')
if tp != instance.type:
return new_meta
old_meta = instance.meta
fields = self.type_fields_map.get(instance.type, [])
for field in fields:
if not field.get('write_only', False):
continue
field_name = field['name']
new_value = new_meta.get(field_name, '')
old_value = old_meta.get(field_name, '')
field_value = new_value if new_value else old_value
new_meta[field_name] = field_value
return new_meta
def update(self, instance, validated_data):
meta = self.process_meta(instance, validated_data)
validated_data['meta'] = meta
return super().update(instance, validated_data)
class ReplayStorageTypeBaseSerializer(serializers.Serializer):
BUCKET = serializers.CharField(
required=True, max_length=1024, label=_('Bucket'), allow_null=True
)
ACCESS_KEY = serializers.CharField(
max_length=1024, required=False, allow_blank=True, write_only=True, label=_('Access key'),
allow_null=True,
)
SECRET_KEY = serializers.CharField(
max_length=1024, required=False, allow_blank=True, write_only=True, label=_('Secret key'),
allow_null=True,
)
ENDPOINT = serializers.CharField(
required=True, max_length=1024, label=_('Endpoint'), allow_null=True,
)
class ReplayStorageSerializer(BaseStorageSerializerMixin,
serializers.ModelSerializer):
class ReplayStorageTypeS3Serializer(ReplayStorageTypeBaseSerializer):
endpoint_help_text = '''
S3 format: http://s3.{REGION_NAME}.amazonaws.com
S3(China) format: http://s3.{REGION_NAME}.amazonaws.com.cn
Such as: http://s3.cn-north-1.amazonaws.com.cn
'''
ENDPOINT = serializers.CharField(
required=True, max_length=1024, label=_('Endpoint'), help_text=_(endpoint_help_text),
allow_null=True,
)
meta = ReplayStorageMetaDictField()
type_fields_map = const.REPLAY_STORAGE_TYPE_FIELDS_MAP
class ReplayStorageTypeCephSerializer(ReplayStorageTypeBaseSerializer):
pass
class ReplayStorageTypeSwiftSerializer(ReplayStorageTypeBaseSerializer):
class ProtocolChoices(TextChoices):
http = 'http', 'http'
https = 'https', 'https'
REGION = serializers.CharField(
required=True, max_length=1024, label=_('Region'), allow_null=True
)
PROTOCOL = serializers.ChoiceField(
choices=ProtocolChoices.choices, default=ProtocolChoices.http.value, label=_('Protocol'),
allow_null=True,
)
class ReplayStorageTypeOSSSerializer(ReplayStorageTypeBaseSerializer):
endpoint_help_text = '''
OSS format: http://{REGION_NAME}.aliyuncs.com
Such as: http://oss-cn-hangzhou.aliyuncs.com
'''
ENDPOINT = serializers.CharField(
max_length=1024, label=_('Endpoint'), help_text=_(endpoint_help_text), allow_null=True,
)
class ReplayStorageTypeAzureSerializer(serializers.Serializer):
class EndpointSuffixChoices(TextChoices):
china = 'core.chinacloudapi.cn', 'core.chinacloudapi.cn'
international = 'core.windows.net', 'core.windows.net'
CONTAINER_NAME = serializers.CharField(max_length=1024, label=_('Container'), allow_null=True)
ACCOUNT_NAME = serializers.CharField(max_length=1024, label=_('Account name'), allow_null=True)
ACCOUNT_KEY = serializers.CharField(max_length=1024, label=_('Account key'), allow_null=True)
ENDPOINT_SUFFIX = serializers.ChoiceField(
choices=EndpointSuffixChoices.choices, default=EndpointSuffixChoices.china.value,
label=_('Endpoint suffix'), allow_null=True,
)
# mapping
replay_storage_type_serializer_classes_mapping = {
const.ReplayStorageTypeChoices.s3.value: ReplayStorageTypeS3Serializer,
const.ReplayStorageTypeChoices.ceph.value: ReplayStorageTypeCephSerializer,
const.ReplayStorageTypeChoices.swift.value: ReplayStorageTypeSwiftSerializer,
const.ReplayStorageTypeChoices.oss.value: ReplayStorageTypeOSSSerializer,
const.ReplayStorageTypeChoices.azure.value: ReplayStorageTypeAzureSerializer
}
# ReplayStorageSerializer
class ReplayStorageSerializer(serializers.ModelSerializer):
meta = MethodSerializer()
class Meta:
model = ReplayStorage
fields = ['id', 'name', 'type', 'meta', 'comment']
def validate_meta(self, meta):
_meta = self.instance.meta if self.instance else {}
_meta.update(meta)
return _meta
class CommandStorageMetaDictField(CustomMetaDictField):
type_fields_map = const.COMMAND_STORAGE_TYPE_FIELDS_MAP
default_type = const.COMMAND_STORAGE_TYPE_SERVER
convert_key_remove_type_prefix = True
convert_key_to_upper = True
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)
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()
return serializer
class CommandStorageSerializer(BaseStorageSerializerMixin,
serializers.ModelSerializer):
# Command storage serializers
# ---------------------------
meta = CommandStorageMetaDictField()
type_fields_map = const.COMMAND_STORAGE_TYPE_FIELDS_MAP
def es_host_format_validator(host):
h = urlparse(host)
default_error_msg = _('The address format is incorrect')
if h.scheme not in ['http', 'https']:
raise serializers.ValidationError(default_error_msg)
if ':' not in h.netloc:
raise serializers.ValidationError(default_error_msg)
_host, _port = h.netloc.split(':')
if not _host:
error_msg = _('Host invalid')
raise serializers.ValidationError(error_msg)
if not _port.isdigit():
error_msg = _('Port invalid')
raise serializers.ValidationError(error_msg)
return host
class CommandStorageTypeESSerializer(serializers.Serializer):
hosts_help_text = '''
Tip: If there are multiple hosts, use a comma (,) to separate them.
(eg: http://www.jumpserver.a.com, http://www.jumpserver.b.com)
'''
HOSTS = serializers.ListField(
child=serializers.CharField(validators=[es_host_format_validator]), label=_('Hosts'),
help_text=_(hosts_help_text), allow_null=True
)
INDEX = serializers.CharField(
max_length=1024, default='jumpserver', label=_('Index'), allow_null=True
)
DOC_TYPE = serializers.CharField(
max_length=1024, read_only=True, default='command', label=_('Doc type'), allow_null=True
)
# mapping
command_storage_type_serializer_classes_mapping = {
const.CommandStorageTypeChoices.es.value: CommandStorageTypeESSerializer
}
# CommandStorageSerializer
class CommandStorageSerializer(serializers.ModelSerializer):
meta = MethodSerializer()
class Meta:
model = CommandStorage
fields = ['id', 'name', 'type', 'meta', 'comment']
def validate_meta(self, meta):
_meta = self.instance.meta if self.instance else {}
_meta.update(meta)
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)
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()
return serializer

View File

@ -73,7 +73,6 @@ def clean_expired_session_period():
logger.info("Clean session replay done")
@shared_task
def upload_session_replay_to_external_storage(session_id):
logger.info(f'Start upload session to external storage: {session_id}')

View File

@ -39,7 +39,7 @@ def download_session_replay(session):
configs = {
storage.name: storage.config
for storage in replay_storages
if not storage.in_defaults()
if not storage.type_null_or_server
}
if settings.SERVER_REPLAY_STORAGE:
configs['SERVER_REPLAY_STORAGE'] = settings.SERVER_REPLAY_STORAGE

View File

@ -19,7 +19,7 @@ __all__ = ['TicketViewSet']
class TicketViewSet(CommonApiMixin, viewsets.ModelViewSet):
permission_classes = (IsValidUser,)
serializer_class = serializers.TicketSerializer
serializer_class = serializers.TicketDisplaySerializer
serializer_classes = {
'default': serializers.TicketDisplaySerializer,
'display': serializers.TicketDisplaySerializer,

View File

@ -10,9 +10,9 @@ from tickets.utils import convert_model_instance_data_field_name_to_verbose_name
class ConstructDisplayFieldMixin:
def construct_meta_apply_application_open_fields_display(self):
meta_display_fields = ['apply_category_display', 'apply_type_display']
apply_category = self.meta['apply_category']
apply_category = self.meta.get('apply_category')
apply_category_display = ApplicationCategoryChoices.get_label(apply_category)
apply_type = self.meta['apply_type']
apply_type = self.meta.get('apply_type')
apply_type_display = ApplicationTypeChoices.get_label(apply_type)
meta_display_values = [apply_category_display, apply_type_display]
meta_display = dict(zip(meta_display_fields, meta_display_values))
@ -20,8 +20,8 @@ class ConstructDisplayFieldMixin:
def construct_meta_apply_application_approve_fields_display(self):
meta_display_fields = ['approve_applications_snapshot', 'approve_system_users_snapshot']
approve_applications_id = self.meta['approve_applications']
approve_system_users_id = self.meta['approve_system_users']
approve_applications_id = self.meta.get('approve_applications', [])
approve_system_users_id = self.meta.get('approve_system_users', [])
with tmp_to_org(self.org_id):
approve_applications_snapshot = list(
Application.objects.filter(id__in=approve_applications_id).values(
@ -42,12 +42,12 @@ class ConstructDisplayFieldMixin:
class ConstructBodyMixin:
def construct_apply_application_applied_body(self):
apply_category_display = self.meta['apply_category_display']
apply_type_display = self.meta['apply_type_display']
apply_application_group = self.meta['apply_application_group']
apply_system_user_group = self.meta['apply_system_user_group']
apply_date_start = self.meta['apply_date_start']
apply_date_expired = self.meta['apply_date_expired']
apply_category_display = self.meta.get('apply_category_display')
apply_type_display = self.meta.get('apply_type_display')
apply_application_group = self.meta.get('apply_application_group', [])
apply_system_user_group = self.meta.get('apply_system_user_group', [])
apply_date_start = self.meta.get('apply_date_start')
apply_date_expired = self.meta.get('apply_date_expired')
applied_body = '''{}: {},
{}: {},
{}: {},
@ -66,16 +66,16 @@ class ConstructBodyMixin:
def construct_apply_application_approved_body(self):
# 审批信息
approve_applications_snapshot = self.meta['approve_applications_snapshot']
approve_applications_snapshot = self.meta.get('approve_applications_snapshot', [])
approve_applications_snapshot_display = convert_model_instance_data_field_name_to_verbose_name(
Application, approve_applications_snapshot
)
approve_system_users_snapshot = self.meta['approve_system_users_snapshot']
approve_system_users_snapshot = self.meta.get('approve_system_users_snapshot', [])
approve_system_users_snapshot_display = convert_model_instance_data_field_name_to_verbose_name(
SystemUser, approve_system_users_snapshot
)
approve_date_start = self.meta['approve_date_start']
approve_date_expired = self.meta['approve_date_expired']
approve_date_start = self.meta.get('approve_date_start')
approve_date_expired = self.meta.get('approve_date_expired')
approved_body = '''{}: {},
{}: {},
{}: {},
@ -97,12 +97,12 @@ class CreatePermissionMixin:
if application_permission:
return application_permission
apply_category = self.meta['apply_category']
apply_type = self.meta['apply_type']
approved_applications_id = self.meta['approve_applications']
approve_system_users_id = self.meta['approve_system_users']
approve_date_start = self.meta['approve_date_start']
approve_date_expired = self.meta['approve_date_expired']
apply_category = self.meta.get('apply_category')
apply_type = self.meta.get('apply_type')
approved_applications_id = self.meta.get('approve_applications', [])
approve_system_users_id = self.meta.get('approve_system_users', [])
approve_date_start = self.meta.get('approve_date_start')
approve_date_expired = self.meta.get('approve_date_expired')
permission_name = '{}({})'.format(
__('Created by ticket ({})'.format(self.title)), str(self.id)[:4]
)

View File

@ -10,7 +10,7 @@ class ConstructDisplayFieldMixin:
def construct_meta_apply_asset_open_fields_display(self):
meta_display_fields = ['apply_actions_display']
apply_actions = self.meta['apply_actions']
apply_actions = self.meta.get('apply_actions', Action.NONE)
apply_actions_display = Action.value_to_choices_display(apply_actions)
meta_display_values = [apply_actions_display]
@ -21,10 +21,10 @@ class ConstructDisplayFieldMixin:
meta_display_fields = [
'approve_actions_display', 'approve_assets_snapshot', 'approve_system_users_snapshot'
]
approve_actions = self.meta['approve_actions']
approve_assets_id = self.meta['approve_assets']
approve_system_users_id = self.meta['approve_system_users']
approve_actions = self.meta.get('approve_actions', Action.NONE)
approve_actions_display = Action.value_to_choices_display(approve_actions)
approve_assets_id = self.meta.get('approve_assets', [])
approve_system_users_id = self.meta.get('approve_system_users', [])
with tmp_to_org(self.org_id):
approve_assets_snapshot = list(
Asset.objects.filter(id__in=approve_assets_id).values(
@ -46,12 +46,12 @@ class ConstructDisplayFieldMixin:
class ConstructBodyMixin:
def construct_apply_asset_applied_body(self):
apply_ip_group = self.meta['apply_ip_group']
apply_hostname_group = self.meta['apply_hostname_group']
apply_system_user_group = self.meta['apply_system_user_group']
apply_actions_display = self.meta['apply_actions_display']
apply_date_start = self.meta['apply_date_start']
apply_date_expired = self.meta['apply_date_expired']
apply_ip_group = self.meta.get('apply_ip_group', [])
apply_hostname_group = self.meta.get('apply_hostname_group', [])
apply_system_user_group = self.meta.get('apply_system_user_group', [])
apply_actions_display = self.meta.get('apply_actions_display', [])
apply_date_start = self.meta.get('apply_date_start')
apply_date_expired = self.meta.get('apply_date_expired')
applied_body = '''{}: {},
{}: {},
{}: {},
@ -68,17 +68,17 @@ class ConstructBodyMixin:
return applied_body
def construct_apply_asset_approved_body(self):
approve_assets_snapshot = self.meta['approve_assets_snapshot']
approve_assets_snapshot = self.meta.get('approve_assets_snapshot', [])
approve_assets_snapshot_display = convert_model_instance_data_field_name_to_verbose_name(
Asset, approve_assets_snapshot
)
approve_system_users_snapshot = self.meta['approve_system_users_snapshot']
approve_system_users_snapshot = self.meta.get('approve_system_users_snapshot', [])
approve_system_users_snapshot_display = convert_model_instance_data_field_name_to_verbose_name(
SystemUser, approve_system_users_snapshot
)
approve_actions_display = self.meta['approve_actions_display']
approve_date_start = self.meta['approve_date_start']
approve_date_expired = self.meta['approve_date_expired']
approve_actions_display = self.meta.get('approve_actions_display', [])
approve_date_start = self.meta.get('approve_date_start')
approve_date_expired = self.meta.get('approve_date_expired')
approved_body = '''{}: {},
{}: {},
{}: {},
@ -101,11 +101,11 @@ class CreatePermissionMixin:
if asset_permission:
return asset_permission
approve_assets_id = self.meta['approve_assets']
approve_system_users_id = self.meta['approve_system_users']
approve_actions = self.meta['approve_actions']
approve_date_start = self.meta['approve_date_start']
approve_date_expired = self.meta['approve_date_expired']
approve_assets_id = self.meta.get('approve_assets', [])
approve_system_users_id = self.meta.get('approve_system_users', [])
approve_actions = self.meta.get('approve_actions', Action.NONE)
approve_date_start = self.meta.get('approve_date_start')
approve_date_expired = self.meta.get('approve_date_expired')
permission_name = '{}({})'.format(
__('Created by ticket ({})'.format(self.title)), str(self.id)[:4]
)

View File

@ -4,9 +4,9 @@ from django.utils.translation import ugettext as __
class ConstructBodyMixin:
def construct_login_confirm_applied_body(self):
apply_login_ip = self.meta['apply_login_ip']
apply_login_city = self.meta['apply_login_city']
apply_login_datetime = self.meta['apply_login_datetime']
apply_login_ip = self.meta.get('apply_login_ip')
apply_login_city = self.meta.get('apply_login_city')
apply_login_datetime = self.meta.get('apply_login_datetime')
applied_body = '''{}: {},
{}: {},
{}: {}

View File

@ -17,14 +17,17 @@ action_approve = const.TicketActionChoices.approve.value
type_serializer_classes_mapping = {
const.TicketTypeChoices.apply_asset.value: {
'default': apply_asset.ApplyAssetSerializer,
action_open: apply_asset.ApplySerializer,
action_approve: apply_asset.ApproveSerializer,
},
const.TicketTypeChoices.apply_application.value: {
'default': apply_application.ApplyApplicationSerializer,
action_open: apply_application.ApplySerializer,
action_approve: apply_application.ApproveSerializer,
},
const.TicketTypeChoices.login_confirm.value: {
'default': login_confirm.LoginConfirmSerializer,
action_open: login_confirm.ApplySerializer,
}
}

View File

@ -1,88 +1,148 @@
from rest_framework import serializers
from django.utils.translation import ugettext_lazy as _
from django.db.models import Q
from applications.models import Application
from applications.const import ApplicationCategoryChoices, ApplicationTypeChoices
from assets.models import SystemUser
from .mixin import BaseApproveSerializerMixin
from orgs.utils import tmp_to_org
from tickets.models import Ticket
__all__ = [
'ApplyApplicationTypeSerializer', 'ApplySerializer', 'ApproveSerializer',
'ApplyApplicationSerializer', 'ApplySerializer', 'ApproveSerializer',
]
class ApplySerializer(serializers.Serializer):
# 申请信息
apply_category = serializers.ChoiceField(
required=True, choices=ApplicationCategoryChoices.choices, label=_('Category')
required=True, choices=ApplicationCategoryChoices.choices, label=_('Category'),
allow_null=True,
)
apply_category_display = serializers.CharField(
read_only=True, label=_('Category display')
read_only=True, label=_('Category display'), allow_null=True,
)
apply_type = serializers.ChoiceField(
required=True, choices=ApplicationTypeChoices.choices, label=_('Type')
required=True, choices=ApplicationTypeChoices.choices, label=_('Type'),
allow_null=True
)
apply_type_display = serializers.CharField(
required=False, read_only=True, label=_('Type display')
required=False, read_only=True, label=_('Type display'),
allow_null=True
)
apply_application_group = serializers.ListField(
required=False, child=serializers.CharField(), label=_('Application group'),
default=list,
default=list, allow_null=True
)
apply_system_user_group = serializers.ListField(
required=False, child=serializers.CharField(), label=_('System user group'),
default=list,
default=list, allow_null=True
)
apply_date_start = serializers.DateTimeField(
required=True, label=_('Date start')
required=True, label=_('Date start'), allow_null=True
)
apply_date_expired = serializers.DateTimeField(
required=True, label=_('Date expired')
required=True, label=_('Date expired'), allow_null=True
)
class ApproveSerializer(BaseApproveSerializerMixin, serializers.Serializer):
class ApproveSerializer(serializers.Serializer):
# 审批信息
approve_applications = serializers.ListField(
required=True, child=serializers.UUIDField(), label=_('Approve applications')
required=True, child=serializers.UUIDField(), label=_('Approve applications'),
allow_null=True
)
approve_applications_snapshot = serializers.ListField(
required=False, read_only=True, child=serializers.CharField(),
label=_('Approve applications display'),
label=_('Approve applications display'), allow_null=True,
default=list
)
approve_system_users = serializers.ListField(
required=True, child=serializers.UUIDField(), label=_('Approve system users')
required=True, child=serializers.UUIDField(), label=_('Approve system users'),
allow_null=True
)
approve_system_users_snapshot = serializers.ListField(
required=False, read_only=True, child=serializers.CharField(),
label=_('Approve system user display'),
label=_('Approve system user display'), allow_null=True,
default=list
)
approve_date_start = serializers.DateTimeField(
required=True, label=_('Date start')
required=True, label=_('Date start'), allow_null=True
)
approve_date_expired = serializers.DateTimeField(
required=True, label=_('Date expired')
required=True, label=_('Date expired'), allow_null=True
)
def validate_approve_applications(self, approve_applications):
application_type = self.root.instance.meta['apply_type']
queries = {'type': application_type}
applications_id = self.filter_approve_resources(
resource_model=Application, resources_id=approve_applications, queries=queries
)
return applications_id
if not isinstance(self.root.instance, Ticket):
return []
with tmp_to_org(self.root.instance.org_id):
apply_type = self.root.instance.meta.get('apply_type')
queries = Q(type=apply_type)
queries &= Q(id__in=approve_applications)
applications_id = Application.objects.filter(queries).values_list('id', flat=True)
applications_id = [str(application_id) for application_id in applications_id]
if applications_id:
return applications_id
raise serializers.ValidationError(_(
'No `Application` are found under Organization `{}`'.format(self.root.instance.org_name)
))
def validate_approve_system_users(self, approve_system_users):
application_type = self.root.instance.meta['apply_type']
protocol = SystemUser.get_protocol_by_application_type(application_type)
queries = {'protocol': protocol}
system_users_id = self.filter_approve_system_users(approve_system_users, queries)
return system_users_id
if not isinstance(self.root.instance, Ticket):
return []
with tmp_to_org(self.root.instance.org_id):
apply_type = self.root.instance.meta.get('apply_type')
protocol = SystemUser.get_protocol_by_application_type(apply_type)
queries = Q(protocol=protocol)
queries &= Q(id__in=approve_system_users)
system_users_id = SystemUser.objects.filter(queries).values_list('id', flat=True)
system_users_id = [str(system_user_id) for system_user_id in system_users_id]
if system_users_id:
return system_users_id
raise serializers.ValidationError(_(
'No `SystemUser` are found under Organization `{}`'.format(self.root.instance.org_name)
))
class ApplyApplicationTypeSerializer(ApplySerializer, ApproveSerializer):
pass
class ApplyApplicationSerializer(ApplySerializer, ApproveSerializer):
# 推荐信息
recommend_applications = serializers.SerializerMethodField()
recommend_system_users = serializers.SerializerMethodField()
def get_recommend_applications(self, value):
if not isinstance(self.root.instance, Ticket):
return []
apply_application_group = value.get('apply_application_group', [])
apply_type = value.get('apply_type')
queries = Q()
for application in apply_application_group:
queries |= Q(name__icontains=application)
queries &= Q(type=apply_type)
with tmp_to_org(self.root.instance.org_id):
applications_id = Application.objects.filter(queries).values_list('id', flat=True)[:5]
applications_id = [str(application_id) for application_id in applications_id]
return applications_id
def get_recommend_system_users(self, value):
if not isinstance(self.root.instance, Ticket):
return []
apply_type = value.get('apply_type')
apply_system_user_group = value.get('apply_system_user_group', [])
protocol = SystemUser.get_protocol_by_application_type(apply_type)
queries = Q()
for system_user in apply_system_user_group:
queries |= Q(username__icontains=system_user)
queries |= Q(name__icontains=system_user)
queries &= Q(protocol=protocol)
with tmp_to_org(self.root.instance.org_id):
system_users_id = SystemUser.objects.filter(queries).values_list('id', flat=True)[:5]
system_users_id = [str(system_user_id) for system_user_id in system_users_id]
return system_users_id

View File

@ -1,8 +1,10 @@
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 assets.models import Asset, SystemUser
from .mixin import BaseApproveSerializerMixin
from orgs.utils import tmp_to_org
from tickets.models import Ticket
__all__ = [
@ -14,74 +16,129 @@ class ApplySerializer(serializers.Serializer):
# 申请信息
apply_ip_group = serializers.ListField(
required=False, child=serializers.IPAddressField(), label=_('IP group'),
default=list,
default=list, allow_null=True,
)
apply_hostname_group = serializers.ListField(
required=False, child=serializers.CharField(), label=_('Hostname group'),
default=list,
default=list, allow_null=True,
)
apply_system_user_group = serializers.ListField(
required=False, child=serializers.CharField(), label=_('System user group'),
default=list,
default=list, allow_null=True
)
apply_actions = ActionsField(
required=True
required=True, allow_null=True
)
apply_actions_display = serializers.ListField(
required=False, read_only=True, child=serializers.CharField(),
label=_('Approve assets display'),
label=_('Approve assets display'), allow_null=True,
default=list,
)
apply_date_start = serializers.DateTimeField(
required=True, label=_('Date start')
required=True, label=_('Date start'), allow_null=True,
)
apply_date_expired = serializers.DateTimeField(
required=True, label=_('Date expired')
required=True, label=_('Date expired'), allow_null=True,
)
class ApproveSerializer(BaseApproveSerializerMixin, serializers.Serializer):
class ApproveSerializer(serializers.Serializer):
# 审批信息
approve_assets = serializers.ListField(
required=True, child=serializers.UUIDField(), label=_('Approve assets')
required=True, allow_null=True, child=serializers.UUIDField(), label=_('Approve assets')
)
approve_assets_snapshot = serializers.ListField(
required=False, read_only=True, child=serializers.DictField(),
label=_('Approve assets display'),
label=_('Approve assets display'), allow_null=True,
default=list,
)
approve_system_users = serializers.ListField(
required=True, child=serializers.UUIDField(), label=_('Approve system users')
required=True, allow_null=True, child=serializers.UUIDField(),
label=_('Approve system users')
)
approve_system_users_snapshot = serializers.ListField(
required=False, read_only=True, child=serializers.DictField(),
label=_('Approve assets display'),
label=_('Approve assets display'), allow_null=True,
default=list,
)
approve_actions = ActionsField(
required=True
required=True, allow_null=True,
)
approve_actions_display = serializers.ListField(
required=False, read_only=True, child=serializers.CharField(),
label=_('Approve assets display'),
label=_('Approve assets display'), allow_null=True,
default=list,
)
approve_date_start = serializers.DateTimeField(
required=True, label=_('Date start')
required=True, label=_('Date start'), allow_null=True,
)
approve_date_expired = serializers.DateTimeField(
required=True, label=_('Date expired')
required=True, label=_('Date expired'), allow_null=True
)
def validate_approve_assets(self, approve_assets):
assets_id = self.filter_approve_resources(resource_model=Asset, resources_id=approve_assets)
return assets_id
if not isinstance(self.root.instance, Ticket):
return []
with tmp_to_org(self.root.instance.org_id):
assets_id = Asset.objects.filter(id__in=approve_assets).values_list('id', flat=True)
assets_id = [str(asset_id) for asset_id in assets_id]
if assets_id:
return assets_id
raise serializers.ValidationError(_(
'No `Asset` are found under Organization `{}`'.format(self.root.instance.org_name)
))
def validate_approve_system_users(self, approve_system_users):
queries = {'protocol__in': SystemUser.ASSET_CATEGORY_PROTOCOLS}
system_users_id = self.filter_approve_system_users(approve_system_users, queries)
return system_users_id
if not isinstance(self.root.instance, Ticket):
return []
with tmp_to_org(self.root.instance.org_id):
queries = Q(protocol__in=SystemUser.ASSET_CATEGORY_PROTOCOLS)
queries &= Q(id__in=approve_system_users)
system_users_id = SystemUser.objects.filter(queries).values_list('id', flat=True)
system_users_id = [str(system_user_id) for system_user_id in system_users_id]
if system_users_id:
return system_users_id
raise serializers.ValidationError(_(
'No `Asset` are found under Organization `{}`'.format(self.root.instance.org_name)
))
class ApplyAssetSerializer(ApplySerializer, ApproveSerializer):
pass
# 推荐信息
recommend_assets = serializers.SerializerMethodField()
recommend_system_users = serializers.SerializerMethodField()
def get_recommend_assets(self, value):
if not isinstance(self.root.instance, Ticket):
return []
apply_ip_group = value.get('apply_ip_group', [])
apply_hostname_group = value.get('apply_hostname_group', [])
queries = Q(ip__in=apply_ip_group)
for hostname in apply_hostname_group:
queries |= Q(hostname__icontains=hostname)
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]
return assets_id
def get_recommend_system_users(self, value):
if not isinstance(self.root.instance, Ticket):
return []
apply_system_user_group = value.get('apply_system_user_group', [])
queries = Q()
for system_user in apply_system_user_group:
queries |= Q(username__icontains=system_user)
queries |= Q(name__icontains=system_user)
queries &= Q(protocol__in=SystemUser.ASSET_CATEGORY_PROTOCOLS)
with tmp_to_org(self.root.instance.org_id):
system_users_id = SystemUser.objects.filter(queries).values_list('id', flat=True)[:5]
system_users_id = [str(system_user_id) for system_user_id in system_users_id]
return system_users_id

View File

@ -4,19 +4,22 @@ from django.utils.translation import ugettext_lazy as _
__all__ = [
'ApplySerializer',
'ApplySerializer', 'LoginConfirmSerializer',
]
class ApplySerializer(serializers.Serializer):
# 申请信息
apply_login_ip = serializers.IPAddressField(
required=True, label=_('Login ip')
required=True, label=_('Login ip'), allow_null=True
)
apply_login_city = serializers.CharField(
required=True, max_length=64, label=_('Login city')
required=True, max_length=64, label=_('Login city'), allow_null=True
)
apply_login_datetime = serializers.DateTimeField(
required=True, label=_('Login datetime')
required=True, label=_('Login datetime'), allow_null=True
)
class LoginConfirmSerializer(ApplySerializer):
pass

View File

@ -1,39 +0,0 @@
from rest_framework import serializers
from django.utils.translation import ugettext_lazy as _
from orgs.utils import tmp_to_org
from assets.models import SystemUser
class BaseApproveSerializerMixin:
def _filter_approve_resources_by_org(self, model, resources_id):
with tmp_to_org(self.root.instance.org_id):
org_resources = model.objects.filter(id__in=resources_id)
if not org_resources:
error = _('None of the approved `{}` belong to Organization `{}`'
''.format(model.__name__, self.root.instance.org_name))
raise serializers.ValidationError(error)
return org_resources
@staticmethod
def _filter_approve_resources_by_queries(model, resources, queries=None):
if queries:
resources = resources.filter(**queries)
if not resources:
error = _('None of the approved `{}` does not comply with the filtering rules `{}`'
''.format(model.__name__, queries))
raise serializers.ValidationError(error)
return resources
def filter_approve_resources(self, resource_model, resources_id, queries=None):
resources = self._filter_approve_resources_by_org(resource_model, resources_id)
resources = self._filter_approve_resources_by_queries(resource_model, resources, queries)
resources_id = list(resources.values_list('id', flat=True))
return resources_id
def filter_approve_system_users(self, system_users_id, queries=None):
system_users_id = self.filter_approve_resources(
resource_model=SystemUser, resources_id=system_users_id, queries=queries
)
return system_users_id

View File

@ -22,6 +22,9 @@ class TicketSerializer(OrgResourceModelSerializerMixin):
type_display = serializers.ReadOnlyField(source='get_type_display', label=_('Type'))
action_display = serializers.ReadOnlyField(source='get_action_display', label=_('Action'))
status_display = serializers.ReadOnlyField(source='get_status_display', label=_('Status'))
action = ReadableHiddenField(default=const.TicketActionChoices.open.value)
applicant = ReadableHiddenField(default=serializers.CurrentUserDefault())
processor = ReadableHiddenField(default=serializers.CurrentUserDefault())
meta = MethodSerializer()
class Meta:
@ -38,48 +41,55 @@ class TicketSerializer(OrgResourceModelSerializerMixin):
def get_meta_serializer(self):
request = self.context['request']
view = self.context['view']
query_type = request.query_params.get('type')
query_action = request.query_params.get('action')
view_action = view.action
action = query_action if query_action else view_action
if query_type:
serializer_class = type_serializer_classes_mapping.get(query_type, {}).get(action)
default_serializer_class = serializers.Serializer
if isinstance(self.instance, Ticket):
_type = self.instance.type
else:
serializer_class = None
if serializer_class is None:
serializer_class = serializers.Serializer
serializer = serializer_class()
return serializer
_type = request.query_params.get('type')
if not _type:
return default_serializer_class()
action_serializer_classes_mapping = type_serializer_classes_mapping.get(_type)
if not action_serializer_classes_mapping:
return default_serializer_class()
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()
serializer_class = action_serializer_classes_mapping.get('default')
if serializer_class:
return serializer_class()
return default_serializer_class()
class TicketDisplaySerializer(TicketSerializer):
class Meta(TicketSerializer.Meta):
class Meta:
model = Ticket
fields = TicketSerializer.Meta.fields
read_only_fields = TicketSerializer.Meta.fields
class TicketActionSerializer(TicketSerializer):
action = ReadableHiddenField(default=const.TicketActionChoices.open.value)
class Meta(TicketSerializer.Meta):
required_fields = ['action']
read_only_fields = list(set(TicketDisplaySerializer.Meta.fields) - set(required_fields))
class TicketApplySerializer(TicketActionSerializer):
applicant = ReadableHiddenField(default=serializers.CurrentUserDefault())
class TicketApplySerializer(TicketSerializer):
org_id = serializers.CharField(
max_length=36, allow_blank=True, required=True, label=_("Organization")
)
class Meta(TicketActionSerializer.Meta):
required_fields = TicketActionSerializer.Meta.required_fields + [
'id', 'title', 'type', 'applicant', 'meta', 'assignees', 'comment', 'org_id'
class Meta:
model = Ticket
fields = TicketSerializer.Meta.fields
required_fields = [
'id', 'title', 'type', 'applicant', 'action', 'meta', 'assignees', 'comment', 'org_id'
]
read_only_fields = list(set(TicketDisplaySerializer.Meta.fields) - set(required_fields))
read_only_fields = list(set(fields) - set(required_fields))
extra_kwargs = {
'type': {'required': True},
'org_id': {'required': True},
}
def validate_type(self, tp):
@ -115,37 +125,43 @@ class TicketApplySerializer(TicketActionSerializer):
return const.TicketActionChoices.open.value
class TicketProcessSerializer(TicketActionSerializer):
processor = ReadableHiddenField(default=serializers.CurrentUserDefault())
class TicketApproveSerializer(TicketSerializer):
class Meta(TicketActionSerializer.Meta):
required_fields = TicketActionSerializer.Meta.required_fields + ['processor']
read_only_fields = list(set(TicketDisplaySerializer.Meta.fields) - set(required_fields))
class TicketApproveSerializer(TicketProcessSerializer):
class Meta(TicketProcessSerializer.Meta):
required_fields = TicketProcessSerializer.Meta.required_fields + ['meta']
read_only_fields = list(set(TicketDisplaySerializer.Meta.fields) - set(required_fields))
class Meta:
model = Ticket
fields = TicketSerializer.Meta.fields
required_fields = ['processor', 'action', 'meta']
read_only_fields = list(set(fields) - set(required_fields))
def validate_meta(self, meta):
instance_meta = self.instance.meta
instance_meta.update(meta)
return instance_meta
_meta = self.instance.meta if self.instance else {}
_meta.update(meta)
return _meta
@staticmethod
def validate_action(action):
return const.TicketActionChoices.approve.value
class TicketRejectSerializer(TicketProcessSerializer):
class TicketRejectSerializer(TicketSerializer):
meta = MethodSerializer(read_only=True)
class Meta:
model = Ticket
fields = TicketSerializer.Meta.fields
read_only_fields = fields
def validate_action(self, action):
return const.TicketActionChoices.reject.value
class TicketCloseSerializer(TicketProcessSerializer):
class TicketCloseSerializer(TicketSerializer):
meta = MethodSerializer(read_only=True)
class Meta:
model = Ticket
fields = TicketSerializer.Meta.fields
read_only_fields = fields
def validate_action(self, action):
return const.TicketActionChoices.close.value

View File

@ -17,26 +17,27 @@ logger = get_logger(__name__)
@receiver(pre_save, sender=Ticket)
def on_ticket_pre_save(sender, instance=None, **kwargs):
instance.set_display_fields()
if instance.has_processed:
instance.set_status_closed()
instance.set_display_fields()
@receiver(post_save, sender=Ticket)
def on_ticket_post_save(sender, instance=None, **kwargs):
instance.create_action_comment()
if instance.action_open:
def on_ticket_post_save(sender, instance=None, created=False, **kwargs):
if created and instance.action_open:
instance.create_action_comment()
instance.create_applied_comment()
return
if instance.action_approve:
instance.create_permission()
instance.create_approved_comment()
logger.debug(
'Ticket () has processed, send mail to applicant: {}'.format(
instance.title, instance.applicant_display
)
)
send_ticket_processed_mail_to_applicant(instance)
if not created and instance.has_processed:
instance.create_action_comment()
msg = 'Ticket () has processed, send mail to applicant: {}'
logger.debug(msg.format(instance.title, instance.applicant_display))
send_ticket_processed_mail_to_applicant(instance)
if instance.action_approve:
instance.create_permission()
instance.create_approved_comment()
@receiver(m2m_changed, sender=Ticket.assignees.through)