2019-12-05 07:09:25 +00:00
|
|
|
# -*- coding: utf-8 -*-
|
|
|
|
#
|
2021-01-12 10:06:42 +00:00
|
|
|
from urllib.parse import urlparse
|
2023-07-24 03:52:25 +00:00
|
|
|
|
2023-11-07 07:10:46 +00:00
|
|
|
from django.core.validators import MaxValueValidator, MinValueValidator, validate_ipv46_address
|
2024-03-28 06:31:27 +00:00
|
|
|
from django.db.models import TextChoices
|
2023-07-24 03:52:25 +00:00
|
|
|
from django.utils.translation import gettext_lazy as _
|
|
|
|
from rest_framework import serializers
|
|
|
|
from rest_framework.validators import UniqueValidator
|
|
|
|
|
2023-01-16 11:02:09 +00:00
|
|
|
from common.serializers import MethodSerializer
|
2023-07-24 03:52:25 +00:00
|
|
|
from common.serializers.fields import LabeledChoiceField
|
2023-01-16 11:02:09 +00:00
|
|
|
from common.serializers.fields import ReadableHiddenField, EncryptedField
|
2019-12-05 07:09:25 +00:00
|
|
|
from .. import const
|
2023-07-24 03:52:25 +00:00
|
|
|
from ..models import ReplayStorage, CommandStorage
|
2019-12-05 07:09:25 +00:00
|
|
|
|
|
|
|
|
2021-01-12 10:06:42 +00:00
|
|
|
# Replay storage serializers
|
|
|
|
# --------------------------
|
2021-01-19 11:03:42 +00:00
|
|
|
def replay_storage_endpoint_format_validator(endpoint):
|
|
|
|
h = urlparse(endpoint)
|
|
|
|
if h.path:
|
|
|
|
raise serializers.ValidationError(_('Endpoint invalid: remove path `{}`').format(h.path))
|
|
|
|
return endpoint
|
|
|
|
|
|
|
|
|
2021-01-12 10:06:42 +00:00
|
|
|
class ReplayStorageTypeBaseSerializer(serializers.Serializer):
|
|
|
|
BUCKET = serializers.CharField(
|
|
|
|
required=True, max_length=1024, label=_('Bucket'), allow_null=True
|
|
|
|
)
|
|
|
|
ACCESS_KEY = serializers.CharField(
|
2022-05-20 02:01:41 +00:00
|
|
|
max_length=1024, required=False, allow_blank=True,
|
2024-02-27 06:59:22 +00:00
|
|
|
label=_('Access key ID'), allow_null=True,
|
2021-01-12 10:06:42 +00:00
|
|
|
)
|
2022-05-20 02:01:41 +00:00
|
|
|
SECRET_KEY = EncryptedField(
|
|
|
|
max_length=1024, required=False, allow_blank=True,
|
|
|
|
label=_('Access key secret'), allow_null=True,
|
2021-01-12 10:06:42 +00:00
|
|
|
)
|
|
|
|
ENDPOINT = serializers.CharField(
|
2021-01-19 11:03:42 +00:00
|
|
|
validators=[replay_storage_endpoint_format_validator],
|
2021-01-12 10:06:42 +00:00
|
|
|
required=True, max_length=1024, label=_('Endpoint'), allow_null=True,
|
|
|
|
)
|
2019-12-05 07:09:25 +00:00
|
|
|
|
|
|
|
|
2021-01-12 10:06:42 +00:00
|
|
|
class ReplayStorageTypeS3Serializer(ReplayStorageTypeBaseSerializer):
|
|
|
|
endpoint_help_text = '''
|
2022-02-28 11:28:58 +00:00
|
|
|
S3 format: http://s3.{REGION_NAME}.amazonaws.com <br>
|
|
|
|
S3(China) format: http://s3.{REGION_NAME}.amazonaws.com.cn <br>
|
2021-01-12 10:06:42 +00:00
|
|
|
Such as: http://s3.cn-north-1.amazonaws.com.cn
|
|
|
|
'''
|
|
|
|
ENDPOINT = serializers.CharField(
|
2021-01-19 11:03:42 +00:00
|
|
|
validators=[replay_storage_endpoint_format_validator],
|
2021-01-12 10:06:42 +00:00
|
|
|
required=True, max_length=1024, label=_('Endpoint'), help_text=_(endpoint_help_text),
|
|
|
|
allow_null=True,
|
|
|
|
)
|
2019-12-05 07:09:25 +00:00
|
|
|
|
|
|
|
|
2021-01-12 10:06:42 +00:00
|
|
|
class ReplayStorageTypeCephSerializer(ReplayStorageTypeBaseSerializer):
|
|
|
|
pass
|
2019-12-05 07:09:25 +00:00
|
|
|
|
|
|
|
|
2021-01-12 10:06:42 +00:00
|
|
|
class ReplayStorageTypeSwiftSerializer(ReplayStorageTypeBaseSerializer):
|
|
|
|
class ProtocolChoices(TextChoices):
|
|
|
|
http = 'http', 'http'
|
|
|
|
https = 'https', 'https'
|
2019-12-05 07:09:25 +00:00
|
|
|
|
2021-01-12 10:06:42 +00:00
|
|
|
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,
|
|
|
|
)
|
2019-12-05 07:09:25 +00:00
|
|
|
|
|
|
|
|
2021-01-12 10:06:42 +00:00
|
|
|
class ReplayStorageTypeOSSSerializer(ReplayStorageTypeBaseSerializer):
|
|
|
|
endpoint_help_text = '''
|
2022-02-28 11:28:58 +00:00
|
|
|
OSS format: http://{REGION_NAME}.aliyuncs.com <br>
|
2021-01-12 10:06:42 +00:00
|
|
|
Such as: http://oss-cn-hangzhou.aliyuncs.com
|
|
|
|
'''
|
|
|
|
ENDPOINT = serializers.CharField(
|
2021-01-19 11:03:42 +00:00
|
|
|
validators=[replay_storage_endpoint_format_validator],
|
2021-01-12 10:06:42 +00:00
|
|
|
max_length=1024, label=_('Endpoint'), help_text=_(endpoint_help_text), allow_null=True,
|
|
|
|
)
|
2019-12-05 07:09:25 +00:00
|
|
|
|
|
|
|
|
2021-04-12 11:37:04 +00:00
|
|
|
class ReplayStorageTypeOBSSerializer(ReplayStorageTypeBaseSerializer):
|
|
|
|
endpoint_help_text = '''
|
2022-02-28 11:28:58 +00:00
|
|
|
OBS format: obs.{REGION_NAME}.myhuaweicloud.com <br>
|
2021-04-12 11:37:04 +00:00
|
|
|
Such as: obs.cn-north-4.myhuaweicloud.com
|
|
|
|
'''
|
|
|
|
ENDPOINT = serializers.CharField(
|
|
|
|
max_length=1024, label=_('Endpoint'), help_text=_(endpoint_help_text), allow_null=True,
|
|
|
|
)
|
|
|
|
|
|
|
|
|
2022-02-28 11:28:58 +00:00
|
|
|
class ReplayStorageTypeCOSSerializer(ReplayStorageTypeS3Serializer):
|
|
|
|
endpoint_help_text = '''Such as: http://cos.{REGION_NAME}.myqcloud.com'''
|
|
|
|
ENDPOINT = serializers.CharField(
|
|
|
|
validators=[replay_storage_endpoint_format_validator],
|
|
|
|
required=True, max_length=1024, label=_('Endpoint'), help_text=_(endpoint_help_text),
|
|
|
|
allow_null=True,
|
|
|
|
)
|
|
|
|
|
|
|
|
|
2021-01-12 10:06:42 +00:00
|
|
|
class ReplayStorageTypeAzureSerializer(serializers.Serializer):
|
|
|
|
class EndpointSuffixChoices(TextChoices):
|
|
|
|
china = 'core.chinacloudapi.cn', 'core.chinacloudapi.cn'
|
|
|
|
international = 'core.windows.net', 'core.windows.net'
|
|
|
|
|
2021-01-17 08:22:06 +00:00
|
|
|
CONTAINER_NAME = serializers.CharField(
|
|
|
|
max_length=1024, label=_('Container name'), allow_null=True
|
|
|
|
)
|
2021-01-12 10:06:42 +00:00
|
|
|
ACCOUNT_NAME = serializers.CharField(max_length=1024, label=_('Account name'), allow_null=True)
|
2022-05-20 02:01:41 +00:00
|
|
|
ACCOUNT_KEY = EncryptedField(max_length=1024, label=_('Account key'), allow_null=True)
|
2021-01-12 10:06:42 +00:00
|
|
|
ENDPOINT_SUFFIX = serializers.ChoiceField(
|
|
|
|
choices=EndpointSuffixChoices.choices, default=EndpointSuffixChoices.china.value,
|
|
|
|
label=_('Endpoint suffix'), allow_null=True,
|
|
|
|
)
|
|
|
|
|
2019-12-05 07:09:25 +00:00
|
|
|
|
2023-11-07 07:10:46 +00:00
|
|
|
class SftpSecretType(TextChoices):
|
|
|
|
PASSWORD = 'password', _('Password')
|
|
|
|
SSH_KEY = 'ssh_key', _('SSH key')
|
|
|
|
|
|
|
|
|
|
|
|
class ReplayStorageTypeSFTPSerializer(serializers.Serializer):
|
|
|
|
SFTP_HOST = serializers.CharField(
|
|
|
|
required=True, max_length=1024, label=_('HOST'), validators=[validate_ipv46_address]
|
|
|
|
)
|
|
|
|
SFTP_PORT = serializers.IntegerField(
|
|
|
|
required=False, default=22, validators=[MaxValueValidator(65535), MinValueValidator(0)],
|
|
|
|
label=_('Port')
|
|
|
|
)
|
|
|
|
SFTP_USERNAME = serializers.CharField(
|
|
|
|
required=True, max_length=1024, label=_('Username')
|
|
|
|
)
|
|
|
|
STP_SECRET_TYPE = serializers.ChoiceField(choices=SftpSecretType.choices,
|
|
|
|
default=SftpSecretType.PASSWORD,
|
|
|
|
label=_('Secret type'))
|
|
|
|
SFTP_PASSWORD = EncryptedField(
|
|
|
|
allow_blank=True, allow_null=True, required=False, max_length=1024, label=_('Password')
|
|
|
|
)
|
|
|
|
STP_PRIVATE_KEY = serializers.CharField(
|
|
|
|
allow_blank=True, allow_null=True, required=False, max_length=4096,
|
|
|
|
write_only=True, label=_('Private key')
|
|
|
|
)
|
|
|
|
STP_PASSPHRASE = EncryptedField(
|
2024-03-28 06:31:27 +00:00
|
|
|
allow_blank=True, allow_null=True, required=False, max_length=1024, label=_('Passphrase')
|
2023-11-07 07:10:46 +00:00
|
|
|
)
|
|
|
|
SFTP_ROOT_PATH = serializers.CharField(
|
|
|
|
required=True, max_length=1024, label=_('SFTP Root')
|
|
|
|
)
|
|
|
|
|
|
|
|
|
2022-04-02 05:26:18 +00:00
|
|
|
# mapping
|
2021-01-12 10:06:42 +00:00
|
|
|
replay_storage_type_serializer_classes_mapping = {
|
2022-11-04 03:40:16 +00:00
|
|
|
const.ReplayStorageType.s3.value: ReplayStorageTypeS3Serializer,
|
|
|
|
const.ReplayStorageType.ceph.value: ReplayStorageTypeCephSerializer,
|
|
|
|
const.ReplayStorageType.swift.value: ReplayStorageTypeSwiftSerializer,
|
|
|
|
const.ReplayStorageType.oss.value: ReplayStorageTypeOSSSerializer,
|
|
|
|
const.ReplayStorageType.azure.value: ReplayStorageTypeAzureSerializer,
|
|
|
|
const.ReplayStorageType.obs.value: ReplayStorageTypeOBSSerializer,
|
2023-11-07 07:10:46 +00:00
|
|
|
const.ReplayStorageType.cos.value: ReplayStorageTypeCOSSerializer,
|
|
|
|
const.ReplayStorageType.sftp.value: ReplayStorageTypeSFTPSerializer
|
2021-01-12 10:06:42 +00:00
|
|
|
}
|
2019-12-05 07:09:25 +00:00
|
|
|
|
2022-04-02 05:26:18 +00:00
|
|
|
|
2021-01-12 10:06:42 +00:00
|
|
|
# Command storage serializers
|
|
|
|
# ---------------------------
|
2021-01-19 11:03:42 +00:00
|
|
|
def command_storage_es_host_format_validator(host):
|
2023-09-19 07:28:29 +00:00
|
|
|
if '#' in host:
|
|
|
|
raise serializers.ValidationError(_('The address cannot contain the special character `#`'))
|
2021-01-12 10:06:42 +00:00
|
|
|
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)
|
2021-02-18 07:07:49 +00:00
|
|
|
_host, _port = h.netloc.rsplit(':', maxsplit=1)
|
2021-01-12 10:06:42 +00:00
|
|
|
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 = serializers.ListField(
|
2021-01-19 11:03:42 +00:00
|
|
|
child=serializers.CharField(validators=[command_storage_es_host_format_validator]),
|
2024-06-12 11:38:45 +00:00
|
|
|
label=_('Hosts'), help_text=_(
|
|
|
|
'If there are multiple hosts, use a comma (,) to separate them. <br>'
|
|
|
|
'(For example: http://www.jumpserver.a.com:9100, http://www.jumpserver.b.com:9100)'),
|
|
|
|
allow_null=True
|
2021-01-12 10:06:42 +00:00
|
|
|
)
|
2022-05-09 08:37:31 +00:00
|
|
|
INDEX_BY_DATE = serializers.BooleanField(
|
|
|
|
default=False, label=_('Index by date'),
|
|
|
|
help_text=_('Whether to create an index by date')
|
|
|
|
)
|
2021-01-12 10:06:42 +00:00
|
|
|
INDEX = serializers.CharField(
|
|
|
|
max_length=1024, default='jumpserver', label=_('Index'), allow_null=True
|
|
|
|
)
|
2021-08-12 07:37:22 +00:00
|
|
|
DOC_TYPE = ReadableHiddenField(default='_doc', label=_('Doc type'), allow_null=True)
|
2021-05-07 08:29:47 +00:00
|
|
|
IGNORE_VERIFY_CERTS = serializers.BooleanField(
|
2021-03-29 04:01:24 +00:00
|
|
|
default=False, label=_('Ignore Certificate Verification'),
|
2021-05-07 08:29:47 +00:00
|
|
|
source='OTHER.IGNORE_VERIFY_CERTS', allow_null=True,
|
2021-03-29 04:01:24 +00:00
|
|
|
)
|
2021-01-12 10:06:42 +00:00
|
|
|
|
|
|
|
|
2022-04-02 05:26:18 +00:00
|
|
|
# mapping
|
2021-01-12 10:06:42 +00:00
|
|
|
command_storage_type_serializer_classes_mapping = {
|
2022-11-04 03:40:16 +00:00
|
|
|
const.CommandStorageType.es.value: CommandStorageTypeESSerializer
|
2021-01-12 10:06:42 +00:00
|
|
|
}
|
|
|
|
|
2021-06-28 02:32:59 +00:00
|
|
|
|
|
|
|
# BaseStorageSerializer
|
|
|
|
class BaseStorageSerializer(serializers.ModelSerializer):
|
|
|
|
storage_type_serializer_classes_mapping = {}
|
2021-01-12 10:06:42 +00:00
|
|
|
meta = MethodSerializer()
|
2019-12-05 07:09:25 +00:00
|
|
|
|
|
|
|
class Meta:
|
2021-06-28 02:32:59 +00:00
|
|
|
model = None
|
|
|
|
fields = ['id', 'name', 'type', 'meta', 'is_default', 'comment']
|
2021-01-12 10:06:42 +00:00
|
|
|
|
|
|
|
def validate_meta(self, meta):
|
|
|
|
_meta = self.instance.meta if self.instance else {}
|
|
|
|
_meta.update(meta)
|
|
|
|
return _meta
|
|
|
|
|
|
|
|
def get_meta_serializer(self):
|
2021-01-19 07:44:19 +00:00
|
|
|
default_serializer = serializers.Serializer(read_only=True)
|
|
|
|
|
2021-06-28 02:32:59 +00:00
|
|
|
if isinstance(self.instance, self.__class__.Meta.model):
|
2021-01-19 07:44:19 +00:00
|
|
|
_type = self.instance.type
|
|
|
|
else:
|
|
|
|
_type = self.context['request'].query_params.get('type')
|
|
|
|
|
|
|
|
if _type:
|
2021-06-28 02:32:59 +00:00
|
|
|
serializer_class = self.storage_type_serializer_classes_mapping.get(_type)
|
2021-01-19 07:44:19 +00:00
|
|
|
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
|
2021-01-12 10:06:42 +00:00
|
|
|
return serializer
|
2021-06-28 02:32:59 +00:00
|
|
|
|
2024-08-14 11:15:31 +00:00
|
|
|
def to_representation(self, instance):
|
|
|
|
data = super().to_representation(instance)
|
|
|
|
need_translate_comments = {
|
|
|
|
'Store locally': _('Store locally'),
|
|
|
|
'Do not save': _('Do not save')
|
|
|
|
}
|
|
|
|
comment = instance.comment
|
|
|
|
data['comment'] = need_translate_comments.get(comment, comment)
|
|
|
|
return data
|
2024-08-06 02:27:15 +00:00
|
|
|
|
2021-06-28 02:32:59 +00:00
|
|
|
def save(self, **kwargs):
|
|
|
|
instance = super().save(**kwargs)
|
|
|
|
if self.validated_data.get('is_default', False):
|
|
|
|
instance.set_to_default()
|
|
|
|
return instance
|
|
|
|
|
|
|
|
|
2024-05-24 06:41:28 +00:00
|
|
|
meta_is_default = {
|
|
|
|
'help_text': _(
|
|
|
|
'set as the default storage, will make new Component use the current '
|
|
|
|
'storage by default, without affecting existing Component'
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
2024-06-12 11:38:45 +00:00
|
|
|
|
2021-06-28 02:32:59 +00:00
|
|
|
# CommandStorageSerializer
|
|
|
|
class CommandStorageSerializer(BaseStorageSerializer):
|
2023-02-16 06:58:42 +00:00
|
|
|
type = LabeledChoiceField(choices=const.CommandStorageType.choices, label=_('Type'))
|
2021-06-28 02:32:59 +00:00
|
|
|
storage_type_serializer_classes_mapping = command_storage_type_serializer_classes_mapping
|
|
|
|
|
|
|
|
class Meta(BaseStorageSerializer.Meta):
|
|
|
|
model = CommandStorage
|
2021-11-12 08:27:15 +00:00
|
|
|
extra_kwargs = {
|
2024-05-24 06:41:28 +00:00
|
|
|
'name': {'validators': [UniqueValidator(queryset=CommandStorage.objects.all())]},
|
|
|
|
'is_default': meta_is_default
|
2021-11-12 08:27:15 +00:00
|
|
|
}
|
2021-06-28 02:32:59 +00:00
|
|
|
|
|
|
|
|
|
|
|
# ReplayStorageSerializer
|
|
|
|
class ReplayStorageSerializer(BaseStorageSerializer):
|
2023-02-16 06:58:42 +00:00
|
|
|
type = LabeledChoiceField(choices=const.ReplayStorageType.choices, label=_('Type'))
|
2021-06-28 02:32:59 +00:00
|
|
|
storage_type_serializer_classes_mapping = replay_storage_type_serializer_classes_mapping
|
|
|
|
|
|
|
|
class Meta(BaseStorageSerializer.Meta):
|
|
|
|
model = ReplayStorage
|
2024-06-12 11:38:45 +00:00
|
|
|
extra_kwargs = {
|
2024-05-24 06:41:28 +00:00
|
|
|
'name': {'validators': [UniqueValidator(queryset=ReplayStorage.objects.all())]},
|
|
|
|
'is_default': meta_is_default
|
2021-11-12 08:27:15 +00:00
|
|
|
}
|
2023-11-28 07:21:40 +00:00
|
|
|
|
|
|
|
def validate_is_default(self, value):
|
|
|
|
if self.initial_data.get('type') == const.ReplayStorageType.sftp.value:
|
|
|
|
# sftp不能设置为默认存储
|
|
|
|
return False
|
|
|
|
else:
|
|
|
|
return value
|