perf: 账号备份优化 (#7503)

* perf: 账号备份优化

* feat: 优化账号备份获取有序备份字段列表

Co-authored-by: feng626 <1304903146@qq.com>
Co-authored-by: Michael Bai <baijiangjie@gmail.com>
pull/7498/head
fit2bot 2022-01-17 15:58:39 +08:00 committed by GitHub
parent d2b3edffca
commit def9bedd30
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
24 changed files with 362 additions and 228 deletions

View File

@ -44,11 +44,7 @@ class ApplicationAccountViewSet(JMSBulkModelViewSet):
permission_classes = (IsOrgAdmin,) permission_classes = (IsOrgAdmin,)
def get_queryset(self): def get_queryset(self):
queryset = Account.objects.all() \ queryset = Account.get_queryset()
.annotate(type=F('app__type')) \
.annotate(app_display=F('app__name')) \
.annotate(systemuser_display=F('systemuser__name')) \
.annotate(category=F('app__category'))
return queryset return queryset

View File

@ -1,5 +1,6 @@
from django.db import models from django.db import models
from simple_history.models import HistoricalRecords from simple_history.models import HistoricalRecords
from django.db.models import F
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
from common.utils import lazyproperty from common.utils import lazyproperty
@ -7,8 +8,12 @@ from assets.models.base import BaseUser
class Account(BaseUser): class Account(BaseUser):
app = models.ForeignKey('applications.Application', on_delete=models.CASCADE, null=True, verbose_name=_('Database')) app = models.ForeignKey(
systemuser = models.ForeignKey('assets.SystemUser', on_delete=models.CASCADE, null=True, verbose_name=_("System user")) 'applications.Application', on_delete=models.CASCADE, null=True, verbose_name=_('Database')
)
systemuser = models.ForeignKey(
'assets.SystemUser', on_delete=models.CASCADE, null=True, verbose_name=_("System user")
)
version = models.IntegerField(default=1, verbose_name=_('Version')) version = models.IntegerField(default=1, verbose_name=_('Version'))
history = HistoricalRecords() history = HistoricalRecords()
@ -60,6 +65,10 @@ class Account(BaseUser):
def type(self): def type(self):
return self.app.type return self.app.type
@lazyproperty
def attrs(self):
return self.app.attrs
@lazyproperty @lazyproperty
def app_display(self): def app_display(self):
return self.systemuser.name return self.systemuser.name
@ -84,5 +93,14 @@ class Account(BaseUser):
app = '*' app = '*'
return '{}@{}'.format(username, app) return '{}@{}'.format(username, app)
@classmethod
def get_queryset(cls):
queryset = cls.objects.all() \
.annotate(type=F('app__type')) \
.annotate(app_display=F('app__name')) \
.annotate(systemuser_display=F('systemuser__name')) \
.annotate(category=F('app__category'))
return queryset
def __str__(self): def __str__(self):
return self.smart_name return self.smart_name

View File

@ -1,6 +1,5 @@
# coding: utf-8 # coding: utf-8
# #
from rest_framework import serializers from rest_framework import serializers
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
@ -9,7 +8,8 @@ from assets.serializers.base import AuthSerializerMixin
from common.drf.serializers import MethodSerializer from common.drf.serializers import MethodSerializer
from .attrs import ( from .attrs import (
category_serializer_classes_mapping, category_serializer_classes_mapping,
type_serializer_classes_mapping type_serializer_classes_mapping,
type_secret_serializer_classes_mapping
) )
from .. import models from .. import models
from .. import const from .. import const
@ -23,17 +23,28 @@ __all__ = [
class AppSerializerMixin(serializers.Serializer): class AppSerializerMixin(serializers.Serializer):
attrs = MethodSerializer() attrs = MethodSerializer()
@property
def app(self):
if isinstance(self.instance, models.Application):
instance = self.instance
else:
instance = None
return instance
def get_attrs_serializer(self): def get_attrs_serializer(self):
default_serializer = serializers.Serializer(read_only=True) default_serializer = serializers.Serializer(read_only=True)
if isinstance(self.instance, models.Application): instance = self.app
_type = self.instance.type if instance:
_category = self.instance.category _type = instance.type
_category = instance.category
else: else:
_type = self.context['request'].query_params.get('type') _type = self.context['request'].query_params.get('type')
_category = self.context['request'].query_params.get('category') _category = self.context['request'].query_params.get('category')
if _type: if _type:
serializer_class = type_serializer_classes_mapping.get(_type) if isinstance(self, AppAccountSecretSerializer):
serializer_class = type_secret_serializer_classes_mapping.get(_type)
else:
serializer_class = type_serializer_classes_mapping.get(_type)
elif _category: elif _category:
serializer_class = category_serializer_classes_mapping.get(_category) serializer_class = category_serializer_classes_mapping.get(_category)
else: else:
@ -84,11 +95,13 @@ class MiniAppSerializer(serializers.ModelSerializer):
fields = AppSerializer.Meta.fields_mini fields = AppSerializer.Meta.fields_mini
class AppAccountSerializer(AuthSerializerMixin, BulkOrgResourceModelSerializer): class AppAccountSerializer(AppSerializerMixin, AuthSerializerMixin, BulkOrgResourceModelSerializer):
category = serializers.ChoiceField(label=_('Category'), choices=const.AppCategory.choices, read_only=True) category = serializers.ChoiceField(label=_('Category'), choices=const.AppCategory.choices, read_only=True)
category_display = serializers.SerializerMethodField(label=_('Category display')) category_display = serializers.SerializerMethodField(label=_('Category display'))
type = serializers.ChoiceField(label=_('Type'), choices=const.AppType.choices, read_only=True) type = serializers.ChoiceField(label=_('Type'), choices=const.AppType.choices, read_only=True)
type_display = serializers.SerializerMethodField(label=_('Type display')) type_display = serializers.SerializerMethodField(label=_('Type display'))
date_created = serializers.DateTimeField(label=_('Date created'), format="%Y/%m/%d %H:%M:%S", read_only=True)
date_updated = serializers.DateTimeField(label=_('Date updated'), format="%Y/%m/%d %H:%M:%S", read_only=True)
category_mapper = dict(const.AppCategory.choices) category_mapper = dict(const.AppCategory.choices)
type_mapper = dict(const.AppType.choices) type_mapper = dict(const.AppType.choices)
@ -96,10 +109,11 @@ class AppAccountSerializer(AuthSerializerMixin, BulkOrgResourceModelSerializer):
class Meta: class Meta:
model = models.Account model = models.Account
fields_mini = ['id', 'username', 'version'] fields_mini = ['id', 'username', 'version']
fields_write_only = ['password', 'private_key', 'passphrase'] fields_write_only = ['password', 'private_key', 'public_key', 'passphrase']
fields_other = ['date_created', 'date_updated']
fields_fk = ['systemuser', 'systemuser_display', 'app', 'app_display'] fields_fk = ['systemuser', 'systemuser_display', 'app', 'app_display']
fields = fields_mini + fields_fk + fields_write_only + [ fields = fields_mini + fields_fk + fields_write_only + fields_other + [
'type', 'type_display', 'category', 'category_display', 'type', 'type_display', 'category', 'category_display', 'attrs'
] ]
extra_kwargs = { extra_kwargs = {
'username': {'default': '', 'required': False}, 'username': {'default': '', 'required': False},
@ -112,6 +126,14 @@ class AppAccountSerializer(AuthSerializerMixin, BulkOrgResourceModelSerializer):
'ignore_conflicts': True 'ignore_conflicts': True
} }
@property
def app(self):
if isinstance(self.instance, models.Account):
instance = self.instance.app
else:
instance = None
return instance
def get_category_display(self, obj): def get_category_display(self, obj):
return self.category_mapper.get(obj.category) return self.category_mapper.get(obj.category)
@ -131,6 +153,10 @@ class AppAccountSerializer(AuthSerializerMixin, BulkOrgResourceModelSerializer):
class AppAccountSecretSerializer(AppAccountSerializer): class AppAccountSecretSerializer(AppAccountSerializer):
class Meta(AppAccountSerializer.Meta): class Meta(AppAccountSerializer.Meta):
fields_backup = [
'id', 'app_display', 'attrs', 'username', 'password', 'private_key',
'public_key', 'date_created', 'date_updated', 'version'
]
extra_kwargs = { extra_kwargs = {
'password': {'write_only': False}, 'password': {'write_only': False},
'private_key': {'write_only': False}, 'private_key': {'write_only': False},

View File

@ -1,7 +1,6 @@
from rest_framework import serializers from rest_framework import serializers
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
__all__ = ['CloudSerializer'] __all__ = ['CloudSerializer']

View File

@ -10,7 +10,6 @@ from assets.models import Asset
logger = get_logger(__file__) logger = get_logger(__file__)
__all__ = ['RemoteAppSerializer'] __all__ = ['RemoteAppSerializer']

View File

@ -3,8 +3,7 @@ from rest_framework import serializers
from ..application_category import RemoteAppSerializer from ..application_category import RemoteAppSerializer
__all__ = ['ChromeSerializer', 'ChromeSecretSerializer']
__all__ = ['ChromeSerializer']
class ChromeSerializer(RemoteAppSerializer): class ChromeSerializer(RemoteAppSerializer):
@ -17,10 +16,16 @@ class ChromeSerializer(RemoteAppSerializer):
max_length=128, allow_blank=True, required=False, label=_('Target URL'), allow_null=True, max_length=128, allow_blank=True, required=False, label=_('Target URL'), allow_null=True,
) )
chrome_username = serializers.CharField( chrome_username = serializers.CharField(
max_length=128, allow_blank=True, required=False, label=_('Username'), allow_null=True, max_length=128, allow_blank=True, required=False, label=_('Chrome username'), allow_null=True,
) )
chrome_password = serializers.CharField( 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=_('Chrome password'),
allow_null=True allow_null=True
) )
class ChromeSecretSerializer(ChromeSerializer):
chrome_password = serializers.CharField(
max_length=128, allow_blank=True, required=False, read_only=True, label=_('Chrome password'),
allow_null=True
)

View File

@ -1,11 +1,9 @@
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
from rest_framework import serializers from rest_framework import serializers
from ..application_category import RemoteAppSerializer from ..application_category import RemoteAppSerializer
__all__ = ['CustomSerializer', 'CustomSecretSerializer']
__all__ = ['CustomSerializer']
class CustomSerializer(RemoteAppSerializer): class CustomSerializer(RemoteAppSerializer):
@ -18,10 +16,17 @@ class CustomSerializer(RemoteAppSerializer):
allow_null=True, allow_null=True,
) )
custom_username = serializers.CharField( custom_username = serializers.CharField(
max_length=128, allow_blank=True, required=False, label=_('Username'), max_length=128, allow_blank=True, required=False, label=_('Custom Username'),
allow_null=True, allow_null=True,
) )
custom_password = serializers.CharField( 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=_('Custom password'),
allow_null=True,
)
class CustomSecretSerializer(RemoteAppSerializer):
custom_password = serializers.CharField(
max_length=128, allow_blank=True, required=False, read_only=True, label=_('Custom password'),
allow_null=True, allow_null=True,
) )

View File

@ -1,6 +1,5 @@
from ..application_category import CloudSerializer from ..application_category import CloudSerializer
__all__ = ['K8SSerializer'] __all__ = ['K8SSerializer']

View File

@ -1,6 +1,5 @@
from .mysql import MySQLSerializer from .mysql import MySQLSerializer
__all__ = ['MariaDBSerializer'] __all__ = ['MariaDBSerializer']

View File

@ -3,13 +3,9 @@ from django.utils.translation import ugettext_lazy as _
from ..application_category import DBSerializer from ..application_category import DBSerializer
__all__ = ['MySQLSerializer'] __all__ = ['MySQLSerializer']
class MySQLSerializer(DBSerializer): class MySQLSerializer(DBSerializer):
port = serializers.IntegerField(default=3306, label=_('Port'), allow_null=True) port = serializers.IntegerField(default=3306, label=_('Port'), allow_null=True)

View File

@ -3,8 +3,7 @@ from rest_framework import serializers
from ..application_category import RemoteAppSerializer from ..application_category import RemoteAppSerializer
__all__ = ['MySQLWorkbenchSerializer', 'MySQLWorkbenchSecretSerializer']
__all__ = ['MySQLWorkbenchSerializer']
class MySQLWorkbenchSerializer(RemoteAppSerializer): class MySQLWorkbenchSerializer(RemoteAppSerializer):
@ -27,10 +26,17 @@ class MySQLWorkbenchSerializer(RemoteAppSerializer):
allow_null=True, allow_null=True,
) )
mysql_workbench_username = serializers.CharField( mysql_workbench_username = serializers.CharField(
max_length=128, allow_blank=True, required=False, label=_('Username'), max_length=128, allow_blank=True, required=False, label=_('Mysql workbench username'),
allow_null=True, allow_null=True,
) )
mysql_workbench_password = serializers.CharField( 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=_('Mysql workbench password'),
allow_null=True,
)
class MySQLWorkbenchSecretSerializer(RemoteAppSerializer):
mysql_workbench_password = serializers.CharField(
max_length=128, allow_blank=True, required=False, read_only=True, label=_('Mysql workbench password'),
allow_null=True, allow_null=True,
) )

View File

@ -3,10 +3,8 @@ from django.utils.translation import ugettext_lazy as _
from ..application_category import DBSerializer from ..application_category import DBSerializer
__all__ = ['OracleSerializer'] __all__ = ['OracleSerializer']
class OracleSerializer(DBSerializer): class OracleSerializer(DBSerializer):
port = serializers.IntegerField(default=1521, label=_('Port'), allow_null=True) port = serializers.IntegerField(default=1521, label=_('Port'), allow_null=True)

View File

@ -3,10 +3,8 @@ from django.utils.translation import ugettext_lazy as _
from ..application_category import DBSerializer from ..application_category import DBSerializer
__all__ = ['PostgreSerializer'] __all__ = ['PostgreSerializer']
class PostgreSerializer(DBSerializer): class PostgreSerializer(DBSerializer):
port = serializers.IntegerField(default=5432, label=_('Port'), allow_null=True) port = serializers.IntegerField(default=5432, label=_('Port'), allow_null=True)

View File

@ -3,13 +3,9 @@ from django.utils.translation import ugettext_lazy as _
from ..application_category import DBSerializer from ..application_category import DBSerializer
__all__ = ['RedisSerializer'] __all__ = ['RedisSerializer']
class RedisSerializer(DBSerializer): class RedisSerializer(DBSerializer):
port = serializers.IntegerField(default=6379, label=_('Port'), allow_null=True) port = serializers.IntegerField(default=6379, label=_('Port'), allow_null=True)

View File

@ -3,7 +3,6 @@ from django.utils.translation import ugettext_lazy as _
from ..application_category import DBSerializer from ..application_category import DBSerializer
__all__ = ['SQLServerSerializer'] __all__ = ['SQLServerSerializer']

View File

@ -3,8 +3,7 @@ from rest_framework import serializers
from ..application_category import RemoteAppSerializer from ..application_category import RemoteAppSerializer
__all__ = ['VMwareClientSerializer', 'VMwareClientSecretSerializer']
__all__ = ['VMwareClientSerializer']
class VMwareClientSerializer(RemoteAppSerializer): class VMwareClientSerializer(RemoteAppSerializer):
@ -23,10 +22,17 @@ class VMwareClientSerializer(RemoteAppSerializer):
allow_null=True allow_null=True
) )
vmware_username = serializers.CharField( vmware_username = serializers.CharField(
max_length=128, allow_blank=True, required=False, label=_('Username'), max_length=128, allow_blank=True, required=False, label=_('Vmware username'),
allow_null=True allow_null=True
) )
vmware_password = serializers.CharField( 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=_('Vmware password'),
allow_null=True
)
class VMwareClientSecretSerializer(RemoteAppSerializer):
vmware_password = serializers.CharField(
max_length=128, allow_blank=True, required=False, read_only=True, label=_('Vmware password'),
allow_null=True allow_null=True
) )

View File

@ -1,15 +1,15 @@
from rest_framework import serializers import copy
from applications import const from applications import const
from . import application_category, application_type from . import application_category, application_type
__all__ = [ __all__ = [
'category_serializer_classes_mapping', 'category_serializer_classes_mapping',
'type_serializer_classes_mapping', 'type_serializer_classes_mapping',
'get_serializer_class_by_application_type', 'get_serializer_class_by_application_type',
'type_secret_serializer_classes_mapping'
] ]
# define `attrs` field `category serializers mapping` # define `attrs` field `category serializers mapping`
# --------------------------------------------------- # ---------------------------------------------------
@ -30,15 +30,32 @@ type_serializer_classes_mapping = {
const.AppType.oracle.value: application_type.OracleSerializer, const.AppType.oracle.value: application_type.OracleSerializer,
const.AppType.pgsql.value: application_type.PostgreSerializer, const.AppType.pgsql.value: application_type.PostgreSerializer,
const.AppType.sqlserver.value: application_type.SQLServerSerializer, const.AppType.sqlserver.value: application_type.SQLServerSerializer,
# remote-app
const.AppType.chrome.value: application_type.ChromeSerializer,
const.AppType.mysql_workbench.value: application_type.MySQLWorkbenchSerializer,
const.AppType.vmware_client.value: application_type.VMwareClientSerializer,
const.AppType.custom.value: application_type.CustomSerializer,
# cloud # cloud
const.AppType.k8s.value: application_type.K8SSerializer const.AppType.k8s.value: application_type.K8SSerializer
} }
remote_app_serializer_classes_mapping = {
# remote-app
const.AppType.chrome.value: application_type.ChromeSerializer,
const.AppType.mysql_workbench.value: application_type.MySQLWorkbenchSerializer,
const.AppType.vmware_client.value: application_type.VMwareClientSerializer,
const.AppType.custom.value: application_type.CustomSerializer
}
type_serializer_classes_mapping.update(remote_app_serializer_classes_mapping)
remote_app_secret_serializer_classes_mapping = {
# remote-app
const.AppType.chrome.value: application_type.ChromeSecretSerializer,
const.AppType.mysql_workbench.value: application_type.MySQLWorkbenchSecretSerializer,
const.AppType.vmware_client.value: application_type.VMwareClientSecretSerializer,
const.AppType.custom.value: application_type.CustomSecretSerializer
}
type_secret_serializer_classes_mapping = copy.deepcopy(type_serializer_classes_mapping)
type_secret_serializer_classes_mapping.update(remote_app_secret_serializer_classes_mapping)
def get_serializer_class_by_application_type(_application_type): def get_serializer_class_by_application_type(_application_type):
return type_serializer_classes_mapping.get(_application_type) return type_serializer_classes_mapping.get(_application_type)

View File

@ -64,9 +64,7 @@ class AccountViewSet(OrgBulkModelViewSet):
permission_classes = (IsOrgAdmin,) permission_classes = (IsOrgAdmin,)
def get_queryset(self): def get_queryset(self):
queryset = super().get_queryset() \ queryset = AuthBook.get_queryset()
.annotate(ip=F('asset__ip')) \
.annotate(hostname=F('asset__hostname'))
return queryset return queryset
@action(methods=['post'], detail=True, url_path='verify') @action(methods=['post'], detail=True, url_path='verify')

View File

@ -2,6 +2,7 @@
# #
from django.db import models from django.db import models
from django.db.models import F
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
from simple_history.models import HistoricalRecords from simple_history.models import HistoricalRecords
@ -116,6 +117,15 @@ class AuthBook(BaseUser, AbsConnectivity):
self.asset.save() self.asset.save()
logger.debug('Update asset admin user: {} {}'.format(self.asset, self.systemuser)) logger.debug('Update asset admin user: {} {}'.format(self.asset, self.systemuser))
@classmethod
def get_queryset(cls):
queryset = cls.objects.all() \
.annotate(ip=F('asset__ip')) \
.annotate(hostname=F('asset__hostname')) \
.annotate(platform=F('asset__platform__name')) \
.annotate(protocols=F('asset__protocols'))
return queryset
def __str__(self): def __str__(self):
return self.smart_name return self.smart_name

View File

@ -11,10 +11,18 @@ from .utils import validate_password_contains_left_double_curly_bracket
class AccountSerializer(AuthSerializerMixin, BulkOrgResourceModelSerializer): class AccountSerializer(AuthSerializerMixin, BulkOrgResourceModelSerializer):
ip = serializers.ReadOnlyField(label=_("IP")) ip = serializers.ReadOnlyField(label=_("IP"))
hostname = serializers.ReadOnlyField(label=_("Hostname")) hostname = serializers.ReadOnlyField(label=_("Hostname"))
platform = serializers.ReadOnlyField(label=_("Platform"))
protocols = serializers.SerializerMethodField(label=_("Protocols"))
date_created = serializers.DateTimeField(
label=_('Date created'), format="%Y/%m/%d %H:%M:%S", read_only=True
)
date_updated = serializers.DateTimeField(
label=_('Date updated'), format="%Y/%m/%d %H:%M:%S", read_only=True
)
class Meta: class Meta:
model = AuthBook model = AuthBook
fields_mini = ['id', 'username', 'ip', 'hostname', 'version'] fields_mini = ['id', 'username', 'ip', 'hostname', 'platform', 'protocols', 'version']
fields_write_only = ['password', 'private_key', "public_key", 'passphrase'] fields_write_only = ['password', 'private_key', "public_key", 'passphrase']
fields_other = ['date_created', 'date_updated', 'connectivity', 'date_verified', 'comment'] fields_other = ['date_created', 'date_updated', 'connectivity', 'date_verified', 'comment']
fields_small = fields_mini + fields_write_only + fields_other fields_small = fields_mini + fields_write_only + fields_other
@ -32,6 +40,9 @@ class AccountSerializer(AuthSerializerMixin, BulkOrgResourceModelSerializer):
} }
ref_name = 'AssetAccountSerializer' ref_name = 'AssetAccountSerializer'
def get_protocols(self, v):
return v.protocols.replace(' ', ', ')
@classmethod @classmethod
def setup_eager_loading(cls, queryset): def setup_eager_loading(cls, queryset):
""" Perform necessary eager loading of data. """ """ Perform necessary eager loading of data. """
@ -45,6 +56,10 @@ class AccountSerializer(AuthSerializerMixin, BulkOrgResourceModelSerializer):
class AccountSecretSerializer(AccountSerializer): class AccountSecretSerializer(AccountSerializer):
class Meta(AccountSerializer.Meta): class Meta(AccountSerializer.Meta):
fields_backup = [
'hostname', 'ip', 'platform', 'protocols', 'username', 'password',
'private_key', 'public_key', 'date_created', 'date_updated', 'version'
]
extra_kwargs = { extra_kwargs = {
'password': {'write_only': False}, 'password': {'write_only': False},
'private_key': {'write_only': False}, 'private_key': {'write_only': False},

View File

@ -5,8 +5,6 @@ from django.core.validators import RegexValidator
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
from orgs.mixins.serializers import BulkOrgResourceModelSerializer from orgs.mixins.serializers import BulkOrgResourceModelSerializer
from users.models import User, UserGroup
from perms.models import AssetPermission
from ..models import Asset, Node, Platform, SystemUser from ..models import Asset, Node, Platform, SystemUser
__all__ = [ __all__ = [

View File

@ -1,15 +1,18 @@
import os import os
import time import time
import pandas as pd import pandas as pd
from collections import defaultdict from collections import defaultdict, OrderedDict
from django.conf import settings from django.conf import settings
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
from rest_framework import serializers
from assets.models import AuthBook, Asset, BaseUser, ProtocolsMixin from assets.models import AuthBook
from assets.serializers import AccountSecretSerializer
from assets.notifications import AccountBackupExecutionTaskMsg from assets.notifications import AccountBackupExecutionTaskMsg
from applications.models import Account, Application from applications.models import Account
from applications.const import AppType from applications.const import AppType
from applications.serializers import AppAccountSecretSerializer
from users.models import User from users.models import User
from common.utils import get_logger from common.utils import get_logger
from common.utils.timezone import local_now_display from common.utils.timezone import local_now_display
@ -20,7 +23,46 @@ logger = get_logger(__file__)
PATH = os.path.join(os.path.dirname(settings.BASE_DIR), 'tmp') PATH = os.path.join(os.path.dirname(settings.BASE_DIR), 'tmp')
class AssetAccountHandler: class BaseAccountHandler:
@classmethod
def unpack_data(cls, serializer_data, data=None):
if data is None:
data = {}
for k, v in serializer_data.items():
if isinstance(v, OrderedDict):
cls.unpack_data(v, data)
else:
data[k] = v
return data
@classmethod
def get_header_fields(cls, serializer: serializers.Serializer):
try:
backup_fields = getattr(serializer, 'Meta').fields_backup
except AttributeError:
backup_fields = serializer.fields.keys()
header_fields = {}
for field in backup_fields:
v = serializer.fields[field]
if isinstance(v, serializers.Serializer):
_fields = cls.get_header_fields(v)
header_fields.update(_fields)
else:
header_fields[field] = v.label
return header_fields
@classmethod
def create_row(cls, account, serializer_cls):
serializer = serializer_cls(account)
data = cls.unpack_data(serializer.data)
header_fields = cls.get_header_fields(serializer)
row_dict = {}
for field, header_name in header_fields.items():
row_dict[header_name] = data[field]
return row_dict
class AssetAccountHandler(BaseAccountHandler):
@staticmethod @staticmethod
def get_filename(plan_name): def get_filename(plan_name):
filename = os.path.join( filename = os.path.join(
@ -28,32 +70,24 @@ class AssetAccountHandler:
) )
return filename return filename
@staticmethod @classmethod
def create_df(): def create_df(cls):
df_dict = defaultdict(list) df_dict = defaultdict(list)
label_key = AuthBook._meta.verbose_name sheet_name = AuthBook._meta.verbose_name
accounts = AuthBook.objects.all().prefetch_related('systemuser', 'asset') accounts = AuthBook.get_queryset()
for account in accounts: for account in accounts:
account.load_auth() account.load_auth()
protocol = account.asset.protocol row = cls.create_row(account, AccountSecretSerializer)
protocol_label = getattr(ProtocolsMixin.Protocol, protocol).label df_dict[sheet_name].append(row)
row = {
getattr(Asset, 'hostname').field.verbose_name: account.asset.hostname,
getattr(Asset, 'ip').field.verbose_name: account.asset.ip,
}
secret_row = AccountBackupHandler.create_secret_row(account)
row.update(secret_row)
row.update({
getattr(Asset, 'protocol').field.verbose_name: protocol_label,
getattr(AuthBook, 'version').field.verbose_name: account.version
})
df_dict[label_key].append(row)
for k, v in df_dict.items(): for k, v in df_dict.items():
df_dict[k] = pd.DataFrame(v) df_dict[k] = pd.DataFrame(v)
logger.info('\n\033[33m- 共收集{}条资产账号\033[0m'.format(accounts.count()))
return df_dict return df_dict
class AppAccountHandler: class AppAccountHandler(BaseAccountHandler):
@staticmethod @staticmethod
def get_filename(plan_name): def get_filename(plan_name):
filename = os.path.join( filename = os.path.join(
@ -61,33 +95,23 @@ class AppAccountHandler:
) )
return filename return filename
@staticmethod @classmethod
def create_df(): def create_df(cls):
df_dict = defaultdict(list) df_dict = defaultdict(list)
accounts = Account.objects.all().prefetch_related('systemuser', 'app') accounts = Account.get_queryset()
for account in accounts: for account in accounts:
account.load_auth() account.load_auth()
app_type = account.app.type app_type = account.type
if app_type == 'postgresql': sheet_name = AppType.get_label(app_type)
label_key = getattr(AppType, 'pgsql').label row = cls.create_row(account, AppAccountSecretSerializer)
else: df_dict[sheet_name].append(row)
label_key = getattr(AppType, app_type).label
row = {
getattr(Application, 'name').field.verbose_name: account.app.name,
getattr(Application, 'attrs').field.verbose_name: account.app.attrs
}
secret_row = AccountBackupHandler.create_secret_row(account)
row.update(secret_row)
row.update({
getattr(Account, 'version').field.verbose_name: account.version
})
df_dict[label_key].append(row)
for k, v in df_dict.items(): for k, v in df_dict.items():
df_dict[k] = pd.DataFrame(v) df_dict[k] = pd.DataFrame(v)
logger.info('\n\033[33m- 共收集{}条应用账号\033[0m'.format(accounts.count()))
return df_dict return df_dict
HANDLER_MAP = { handler_map = {
'asset': AssetAccountHandler, 'asset': AssetAccountHandler,
'application': AppAccountHandler 'application': AppAccountHandler
} }
@ -102,31 +126,37 @@ class AccountBackupHandler:
def create_excel(self): def create_excel(self):
logger.info( logger.info(
'\n' '\n'
'\033[32m>>> 正在生成资产应用相关备份信息文件\033[0m' '\033[32m>>> 正在生成资产应用相关备份信息文件\033[0m'
'' ''
) )
# Print task start date # Print task start date
time_start = time.time() time_start = time.time()
info = {} files = []
for account_type in self.execution.types: for account_type in self.execution.types:
if account_type in HANDLER_MAP: handler = handler_map.get(account_type)
account_handler = HANDLER_MAP[account_type] if not handler:
df = account_handler.create_df() continue
filename = account_handler.get_filename(self.plan_name)
info[filename] = df df_dict = handler.create_df()
for filename, df_dict in info.items(): if not df_dict:
continue
filename = handler.get_filename(self.plan_name)
with pd.ExcelWriter(filename) as w: with pd.ExcelWriter(filename) as w:
for sheet, df in df_dict.items(): for sheet, df in df_dict.items():
sheet = sheet.replace(' ', '-') sheet = sheet.replace(' ', '-')
getattr(df, 'to_excel')(w, sheet_name=sheet, index=False) getattr(df, 'to_excel')(w, sheet_name=sheet, index=False)
files.append(filename)
timedelta = round((time.time() - time_start), 2) timedelta = round((time.time() - time_start), 2)
logger.info('步骤完成: 用时 {}s'.format(timedelta)) logger.info('步骤完成: 用时 {}s'.format(timedelta))
return list(info.keys()) return files
def send_backup_mail(self, files): def send_backup_mail(self, files):
recipients = self.execution.plan_snapshot.get('recipients') recipients = self.execution.plan_snapshot.get('recipients')
if not recipients: if not recipients:
return return
if not files:
return
recipients = User.objects.filter(id__in=list(recipients)) recipients = User.objects.filter(id__in=list(recipients))
logger.info( logger.info(
'\n' '\n'
@ -191,13 +221,3 @@ class AccountBackupHandler:
logger.info('\n任务结束: {}'.format(local_now_display())) logger.info('\n任务结束: {}'.format(local_now_display()))
timedelta = round((time.time() - time_start), 2) timedelta = round((time.time() - time_start), 2)
logger.info('用时: {}'.format(timedelta)) logger.info('用时: {}'.format(timedelta))
@staticmethod
def create_secret_row(instance):
row = {
getattr(BaseUser, 'username').field.verbose_name: instance.username,
getattr(BaseUser, 'password').field.verbose_name: instance.password,
getattr(BaseUser, 'private_key').field.verbose_name: instance.private_key,
getattr(BaseUser, 'public_key').field.verbose_name: instance.public_key
}
return row

View File

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1 version https://git-lfs.github.com/spec/v1
oid sha256:65ae747dcbddab2bbf9238b0ee589037805c9cf04a6c3a2e312d4c6c5e486b2d oid sha256:041711683ed0cfbf9ffd58f402f0acb98f77a1edde5f4582314a2568d539212c
size 96320 size 96641

View File

@ -7,7 +7,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: JumpServer 0.3.3\n" "Project-Id-Version: JumpServer 0.3.3\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2022-01-13 16:57+0800\n" "POT-Creation-Date: 2022-01-15 22:47+0800\n"
"PO-Revision-Date: 2021-05-20 10:54+0800\n" "PO-Revision-Date: 2021-05-20 10:54+0800\n"
"Last-Translator: ibuler <ibuler@qq.com>\n" "Last-Translator: ibuler <ibuler@qq.com>\n"
"Language-Team: JumpServer team<ibuler@qq.com>\n" "Language-Team: JumpServer team<ibuler@qq.com>\n"
@ -123,14 +123,14 @@ msgid "Login acl"
msgstr "登录访问控制" msgstr "登录访问控制"
#: acls/models/login_asset_acl.py:21 #: acls/models/login_asset_acl.py:21
#: applications/serializers/application.py:108 #: applications/serializers/application.py:122
#: applications/serializers/application.py:139 #: applications/serializers/application.py:165
msgid "System User" msgid "System User"
msgstr "系统用户" msgstr "系统用户"
#: acls/models/login_asset_acl.py:22 #: acls/models/login_asset_acl.py:22
#: applications/serializers/attrs/application_category/remote_app.py:37 #: applications/serializers/attrs/application_category/remote_app.py:36
#: assets/models/asset.py:356 assets/models/authbook.py:18 #: assets/models/asset.py:356 assets/models/authbook.py:19
#: assets/models/backup.py:31 assets/models/cmd_filter.py:34 #: assets/models/backup.py:31 assets/models/cmd_filter.py:34
#: assets/models/gathered_user.py:14 assets/serializers/system_user.py:264 #: assets/models/gathered_user.py:14 assets/serializers/system_user.py:264
#: audits/models.py:38 perms/models/asset_permission.py:24 #: audits/models.py:38 perms/models/asset_permission.py:24
@ -157,13 +157,9 @@ msgid "Format for comma-delimited string, with * indicating a match all. "
msgstr "格式为逗号分隔的字符串, * 表示匹配所有. " msgstr "格式为逗号分隔的字符串, * 表示匹配所有. "
#: acls/serializers/login_acl.py:15 acls/serializers/login_asset_acl.py:17 #: acls/serializers/login_acl.py:15 acls/serializers/login_asset_acl.py:17
#: acls/serializers/login_asset_acl.py:51 #: acls/serializers/login_asset_acl.py:51 assets/models/base.py:176
#: applications/serializers/attrs/application_type/chrome.py:20 #: assets/models/gathered_user.py:15 audits/models.py:105
#: applications/serializers/attrs/application_type/custom.py:21 #: authentication/forms.py:15 authentication/forms.py:17
#: applications/serializers/attrs/application_type/mysql_workbench.py:30
#: applications/serializers/attrs/application_type/vmware_client.py:26
#: assets/models/base.py:176 assets/models/gathered_user.py:15
#: audits/models.py:105 authentication/forms.py:15 authentication/forms.py:17
#: authentication/templates/authentication/_msg_different_city.html:9 #: authentication/templates/authentication/_msg_different_city.html:9
#: authentication/templates/authentication/_msg_oauth_bind.html:9 #: authentication/templates/authentication/_msg_oauth_bind.html:9
#: ops/models/adhoc.py:159 users/forms/profile.py:31 users/models/user.py:547 #: ops/models/adhoc.py:159 users/forms/profile.py:31 users/models/user.py:547
@ -185,7 +181,7 @@ msgstr ""
"10.1.1.1-10.1.1.20, 2001:db8:2de::e13, 2001:db8:1a:1110::/64 (支持网域)" "10.1.1.1-10.1.1.20, 2001:db8:2de::e13, 2001:db8:1a:1110::/64 (支持网域)"
#: acls/serializers/login_asset_acl.py:31 acls/serializers/rules/rules.py:33 #: acls/serializers/login_asset_acl.py:31 acls/serializers/rules/rules.py:33
#: applications/serializers/attrs/application_type/mysql_workbench.py:18 #: applications/serializers/attrs/application_type/mysql_workbench.py:17
#: assets/models/asset.py:211 assets/models/domain.py:61 #: assets/models/asset.py:211 assets/models/domain.py:61
#: assets/serializers/account.py:12 #: assets/serializers/account.py:12
#: authentication/templates/authentication/_msg_oauth_bind.html:12 #: authentication/templates/authentication/_msg_oauth_bind.html:12
@ -252,9 +248,9 @@ msgstr "时段"
msgid "My applications" msgid "My applications"
msgstr "我的应用" msgstr "我的应用"
#: applications/const.py:8 applications/models/account.py:10 #: applications/const.py:8 applications/models/account.py:12
#: applications/serializers/attrs/application_category/db.py:14 #: applications/serializers/attrs/application_category/db.py:14
#: applications/serializers/attrs/application_type/mysql_workbench.py:26 #: applications/serializers/attrs/application_type/mysql_workbench.py:25
#: xpack/plugins/change_auth_plan/models/app.py:32 #: xpack/plugins/change_auth_plan/models/app.py:32
msgid "Database" msgid "Database"
msgstr "数据库" msgstr "数据库"
@ -267,7 +263,7 @@ msgstr "远程应用"
msgid "Custom" msgid "Custom"
msgstr "自定义" msgstr "自定义"
#: applications/models/account.py:11 assets/models/authbook.py:19 #: applications/models/account.py:15 assets/models/authbook.py:20
#: assets/models/cmd_filter.py:38 assets/models/user.py:302 audits/models.py:39 #: assets/models/cmd_filter.py:38 assets/models/user.py:302 audits/models.py:39
#: perms/models/application_permission.py:32 #: perms/models/application_permission.py:32
#: perms/models/asset_permission.py:26 templates/_nav.html:45 #: perms/models/asset_permission.py:26 templates/_nav.html:45
@ -284,12 +280,12 @@ msgstr "自定义"
msgid "System user" msgid "System user"
msgstr "系统用户" msgstr "系统用户"
#: applications/models/account.py:12 assets/models/authbook.py:20 #: applications/models/account.py:17 assets/models/authbook.py:21
#: settings/serializers/auth/cas.py:15 #: settings/serializers/auth/cas.py:15
msgid "Version" msgid "Version"
msgstr "版本" msgstr "版本"
#: applications/models/account.py:18 xpack/plugins/cloud/models.py:82 #: applications/models/account.py:23 xpack/plugins/cloud/models.py:82
#: xpack/plugins/cloud/serializers/task.py:66 #: xpack/plugins/cloud/serializers/task.py:66
msgid "Account" msgid "Account"
msgstr "账户" msgstr "账户"
@ -299,7 +295,7 @@ msgid "Applications"
msgstr "应用管理" msgstr "应用管理"
#: applications/models/application.py:204 #: applications/models/application.py:204
#: applications/serializers/application.py:88 assets/models/label.py:21 #: applications/serializers/application.py:99 assets/models/label.py:21
#: perms/models/application_permission.py:20 #: perms/models/application_permission.py:20
#: perms/serializers/application/user_permission.py:33 #: perms/serializers/application/user_permission.py:33
#: tickets/serializers/ticket/meta/ticket_type/apply_application.py:22 #: tickets/serializers/ticket/meta/ticket_type/apply_application.py:22
@ -308,7 +304,7 @@ msgid "Category"
msgstr "类别" msgstr "类别"
#: applications/models/application.py:207 #: applications/models/application.py:207
#: applications/serializers/application.py:90 assets/models/backup.py:49 #: applications/serializers/application.py:101 assets/models/backup.py:49
#: assets/models/cmd_filter.py:76 assets/models/user.py:210 #: assets/models/cmd_filter.py:76 assets/models/user.py:210
#: perms/models/application_permission.py:23 #: perms/models/application_permission.py:23
#: perms/serializers/application/user_permission.py:34 #: perms/serializers/application/user_permission.py:34
@ -335,15 +331,15 @@ msgstr "属性"
msgid "Application" msgid "Application"
msgstr "应用程序" msgstr "应用程序"
#: applications/serializers/application.py:59 #: applications/serializers/application.py:70
#: applications/serializers/application.py:89 assets/serializers/label.py:13 #: applications/serializers/application.py:100 assets/serializers/label.py:13
#: perms/serializers/application/permission.py:18 #: perms/serializers/application/permission.py:18
#: tickets/serializers/ticket/meta/ticket_type/apply_application.py:26 #: tickets/serializers/ticket/meta/ticket_type/apply_application.py:26
msgid "Category display" msgid "Category display"
msgstr "类别名称" msgstr "类别名称"
#: applications/serializers/application.py:60 #: applications/serializers/application.py:71
#: applications/serializers/application.py:91 #: applications/serializers/application.py:102
#: assets/serializers/system_user.py:27 audits/serializers.py:29 #: assets/serializers/system_user.py:27 audits/serializers.py:29
#: perms/serializers/application/permission.py:19 #: perms/serializers/application/permission.py:19
#: tickets/serializers/ticket/meta/ticket_type/apply_application.py:33 #: tickets/serializers/ticket/meta/ticket_type/apply_application.py:33
@ -352,12 +348,31 @@ msgstr "类别名称"
msgid "Type display" msgid "Type display"
msgstr "类型名称" msgstr "类型名称"
#: applications/serializers/application.py:107 #: applications/serializers/application.py:103 assets/models/asset.py:231
#: applications/serializers/application.py:138 #: assets/models/base.py:181 assets/models/cluster.py:26
#: assets/models/domain.py:27 assets/models/gathered_user.py:19
#: assets/models/group.py:22 assets/models/label.py:25
#: assets/serializers/account.py:17 common/db/models.py:113
#: common/mixins/models.py:50 ops/models/adhoc.py:38 ops/models/command.py:29
#: orgs/models.py:26 orgs/models.py:435 perms/models/base.py:92
#: users/models/group.py:18 users/models/user.py:783
#: xpack/plugins/cloud/models.py:122
msgid "Date created"
msgstr "创建日期"
#: applications/serializers/application.py:104 assets/models/base.py:182
#: assets/models/gathered_user.py:20 assets/serializers/account.py:20
#: common/db/models.py:114 common/mixins/models.py:51 ops/models/adhoc.py:39
#: orgs/models.py:436
msgid "Date updated"
msgstr "更新日期"
#: applications/serializers/application.py:121
#: applications/serializers/application.py:164
msgid "Application display" msgid "Application display"
msgstr "应用名称" msgstr "应用名称"
#: applications/serializers/attrs/application_category/cloud.py:9 #: applications/serializers/attrs/application_category/cloud.py:8
#: assets/models/cluster.py:40 #: assets/models/cluster.py:40
msgid "Cluster" msgid "Cluster"
msgstr "集群" msgstr "集群"
@ -369,26 +384,26 @@ msgid "Host"
msgstr "主机" msgstr "主机"
#: applications/serializers/attrs/application_category/db.py:12 #: applications/serializers/attrs/application_category/db.py:12
#: applications/serializers/attrs/application_type/mysql.py:11 #: applications/serializers/attrs/application_type/mysql.py:10
#: applications/serializers/attrs/application_type/mysql_workbench.py:22 #: applications/serializers/attrs/application_type/mysql_workbench.py:21
#: applications/serializers/attrs/application_type/oracle.py:11 #: applications/serializers/attrs/application_type/oracle.py:10
#: applications/serializers/attrs/application_type/pgsql.py:11 #: applications/serializers/attrs/application_type/pgsql.py:10
#: applications/serializers/attrs/application_type/redis.py:11 #: applications/serializers/attrs/application_type/redis.py:10
#: applications/serializers/attrs/application_type/sqlserver.py:11 #: applications/serializers/attrs/application_type/sqlserver.py:10
#: assets/models/asset.py:215 assets/models/domain.py:62 #: assets/models/asset.py:215 assets/models/domain.py:62
#: settings/serializers/auth/radius.py:15 #: settings/serializers/auth/radius.py:15
#: xpack/plugins/cloud/serializers/account_attrs.py:69 #: xpack/plugins/cloud/serializers/account_attrs.py:69
msgid "Port" msgid "Port"
msgstr "端口" msgstr "端口"
#: applications/serializers/attrs/application_category/remote_app.py:40 #: applications/serializers/attrs/application_category/remote_app.py:39
#: applications/serializers/attrs/application_type/chrome.py:14 #: applications/serializers/attrs/application_type/chrome.py:13
#: applications/serializers/attrs/application_type/mysql_workbench.py:14 #: applications/serializers/attrs/application_type/mysql_workbench.py:13
#: applications/serializers/attrs/application_type/vmware_client.py:18 #: applications/serializers/attrs/application_type/vmware_client.py:17
msgid "Application path" msgid "Application path"
msgstr "应用路径" msgstr "应用路径"
#: applications/serializers/attrs/application_category/remote_app.py:45 #: applications/serializers/attrs/application_category/remote_app.py:44
#: assets/serializers/system_user.py:163 #: assets/serializers/system_user.py:163
#: xpack/plugins/change_auth_plan/serializers/asset.py:65 #: xpack/plugins/change_auth_plan/serializers/asset.py:65
#: xpack/plugins/change_auth_plan/serializers/asset.py:68 #: xpack/plugins/change_auth_plan/serializers/asset.py:68
@ -398,37 +413,58 @@ msgstr "应用路径"
msgid "This field is required." msgid "This field is required."
msgstr "该字段是必填项。" msgstr "该字段是必填项。"
#: applications/serializers/attrs/application_type/chrome.py:17 #: applications/serializers/attrs/application_type/chrome.py:16
#: applications/serializers/attrs/application_type/vmware_client.py:22 #: applications/serializers/attrs/application_type/vmware_client.py:21
msgid "Target URL" msgid "Target URL"
msgstr "目标URL" msgstr "目标URL"
#: applications/serializers/attrs/application_type/chrome.py:23 #: applications/serializers/attrs/application_type/chrome.py:19
#: applications/serializers/attrs/application_type/custom.py:25 msgid "Chrome username"
#: applications/serializers/attrs/application_type/mysql_workbench.py:34 msgstr "Chrome 用户名"
#: applications/serializers/attrs/application_type/vmware_client.py:30
#: assets/models/base.py:177 audits/signals_handler.py:65
#: authentication/forms.py:22
#: authentication/templates/authentication/login.html:151
#: settings/serializers/auth/ldap.py:44 users/forms/profile.py:21
#: users/templates/users/_msg_user_created.html:13
#: users/templates/users/user_password_update.html:43
#: users/templates/users/user_password_verify.html:18
#: xpack/plugins/change_auth_plan/models/base.py:39
#: xpack/plugins/change_auth_plan/models/base.py:118
#: xpack/plugins/change_auth_plan/models/base.py:193
#: xpack/plugins/cloud/serializers/account_attrs.py:24
msgid "Password"
msgstr "密码"
#: applications/serializers/attrs/application_type/custom.py:13 #: applications/serializers/attrs/application_type/chrome.py:22
#: applications/serializers/attrs/application_type/chrome.py:29
msgid "Chrome password"
msgstr "Chrome 密码"
#: applications/serializers/attrs/application_type/custom.py:11
msgid "Operating parameter" msgid "Operating parameter"
msgstr "运行参数" msgstr "运行参数"
#: applications/serializers/attrs/application_type/custom.py:17 #: applications/serializers/attrs/application_type/custom.py:15
msgid "Target url" msgid "Target url"
msgstr "目标URL" msgstr "目标URL"
#: applications/serializers/attrs/application_type/custom.py:19
#, fuzzy
#| msgid "Custom username"
msgid "Custom Username"
msgstr "自定义用户名"
#: applications/serializers/attrs/application_type/custom.py:23
#: applications/serializers/attrs/application_type/custom.py:30
#: xpack/plugins/change_auth_plan/models/base.py:24
msgid "Custom password"
msgstr "自定义密码"
#: applications/serializers/attrs/application_type/mysql_workbench.py:29
msgid "Mysql workbench username"
msgstr "Mysql 工作台 用户名"
#: applications/serializers/attrs/application_type/mysql_workbench.py:33
#: applications/serializers/attrs/application_type/mysql_workbench.py:40
msgid "Mysql workbench password"
msgstr "Mysql 工作台 密码"
#: applications/serializers/attrs/application_type/vmware_client.py:25
msgid "Vmware username"
msgstr "Vmware 用户名"
#: applications/serializers/attrs/application_type/vmware_client.py:29
#: applications/serializers/attrs/application_type/vmware_client.py:36
msgid "Vmware password"
msgstr "Vmware 密码"
#: assets/api/domain.py:52 #: assets/api/domain.py:52
msgid "Number required" msgid "Number required"
msgstr "需要为数字" msgstr "需要为数字"
@ -453,7 +489,7 @@ msgstr "基础"
msgid "Charset" msgid "Charset"
msgstr "编码" msgstr "编码"
#: assets/models/asset.py:142 assets/serializers/asset.py:178 #: assets/models/asset.py:142 assets/serializers/asset.py:176
#: tickets/models/ticket.py:54 #: tickets/models/ticket.py:54
msgid "Meta" msgid "Meta"
msgstr "元数据" msgstr "元数据"
@ -463,7 +499,8 @@ msgid "Internal"
msgstr "内部的" msgstr "内部的"
#: assets/models/asset.py:163 assets/models/asset.py:217 #: assets/models/asset.py:163 assets/models/asset.py:217
#: assets/serializers/asset.py:65 perms/serializers/asset/user_permission.py:43 #: assets/serializers/account.py:14 assets/serializers/asset.py:63
#: perms/serializers/asset/user_permission.py:43
msgid "Platform" msgid "Platform"
msgstr "系统平台" msgstr "系统平台"
@ -523,8 +560,8 @@ msgstr "系统架构"
msgid "Hostname raw" msgid "Hostname raw"
msgstr "主机名原始" msgstr "主机名原始"
#: assets/models/asset.py:216 assets/serializers/asset.py:67 #: assets/models/asset.py:216 assets/serializers/account.py:15
#: perms/serializers/asset/user_permission.py:41 #: assets/serializers/asset.py:65 perms/serializers/asset/user_permission.py:41
#: xpack/plugins/cloud/models.py:104 xpack/plugins/cloud/serializers/task.py:42 #: xpack/plugins/cloud/models.py:104 xpack/plugins/cloud/serializers/task.py:42
msgid "Protocols" msgid "Protocols"
msgstr "协议组" msgstr "协议组"
@ -569,17 +606,7 @@ msgstr "标签管理"
msgid "Created by" msgid "Created by"
msgstr "创建者" msgstr "创建者"
#: assets/models/asset.py:231 assets/models/base.py:181 #: assets/models/authbook.py:27
#: assets/models/cluster.py:26 assets/models/domain.py:27
#: assets/models/gathered_user.py:19 assets/models/group.py:22
#: assets/models/label.py:25 common/db/models.py:113 common/mixins/models.py:50
#: ops/models/adhoc.py:38 ops/models/command.py:29 orgs/models.py:26
#: orgs/models.py:435 perms/models/base.py:92 users/models/group.py:18
#: users/models/user.py:783 xpack/plugins/cloud/models.py:122
msgid "Date created"
msgstr "创建日期"
#: assets/models/authbook.py:26
msgid "AuthBook" msgid "AuthBook"
msgstr "账号" msgstr "账号"
@ -678,6 +705,20 @@ msgstr "可连接性"
msgid "Date verified" msgid "Date verified"
msgstr "校验日期" msgstr "校验日期"
#: assets/models/base.py:177 audits/signals_handler.py:65
#: authentication/forms.py:22
#: authentication/templates/authentication/login.html:151
#: settings/serializers/auth/ldap.py:44 users/forms/profile.py:21
#: users/templates/users/_msg_user_created.html:13
#: users/templates/users/user_password_update.html:43
#: users/templates/users/user_password_verify.html:18
#: xpack/plugins/change_auth_plan/models/base.py:39
#: xpack/plugins/change_auth_plan/models/base.py:118
#: xpack/plugins/change_auth_plan/models/base.py:193
#: xpack/plugins/cloud/serializers/account_attrs.py:24
msgid "Password"
msgstr "密码"
#: assets/models/base.py:178 xpack/plugins/change_auth_plan/models/asset.py:53 #: assets/models/base.py:178 xpack/plugins/change_auth_plan/models/asset.py:53
#: xpack/plugins/change_auth_plan/models/asset.py:130 #: xpack/plugins/change_auth_plan/models/asset.py:130
#: xpack/plugins/change_auth_plan/models/asset.py:206 #: xpack/plugins/change_auth_plan/models/asset.py:206
@ -690,12 +731,6 @@ msgstr "SSH密钥"
msgid "SSH public key" msgid "SSH public key"
msgstr "SSH公钥" msgstr "SSH公钥"
#: assets/models/base.py:182 assets/models/gathered_user.py:20
#: common/db/models.py:114 common/mixins/models.py:51 ops/models/adhoc.py:39
#: orgs/models.py:436
msgid "Date updated"
msgstr "更新日期"
#: assets/models/cluster.py:20 #: assets/models/cluster.py:20
msgid "Bandwidth" msgid "Bandwidth"
msgstr "带宽" msgstr "带宽"
@ -965,39 +1000,39 @@ msgstr ""
"{} - 账号备份任务已完成: 未设置加密密码 - 请前往个人信息 -> 文件加密密码中设" "{} - 账号备份任务已完成: 未设置加密密码 - 请前往个人信息 -> 文件加密密码中设"
"置加密密码" "置加密密码"
#: assets/serializers/account.py:31 assets/serializers/account.py:52 #: assets/serializers/account.py:39 assets/serializers/account.py:67
msgid "System user display" msgid "System user display"
msgstr "系统用户名称" msgstr "系统用户名称"
#: assets/serializers/asset.py:22 #: assets/serializers/asset.py:20
msgid "Protocol format should {}/{}" msgid "Protocol format should {}/{}"
msgstr "协议格式 {}/{}" msgstr "协议格式 {}/{}"
#: assets/serializers/asset.py:39 #: assets/serializers/asset.py:37
msgid "Protocol duplicate: {}" msgid "Protocol duplicate: {}"
msgstr "协议重复: {}" msgstr "协议重复: {}"
#: assets/serializers/asset.py:68 #: assets/serializers/asset.py:66
msgid "Domain name" msgid "Domain name"
msgstr "网域名称" msgstr "网域名称"
#: assets/serializers/asset.py:70 #: assets/serializers/asset.py:68
msgid "Nodes name" msgid "Nodes name"
msgstr "节点名称" msgstr "节点名称"
#: assets/serializers/asset.py:73 #: assets/serializers/asset.py:71
msgid "Labels name" msgid "Labels name"
msgstr "标签名称" msgstr "标签名称"
#: assets/serializers/asset.py:107 #: assets/serializers/asset.py:105
msgid "Hardware info" msgid "Hardware info"
msgstr "硬件信息" msgstr "硬件信息"
#: assets/serializers/asset.py:108 #: assets/serializers/asset.py:106
msgid "Admin user display" msgid "Admin user display"
msgstr "特权用户名称" msgstr "特权用户名称"
#: assets/serializers/asset.py:109 #: assets/serializers/asset.py:107
msgid "CPU info" msgid "CPU info"
msgstr "CPU信息" msgstr "CPU信息"
@ -1622,11 +1657,11 @@ msgstr "{ApplicationPermission} 移除 {SystemUser}"
msgid "Invalid token" msgid "Invalid token"
msgstr "无效的令牌" msgstr "无效的令牌"
#: authentication/api/mfa.py:50 #: authentication/api/mfa.py:63
msgid "Current user not support mfa type: {}" msgid "Current user not support mfa type: {}"
msgstr "当前用户不支持 MFA 类型: {}" msgstr "当前用户不支持 MFA 类型: {}"
#: authentication/api/mfa.py:97 #: authentication/api/mfa.py:110
msgid "Code is invalid, {}" msgid "Code is invalid, {}"
msgstr "验证码无效: {}" msgstr "验证码无效: {}"
@ -1795,15 +1830,15 @@ msgstr "该 时间段 不被允许登录"
msgid "SSO auth closed" msgid "SSO auth closed"
msgstr "SSO 认证关闭了" msgstr "SSO 认证关闭了"
#: authentication/errors.py:300 authentication/mixins.py:359 #: authentication/errors.py:300 authentication/mixins.py:364
msgid "Your password is too simple, please change it for security" msgid "Your password is too simple, please change it for security"
msgstr "你的密码过于简单,为了安全,请修改" msgstr "你的密码过于简单,为了安全,请修改"
#: authentication/errors.py:309 authentication/mixins.py:366 #: authentication/errors.py:309 authentication/mixins.py:371
msgid "You should to change your password before login" msgid "You should to change your password before login"
msgstr "登录完成前,请先修改密码" msgstr "登录完成前,请先修改密码"
#: authentication/errors.py:318 authentication/mixins.py:373 #: authentication/errors.py:318 authentication/mixins.py:378
msgid "Your password has expired, please reset before logging in" msgid "Your password has expired, please reset before logging in"
msgstr "您的密码已过期,先修改再登录" msgstr "您的密码已过期,先修改再登录"
@ -1899,7 +1934,7 @@ msgstr "清空手机号码禁用"
msgid "The MFA type ({}) is not enabled" msgid "The MFA type ({}) is not enabled"
msgstr "该 MFA ({}) 方式没有启用" msgstr "该 MFA ({}) 方式没有启用"
#: authentication/mixins.py:349 #: authentication/mixins.py:354
msgid "Please change your password" msgid "Please change your password"
msgstr "请修改密码" msgstr "请修改密码"
@ -2824,7 +2859,7 @@ msgstr "用户组数量"
msgid "System users amount" msgid "System users amount"
msgstr "系统用户数量" msgstr "系统用户数量"
#: perms/serializers/application/permission.py:88 #: perms/serializers/application/permission.py:79
msgid "" msgid ""
"The application list contains applications that are different from the " "The application list contains applications that are different from the "
"permission type. ({})" "permission type. ({})"
@ -5755,10 +5790,6 @@ msgstr "改密计划执行"
msgid "Change auth plan task" msgid "Change auth plan task"
msgstr "改密计划任务" msgstr "改密计划任务"
#: xpack/plugins/change_auth_plan/models/base.py:24
msgid "Custom password"
msgstr "自定义密码"
#: xpack/plugins/change_auth_plan/models/base.py:25 #: xpack/plugins/change_auth_plan/models/base.py:25
msgid "All assets use the same random password" msgid "All assets use the same random password"
msgstr "使用相同的随机密码" msgstr "使用相同的随机密码"