feat: 支持设置默认存储(命令、录像) (#6336)

* fix: 修改LDAP用户导入的组织为当前组织

* fix: 修改翻译信息

* feat: 支持设置默认存储

* feat: 支持设置默认存储(2)

* feat: 支持设置默认存储(3)
pull/6343/head
Jiangjie.Bai 2021-06-28 10:32:59 +08:00 committed by GitHub
parent aa6e550ba2
commit c0ec0f1343
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 158 additions and 80 deletions

View File

@ -0,0 +1,25 @@
# Generated by Django 3.1.6 on 2021-06-23 09:48
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('assets', '0070_auto_20210426_1515'),
('applications', '0008_auto_20210104_0435'),
]
operations = [
migrations.CreateModel(
name='ApplicationUser',
fields=[
],
options={
'proxy': True,
'indexes': [],
'constraints': [],
},
bases=('assets.systemuser',),
),
]

Binary file not shown.

View File

@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: JumpServer 0.3.3\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2021-06-22 19:04+0800\n"
"POT-Creation-Date: 2021-06-25 17:12+0800\n"
"PO-Revision-Date: 2021-05-20 10:54+0800\n"
"Last-Translator: ibuler <ibuler@qq.com>\n"
"Language-Team: JumpServer team<ibuler@qq.com>\n"
@ -23,9 +23,9 @@ msgstr ""
#: assets/models/cmd_filter.py:21 assets/models/domain.py:21
#: assets/models/group.py:20 assets/models/label.py:18 ops/mixin.py:24
#: orgs/models.py:23 perms/models/base.py:49 settings/models.py:29
#: terminal/models/storage.py:23 terminal/models/storage.py:90
#: terminal/models/task.py:16 terminal/models/terminal.py:100
#: users/forms/profile.py:32 users/models/group.py:15 users/models/user.py:550
#: terminal/models/storage.py:23 terminal/models/task.py:16
#: terminal/models/terminal.py:100 users/forms/profile.py:32
#: users/models/group.py:15 users/models/user.py:550
#: users/templates/users/_select_user_modal.html:13
#: users/templates/users/user_asset_permission.html:37
#: users/templates/users/user_asset_permission.html:154
@ -59,11 +59,11 @@ msgstr "激活中"
#: assets/models/domain.py:22 assets/models/domain.py:56
#: assets/models/group.py:23 assets/models/label.py:23 ops/models/adhoc.py:37
#: orgs/models.py:26 perms/models/base.py:57 settings/models.py:34
#: terminal/models/storage.py:29 terminal/models/storage.py:96
#: terminal/models/terminal.py:114 tickets/models/ticket.py:73
#: users/models/group.py:16 users/models/user.py:583
#: xpack/plugins/change_auth_plan/models.py:77 xpack/plugins/cloud/models.py:35
#: xpack/plugins/cloud/models.py:98 xpack/plugins/gathered_user/models.py:26
#: terminal/models/storage.py:26 terminal/models/terminal.py:114
#: tickets/models/ticket.py:73 users/models/group.py:16
#: users/models/user.py:583 xpack/plugins/change_auth_plan/models.py:77
#: xpack/plugins/cloud/models.py:35 xpack/plugins/cloud/models.py:98
#: xpack/plugins/gathered_user/models.py:26
msgid "Comment"
msgstr "备注"
@ -257,7 +257,7 @@ msgstr "类别"
#: perms/models/application_permission.py:23
#: perms/serializers/application/permission.py:17
#: perms/serializers/application/user_permission.py:34
#: terminal/models/storage.py:26 terminal/models/storage.py:93
#: terminal/models/storage.py:47 terminal/models/storage.py:108
#: tickets/models/ticket.py:38
#: tickets/serializers/ticket/meta/ticket_type/apply_application.py:27
msgid "Type"
@ -1202,7 +1202,7 @@ msgstr "主机 (显示名称)"
msgid "Result"
msgstr "结果"
#: audits/serializers.py:92 terminal/serializers/storage.py:189
#: audits/serializers.py:92 terminal/serializers/storage.py:195
msgid "Hosts"
msgstr "主机"
@ -3252,6 +3252,10 @@ msgstr "线程数"
msgid "Boot Time"
msgstr "运行时间"
#: terminal/models/storage.py:25
msgid "Default storage"
msgstr "默认存储"
#: terminal/models/task.py:17
msgid "Args"
msgstr "参数"
@ -3426,27 +3430,27 @@ msgstr "账户密钥"
msgid "Endpoint suffix"
msgstr "端点后缀"
#: terminal/serializers/storage.py:166
#: terminal/serializers/storage.py:172
msgid "The address format is incorrect"
msgstr "地址格式不正确"
#: terminal/serializers/storage.py:173
#: terminal/serializers/storage.py:179
msgid "Host invalid"
msgstr "主机无效"
#: terminal/serializers/storage.py:176
#: terminal/serializers/storage.py:182
msgid "Port invalid"
msgstr "端口无效"
#: terminal/serializers/storage.py:192
#: terminal/serializers/storage.py:198
msgid "Index"
msgstr "索引"
#: terminal/serializers/storage.py:194
#: terminal/serializers/storage.py:200
msgid "Doc type"
msgstr "文档类型"
#: terminal/serializers/storage.py:196
#: terminal/serializers/storage.py:202
msgid "Ignore Certificate Verification"
msgstr "忽略证书认证"
@ -5114,6 +5118,3 @@ msgstr "旗舰版"
#: xpack/plugins/license/models.py:77
msgid "Community edition"
msgstr "社区版"
#~ msgid "Terminal command alert"
#~ msgstr "终端命令告警"

View File

@ -36,7 +36,7 @@ class BaseStorageViewSetMixin:
class CommandStorageViewSet(BaseStorageViewSetMixin, viewsets.ModelViewSet):
search_fields = ('name', 'type',)
search_fields = ('name', 'type')
queryset = CommandStorage.objects.all()
serializer_class = CommandStorageSerializer
permission_classes = (IsSuperUser,)
@ -103,7 +103,7 @@ class CommandStorageViewSet(BaseStorageViewSetMixin, viewsets.ModelViewSet):
class ReplayStorageViewSet(BaseStorageViewSetMixin, viewsets.ModelViewSet):
filterset_fields = ('name', 'type',)
filterset_fields = ('name', 'type', 'is_default')
search_fields = filterset_fields
queryset = ReplayStorage.objects.all()
serializer_class = ReplayStorageSerializer

View File

@ -71,7 +71,7 @@ class CommandStorageFilter(filters.FilterSet):
class Meta:
model = CommandStorage
fields = ['real', 'name', 'type']
fields = ['real', 'name', 'type', 'is_default']
def filter_real(self, queryset, name, value):
if value:

View File

@ -0,0 +1,37 @@
# Generated by Django 3.1.6 on 2021-06-23 09:48
from django.db import migrations, models
def set_default_storage(apps, schema_editor):
command_storage_model = apps.get_model("terminal", "CommandStorage")
command_storage = command_storage_model.objects.filter(name='default', type='server').first()
if command_storage:
command_storage.is_default = True
command_storage.save()
replay_storage_model = apps.get_model("terminal", "ReplayStorage")
replay_storage = replay_storage_model.objects.filter(name='default', type='server').first()
if replay_storage:
replay_storage.is_default = True
replay_storage.save()
class Migration(migrations.Migration):
dependencies = [
('terminal', '0036_auto_20210604_1124'),
]
operations = [
migrations.AddField(
model_name='commandstorage',
name='is_default',
field=models.BooleanField(default=False, verbose_name='Default storage'),
),
migrations.AddField(
model_name='replaystorage',
name='is_default',
field=models.BooleanField(default=False, verbose_name='Default storage'),
),
migrations.RunPython(set_default_storage)
]

View File

@ -19,17 +19,41 @@ from .. import const
logger = get_logger(__file__)
class CommandStorage(CommonModelMixin):
class CommonStorageModelMixin(models.Model):
name = models.CharField(max_length=128, verbose_name=_("Name"), unique=True)
meta = EncryptJsonDictTextField(default={})
is_default = models.BooleanField(default=False, verbose_name=_('Default storage'))
comment = models.TextField(default='', blank=True, verbose_name=_('Comment'))
class Meta:
abstract = True
def __str__(self):
return self.name
def set_to_default(self):
self.is_default = True
self.save()
self.__class__.objects.select_for_update()\
.filter(is_default=True)\
.exclude(id=self.id)\
.update(is_default=False)
@classmethod
def default(cls):
objs = cls.objects.filter(is_default=True)
if not objs:
objs = cls.objects.filter(name='default', type='server')
if not objs:
objs = cls.objects.all()
return objs.first()
class CommandStorage(CommonStorageModelMixin, CommonModelMixin):
type = models.CharField(
max_length=16, choices=const.CommandStorageTypeChoices.choices,
default=const.CommandStorageTypeChoices.server.value, verbose_name=_('Type'),
)
meta = EncryptJsonDictTextField(default={})
comment = models.TextField(default='', blank=True, verbose_name=_('Comment'))
def __str__(self):
return self.name
@property
def type_null(self):
@ -86,17 +110,11 @@ class CommandStorage(CommonModelMixin):
backend.pre_use_check()
class ReplayStorage(CommonModelMixin):
name = models.CharField(max_length=128, verbose_name=_("Name"), unique=True)
class ReplayStorage(CommonStorageModelMixin, CommonModelMixin):
type = models.CharField(
max_length=16, choices=const.ReplayStorageTypeChoices.choices,
default=const.ReplayStorageTypeChoices.server.value, verbose_name=_('Type')
)
meta = EncryptJsonDictTextField(default={})
comment = models.TextField(default='', blank=True, verbose_name=_('Comment'))
def __str__(self):
return self.name
@property
def type_null(self):

View File

@ -176,6 +176,12 @@ class Terminal(StorageMixin, TerminalStatusMixin, models.Model):
self.save()
return
def save(self, **kwargs):
from .storage import CommandStorage, ReplayStorage
self.command_storage = CommandStorage.default().name
self.replay_storage = ReplayStorage.default().name
return super().save(**kwargs)
def __str__(self):
status = "Active"
if not self.is_accepted:

View File

@ -119,44 +119,6 @@ replay_storage_type_serializer_classes_mapping = {
const.ReplayStorageTypeChoices.obs.value: ReplayStorageTypeOBSSerializer
}
# 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
def get_meta_serializer(self):
default_serializer = serializers.Serializer(read_only=True)
if isinstance(self.instance, ReplayStorage):
_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
# Command storage serializers
# ---------------------------
@ -204,15 +166,17 @@ command_storage_type_serializer_classes_mapping = {
const.CommandStorageTypeChoices.es.value: CommandStorageTypeESSerializer
}
# CommandStorageSerializer
# BaseStorageSerializer
class CommandStorageSerializer(serializers.ModelSerializer):
class BaseStorageSerializer(serializers.ModelSerializer):
storage_type_serializer_classes_mapping = {}
meta = MethodSerializer()
class Meta:
model = CommandStorage
fields = ['id', 'name', 'type', 'meta', 'comment']
model = None
fields = ['id', 'name', 'type', 'meta', 'is_default', 'comment']
def validate_meta(self, meta):
_meta = self.instance.meta if self.instance else {}
@ -222,13 +186,13 @@ class CommandStorageSerializer(serializers.ModelSerializer):
def get_meta_serializer(self):
default_serializer = serializers.Serializer(read_only=True)
if isinstance(self.instance, CommandStorage):
if isinstance(self.instance, self.__class__.Meta.model):
_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)
serializer_class = self.storage_type_serializer_classes_mapping.get(_type)
else:
serializer_class = default_serializer
@ -240,3 +204,30 @@ class CommandStorageSerializer(serializers.ModelSerializer):
else:
serializer = serializer_class
return serializer
def save(self, **kwargs):
instance = super().save(**kwargs)
if self.validated_data.get('is_default', False):
instance.set_to_default()
return instance
# CommandStorageSerializer
class CommandStorageSerializer(BaseStorageSerializer):
storage_type_serializer_classes_mapping = command_storage_type_serializer_classes_mapping
class Meta(BaseStorageSerializer.Meta):
model = CommandStorage
# ReplayStorageSerializer
class ReplayStorageSerializer(BaseStorageSerializer):
storage_type_serializer_classes_mapping = replay_storage_type_serializer_classes_mapping
class Meta(BaseStorageSerializer.Meta):
model = ReplayStorage