mirror of https://github.com/jumpserver/jumpserver
commit
6bd597eadd
|
@ -44,11 +44,7 @@ class ApplicationAccountViewSet(JMSBulkModelViewSet):
|
|||
permission_classes = (IsOrgAdmin,)
|
||||
|
||||
def get_queryset(self):
|
||||
queryset = Account.objects.all() \
|
||||
.annotate(type=F('app__type')) \
|
||||
.annotate(app_display=F('app__name')) \
|
||||
.annotate(systemuser_display=F('systemuser__name')) \
|
||||
.annotate(category=F('app__category'))
|
||||
queryset = Account.get_queryset()
|
||||
return queryset
|
||||
|
||||
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
from django.db import models
|
||||
from simple_history.models import HistoricalRecords
|
||||
from django.db.models import F
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
from common.utils import lazyproperty
|
||||
|
@ -7,8 +8,12 @@ from assets.models.base import BaseUser
|
|||
|
||||
|
||||
class Account(BaseUser):
|
||||
app = models.ForeignKey('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"))
|
||||
app = models.ForeignKey(
|
||||
'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'))
|
||||
history = HistoricalRecords()
|
||||
|
||||
|
@ -60,6 +65,10 @@ class Account(BaseUser):
|
|||
def type(self):
|
||||
return self.app.type
|
||||
|
||||
@lazyproperty
|
||||
def attrs(self):
|
||||
return self.app.attrs
|
||||
|
||||
@lazyproperty
|
||||
def app_display(self):
|
||||
return self.systemuser.name
|
||||
|
@ -84,5 +93,14 @@ class Account(BaseUser):
|
|||
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):
|
||||
return self.smart_name
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
# coding: utf-8
|
||||
#
|
||||
|
||||
from rest_framework import serializers
|
||||
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 .attrs import (
|
||||
category_serializer_classes_mapping,
|
||||
type_serializer_classes_mapping
|
||||
type_serializer_classes_mapping,
|
||||
type_secret_serializer_classes_mapping
|
||||
)
|
||||
from .. import models
|
||||
from .. import const
|
||||
|
@ -23,17 +23,28 @@ __all__ = [
|
|||
class AppSerializerMixin(serializers.Serializer):
|
||||
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):
|
||||
default_serializer = serializers.Serializer(read_only=True)
|
||||
if isinstance(self.instance, models.Application):
|
||||
_type = self.instance.type
|
||||
_category = self.instance.category
|
||||
instance = self.app
|
||||
if instance:
|
||||
_type = instance.type
|
||||
_category = instance.category
|
||||
else:
|
||||
_type = self.context['request'].query_params.get('type')
|
||||
_category = self.context['request'].query_params.get('category')
|
||||
|
||||
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:
|
||||
serializer_class = category_serializer_classes_mapping.get(_category)
|
||||
else:
|
||||
|
@ -84,11 +95,13 @@ class MiniAppSerializer(serializers.ModelSerializer):
|
|||
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_display = serializers.SerializerMethodField(label=_('Category display'))
|
||||
type = serializers.ChoiceField(label=_('Type'), choices=const.AppType.choices, read_only=True)
|
||||
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)
|
||||
type_mapper = dict(const.AppType.choices)
|
||||
|
@ -96,10 +109,11 @@ class AppAccountSerializer(AuthSerializerMixin, BulkOrgResourceModelSerializer):
|
|||
class Meta:
|
||||
model = models.Account
|
||||
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 = fields_mini + fields_fk + fields_write_only + [
|
||||
'type', 'type_display', 'category', 'category_display',
|
||||
fields = fields_mini + fields_fk + fields_write_only + fields_other + [
|
||||
'type', 'type_display', 'category', 'category_display', 'attrs'
|
||||
]
|
||||
extra_kwargs = {
|
||||
'username': {'default': '', 'required': False},
|
||||
|
@ -112,6 +126,14 @@ class AppAccountSerializer(AuthSerializerMixin, BulkOrgResourceModelSerializer):
|
|||
'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):
|
||||
return self.category_mapper.get(obj.category)
|
||||
|
||||
|
@ -131,6 +153,10 @@ class AppAccountSerializer(AuthSerializerMixin, BulkOrgResourceModelSerializer):
|
|||
|
||||
class AppAccountSecretSerializer(AppAccountSerializer):
|
||||
class Meta(AppAccountSerializer.Meta):
|
||||
fields_backup = [
|
||||
'id', 'app_display', 'attrs', 'username', 'password', 'private_key',
|
||||
'public_key', 'date_created', 'date_updated', 'version'
|
||||
]
|
||||
extra_kwargs = {
|
||||
'password': {'write_only': False},
|
||||
'private_key': {'write_only': False},
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
from rest_framework import serializers
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
|
||||
__all__ = ['CloudSerializer']
|
||||
|
||||
|
||||
|
|
|
@ -10,7 +10,6 @@ from assets.models import Asset
|
|||
|
||||
logger = get_logger(__file__)
|
||||
|
||||
|
||||
__all__ = ['RemoteAppSerializer']
|
||||
|
||||
|
||||
|
|
|
@ -3,8 +3,7 @@ from rest_framework import serializers
|
|||
|
||||
from ..application_category import RemoteAppSerializer
|
||||
|
||||
|
||||
__all__ = ['ChromeSerializer']
|
||||
__all__ = ['ChromeSerializer', 'ChromeSecretSerializer']
|
||||
|
||||
|
||||
class ChromeSerializer(RemoteAppSerializer):
|
||||
|
@ -17,10 +16,16 @@ class ChromeSerializer(RemoteAppSerializer):
|
|||
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'), allow_null=True,
|
||||
max_length=128, allow_blank=True, required=False, label=_('Chrome 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=_('Chrome password'),
|
||||
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
|
||||
)
|
||||
|
|
|
@ -1,11 +1,9 @@
|
|||
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
from rest_framework import serializers
|
||||
|
||||
from ..application_category import RemoteAppSerializer
|
||||
|
||||
|
||||
__all__ = ['CustomSerializer']
|
||||
__all__ = ['CustomSerializer', 'CustomSecretSerializer']
|
||||
|
||||
|
||||
class CustomSerializer(RemoteAppSerializer):
|
||||
|
@ -18,10 +16,17 @@ class CustomSerializer(RemoteAppSerializer):
|
|||
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=_('Custom 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=_('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,
|
||||
)
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
from ..application_category import CloudSerializer
|
||||
|
||||
|
||||
__all__ = ['K8SSerializer']
|
||||
|
||||
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
from .mysql import MySQLSerializer
|
||||
|
||||
|
||||
__all__ = ['MariaDBSerializer']
|
||||
|
||||
|
||||
|
|
|
@ -3,13 +3,9 @@ from django.utils.translation import ugettext_lazy as _
|
|||
|
||||
from ..application_category import DBSerializer
|
||||
|
||||
|
||||
__all__ = ['MySQLSerializer']
|
||||
|
||||
|
||||
class MySQLSerializer(DBSerializer):
|
||||
port = serializers.IntegerField(default=3306, label=_('Port'), allow_null=True)
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -3,8 +3,7 @@ from rest_framework import serializers
|
|||
|
||||
from ..application_category import RemoteAppSerializer
|
||||
|
||||
|
||||
__all__ = ['MySQLWorkbenchSerializer']
|
||||
__all__ = ['MySQLWorkbenchSerializer', 'MySQLWorkbenchSecretSerializer']
|
||||
|
||||
|
||||
class MySQLWorkbenchSerializer(RemoteAppSerializer):
|
||||
|
@ -27,10 +26,17 @@ class MySQLWorkbenchSerializer(RemoteAppSerializer):
|
|||
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=_('Mysql workbench 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=_('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,
|
||||
)
|
||||
|
|
|
@ -3,10 +3,8 @@ from django.utils.translation import ugettext_lazy as _
|
|||
|
||||
from ..application_category import DBSerializer
|
||||
|
||||
|
||||
__all__ = ['OracleSerializer']
|
||||
|
||||
|
||||
class OracleSerializer(DBSerializer):
|
||||
port = serializers.IntegerField(default=1521, label=_('Port'), allow_null=True)
|
||||
|
||||
|
|
|
@ -3,10 +3,8 @@ from django.utils.translation import ugettext_lazy as _
|
|||
|
||||
from ..application_category import DBSerializer
|
||||
|
||||
|
||||
__all__ = ['PostgreSerializer']
|
||||
|
||||
|
||||
class PostgreSerializer(DBSerializer):
|
||||
port = serializers.IntegerField(default=5432, label=_('Port'), allow_null=True)
|
||||
|
||||
|
|
|
@ -3,13 +3,9 @@ from django.utils.translation import ugettext_lazy as _
|
|||
|
||||
from ..application_category import DBSerializer
|
||||
|
||||
|
||||
__all__ = ['RedisSerializer']
|
||||
|
||||
|
||||
class RedisSerializer(DBSerializer):
|
||||
port = serializers.IntegerField(default=6379, label=_('Port'), allow_null=True)
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -3,7 +3,6 @@ from django.utils.translation import ugettext_lazy as _
|
|||
|
||||
from ..application_category import DBSerializer
|
||||
|
||||
|
||||
__all__ = ['SQLServerSerializer']
|
||||
|
||||
|
||||
|
|
|
@ -3,8 +3,7 @@ from rest_framework import serializers
|
|||
|
||||
from ..application_category import RemoteAppSerializer
|
||||
|
||||
|
||||
__all__ = ['VMwareClientSerializer']
|
||||
__all__ = ['VMwareClientSerializer', 'VMwareClientSecretSerializer']
|
||||
|
||||
|
||||
class VMwareClientSerializer(RemoteAppSerializer):
|
||||
|
@ -23,10 +22,17 @@ class VMwareClientSerializer(RemoteAppSerializer):
|
|||
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=_('Vmware 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=_('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
|
||||
)
|
||||
|
|
|
@ -1,15 +1,15 @@
|
|||
from rest_framework import serializers
|
||||
import copy
|
||||
|
||||
from applications import const
|
||||
from . import application_category, application_type
|
||||
|
||||
|
||||
__all__ = [
|
||||
'category_serializer_classes_mapping',
|
||||
'type_serializer_classes_mapping',
|
||||
'get_serializer_class_by_application_type',
|
||||
'type_secret_serializer_classes_mapping'
|
||||
]
|
||||
|
||||
|
||||
# define `attrs` field `category serializers mapping`
|
||||
# ---------------------------------------------------
|
||||
|
||||
|
@ -30,15 +30,32 @@ type_serializer_classes_mapping = {
|
|||
const.AppType.oracle.value: application_type.OracleSerializer,
|
||||
const.AppType.pgsql.value: application_type.PostgreSerializer,
|
||||
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
|
||||
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):
|
||||
return type_serializer_classes_mapping.get(_application_type)
|
||||
|
|
|
@ -64,9 +64,7 @@ class AccountViewSet(OrgBulkModelViewSet):
|
|||
permission_classes = (IsOrgAdmin,)
|
||||
|
||||
def get_queryset(self):
|
||||
queryset = super().get_queryset() \
|
||||
.annotate(ip=F('asset__ip')) \
|
||||
.annotate(hostname=F('asset__hostname'))
|
||||
queryset = AuthBook.get_queryset()
|
||||
return queryset
|
||||
|
||||
@action(methods=['post'], detail=True, url_path='verify')
|
||||
|
|
|
@ -32,8 +32,8 @@ class AccountBackupPlanExecutionViewSet(
|
|||
mixins.RetrieveModelMixin, viewsets.GenericViewSet
|
||||
):
|
||||
serializer_class = serializers.AccountBackupPlanExecutionSerializer
|
||||
search_fields = ('trigger', 'plan_id')
|
||||
filterset_fields = search_fields
|
||||
search_fields = ('trigger',)
|
||||
filterset_fields = ('trigger', 'plan_id')
|
||||
permission_classes = (IsOrgAdmin,)
|
||||
|
||||
def get_queryset(self):
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
#
|
||||
|
||||
from django.db import models
|
||||
from django.db.models import F
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
from simple_history.models import HistoricalRecords
|
||||
|
||||
|
@ -116,6 +117,15 @@ class AuthBook(BaseUser, AbsConnectivity):
|
|||
self.asset.save()
|
||||
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):
|
||||
return self.smart_name
|
||||
|
||||
|
|
|
@ -11,10 +11,18 @@ from .utils import validate_password_contains_left_double_curly_bracket
|
|||
class AccountSerializer(AuthSerializerMixin, BulkOrgResourceModelSerializer):
|
||||
ip = serializers.ReadOnlyField(label=_("IP"))
|
||||
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:
|
||||
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_other = ['date_created', 'date_updated', 'connectivity', 'date_verified', 'comment']
|
||||
fields_small = fields_mini + fields_write_only + fields_other
|
||||
|
@ -32,6 +40,9 @@ class AccountSerializer(AuthSerializerMixin, BulkOrgResourceModelSerializer):
|
|||
}
|
||||
ref_name = 'AssetAccountSerializer'
|
||||
|
||||
def get_protocols(self, v):
|
||||
return v.protocols.replace(' ', ', ')
|
||||
|
||||
@classmethod
|
||||
def setup_eager_loading(cls, queryset):
|
||||
""" Perform necessary eager loading of data. """
|
||||
|
@ -45,6 +56,10 @@ class AccountSerializer(AuthSerializerMixin, BulkOrgResourceModelSerializer):
|
|||
|
||||
class AccountSecretSerializer(AccountSerializer):
|
||||
class Meta(AccountSerializer.Meta):
|
||||
fields_backup = [
|
||||
'hostname', 'ip', 'platform', 'protocols', 'username', 'password',
|
||||
'private_key', 'public_key', 'date_created', 'date_updated', 'version'
|
||||
]
|
||||
extra_kwargs = {
|
||||
'password': {'write_only': False},
|
||||
'private_key': {'write_only': False},
|
||||
|
|
|
@ -5,8 +5,6 @@ from django.core.validators import RegexValidator
|
|||
from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
from orgs.mixins.serializers import BulkOrgResourceModelSerializer
|
||||
from users.models import User, UserGroup
|
||||
from perms.models import AssetPermission
|
||||
from ..models import Asset, Node, Platform, SystemUser
|
||||
|
||||
__all__ = [
|
||||
|
|
|
@ -1,15 +1,18 @@
|
|||
import os
|
||||
import time
|
||||
import pandas as pd
|
||||
from collections import defaultdict
|
||||
from collections import defaultdict, OrderedDict
|
||||
|
||||
from django.conf import settings
|
||||
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 applications.models import Account, Application
|
||||
from applications.models import Account
|
||||
from applications.const import AppType
|
||||
from applications.serializers import AppAccountSecretSerializer
|
||||
from users.models import User
|
||||
from common.utils import get_logger
|
||||
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')
|
||||
|
||||
|
||||
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
|
||||
def get_filename(plan_name):
|
||||
filename = os.path.join(
|
||||
|
@ -28,32 +70,24 @@ class AssetAccountHandler:
|
|||
)
|
||||
return filename
|
||||
|
||||
@staticmethod
|
||||
def create_df():
|
||||
@classmethod
|
||||
def create_df(cls):
|
||||
df_dict = defaultdict(list)
|
||||
label_key = AuthBook._meta.verbose_name
|
||||
accounts = AuthBook.objects.all().prefetch_related('systemuser', 'asset')
|
||||
sheet_name = AuthBook._meta.verbose_name
|
||||
accounts = AuthBook.get_queryset()
|
||||
for account in accounts:
|
||||
account.load_auth()
|
||||
protocol = account.asset.protocol
|
||||
protocol_label = getattr(ProtocolsMixin.Protocol, protocol).label
|
||||
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)
|
||||
row = cls.create_row(account, AccountSecretSerializer)
|
||||
df_dict[sheet_name].append(row)
|
||||
|
||||
for k, v in df_dict.items():
|
||||
df_dict[k] = pd.DataFrame(v)
|
||||
|
||||
logger.info('\n\033[33m- 共收集{}条资产账号\033[0m'.format(accounts.count()))
|
||||
return df_dict
|
||||
|
||||
|
||||
class AppAccountHandler:
|
||||
class AppAccountHandler(BaseAccountHandler):
|
||||
@staticmethod
|
||||
def get_filename(plan_name):
|
||||
filename = os.path.join(
|
||||
|
@ -61,33 +95,23 @@ class AppAccountHandler:
|
|||
)
|
||||
return filename
|
||||
|
||||
@staticmethod
|
||||
def create_df():
|
||||
@classmethod
|
||||
def create_df(cls):
|
||||
df_dict = defaultdict(list)
|
||||
accounts = Account.objects.all().prefetch_related('systemuser', 'app')
|
||||
accounts = Account.get_queryset()
|
||||
for account in accounts:
|
||||
account.load_auth()
|
||||
app_type = account.app.type
|
||||
if app_type == 'postgresql':
|
||||
label_key = getattr(AppType, 'pgsql').label
|
||||
else:
|
||||
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)
|
||||
app_type = account.type
|
||||
sheet_name = AppType.get_label(app_type)
|
||||
row = cls.create_row(account, AppAccountSecretSerializer)
|
||||
df_dict[sheet_name].append(row)
|
||||
for k, v in df_dict.items():
|
||||
df_dict[k] = pd.DataFrame(v)
|
||||
logger.info('\n\033[33m- 共收集{}条应用账号\033[0m'.format(accounts.count()))
|
||||
return df_dict
|
||||
|
||||
|
||||
HANDLER_MAP = {
|
||||
handler_map = {
|
||||
'asset': AssetAccountHandler,
|
||||
'application': AppAccountHandler
|
||||
}
|
||||
|
@ -102,31 +126,37 @@ class AccountBackupHandler:
|
|||
def create_excel(self):
|
||||
logger.info(
|
||||
'\n'
|
||||
'\033[32m>>> 正在生成资产及应用相关备份信息文件\033[0m'
|
||||
'\033[32m>>> 正在生成资产或应用相关备份信息文件\033[0m'
|
||||
''
|
||||
)
|
||||
# Print task start date
|
||||
time_start = time.time()
|
||||
info = {}
|
||||
files = []
|
||||
for account_type in self.execution.types:
|
||||
if account_type in HANDLER_MAP:
|
||||
account_handler = HANDLER_MAP[account_type]
|
||||
df = account_handler.create_df()
|
||||
filename = account_handler.get_filename(self.plan_name)
|
||||
info[filename] = df
|
||||
for filename, df_dict in info.items():
|
||||
handler = handler_map.get(account_type)
|
||||
if not handler:
|
||||
continue
|
||||
|
||||
df_dict = handler.create_df()
|
||||
if not df_dict:
|
||||
continue
|
||||
|
||||
filename = handler.get_filename(self.plan_name)
|
||||
with pd.ExcelWriter(filename) as w:
|
||||
for sheet, df in df_dict.items():
|
||||
sheet = sheet.replace(' ', '-')
|
||||
getattr(df, 'to_excel')(w, sheet_name=sheet, index=False)
|
||||
files.append(filename)
|
||||
timedelta = round((time.time() - time_start), 2)
|
||||
logger.info('步骤完成: 用时 {}s'.format(timedelta))
|
||||
return list(info.keys())
|
||||
return files
|
||||
|
||||
def send_backup_mail(self, files):
|
||||
recipients = self.execution.plan_snapshot.get('recipients')
|
||||
if not recipients:
|
||||
return
|
||||
if not files:
|
||||
return
|
||||
recipients = User.objects.filter(id__in=list(recipients))
|
||||
logger.info(
|
||||
'\n'
|
||||
|
@ -191,13 +221,3 @@ class AccountBackupHandler:
|
|||
logger.info('\n任务结束: {}'.format(local_now_display()))
|
||||
timedelta = round((time.time() - time_start), 2)
|
||||
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
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
# ~*~ coding: utf-8 ~*~
|
||||
|
||||
from celery import shared_task
|
||||
from django.utils.translation import ugettext as _
|
||||
from django.utils.translation import ugettext as _, gettext_noop
|
||||
|
||||
from common.utils import get_logger
|
||||
from orgs.utils import org_aware_func
|
||||
|
@ -104,6 +104,6 @@ def test_accounts_connectivity_manual(accounts):
|
|||
:param accounts: <AuthBook>对象
|
||||
"""
|
||||
for account in accounts:
|
||||
task_name = _("Test account connectivity: {}").format(account)
|
||||
task_name = gettext_noop("Test account connectivity: ") + str(account)
|
||||
test_account_connectivity_util(account, task_name)
|
||||
print(".\n")
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
from itertools import groupby
|
||||
from collections import defaultdict
|
||||
from celery import shared_task
|
||||
from django.utils.translation import ugettext as _
|
||||
from django.utils.translation import gettext_noop
|
||||
|
||||
from common.utils import get_logger
|
||||
from orgs.utils import org_aware_func
|
||||
|
@ -46,7 +46,7 @@ def test_asset_connectivity_util(assets, task_name=None):
|
|||
from ops.utils import update_or_create_ansible_task
|
||||
|
||||
if task_name is None:
|
||||
task_name = _("Test assets connectivity")
|
||||
task_name = gettext_noop("Test assets connectivity. ")
|
||||
|
||||
hosts = clean_ansible_task_hosts(assets)
|
||||
if not hosts:
|
||||
|
@ -88,7 +88,7 @@ def test_asset_connectivity_util(assets, task_name=None):
|
|||
|
||||
@shared_task(queue="ansible")
|
||||
def test_asset_connectivity_manual(asset):
|
||||
task_name = _("Test assets connectivity: {}").format(asset)
|
||||
task_name = gettext_noop("Test assets connectivity: ") + str(asset)
|
||||
summary = test_asset_connectivity_util([asset], task_name=task_name)
|
||||
|
||||
if summary.get('dark'):
|
||||
|
@ -99,7 +99,7 @@ def test_asset_connectivity_manual(asset):
|
|||
|
||||
@shared_task(queue="ansible")
|
||||
def test_assets_connectivity_manual(assets):
|
||||
task_name = _("Test assets connectivity: {}").format([asset.hostname for asset in assets])
|
||||
task_name = gettext_noop("Test assets connectivity: ") + str([asset.hostname for asset in assets])
|
||||
summary = test_asset_connectivity_util(assets, task_name=task_name)
|
||||
|
||||
if summary.get('dark'):
|
||||
|
@ -110,8 +110,7 @@ def test_assets_connectivity_manual(assets):
|
|||
|
||||
@shared_task(queue="ansible")
|
||||
def test_node_assets_connectivity_manual(node):
|
||||
task_name = _("Test if the assets under the node are connectable: {}".format(node.name))
|
||||
task_name = gettext_noop("Test if the assets under the node are connectable: ") + node.name
|
||||
assets = node.get_all_assets()
|
||||
result = test_asset_connectivity_util(assets, task_name=task_name)
|
||||
return result
|
||||
|
||||
|
|
|
@ -4,7 +4,7 @@ import json
|
|||
import re
|
||||
|
||||
from celery import shared_task
|
||||
from django.utils.translation import ugettext as _
|
||||
from django.utils.translation import ugettext as _, gettext_noop
|
||||
|
||||
from common.utils import (
|
||||
capacity_convert, sum_capacity, get_logger
|
||||
|
@ -94,7 +94,7 @@ def update_assets_hardware_info_util(assets, task_name=None):
|
|||
"""
|
||||
from ops.utils import update_or_create_ansible_task
|
||||
if task_name is None:
|
||||
task_name = _("Update some assets hardware info")
|
||||
task_name = gettext_noop("Update some assets hardware info. ")
|
||||
tasks = const.UPDATE_ASSETS_HARDWARE_TASKS
|
||||
hosts = clean_ansible_task_hosts(assets)
|
||||
if not hosts:
|
||||
|
@ -111,13 +111,13 @@ def update_assets_hardware_info_util(assets, task_name=None):
|
|||
|
||||
@shared_task(queue="ansible")
|
||||
def update_asset_hardware_info_manual(asset):
|
||||
task_name = _("Update asset hardware info: {}").format(asset.hostname)
|
||||
task_name = gettext_noop("Update asset hardware info: ") + str(asset.hostname)
|
||||
update_assets_hardware_info_util([asset], task_name=task_name)
|
||||
|
||||
|
||||
@shared_task(queue="ansible")
|
||||
def update_assets_hardware_info_manual(assets):
|
||||
task_name = _("Update assets hardware info: {}").format([asset.hostname for asset in assets])
|
||||
task_name = gettext_noop("Update assets hardware info: ") + str([asset.hostname for asset in assets])
|
||||
update_assets_hardware_info_util(assets, task_name=task_name)
|
||||
|
||||
|
||||
|
@ -134,7 +134,7 @@ def update_assets_hardware_info_period():
|
|||
|
||||
@shared_task(queue="ansible")
|
||||
def update_node_assets_hardware_info_manual(node):
|
||||
task_name = _("Update node asset hardware information: {}").format(node.name)
|
||||
task_name = gettext_noop("Update node asset hardware information: ") + str(node.name)
|
||||
assets = node.get_all_assets()
|
||||
result = update_assets_hardware_info_util(assets, task_name=task_name)
|
||||
return result
|
||||
|
|
|
@ -4,7 +4,7 @@ import re
|
|||
from collections import defaultdict
|
||||
|
||||
from celery import shared_task
|
||||
from django.utils.translation import ugettext as _
|
||||
from django.utils.translation import gettext_noop
|
||||
from django.utils import timezone
|
||||
|
||||
from orgs.utils import tmp_to_org, org_aware_func
|
||||
|
@ -108,7 +108,7 @@ def add_asset_users(assets, results):
|
|||
def gather_asset_users(assets, task_name=None):
|
||||
from ops.utils import update_or_create_ansible_task
|
||||
if task_name is None:
|
||||
task_name = _("Gather assets users")
|
||||
task_name = gettext_noop("Gather assets users")
|
||||
assets = clean_ansible_task_hosts(assets)
|
||||
if not assets:
|
||||
return
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
from itertools import groupby
|
||||
from celery import shared_task
|
||||
from common.db.utils import get_object_if_need, get_objects
|
||||
from django.utils.translation import ugettext as _
|
||||
from django.utils.translation import ugettext as _, gettext_noop
|
||||
from django.db.models import Empty, Q
|
||||
|
||||
from common.utils import encrypt_password, get_logger
|
||||
|
@ -279,7 +279,7 @@ def push_system_user_to_assets_manual(system_user, username=None):
|
|||
"""
|
||||
system_user = get_object_if_need(SystemUser, system_user)
|
||||
assets = system_user.get_related_assets()
|
||||
task_name = _("Push system users to assets: {}").format(system_user.name)
|
||||
task_name = gettext_noop("Push system users to assets: ") + system_user.name
|
||||
return push_system_user_util(system_user, assets, task_name=task_name, username=username)
|
||||
|
||||
|
||||
|
@ -291,7 +291,7 @@ def push_system_user_a_asset_manual(system_user, asset, username=None):
|
|||
"""
|
||||
# if username is None:
|
||||
# username = system_user.username
|
||||
task_name = _("Push system users to asset: {}({}) => {}").format(
|
||||
task_name = gettext_noop("Push system users to asset: ") + "{}({}) => {}".format(
|
||||
system_user.name, username, asset
|
||||
)
|
||||
return push_system_user_util(system_user, [asset], task_name=task_name, username=username)
|
||||
|
@ -312,7 +312,7 @@ def push_system_user_to_assets(system_user_id, asset_ids, username=None):
|
|||
"""
|
||||
system_user = SystemUser.objects.get(id=system_user_id)
|
||||
assets = get_objects(Asset, asset_ids)
|
||||
task_name = _("Push system users to assets: {}").format(system_user.name)
|
||||
task_name = gettext_noop("Push system users to assets: ") + system_user.name
|
||||
|
||||
return push_system_user_util(system_user, assets, task_name, username=username)
|
||||
|
||||
|
|
|
@ -3,7 +3,7 @@ from itertools import groupby
|
|||
from collections import defaultdict
|
||||
|
||||
from celery import shared_task
|
||||
from django.utils.translation import ugettext as _
|
||||
from django.utils.translation import ugettext as _, gettext_noop
|
||||
|
||||
from assets.models import Asset
|
||||
from common.utils import get_logger
|
||||
|
@ -115,7 +115,7 @@ def test_system_user_connectivity_util(system_user, assets, task_name):
|
|||
@shared_task(queue="ansible")
|
||||
@org_aware_func("system_user")
|
||||
def test_system_user_connectivity_manual(system_user, asset_ids=None):
|
||||
task_name = _("Test system user connectivity: {}").format(system_user)
|
||||
task_name = gettext_noop("Test system user connectivity: ") + str(system_user)
|
||||
if asset_ids:
|
||||
assets = Asset.objects.filter(id__in=asset_ids)
|
||||
else:
|
||||
|
@ -126,7 +126,7 @@ def test_system_user_connectivity_manual(system_user, asset_ids=None):
|
|||
@shared_task(queue="ansible")
|
||||
@org_aware_func("system_user")
|
||||
def test_system_user_connectivity_a_asset(system_user, asset):
|
||||
task_name = _("Test system user connectivity: {} => {}").format(
|
||||
task_name = gettext_noop("Test system user connectivity: ") + "{} => {}".format(
|
||||
system_user, asset
|
||||
)
|
||||
test_system_user_connectivity_util(system_user, [asset], task_name)
|
||||
|
@ -145,7 +145,7 @@ def test_system_user_connectivity_period():
|
|||
return
|
||||
queryset_map = SystemUser.objects.all_group_by_org()
|
||||
for org, system_user in queryset_map.items():
|
||||
task_name = _("Test system user connectivity period: {}").format(system_user)
|
||||
task_name = gettext_noop("Test system user connectivity period: ") + str(system_user)
|
||||
with tmp_to_org(org):
|
||||
assets = system_user.get_related_assets()
|
||||
test_system_user_connectivity_util(system_user, assets, task_name)
|
||||
|
|
|
@ -12,6 +12,7 @@ from rest_framework.response import Response
|
|||
|
||||
from common.permissions import IsValidUser, NeedMFAVerify
|
||||
from common.utils import get_logger
|
||||
from common.exceptions import UnexpectError
|
||||
from users.models.user import User
|
||||
from ..serializers import OtpVerifySerializer
|
||||
from .. import serializers
|
||||
|
@ -35,30 +36,45 @@ class MFASendCodeApi(AuthMixin, CreateAPIView):
|
|||
"""
|
||||
permission_classes = (AllowAny,)
|
||||
serializer_class = serializers.MFASelectTypeSerializer
|
||||
username = ''
|
||||
ip = ''
|
||||
|
||||
def get_user_from_db(self, username):
|
||||
try:
|
||||
user = get_object_or_404(User, username=username)
|
||||
return user
|
||||
except Exception as e:
|
||||
self.incr_mfa_failed_time(username, self.ip)
|
||||
raise e
|
||||
|
||||
def get_user_from_db(self, username):
|
||||
"""避免暴力测试用户名"""
|
||||
ip = self.get_request_ip()
|
||||
self.check_mfa_is_block(username, ip)
|
||||
try:
|
||||
user = get_object_or_404(User, username=username)
|
||||
return user
|
||||
except Exception as e:
|
||||
self.incr_mfa_failed_time(username, ip)
|
||||
raise e
|
||||
|
||||
def perform_create(self, serializer):
|
||||
username = serializer.validated_data.get('username', '')
|
||||
mfa_type = serializer.validated_data['type']
|
||||
|
||||
if not username:
|
||||
user = self.get_user_from_session()
|
||||
else:
|
||||
user = get_object_or_404(User, username=username)
|
||||
user = self.get_user_from_db(username)
|
||||
|
||||
mfa_backend = user.get_active_mfa_backend_by_type(mfa_type)
|
||||
if not mfa_backend or not mfa_backend.challenge_required:
|
||||
raise ValidationError('MFA type not support: {} {}'.format(mfa_type, mfa_backend))
|
||||
mfa_backend.send_challenge()
|
||||
|
||||
def create(self, request, *args, **kwargs):
|
||||
serializer = self.get_serializer(data=request.data)
|
||||
serializer.is_valid(raise_exception=True)
|
||||
|
||||
error = _('Current user not support mfa type: {}').format(mfa_type)
|
||||
raise ValidationError({'error': error})
|
||||
try:
|
||||
self.perform_create(serializer)
|
||||
return Response(serializer.data, status=201)
|
||||
mfa_backend.send_challenge()
|
||||
except Exception as e:
|
||||
logger.exception(e)
|
||||
return Response({'error': str(e)}, status=400)
|
||||
raise UnexpectError(str(e))
|
||||
|
||||
|
||||
class MFAChallengeVerifyApi(AuthMixin, CreateAPIView):
|
||||
|
|
|
@ -76,11 +76,10 @@ class PrepareRequestMixin:
|
|||
@staticmethod
|
||||
def get_attribute_consuming_service():
|
||||
attr_mapping = settings.SAML2_RENAME_ATTRIBUTES
|
||||
name_prefix = settings.SITE_URL
|
||||
if attr_mapping and isinstance(attr_mapping, dict):
|
||||
attr_list = [
|
||||
{
|
||||
"name": '{}/{}'.format(name_prefix, sp_key),
|
||||
"name": sp_key,
|
||||
"friendlyName": idp_key, "isRequired": True
|
||||
}
|
||||
for idp_key, sp_key in attr_mapping.items()
|
||||
|
@ -168,12 +167,10 @@ class PrepareRequestMixin:
|
|||
|
||||
def get_attributes(self, saml_instance):
|
||||
user_attrs = {}
|
||||
real_key_index = len(settings.SITE_URL) + 1
|
||||
attrs = saml_instance.get_attributes()
|
||||
valid_attrs = ['username', 'name', 'email', 'comment', 'phone']
|
||||
|
||||
for attr, value in attrs.items():
|
||||
attr = attr[real_key_index:]
|
||||
if attr not in valid_attrs:
|
||||
continue
|
||||
user_attrs[attr] = self.value_to_str(value)
|
||||
|
|
|
@ -335,6 +335,11 @@ class MFAMixin:
|
|||
mfa_backends = User.get_user_mfa_backends(user)
|
||||
return {'mfa_backends': mfa_backends}
|
||||
|
||||
@staticmethod
|
||||
def incr_mfa_failed_time(username, ip):
|
||||
util = MFABlockUtils(username, ip)
|
||||
util.incr_failed_count()
|
||||
|
||||
|
||||
class AuthPostCheckMixin:
|
||||
@classmethod
|
||||
|
@ -450,7 +455,10 @@ class AuthMixin(CommonMixin, AuthPreCheckMixin, AuthACLMixin, MFAMixin, AuthPost
|
|||
)
|
||||
if not user:
|
||||
self.raise_credential_error(errors.reason_password_failed)
|
||||
elif user.is_expired:
|
||||
|
||||
self.request.session['auth_backend'] = getattr(user, 'backend', settings.AUTH_BACKEND_MODEL)
|
||||
|
||||
if user.is_expired:
|
||||
self.raise_credential_error(errors.reason_user_expired)
|
||||
elif not user.is_active:
|
||||
self.raise_credential_error(errors.reason_user_inactive)
|
||||
|
|
|
@ -109,7 +109,7 @@
|
|||
}
|
||||
|
||||
.select-con {
|
||||
width: 30%;
|
||||
width: 35%;
|
||||
}
|
||||
|
||||
.mfa-div {
|
||||
|
|
|
@ -44,6 +44,7 @@ def get_objects(model, pks):
|
|||
return objs
|
||||
|
||||
|
||||
# 复制 django.db.close_old_connections, 因为它没有导出,ide 提示有问题
|
||||
def close_old_connections():
|
||||
for conn in connections.all():
|
||||
conn.close_if_unusable_or_obsolete()
|
||||
|
|
|
@ -45,3 +45,9 @@ class MFAVerifyRequired(JMSException):
|
|||
status_code = status.HTTP_400_BAD_REQUEST
|
||||
default_code = 'mfa_verify_required'
|
||||
default_detail = _('This action require verify your MFA')
|
||||
|
||||
|
||||
class UnexpectError(JMSException):
|
||||
status_code = status.HTTP_500_INTERNAL_SERVER_ERROR
|
||||
default_code = 'unexpect_error'
|
||||
default_detail = _('Unexpect error occur')
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
from django.core.cache import cache
|
||||
from django.shortcuts import reverse
|
||||
from django.shortcuts import redirect
|
||||
from django.shortcuts import reverse, redirect
|
||||
from django.utils.translation import gettext_noop
|
||||
|
||||
from .random import random_string
|
||||
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
#
|
||||
from functools import partial
|
||||
from werkzeug.local import LocalProxy
|
||||
from datetime import datetime
|
||||
|
||||
from django.conf import settings
|
||||
from common.local import thread_local
|
||||
|
@ -26,4 +27,18 @@ def has_valid_xpack_license():
|
|||
return License.has_valid_license()
|
||||
|
||||
|
||||
def get_xpack_license_info() -> dict:
|
||||
if has_valid_xpack_license():
|
||||
from xpack.plugins.license.models import License
|
||||
info = License.get_license_detail()
|
||||
corporation = info.get('corporation', '')
|
||||
else:
|
||||
current_year = datetime.now().year
|
||||
corporation = f'Copyright - FIT2CLOUD 飞致云 © 2014-{current_year}'
|
||||
info = {
|
||||
'corporation': corporation
|
||||
}
|
||||
return info
|
||||
|
||||
|
||||
current_request = LocalProxy(partial(_find, 'current_request'))
|
||||
|
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:942e981be66e5d0c32efb59583a377503ee3dc285e2794da40c312694c4a9dc2
|
||||
size 96378
|
||||
oid sha256:a08014e3eed6152aaff1e42758f20666f0e90e1ed4264a9d7cd44e34191d607e
|
||||
size 96692
|
||||
|
|
|
@ -7,7 +7,7 @@ msgid ""
|
|||
msgstr ""
|
||||
"Project-Id-Version: JumpServer 0.3.3\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2022-01-12 20:51+0800\n"
|
||||
"POT-Creation-Date: 2022-01-17 19:06+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"
|
||||
|
@ -123,14 +123,14 @@ msgid "Login acl"
|
|||
msgstr "登录访问控制"
|
||||
|
||||
#: acls/models/login_asset_acl.py:21
|
||||
#: applications/serializers/application.py:108
|
||||
#: applications/serializers/application.py:139
|
||||
#: applications/serializers/application.py:122
|
||||
#: applications/serializers/application.py:165
|
||||
msgid "System User"
|
||||
msgstr "系统用户"
|
||||
|
||||
#: acls/models/login_asset_acl.py:22
|
||||
#: applications/serializers/attrs/application_category/remote_app.py:37
|
||||
#: assets/models/asset.py:356 assets/models/authbook.py:18
|
||||
#: applications/serializers/attrs/application_category/remote_app.py:36
|
||||
#: assets/models/asset.py:356 assets/models/authbook.py:19
|
||||
#: assets/models/backup.py:31 assets/models/cmd_filter.py:34
|
||||
#: assets/models/gathered_user.py:14 assets/serializers/system_user.py:264
|
||||
#: audits/models.py:38 perms/models/asset_permission.py:24
|
||||
|
@ -157,16 +157,12 @@ msgid "Format for comma-delimited string, with * indicating a match all. "
|
|||
msgstr "格式为逗号分隔的字符串, * 表示匹配所有. "
|
||||
|
||||
#: acls/serializers/login_acl.py:15 acls/serializers/login_asset_acl.py:17
|
||||
#: acls/serializers/login_asset_acl.py:51
|
||||
#: applications/serializers/attrs/application_type/chrome.py:20
|
||||
#: applications/serializers/attrs/application_type/custom.py:21
|
||||
#: 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
|
||||
#: acls/serializers/login_asset_acl.py:51 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_oauth_bind.html:9
|
||||
#: ops/models/adhoc.py:148 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
|
||||
#: users/templates/users/_msg_user_created.html:12
|
||||
#: users/templates/users/_select_user_modal.html:14
|
||||
#: xpack/plugins/change_auth_plan/models/asset.py:34
|
||||
|
@ -185,7 +181,7 @@ msgstr ""
|
|||
"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
|
||||
#: 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/serializers/account.py:12
|
||||
#: authentication/templates/authentication/_msg_oauth_bind.html:12
|
||||
|
@ -252,9 +248,9 @@ msgstr "时段"
|
|||
msgid "My applications"
|
||||
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_type/mysql_workbench.py:26
|
||||
#: applications/serializers/attrs/application_type/mysql_workbench.py:25
|
||||
#: xpack/plugins/change_auth_plan/models/app.py:32
|
||||
msgid "Database"
|
||||
msgstr "数据库"
|
||||
|
@ -267,7 +263,7 @@ msgstr "远程应用"
|
|||
msgid "Custom"
|
||||
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
|
||||
#: perms/models/application_permission.py:32
|
||||
#: perms/models/asset_permission.py:26 templates/_nav.html:45
|
||||
|
@ -284,12 +280,12 @@ msgstr "自定义"
|
|||
msgid "System user"
|
||||
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
|
||||
msgid "Version"
|
||||
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
|
||||
msgid "Account"
|
||||
msgstr "账户"
|
||||
|
@ -299,7 +295,7 @@ msgid "Applications"
|
|||
msgstr "应用管理"
|
||||
|
||||
#: 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/serializers/application/user_permission.py:33
|
||||
#: tickets/serializers/ticket/meta/ticket_type/apply_application.py:22
|
||||
|
@ -308,7 +304,7 @@ msgid "Category"
|
|||
msgstr "类别"
|
||||
|
||||
#: 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
|
||||
#: perms/models/application_permission.py:23
|
||||
#: perms/serializers/application/user_permission.py:34
|
||||
|
@ -335,15 +331,15 @@ msgstr "属性"
|
|||
msgid "Application"
|
||||
msgstr "应用程序"
|
||||
|
||||
#: applications/serializers/application.py:59
|
||||
#: applications/serializers/application.py:89 assets/serializers/label.py:13
|
||||
#: applications/serializers/application.py:70
|
||||
#: applications/serializers/application.py:100 assets/serializers/label.py:13
|
||||
#: perms/serializers/application/permission.py:18
|
||||
#: tickets/serializers/ticket/meta/ticket_type/apply_application.py:26
|
||||
msgid "Category display"
|
||||
msgstr "类别名称"
|
||||
|
||||
#: applications/serializers/application.py:60
|
||||
#: applications/serializers/application.py:91
|
||||
#: applications/serializers/application.py:71
|
||||
#: applications/serializers/application.py:102
|
||||
#: assets/serializers/system_user.py:27 audits/serializers.py:29
|
||||
#: perms/serializers/application/permission.py:19
|
||||
#: tickets/serializers/ticket/meta/ticket_type/apply_application.py:33
|
||||
|
@ -352,43 +348,62 @@ msgstr "类别名称"
|
|||
msgid "Type display"
|
||||
msgstr "类型名称"
|
||||
|
||||
#: applications/serializers/application.py:107
|
||||
#: applications/serializers/application.py:138
|
||||
#: applications/serializers/application.py:103 assets/models/asset.py:231
|
||||
#: 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"
|
||||
msgstr "应用名称"
|
||||
|
||||
#: applications/serializers/attrs/application_category/cloud.py:9
|
||||
#: applications/serializers/attrs/application_category/cloud.py:8
|
||||
#: assets/models/cluster.py:40
|
||||
msgid "Cluster"
|
||||
msgstr "集群"
|
||||
|
||||
#: applications/serializers/attrs/application_category/db.py:11
|
||||
#: ops/models/adhoc.py:146 settings/serializers/auth/radius.py:14
|
||||
#: ops/models/adhoc.py:157 settings/serializers/auth/radius.py:14
|
||||
#: xpack/plugins/cloud/serializers/account_attrs.py:68
|
||||
msgid "Host"
|
||||
msgstr "主机"
|
||||
|
||||
#: applications/serializers/attrs/application_category/db.py:12
|
||||
#: applications/serializers/attrs/application_type/mysql.py:11
|
||||
#: applications/serializers/attrs/application_type/mysql_workbench.py:22
|
||||
#: applications/serializers/attrs/application_type/oracle.py:11
|
||||
#: applications/serializers/attrs/application_type/pgsql.py:11
|
||||
#: applications/serializers/attrs/application_type/redis.py:11
|
||||
#: applications/serializers/attrs/application_type/sqlserver.py:11
|
||||
#: applications/serializers/attrs/application_type/mysql.py:10
|
||||
#: applications/serializers/attrs/application_type/mysql_workbench.py:21
|
||||
#: applications/serializers/attrs/application_type/oracle.py:10
|
||||
#: applications/serializers/attrs/application_type/pgsql.py:10
|
||||
#: applications/serializers/attrs/application_type/redis.py:10
|
||||
#: applications/serializers/attrs/application_type/sqlserver.py:10
|
||||
#: assets/models/asset.py:215 assets/models/domain.py:62
|
||||
#: settings/serializers/auth/radius.py:15
|
||||
#: xpack/plugins/cloud/serializers/account_attrs.py:69
|
||||
msgid "Port"
|
||||
msgstr "端口"
|
||||
|
||||
#: applications/serializers/attrs/application_category/remote_app.py:40
|
||||
#: applications/serializers/attrs/application_type/chrome.py:14
|
||||
#: applications/serializers/attrs/application_type/mysql_workbench.py:14
|
||||
#: applications/serializers/attrs/application_type/vmware_client.py:18
|
||||
#: applications/serializers/attrs/application_category/remote_app.py:39
|
||||
#: applications/serializers/attrs/application_type/chrome.py:13
|
||||
#: applications/serializers/attrs/application_type/mysql_workbench.py:13
|
||||
#: applications/serializers/attrs/application_type/vmware_client.py:17
|
||||
msgid "Application path"
|
||||
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
|
||||
#: xpack/plugins/change_auth_plan/serializers/asset.py:65
|
||||
#: xpack/plugins/change_auth_plan/serializers/asset.py:68
|
||||
|
@ -398,37 +413,56 @@ msgstr "应用路径"
|
|||
msgid "This field is required."
|
||||
msgstr "该字段是必填项。"
|
||||
|
||||
#: applications/serializers/attrs/application_type/chrome.py:17
|
||||
#: applications/serializers/attrs/application_type/vmware_client.py:22
|
||||
#: applications/serializers/attrs/application_type/chrome.py:16
|
||||
#: applications/serializers/attrs/application_type/vmware_client.py:21
|
||||
msgid "Target URL"
|
||||
msgstr "目标URL"
|
||||
|
||||
#: applications/serializers/attrs/application_type/chrome.py:23
|
||||
#: applications/serializers/attrs/application_type/custom.py:25
|
||||
#: applications/serializers/attrs/application_type/mysql_workbench.py:34
|
||||
#: 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/chrome.py:19
|
||||
msgid "Chrome username"
|
||||
msgstr "Chrome 用户名"
|
||||
|
||||
#: 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"
|
||||
msgstr "运行参数"
|
||||
|
||||
#: applications/serializers/attrs/application_type/custom.py:17
|
||||
#: applications/serializers/attrs/application_type/custom.py:15
|
||||
msgid "Target url"
|
||||
msgstr "目标URL"
|
||||
|
||||
#: applications/serializers/attrs/application_type/custom.py:19
|
||||
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
|
||||
msgid "Number required"
|
||||
msgstr "需要为数字"
|
||||
|
@ -453,7 +487,7 @@ msgstr "基础"
|
|||
msgid "Charset"
|
||||
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
|
||||
msgid "Meta"
|
||||
msgstr "元数据"
|
||||
|
@ -463,7 +497,8 @@ msgid "Internal"
|
|||
msgstr "内部的"
|
||||
|
||||
#: 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"
|
||||
msgstr "系统平台"
|
||||
|
||||
|
@ -523,8 +558,8 @@ msgstr "系统架构"
|
|||
msgid "Hostname raw"
|
||||
msgstr "主机名原始"
|
||||
|
||||
#: assets/models/asset.py:216 assets/serializers/asset.py:67
|
||||
#: perms/serializers/asset/user_permission.py:41
|
||||
#: assets/models/asset.py:216 assets/serializers/account.py:15
|
||||
#: 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
|
||||
msgid "Protocols"
|
||||
msgstr "协议组"
|
||||
|
@ -569,17 +604,7 @@ msgstr "标签管理"
|
|||
msgid "Created by"
|
||||
msgstr "创建者"
|
||||
|
||||
#: assets/models/asset.py:231 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 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
|
||||
#: assets/models/authbook.py:27
|
||||
msgid "AuthBook"
|
||||
msgstr "账号"
|
||||
|
||||
|
@ -621,7 +646,7 @@ msgstr "开始日期"
|
|||
|
||||
#: assets/models/backup.py:108
|
||||
#: authentication/templates/authentication/_msg_oauth_bind.html:11
|
||||
#: notifications/notifications.py:187 ops/models/adhoc.py:246
|
||||
#: notifications/notifications.py:187 ops/models/adhoc.py:257
|
||||
#: xpack/plugins/change_auth_plan/models/base.py:112
|
||||
#: xpack/plugins/change_auth_plan/models/base.py:201
|
||||
#: xpack/plugins/gathered_user/models.py:79
|
||||
|
@ -646,7 +671,7 @@ msgid "Reason"
|
|||
msgstr "原因"
|
||||
|
||||
#: assets/models/backup.py:121 audits/serializers.py:76
|
||||
#: audits/serializers.py:91 ops/models/adhoc.py:248
|
||||
#: audits/serializers.py:91 ops/models/adhoc.py:259
|
||||
#: terminal/serializers/session.py:35
|
||||
#: xpack/plugins/change_auth_plan/models/base.py:199
|
||||
msgid "Is success"
|
||||
|
@ -678,6 +703,20 @@ msgstr "可连接性"
|
|||
msgid "Date verified"
|
||||
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
|
||||
#: xpack/plugins/change_auth_plan/models/asset.py:130
|
||||
#: xpack/plugins/change_auth_plan/models/asset.py:206
|
||||
|
@ -690,12 +729,6 @@ msgstr "SSH密钥"
|
|||
msgid "SSH public key"
|
||||
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
|
||||
msgid "Bandwidth"
|
||||
msgstr "带宽"
|
||||
|
@ -965,39 +998,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"
|
||||
msgstr "系统用户名称"
|
||||
|
||||
#: assets/serializers/asset.py:22
|
||||
#: assets/serializers/asset.py:20
|
||||
msgid "Protocol format should {}/{}"
|
||||
msgstr "协议格式 {}/{}"
|
||||
|
||||
#: assets/serializers/asset.py:39
|
||||
#: assets/serializers/asset.py:37
|
||||
msgid "Protocol duplicate: {}"
|
||||
msgstr "协议重复: {}"
|
||||
|
||||
#: assets/serializers/asset.py:68
|
||||
#: assets/serializers/asset.py:66
|
||||
msgid "Domain name"
|
||||
msgstr "网域名称"
|
||||
|
||||
#: assets/serializers/asset.py:70
|
||||
#: assets/serializers/asset.py:68
|
||||
msgid "Nodes name"
|
||||
msgstr "节点名称"
|
||||
|
||||
#: assets/serializers/asset.py:73
|
||||
#: assets/serializers/asset.py:71
|
||||
msgid "Labels name"
|
||||
msgstr "标签名称"
|
||||
|
||||
#: assets/serializers/asset.py:107
|
||||
#: assets/serializers/asset.py:105
|
||||
msgid "Hardware info"
|
||||
msgstr "硬件信息"
|
||||
|
||||
#: assets/serializers/asset.py:108
|
||||
#: assets/serializers/asset.py:106
|
||||
msgid "Admin user display"
|
||||
msgstr "特权用户名称"
|
||||
|
||||
#: assets/serializers/asset.py:109
|
||||
#: assets/serializers/asset.py:107
|
||||
msgid "CPU info"
|
||||
msgstr "CPU信息"
|
||||
|
||||
|
@ -1136,20 +1169,20 @@ msgid "The asset {} system platform {} does not support run Ansible tasks"
|
|||
msgstr "资产 {} 系统平台 {} 不支持运行 Ansible 任务"
|
||||
|
||||
#: assets/tasks/account_connectivity.py:107
|
||||
msgid "Test account connectivity: {}"
|
||||
msgstr "测试账号可连接性: {}"
|
||||
msgid "Test account connectivity: "
|
||||
msgstr "测试账号可连接性: "
|
||||
|
||||
#: assets/tasks/asset_connectivity.py:49
|
||||
msgid "Test assets connectivity"
|
||||
msgstr "测试资产可连接性"
|
||||
msgid "Test assets connectivity. "
|
||||
msgstr "测试资产可连接性. "
|
||||
|
||||
#: assets/tasks/asset_connectivity.py:91 assets/tasks/asset_connectivity.py:102
|
||||
msgid "Test assets connectivity: {}"
|
||||
msgstr "测试资产可连接性: {}"
|
||||
msgid "Test assets connectivity: "
|
||||
msgstr "测试资产可连接性: "
|
||||
|
||||
#: assets/tasks/asset_connectivity.py:113
|
||||
msgid "Test if the assets under the node are connectable: {}"
|
||||
msgstr "测试节点下资产是否可连接: {}"
|
||||
msgid "Test if the assets under the node are connectable: "
|
||||
msgstr "测试节点下资产是否可连接: "
|
||||
|
||||
#: assets/tasks/const.py:49
|
||||
msgid "Unreachable"
|
||||
|
@ -1164,20 +1197,20 @@ msgid "Get asset info failed: {}"
|
|||
msgstr "获取资产信息失败:{}"
|
||||
|
||||
#: assets/tasks/gather_asset_hardware_info.py:97
|
||||
msgid "Update some assets hardware info"
|
||||
msgstr "更新资产硬件信息"
|
||||
msgid "Update some assets hardware info. "
|
||||
msgstr "更新资产硬件信息. "
|
||||
|
||||
#: assets/tasks/gather_asset_hardware_info.py:114
|
||||
msgid "Update asset hardware info: {}"
|
||||
msgstr "更新资产硬件信息: {}"
|
||||
msgid "Update asset hardware info: "
|
||||
msgstr "更新资产硬件信息: "
|
||||
|
||||
#: assets/tasks/gather_asset_hardware_info.py:120
|
||||
msgid "Update assets hardware info: {}"
|
||||
msgstr "更新资产硬件信息: {}"
|
||||
msgid "Update assets hardware info: "
|
||||
msgstr "更新资产硬件信息: "
|
||||
|
||||
#: assets/tasks/gather_asset_hardware_info.py:137
|
||||
msgid "Update node asset hardware information: {}"
|
||||
msgstr "更新节点资产硬件信息: {}"
|
||||
msgid "Update node asset hardware information: "
|
||||
msgstr "更新节点资产硬件信息: "
|
||||
|
||||
#: assets/tasks/gather_asset_users.py:111
|
||||
msgid "Gather assets users"
|
||||
|
@ -1202,12 +1235,12 @@ msgid "Hosts count: {}"
|
|||
msgstr "主机数量: {}"
|
||||
|
||||
#: assets/tasks/push_system_user.py:282 assets/tasks/push_system_user.py:315
|
||||
msgid "Push system users to assets: {}"
|
||||
msgstr "推送系统用户到入资产: {}"
|
||||
msgid "Push system users to assets: "
|
||||
msgstr "推送系统用户到入资产: "
|
||||
|
||||
#: assets/tasks/push_system_user.py:294
|
||||
msgid "Push system users to asset: {}({}) => {}"
|
||||
msgstr "推送系统用户到入资产: {}({}) => {}"
|
||||
msgid "Push system users to asset: "
|
||||
msgstr "推送系统用户到入资产: "
|
||||
|
||||
#: assets/tasks/system_user_connectivity.py:56
|
||||
msgid "Dynamic system user not support test"
|
||||
|
@ -1218,16 +1251,13 @@ msgid "Start test system user connectivity for platform: [{}]"
|
|||
msgstr "开始测试系统用户在该系统平台的可连接性: [{}]"
|
||||
|
||||
#: assets/tasks/system_user_connectivity.py:118
|
||||
msgid "Test system user connectivity: {}"
|
||||
msgstr "测试系统用户可连接性: {}"
|
||||
|
||||
#: assets/tasks/system_user_connectivity.py:129
|
||||
msgid "Test system user connectivity: {} => {}"
|
||||
msgstr "测试系统用户可连接性: {} => {}"
|
||||
msgid "Test system user connectivity: "
|
||||
msgstr "测试系统用户可连接性: "
|
||||
|
||||
#: assets/tasks/system_user_connectivity.py:148
|
||||
msgid "Test system user connectivity period: {}"
|
||||
msgstr "定期测试系统用户可连接性: {}"
|
||||
msgid "Test system user connectivity period: "
|
||||
msgstr "定期测试系统用户可连接性: "
|
||||
|
||||
#: assets/tasks/utils.py:17
|
||||
msgid "Asset has been disabled, skipped: {}"
|
||||
|
@ -1427,14 +1457,14 @@ msgid "Auth Token"
|
|||
msgstr "认证令牌"
|
||||
|
||||
#: audits/signals_handler.py:68 authentication/notifications.py:73
|
||||
#: authentication/views/dingtalk.py:160 authentication/views/feishu.py:148
|
||||
#: authentication/views/login.py:164 authentication/views/wecom.py:158
|
||||
#: notifications/backends/__init__.py:11 users/models/user.py:607
|
||||
msgid "WeCom"
|
||||
msgstr "企业微信"
|
||||
|
||||
#: audits/signals_handler.py:69 authentication/views/login.py:170
|
||||
#: notifications/backends/__init__.py:12 users/models/user.py:608
|
||||
#: audits/signals_handler.py:69 authentication/views/dingtalk.py:160
|
||||
#: authentication/views/login.py:170 notifications/backends/__init__.py:12
|
||||
#: users/models/user.py:608
|
||||
msgid "DingTalk"
|
||||
msgstr "钉钉"
|
||||
|
||||
|
@ -1625,7 +1655,11 @@ msgstr "{ApplicationPermission} 移除 {SystemUser}"
|
|||
msgid "Invalid token"
|
||||
msgstr "无效的令牌"
|
||||
|
||||
#: authentication/api/mfa.py:103
|
||||
#: authentication/api/mfa.py:72
|
||||
msgid "Current user not support mfa type: {}"
|
||||
msgstr "当前用户不支持 MFA 类型: {}"
|
||||
|
||||
#: authentication/api/mfa.py:119
|
||||
msgid "Code is invalid, {}"
|
||||
msgstr "验证码无效: {}"
|
||||
|
||||
|
@ -1794,15 +1828,15 @@ msgstr "该 时间段 不被允许登录"
|
|||
msgid "SSO auth closed"
|
||||
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"
|
||||
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"
|
||||
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"
|
||||
msgstr "您的密码已过期,先修改再登录"
|
||||
|
||||
|
@ -1898,7 +1932,7 @@ msgstr "清空手机号码禁用"
|
|||
msgid "The MFA type ({}) is not enabled"
|
||||
msgstr "该 MFA ({}) 方式没有启用"
|
||||
|
||||
#: authentication/mixins.py:349
|
||||
#: authentication/mixins.py:354
|
||||
msgid "Please change your password"
|
||||
msgstr "请修改密码"
|
||||
|
||||
|
@ -2006,7 +2040,7 @@ msgstr "代码错误"
|
|||
#: authentication/templates/authentication/_msg_reset_password.html:3
|
||||
#: authentication/templates/authentication/_msg_rest_password_success.html:2
|
||||
#: authentication/templates/authentication/_msg_rest_public_key_success.html:2
|
||||
#: jumpserver/conf.py:293
|
||||
#: jumpserver/conf.py:293 ops/tasks.py:145 ops/tasks.py:148
|
||||
#: perms/templates/perms/_msg_item_permissions_expire.html:3
|
||||
#: perms/templates/perms/_msg_permed_items_expire.html:3
|
||||
#: users/templates/users/_msg_account_expire_reminder.html:4
|
||||
|
@ -2206,6 +2240,11 @@ msgstr "飞书查询用户失败"
|
|||
msgid "The FeiShu is already bound to another user"
|
||||
msgstr "该飞书已经绑定其他用户"
|
||||
|
||||
#: authentication/views/feishu.py:148 authentication/views/login.py:176
|
||||
#: notifications/backends/__init__.py:14 users/models/user.py:609
|
||||
msgid "FeiShu"
|
||||
msgstr "飞书"
|
||||
|
||||
#: authentication/views/feishu.py:149
|
||||
msgid "Binding FeiShu successfully"
|
||||
msgstr "绑定 飞书 成功"
|
||||
|
@ -2234,11 +2273,6 @@ msgstr "正在跳转到 {} 认证"
|
|||
msgid "Please enable cookies and try again."
|
||||
msgstr "设置你的浏览器支持cookie"
|
||||
|
||||
#: authentication/views/login.py:176 notifications/backends/__init__.py:14
|
||||
#: users/models/user.py:609
|
||||
msgid "FeiShu"
|
||||
msgstr "飞书"
|
||||
|
||||
#: authentication/views/login.py:265
|
||||
msgid ""
|
||||
"Wait for <b>{}</b> confirm, You also can copy link to her/him <br/>\n"
|
||||
|
@ -2350,6 +2384,10 @@ msgstr "被其他对象关联,不能删除"
|
|||
msgid "This action require verify your MFA"
|
||||
msgstr "这个操作需要验证 MFA"
|
||||
|
||||
#: common/exceptions.py:53
|
||||
msgid "Unexpect error occur"
|
||||
msgstr ""
|
||||
|
||||
#: common/fields/model.py:80
|
||||
msgid "Marshal dict data to char field"
|
||||
msgstr "编码 dict 为 char"
|
||||
|
@ -2558,56 +2596,56 @@ msgstr "单位: 时"
|
|||
msgid "Callback"
|
||||
msgstr "回调"
|
||||
|
||||
#: ops/models/adhoc.py:143
|
||||
#: ops/models/adhoc.py:154
|
||||
msgid "Tasks"
|
||||
msgstr "任务"
|
||||
|
||||
#: ops/models/adhoc.py:144
|
||||
#: ops/models/adhoc.py:155
|
||||
msgid "Pattern"
|
||||
msgstr "模式"
|
||||
|
||||
#: ops/models/adhoc.py:145
|
||||
#: ops/models/adhoc.py:156
|
||||
msgid "Options"
|
||||
msgstr "选项"
|
||||
|
||||
#: ops/models/adhoc.py:147
|
||||
#: ops/models/adhoc.py:158
|
||||
msgid "Run as admin"
|
||||
msgstr "再次执行"
|
||||
|
||||
#: ops/models/adhoc.py:150
|
||||
#: ops/models/adhoc.py:161
|
||||
msgid "Become"
|
||||
msgstr "Become"
|
||||
|
||||
#: ops/models/adhoc.py:151
|
||||
#: ops/models/adhoc.py:162
|
||||
msgid "Create by"
|
||||
msgstr "创建者"
|
||||
|
||||
#: ops/models/adhoc.py:240
|
||||
#: ops/models/adhoc.py:251
|
||||
msgid "Task display"
|
||||
msgstr "任务名称"
|
||||
|
||||
#: ops/models/adhoc.py:242
|
||||
#: ops/models/adhoc.py:253
|
||||
msgid "Host amount"
|
||||
msgstr "主机数量"
|
||||
|
||||
#: ops/models/adhoc.py:244
|
||||
#: ops/models/adhoc.py:255
|
||||
msgid "Start time"
|
||||
msgstr "开始时间"
|
||||
|
||||
#: ops/models/adhoc.py:245
|
||||
#: ops/models/adhoc.py:256
|
||||
msgid "End time"
|
||||
msgstr "完成时间"
|
||||
|
||||
#: ops/models/adhoc.py:247 ops/models/command.py:28
|
||||
#: ops/models/adhoc.py:258 ops/models/command.py:28
|
||||
#: terminal/serializers/session.py:39
|
||||
msgid "Is finished"
|
||||
msgstr "是否完成"
|
||||
|
||||
#: ops/models/adhoc.py:249
|
||||
#: ops/models/adhoc.py:260
|
||||
msgid "Adhoc raw result"
|
||||
msgstr "结果"
|
||||
|
||||
#: ops/models/adhoc.py:250
|
||||
#: ops/models/adhoc.py:261
|
||||
msgid "Adhoc result summary"
|
||||
msgstr "汇总"
|
||||
|
||||
|
@ -2655,11 +2693,11 @@ msgstr "内存使用率超过 {max_threshold}%: => {value}"
|
|||
msgid "CPU load more than {max_threshold}: => {value}"
|
||||
msgstr "CPU 使用率超过 {max_threshold}: => {value}"
|
||||
|
||||
#: ops/tasks.py:71
|
||||
#: ops/tasks.py:72
|
||||
msgid "Clean task history period"
|
||||
msgstr "定期清除任务历史"
|
||||
|
||||
#: ops/tasks.py:84
|
||||
#: ops/tasks.py:85
|
||||
msgid "Clean celery log period"
|
||||
msgstr "定期清除Celery日志"
|
||||
|
||||
|
@ -2819,7 +2857,7 @@ msgstr "用户组数量"
|
|||
msgid "System users amount"
|
||||
msgstr "系统用户数量"
|
||||
|
||||
#: perms/serializers/application/permission.py:88
|
||||
#: perms/serializers/application/permission.py:79
|
||||
msgid ""
|
||||
"The application list contains applications that are different from the "
|
||||
"permission type. ({})"
|
||||
|
@ -2895,7 +2933,7 @@ msgstr "获取 LDAP 用户为 None"
|
|||
msgid "Imported {} users successfully (Organization: {})"
|
||||
msgstr "成功导入 {} 个用户 ( 组织: {} )"
|
||||
|
||||
#: settings/models.py:196 users/templates/users/reset_password.html:29
|
||||
#: settings/models.py:195 users/templates/users/reset_password.html:29
|
||||
msgid "Setting"
|
||||
msgstr "设置"
|
||||
|
||||
|
@ -4938,7 +4976,7 @@ msgstr "流程"
|
|||
msgid "TicketFlow"
|
||||
msgstr "工单流程"
|
||||
|
||||
#: tickets/models/ticket.py:296
|
||||
#: tickets/models/ticket.py:297
|
||||
msgid "Please try again"
|
||||
msgstr "请再次尝试"
|
||||
|
||||
|
@ -5750,10 +5788,6 @@ msgstr "改密计划执行"
|
|||
msgid "Change auth plan task"
|
||||
msgstr "改密计划任务"
|
||||
|
||||
#: xpack/plugins/change_auth_plan/models/base.py:24
|
||||
msgid "Custom password"
|
||||
msgstr "自定义密码"
|
||||
|
||||
#: xpack/plugins/change_auth_plan/models/base.py:25
|
||||
msgid "All assets use the same random password"
|
||||
msgstr "使用相同的随机密码"
|
||||
|
@ -6311,6 +6345,15 @@ msgstr "旗舰版"
|
|||
msgid "Community edition"
|
||||
msgstr "社区版"
|
||||
|
||||
#~ msgid "MFA type not support: {}"
|
||||
#~ msgstr "MFA 类型不支持:{}"
|
||||
|
||||
#~ msgid "Push system users to asset: {}({}) => {}"
|
||||
#~ msgstr "推送系统用户到入资产: {}({}) => {}"
|
||||
|
||||
#~ msgid "Test system user connectivity: {} => {}"
|
||||
#~ msgstr "测试系统用户可连接性: {} => {}"
|
||||
|
||||
#~ msgid "Account backup plan execution"
|
||||
#~ msgstr "改密计划执行"
|
||||
|
||||
|
|
|
@ -13,5 +13,6 @@ class OpsConfig(AppConfig):
|
|||
from orgs.utils import set_current_org
|
||||
set_current_org(Organization.root())
|
||||
from .celery import signal_handler
|
||||
from . import signals_handler
|
||||
from . import notifications
|
||||
super().ready()
|
||||
|
|
|
@ -9,7 +9,7 @@ from celery import current_task
|
|||
from django.db import models
|
||||
from django.conf import settings
|
||||
from django.utils import timezone
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
from django.utils.translation import ugettext_lazy as _, gettext
|
||||
|
||||
from common.utils import get_logger, lazyproperty
|
||||
from common.fields.model import (
|
||||
|
@ -58,6 +58,17 @@ class Task(PeriodTaskModelMixin, OrgModelMixin):
|
|||
else:
|
||||
return False
|
||||
|
||||
@lazyproperty
|
||||
def display_name(self):
|
||||
sps = ['. ', ': ']
|
||||
spb = {str(sp in self.name): sp for sp in sps}
|
||||
sp = spb.get('True')
|
||||
if not sp:
|
||||
return self.name
|
||||
|
||||
tpl, data = self.name.split(sp, 1)
|
||||
return gettext(tpl + sp) + data
|
||||
|
||||
@property
|
||||
def timedelta(self):
|
||||
if self.latest_execution:
|
||||
|
|
|
@ -56,7 +56,7 @@ class TaskSerializer(BulkOrgResourceModelSerializer):
|
|||
|
||||
class Meta:
|
||||
model = Task
|
||||
fields_mini = ['id', 'name']
|
||||
fields_mini = ['id', 'name', 'display_name']
|
||||
fields_small = fields_mini + [
|
||||
'interval', 'crontab',
|
||||
'is_periodic', 'is_deleted',
|
||||
|
|
|
@ -0,0 +1,34 @@
|
|||
from django.utils import translation
|
||||
from django.core.cache import cache
|
||||
from celery.signals import task_prerun, task_postrun, before_task_publish
|
||||
|
||||
from common.db.utils import close_old_connections
|
||||
|
||||
|
||||
TASK_LANG_CACHE_KEY = 'TASK_LANG_{}'
|
||||
TASK_LANG_CACHE_TTL = 1800
|
||||
|
||||
|
||||
@before_task_publish.connect()
|
||||
def before_task_publish(headers=None, **kwargs):
|
||||
task_id = headers.get('id')
|
||||
current_lang = translation.get_language()
|
||||
key = TASK_LANG_CACHE_KEY.format(task_id)
|
||||
cache.set(key, current_lang, 1800)
|
||||
|
||||
|
||||
@task_prerun.connect()
|
||||
def on_celery_task_pre_run(task_id='', **kwargs):
|
||||
# 关闭之前的数据库连接
|
||||
close_old_connections()
|
||||
|
||||
# 保存 Lang context
|
||||
key = TASK_LANG_CACHE_KEY.format(task_id)
|
||||
task_lang = cache.get(key)
|
||||
if task_lang:
|
||||
translation.activate(task_lang)
|
||||
|
||||
|
||||
@task_postrun.connect()
|
||||
def on_celery_task_post_run(**kwargs):
|
||||
close_old_connections()
|
|
@ -5,9 +5,10 @@ import time
|
|||
|
||||
from django.conf import settings
|
||||
from celery import shared_task, subtask
|
||||
|
||||
from celery.exceptions import SoftTimeLimitExceeded
|
||||
from django.utils import timezone
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
from django.utils.translation import ugettext_lazy as _, gettext
|
||||
|
||||
from common.utils import get_logger, get_object_or_none, get_log_keep_day
|
||||
from orgs.utils import tmp_to_root_org, tmp_to_org
|
||||
|
@ -141,10 +142,10 @@ def hello(name, callback=None):
|
|||
import time
|
||||
|
||||
count = User.objects.count()
|
||||
print("Hello {}".format(name))
|
||||
print(gettext("Hello") + ': ' + name)
|
||||
print("Count: ", count)
|
||||
time.sleep(1)
|
||||
return count
|
||||
return gettext("Hello")
|
||||
|
||||
|
||||
@shared_task
|
||||
|
@ -177,3 +178,4 @@ def add_m(x):
|
|||
s.append(add.s(i))
|
||||
res = chain(*tuple(s))()
|
||||
return res
|
||||
|
||||
|
|
|
@ -6,14 +6,14 @@ from django.utils.translation import ugettext_lazy as _
|
|||
|
||||
from orgs.mixins.serializers import BulkOrgResourceModelSerializer
|
||||
from perms.models import ApplicationPermission
|
||||
from ..base import ActionsField
|
||||
from ..base import ActionsField, BasePermissionSerializer
|
||||
|
||||
__all__ = [
|
||||
'ApplicationPermissionSerializer'
|
||||
]
|
||||
|
||||
|
||||
class ApplicationPermissionSerializer(BulkOrgResourceModelSerializer):
|
||||
class ApplicationPermissionSerializer(BasePermissionSerializer):
|
||||
actions = ActionsField(required=False, allow_null=True, label=_("Actions"))
|
||||
category_display = serializers.ReadOnlyField(source='get_category_display', label=_('Category display'))
|
||||
type_display = serializers.ReadOnlyField(source='get_type_display', label=_('Type display'))
|
||||
|
@ -46,15 +46,7 @@ class ApplicationPermissionSerializer(BulkOrgResourceModelSerializer):
|
|||
'applications_amount': {'label': _('Applications amount')},
|
||||
}
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
self.set_actions_choices()
|
||||
|
||||
def set_actions_choices(self):
|
||||
actions = self.fields.get('actions')
|
||||
if not actions:
|
||||
return
|
||||
choices = actions._choices
|
||||
def _filter_actions_choices(self, choices):
|
||||
if request := self.context.get('request'):
|
||||
category = request.query_params.get('category')
|
||||
else:
|
||||
|
@ -62,8 +54,7 @@ class ApplicationPermissionSerializer(BulkOrgResourceModelSerializer):
|
|||
exclude_choices = ApplicationPermission.get_exclude_actions_choices(category=category)
|
||||
for choice in exclude_choices:
|
||||
choices.pop(choice, None)
|
||||
actions._choices = choices
|
||||
actions.default = list(choices.keys())
|
||||
return choices
|
||||
|
||||
@classmethod
|
||||
def setup_eager_loading(cls, queryset):
|
||||
|
|
|
@ -9,12 +9,12 @@ from orgs.mixins.serializers import BulkOrgResourceModelSerializer
|
|||
from perms.models import AssetPermission, Action
|
||||
from assets.models import Asset, Node, SystemUser
|
||||
from users.models import User, UserGroup
|
||||
from ..base import ActionsField
|
||||
from ..base import ActionsField, BasePermissionSerializer
|
||||
|
||||
__all__ = ['AssetPermissionSerializer']
|
||||
|
||||
|
||||
class AssetPermissionSerializer(BulkOrgResourceModelSerializer):
|
||||
class AssetPermissionSerializer(BasePermissionSerializer):
|
||||
actions = ActionsField(required=False, allow_null=True, label=_("Actions"))
|
||||
is_valid = serializers.BooleanField(read_only=True, label=_("Is valid"))
|
||||
is_expired = serializers.BooleanField(read_only=True, label=_('Is expired'))
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
from rest_framework import serializers
|
||||
from perms.models import Action
|
||||
from orgs.mixins.serializers import BulkOrgResourceModelSerializer
|
||||
|
||||
__all__ = ['ActionsDisplayField', 'ActionsField']
|
||||
__all__ = ['ActionsDisplayField', 'ActionsField', 'BasePermissionSerializer']
|
||||
|
||||
|
||||
class ActionsField(serializers.MultipleChoiceField):
|
||||
|
@ -24,3 +25,21 @@ class ActionsDisplayField(ActionsField):
|
|||
choices = dict(Action.CHOICES)
|
||||
return [choices.get(i) for i in values]
|
||||
|
||||
|
||||
class BasePermissionSerializer(BulkOrgResourceModelSerializer):
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
self.set_actions_field()
|
||||
|
||||
def set_actions_field(self):
|
||||
actions = self.fields.get('actions')
|
||||
if not actions:
|
||||
return
|
||||
choices = actions._choices
|
||||
choices = self._filter_actions_choices(choices)
|
||||
actions._choices = choices
|
||||
actions.default = list(choices.keys())
|
||||
|
||||
def _filter_actions_choices(self, choices):
|
||||
return choices
|
||||
|
|
|
@ -2,7 +2,7 @@ from rest_framework import generics
|
|||
from rest_framework.permissions import AllowAny
|
||||
from django.conf import settings
|
||||
|
||||
from jumpserver.utils import has_valid_xpack_license
|
||||
from jumpserver.utils import has_valid_xpack_license, get_xpack_license_info
|
||||
from common.utils import get_logger
|
||||
from .. import serializers
|
||||
from ..utils import get_interface_setting
|
||||
|
@ -40,6 +40,7 @@ class PublicSettingApi(generics.RetrieveAPIView):
|
|||
"SECURITY_PASSWORD_EXPIRATION_TIME": settings.SECURITY_PASSWORD_EXPIRATION_TIME,
|
||||
"SECURITY_LUNA_REMEMBER_AUTH": settings.SECURITY_LUNA_REMEMBER_AUTH,
|
||||
"XPACK_LICENSE_IS_VALID": has_valid_xpack_license(),
|
||||
"XPACK_LICENSE_INFO": get_xpack_license_info(),
|
||||
"LOGIN_TITLE": self.get_login_title(),
|
||||
"LOGO_URLS": self.get_logo_urls(),
|
||||
"TICKETS_ENABLED": settings.TICKETS_ENABLED,
|
||||
|
|
|
@ -79,7 +79,6 @@ class Setting(models.Model):
|
|||
item.refresh_setting()
|
||||
|
||||
def refresh_setting(self):
|
||||
logger.debug(f"Refresh setting: {self.name}")
|
||||
if hasattr(self.__class__, f'refresh_{self.name}'):
|
||||
getattr(self.__class__, f'refresh_{self.name}')()
|
||||
else:
|
||||
|
|
|
@ -121,11 +121,7 @@
|
|||
url: url,
|
||||
method: "POST",
|
||||
body: JSON.stringify(data),
|
||||
success: onSuccess,
|
||||
error: function (text, data) {
|
||||
toastr.error(data.error)
|
||||
},
|
||||
flash_message: false
|
||||
success: onSuccess
|
||||
})
|
||||
}
|
||||
</script>
|
||||
|
|
|
@ -29,7 +29,10 @@ class TicketViewSet(CommonApiMixin, viewsets.ModelViewSet):
|
|||
search_fields = [
|
||||
'title', 'action', 'type', 'status', 'applicant_display'
|
||||
]
|
||||
ordering_fields = ('title', 'applicant_display', 'status', 'state', 'action_display', 'date_created')
|
||||
ordering_fields = (
|
||||
'title', 'applicant_display', 'status', 'state', 'action_display',
|
||||
'date_created', 'serial_num',
|
||||
)
|
||||
ordering = ('-date_created', )
|
||||
|
||||
def create(self, request, *args, **kwargs):
|
||||
|
|
|
@ -38,7 +38,7 @@ class Migration(migrations.Migration):
|
|||
migrations.AddField(
|
||||
model_name='ticket',
|
||||
name='serial_num',
|
||||
field=models.CharField(max_length=256, null=True, unique=True, verbose_name='Serial number'),
|
||||
field=models.CharField(max_length=128, null=True, unique=True, verbose_name='Serial number'),
|
||||
),
|
||||
migrations.RunPython(fill_ticket_serial_number),
|
||||
]
|
||||
|
|
|
@ -77,7 +77,7 @@ class Ticket(CommonModelMixin, OrgModelMixin):
|
|||
'TicketFlow', related_name='tickets', on_delete=models.SET_NULL, null=True,
|
||||
verbose_name=_("TicketFlow")
|
||||
)
|
||||
serial_num = models.CharField(max_length=256, unique=True, null=True, verbose_name=_('Serial number'))
|
||||
serial_num = models.CharField(max_length=128, unique=True, null=True, verbose_name=_('Serial number'))
|
||||
|
||||
class Meta:
|
||||
ordering = ('-date_created',)
|
||||
|
@ -195,7 +195,8 @@ class Ticket(CommonModelMixin, OrgModelMixin):
|
|||
def update_current_step_state_and_assignee(self, processor, state):
|
||||
if self.status_closed:
|
||||
raise AlreadyClosed
|
||||
self.state = state
|
||||
if state != TicketState.approved:
|
||||
self.state = state
|
||||
current_node = self.current_node
|
||||
current_node.update(state=state)
|
||||
current_node.first().ticket_assignees.filter(assignee=processor).update(state=state)
|
||||
|
@ -260,7 +261,7 @@ class Ticket(CommonModelMixin, OrgModelMixin):
|
|||
date_created = as_current_tz(self.date_created)
|
||||
date_prefix = date_created.strftime('%Y%m%d')
|
||||
|
||||
ticket = Ticket.objects.select_for_update().filter(
|
||||
ticket = Ticket.all().select_for_update().filter(
|
||||
serial_num__startswith=date_prefix
|
||||
).order_by('-date_created').first()
|
||||
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
|
||||
<div>
|
||||
<p>
|
||||
{{ content }}
|
||||
{{ content | safe }}
|
||||
</p>
|
||||
<p>
|
||||
{% trans 'Username' %}: {{ user.username }} <br />
|
||||
|
|
|
@ -1 +1 @@
|
|||
g++ make iputils-ping default-libmysqlclient-dev libpq-dev libffi-dev libldap2-dev libsasl2-dev sshpass pkg-config libxml2-dev libxmlsec1-dev libxmlsec1-openssl
|
||||
g++ make iputils-ping default-libmysqlclient-dev libpq-dev libffi-dev libldap2-dev libsasl2-dev sshpass pkg-config libxml2-dev libxmlsec1-dev libxmlsec1-openssl libaio-dev freetds-dev
|
||||
|
|
Loading…
Reference in New Issue