mirror of https://github.com/jumpserver/jumpserver
perf: 统一应用树 (#6535)
* perf: 添加应用树api * perf: perms tree * perf: 统一应用树 * perf: 修改icon * perf: stash it * perf: 优化应用账号 * perf: 基本完成应用账号重构 * perf: 修改翻译 Co-authored-by: ibuler <ibuler@qq.com>pull/6552/head
parent
d347ed9862
commit
905d0d5131
|
@ -1,4 +1,4 @@
|
|||
from .application import *
|
||||
from .application_user import *
|
||||
from .account import *
|
||||
from .mixin import *
|
||||
from .remote_app import *
|
||||
|
|
|
@ -0,0 +1,74 @@
|
|||
# coding: utf-8
|
||||
#
|
||||
|
||||
from django_filters import rest_framework as filters
|
||||
from django.conf import settings
|
||||
from django.db.models import F, Value, CharField
|
||||
from django.db.models.functions import Concat
|
||||
from django.http import Http404
|
||||
|
||||
from common.drf.filters import BaseFilterSet
|
||||
from common.drf.api import JMSModelViewSet
|
||||
from common.utils import unique
|
||||
from perms.models import ApplicationPermission
|
||||
from ..hands import IsOrgAdminOrAppUser, IsOrgAdmin, NeedMFAVerify
|
||||
from .. import serializers
|
||||
|
||||
|
||||
class AccountFilterSet(BaseFilterSet):
|
||||
username = filters.CharFilter(field_name='username')
|
||||
app = filters.CharFilter(field_name='applications', lookup_expr='exact')
|
||||
app_name = filters.CharFilter(field_name='app_name', lookup_expr='exact')
|
||||
app_type = filters.CharFilter(field_name='app_type', lookup_expr='exact')
|
||||
app_category = filters.CharFilter(field_name='app_category', lookup_expr='exact')
|
||||
|
||||
class Meta:
|
||||
model = ApplicationPermission
|
||||
fields = []
|
||||
|
||||
|
||||
class ApplicationAccountViewSet(JMSModelViewSet):
|
||||
permission_classes = (IsOrgAdmin, )
|
||||
search_fields = ['username', 'app_name']
|
||||
filterset_class = AccountFilterSet
|
||||
filterset_fields = ['username', 'app_name', 'app_type', 'app_category']
|
||||
serializer_class = serializers.ApplicationAccountSerializer
|
||||
|
||||
http_method_names = ['get', 'put', 'patch', 'options']
|
||||
|
||||
def get_queryset(self):
|
||||
queryset = ApplicationPermission.objects.all() \
|
||||
.annotate(uid=Concat(
|
||||
'applications', Value('_'), 'system_users', output_field=CharField()
|
||||
)) \
|
||||
.annotate(systemuser=F('system_users')) \
|
||||
.annotate(systemuser_display=F('system_users__name')) \
|
||||
.annotate(username=F('system_users__username')) \
|
||||
.annotate(password=F('system_users__password')) \
|
||||
.annotate(app=F('applications')) \
|
||||
.annotate(app_name=F("applications__name")) \
|
||||
.annotate(app_category=F("applications__category")) \
|
||||
.annotate(app_type=F("applications__type"))\
|
||||
.values('username', 'password', 'systemuser', 'systemuser_display',
|
||||
'app', 'app_name', 'app_category', 'app_type', 'uid')
|
||||
return queryset
|
||||
|
||||
def get_object(self):
|
||||
obj = self.get_queryset().filter(
|
||||
uid=self.kwargs['pk']
|
||||
).first()
|
||||
if not obj:
|
||||
raise Http404()
|
||||
return obj
|
||||
|
||||
def filter_queryset(self, queryset):
|
||||
queryset = super().filter_queryset(queryset)
|
||||
queryset_list = unique(queryset, key=lambda x: (x['app'], x['systemuser']))
|
||||
return queryset_list
|
||||
|
||||
|
||||
class ApplicationAccountSecretViewSet(ApplicationAccountViewSet):
|
||||
serializer_class = serializers.ApplicationAccountSecretSerializer
|
||||
permission_classes = [IsOrgAdminOrAppUser, NeedMFAVerify]
|
||||
http_method_names = ['get', 'options']
|
||||
|
|
@ -1,7 +1,11 @@
|
|||
# coding: utf-8
|
||||
#
|
||||
from orgs.mixins.api import OrgBulkModelViewSet
|
||||
|
||||
from orgs.mixins.api import OrgBulkModelViewSet
|
||||
from rest_framework.decorators import action
|
||||
from rest_framework.response import Response
|
||||
|
||||
from common.tree import TreeNodeSerializer
|
||||
from ..hands import IsOrgAdminOrAppUser
|
||||
from .. import serializers
|
||||
from ..models import Application
|
||||
|
@ -19,4 +23,15 @@ class ApplicationViewSet(OrgBulkModelViewSet):
|
|||
}
|
||||
search_fields = ('name', 'type', 'category')
|
||||
permission_classes = (IsOrgAdminOrAppUser,)
|
||||
serializer_class = serializers.ApplicationSerializer
|
||||
serializer_classes = {
|
||||
'default': serializers.ApplicationSerializer,
|
||||
'get_tree': TreeNodeSerializer
|
||||
}
|
||||
|
||||
@action(methods=['GET'], detail=False, url_path='tree')
|
||||
def get_tree(self, request, *args, **kwargs):
|
||||
show_count = request.query_params.get('show_count', '1') == '1'
|
||||
queryset = self.filter_queryset(self.get_queryset())
|
||||
tree_nodes = Application.create_tree_nodes(queryset, show_count=show_count)
|
||||
serializer = self.get_serializer(tree_nodes, many=True)
|
||||
return Response(serializer.data)
|
||||
|
|
|
@ -1,55 +0,0 @@
|
|||
# coding: utf-8
|
||||
#
|
||||
|
||||
from rest_framework import generics
|
||||
from django.conf import settings
|
||||
|
||||
from ..hands import IsOrgAdminOrAppUser, IsOrgAdmin, NeedMFAVerify
|
||||
from .. import serializers
|
||||
from ..models import Application, ApplicationUser
|
||||
from perms.models import ApplicationPermission
|
||||
|
||||
|
||||
class ApplicationUserListApi(generics.ListAPIView):
|
||||
permission_classes = (IsOrgAdmin, )
|
||||
filterset_fields = ('name', 'username')
|
||||
search_fields = filterset_fields
|
||||
serializer_class = serializers.ApplicationUserSerializer
|
||||
_application = None
|
||||
|
||||
@property
|
||||
def application(self):
|
||||
if self._application is None:
|
||||
app_id = self.request.query_params.get('application_id')
|
||||
if app_id:
|
||||
self._application = Application.objects.get(id=app_id)
|
||||
return self._application
|
||||
|
||||
def get_serializer_context(self):
|
||||
context = super().get_serializer_context()
|
||||
context.update({
|
||||
'application': self.application
|
||||
})
|
||||
return context
|
||||
|
||||
def get_queryset(self):
|
||||
queryset = ApplicationUser.objects.none()
|
||||
if not self.application:
|
||||
return queryset
|
||||
system_user_ids = ApplicationPermission.objects.filter(applications=self.application)\
|
||||
.values_list('system_users', flat=True)
|
||||
if not system_user_ids:
|
||||
return queryset
|
||||
queryset = ApplicationUser.objects.filter(id__in=system_user_ids)
|
||||
return queryset
|
||||
|
||||
|
||||
class ApplicationUserAuthInfoListApi(ApplicationUserListApi):
|
||||
serializer_class = serializers.ApplicationUserWithAuthInfoSerializer
|
||||
http_method_names = ['get']
|
||||
permission_classes = [IsOrgAdminOrAppUser]
|
||||
|
||||
def get_permissions(self):
|
||||
if settings.SECURITY_VIEW_AUTH_NEED_MFA:
|
||||
self.permission_classes = [IsOrgAdminOrAppUser, NeedMFAVerify]
|
||||
return super().get_permissions()
|
|
@ -1,89 +1,52 @@
|
|||
from orgs.models import Organization
|
||||
from django.utils.translation import ugettext as _
|
||||
|
||||
from common.tree import TreeNode
|
||||
from orgs.models import Organization
|
||||
from ..models import Application
|
||||
|
||||
__all__ = ['SerializeApplicationToTreeNodeMixin']
|
||||
|
||||
|
||||
class SerializeApplicationToTreeNodeMixin:
|
||||
|
||||
@staticmethod
|
||||
def _serialize_db(db):
|
||||
return {
|
||||
'id': db.id,
|
||||
'name': db.name,
|
||||
'title': db.name,
|
||||
'pId': '',
|
||||
'open': False,
|
||||
'iconSkin': 'database',
|
||||
'meta': {'type': 'database_app'}
|
||||
}
|
||||
|
||||
@staticmethod
|
||||
def _serialize_remote_app(remote_app):
|
||||
return {
|
||||
'id': remote_app.id,
|
||||
'name': remote_app.name,
|
||||
'title': remote_app.name,
|
||||
'pId': '',
|
||||
'open': False,
|
||||
'isParent': False,
|
||||
'iconSkin': 'chrome',
|
||||
'meta': {'type': 'remote_app'}
|
||||
}
|
||||
|
||||
@staticmethod
|
||||
def _serialize_cloud(cloud):
|
||||
return {
|
||||
'id': cloud.id,
|
||||
'name': cloud.name,
|
||||
'title': cloud.name,
|
||||
'pId': '',
|
||||
'open': False,
|
||||
'isParent': False,
|
||||
'iconSkin': 'k8s',
|
||||
'meta': {'type': 'k8s_app'}
|
||||
}
|
||||
|
||||
def _serialize_application(self, application):
|
||||
method_name = f'_serialize_{application.category}'
|
||||
data = getattr(self, method_name)(application)
|
||||
data.update({
|
||||
'pId': application.org.id,
|
||||
'org_name': application.org_name
|
||||
})
|
||||
return data
|
||||
|
||||
def serialize_applications(self, applications):
|
||||
data = [self._serialize_application(application) for application in applications]
|
||||
return data
|
||||
|
||||
@staticmethod
|
||||
def _serialize_organization(org):
|
||||
return {
|
||||
'id': org.id,
|
||||
'name': org.name,
|
||||
'title': org.name,
|
||||
'pId': '',
|
||||
'open': True,
|
||||
'isParent': True,
|
||||
'meta': {
|
||||
'type': 'node'
|
||||
}
|
||||
}
|
||||
|
||||
def serialize_organizations(self, organizations):
|
||||
data = [self._serialize_organization(org) for org in organizations]
|
||||
return data
|
||||
|
||||
@staticmethod
|
||||
def filter_organizations(applications):
|
||||
organization_ids = set(applications.values_list('org_id', flat=True))
|
||||
organizations = [Organization.get_instance(org_id) for org_id in organization_ids]
|
||||
return organizations
|
||||
|
||||
@staticmethod
|
||||
def create_root_node():
|
||||
name = _('My applications')
|
||||
node = TreeNode(**{
|
||||
'id': 'applications',
|
||||
'name': name,
|
||||
'title': name,
|
||||
'pId': '',
|
||||
'open': True,
|
||||
'isParent': True,
|
||||
'meta': {
|
||||
'type': 'root'
|
||||
}
|
||||
})
|
||||
return node
|
||||
|
||||
def serialize_applications_with_org(self, applications):
|
||||
root_node = self.create_root_node()
|
||||
tree_nodes = [root_node]
|
||||
organizations = self.filter_organizations(applications)
|
||||
data_organizations = self.serialize_organizations(organizations)
|
||||
data_applications = self.serialize_applications(applications)
|
||||
data = data_organizations + data_applications
|
||||
return data
|
||||
|
||||
for i, org in enumerate(organizations):
|
||||
# 组织节点
|
||||
org_node = org.as_tree_node(pid=root_node.id)
|
||||
tree_nodes.append(org_node)
|
||||
org_applications = applications.filter(org_id=org.id)
|
||||
count = org_applications.count()
|
||||
org_node.name += '({})'.format(count)
|
||||
|
||||
# 各应用节点
|
||||
apps_nodes = Application.create_tree_nodes(
|
||||
queryset=org_applications, root_node=org_node,
|
||||
show_empty=False
|
||||
)
|
||||
tree_nodes += apps_nodes
|
||||
return tree_nodes
|
||||
|
|
|
@ -1,11 +1,10 @@
|
|||
# coding: utf-8
|
||||
#
|
||||
|
||||
from django.db.models import TextChoices
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
|
||||
class ApplicationCategoryChoices(TextChoices):
|
||||
class AppCategory(TextChoices):
|
||||
db = 'db', _('Database')
|
||||
remote_app = 'remote_app', _('Remote app')
|
||||
cloud = 'cloud', 'Cloud'
|
||||
|
@ -15,7 +14,7 @@ class ApplicationCategoryChoices(TextChoices):
|
|||
return dict(cls.choices).get(category, '')
|
||||
|
||||
|
||||
class ApplicationTypeChoices(TextChoices):
|
||||
class AppType(TextChoices):
|
||||
# db category
|
||||
mysql = 'mysql', 'MySQL'
|
||||
oracle = 'oracle', 'Oracle'
|
||||
|
@ -31,19 +30,38 @@ class ApplicationTypeChoices(TextChoices):
|
|||
# cloud category
|
||||
k8s = 'k8s', 'Kubernetes'
|
||||
|
||||
@classmethod
|
||||
def category_types_mapper(cls):
|
||||
return {
|
||||
AppCategory.db: [cls.mysql, cls.oracle, cls.pgsql, cls.mariadb],
|
||||
AppCategory.remote_app: [cls.chrome, cls.mysql_workbench, cls.vmware_client, cls.custom],
|
||||
AppCategory.cloud: [cls.k8s]
|
||||
}
|
||||
|
||||
@classmethod
|
||||
def type_category_mapper(cls):
|
||||
mapper = {}
|
||||
for category, tps in cls.category_types_mapper().items():
|
||||
for tp in tps:
|
||||
mapper[tp] = category
|
||||
return mapper
|
||||
|
||||
@classmethod
|
||||
def get_label(cls, tp):
|
||||
return dict(cls.choices).get(tp, '')
|
||||
|
||||
@classmethod
|
||||
def db_types(cls):
|
||||
return [cls.mysql.value, cls.oracle.value, cls.pgsql.value, cls.mariadb.value]
|
||||
return [tp.value for tp in cls.category_types_mapper()[AppCategory.db]]
|
||||
|
||||
@classmethod
|
||||
def remote_app_types(cls):
|
||||
return [cls.chrome.value, cls.mysql_workbench.value, cls.vmware_client.value, cls.custom.value]
|
||||
return [tp.value for tp in cls.category_types_mapper()[AppCategory.remote_app]]
|
||||
|
||||
@classmethod
|
||||
def cloud_types(cls):
|
||||
return [cls.k8s.value]
|
||||
return [tp.value for tp in cls.category_types_mapper()[AppCategory.cloud]]
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -1,19 +1,170 @@
|
|||
from django.db import models
|
||||
from django.db.models import Count
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
from orgs.mixins.models import OrgModelMixin
|
||||
from common.mixins import CommonModelMixin
|
||||
from common.tree import TreeNode
|
||||
from assets.models import Asset, SystemUser
|
||||
from .. import const
|
||||
|
||||
|
||||
class Application(CommonModelMixin, OrgModelMixin):
|
||||
class ApplicationTreeNodeMixin:
|
||||
id: str
|
||||
name: str
|
||||
type: str
|
||||
category: str
|
||||
|
||||
@classmethod
|
||||
def create_choice_node(cls, c, pid, tp, opened=False, counts=None,
|
||||
show_empty=True, show_count=True):
|
||||
count = counts.get(c.name, 0)
|
||||
if count == 0 and not show_empty:
|
||||
return None
|
||||
name = c.name
|
||||
label = c.label
|
||||
if count is not None and show_count:
|
||||
label = '{} ({})'.format(label, count)
|
||||
data = {
|
||||
'id': name,
|
||||
'name': label,
|
||||
'title': label,
|
||||
'pId': pid,
|
||||
'isParent': bool(count),
|
||||
'open': opened,
|
||||
'iconSkin': '',
|
||||
'meta': {
|
||||
'type': tp,
|
||||
}
|
||||
}
|
||||
return TreeNode(**data)
|
||||
|
||||
@classmethod
|
||||
def create_root_tree_node(cls, queryset, show_count=True):
|
||||
count = queryset.count() if show_count else None
|
||||
root_id = 'applications'
|
||||
root_name = _('Applications')
|
||||
if count is not None and show_count:
|
||||
root_name = '{} ({})'.format(root_name, count)
|
||||
node = TreeNode(**{
|
||||
'id': root_id,
|
||||
'name': root_name,
|
||||
'title': root_name,
|
||||
'pId': '',
|
||||
'isParent': True,
|
||||
'open': True,
|
||||
'iconSkin': '',
|
||||
'meta': {
|
||||
'type': 'applications_root',
|
||||
}
|
||||
})
|
||||
return node
|
||||
|
||||
@classmethod
|
||||
def create_category_tree_nodes(cls, root_node, counts=None, show_empty=True, show_count=True):
|
||||
nodes = []
|
||||
categories = const.AppType.category_types_mapper().keys()
|
||||
for category in categories:
|
||||
node = cls.create_choice_node(
|
||||
category, pid=root_node.id, tp='category',
|
||||
counts=counts, opened=True, show_empty=show_empty,
|
||||
show_count=show_count
|
||||
)
|
||||
if not node:
|
||||
continue
|
||||
nodes.append(node)
|
||||
return nodes
|
||||
|
||||
@classmethod
|
||||
def create_types_tree_nodes(cls, counts, show_empty=True, show_count=True):
|
||||
nodes = []
|
||||
type_category_mapper = const.AppType.type_category_mapper()
|
||||
for tp in const.AppType.type_category_mapper().keys():
|
||||
category = type_category_mapper.get(tp)
|
||||
node = cls.create_choice_node(
|
||||
tp, pid=category.name, tp='type', counts=counts,
|
||||
show_empty=show_empty, show_count=show_count
|
||||
)
|
||||
if not node:
|
||||
continue
|
||||
nodes.append(node)
|
||||
return nodes
|
||||
|
||||
@staticmethod
|
||||
def get_tree_node_counts(queryset):
|
||||
counts = {'applications': queryset.count()}
|
||||
category_counts = queryset.values('category') \
|
||||
.annotate(count=Count('id')) \
|
||||
.order_by()
|
||||
for item in category_counts:
|
||||
counts[item['category']] = item['count']
|
||||
|
||||
type_counts = queryset.values('type') \
|
||||
.annotate(count=Count('id')) \
|
||||
.order_by()
|
||||
for item in type_counts:
|
||||
counts[item['type']] = item['count']
|
||||
return counts
|
||||
|
||||
@classmethod
|
||||
def create_tree_nodes(cls, queryset, root_node=None, show_empty=True, show_count=True):
|
||||
counts = cls.get_tree_node_counts(queryset) if show_count else {}
|
||||
tree_nodes = []
|
||||
|
||||
# 根节点有可能是组织名称
|
||||
if root_node is None:
|
||||
root_node = cls.create_root_tree_node(queryset, show_count=show_count)
|
||||
tree_nodes.append(root_node)
|
||||
|
||||
# 类别的节点
|
||||
tree_nodes += cls.create_category_tree_nodes(
|
||||
root_node, counts, show_empty=show_empty,
|
||||
show_count=show_count
|
||||
)
|
||||
|
||||
# 类型的节点
|
||||
tree_nodes += cls.create_types_tree_nodes(
|
||||
counts, show_empty=show_empty, show_count=show_count
|
||||
)
|
||||
|
||||
# 应用的节点
|
||||
for app in queryset:
|
||||
tree_nodes.append(app.as_tree_node())
|
||||
return tree_nodes
|
||||
|
||||
def as_tree_node(self):
|
||||
icon_skin_category_mapper = {
|
||||
'remote_app': 'chrome',
|
||||
'db': 'database',
|
||||
'cloud': 'cloud'
|
||||
}
|
||||
icon_skin = icon_skin_category_mapper.get(self.category, 'file')
|
||||
node = TreeNode(**{
|
||||
'id': str(self.id),
|
||||
'name': self.name,
|
||||
'title': self.name,
|
||||
'pId': str(self.type),
|
||||
'isParent': False,
|
||||
'open': False,
|
||||
'iconSkin': icon_skin,
|
||||
'meta': {
|
||||
'type': 'application',
|
||||
'data': {
|
||||
'category': self.category,
|
||||
'type': self.type,
|
||||
}
|
||||
}
|
||||
})
|
||||
return node
|
||||
|
||||
|
||||
class Application(CommonModelMixin, OrgModelMixin, ApplicationTreeNodeMixin):
|
||||
name = models.CharField(max_length=128, verbose_name=_('Name'))
|
||||
category = models.CharField(
|
||||
max_length=16, choices=const.ApplicationCategoryChoices.choices, verbose_name=_('Category')
|
||||
max_length=16, choices=const.AppCategory.choices, verbose_name=_('Category')
|
||||
)
|
||||
type = models.CharField(
|
||||
max_length=16, choices=const.ApplicationTypeChoices.choices, verbose_name=_('Type')
|
||||
max_length=16, choices=const.AppType.choices, verbose_name=_('Type')
|
||||
)
|
||||
domain = models.ForeignKey(
|
||||
'assets.Domain', null=True, blank=True, related_name='applications',
|
||||
|
@ -35,7 +186,7 @@ class Application(CommonModelMixin, OrgModelMixin):
|
|||
|
||||
@property
|
||||
def category_remote_app(self):
|
||||
return self.category == const.ApplicationCategoryChoices.remote_app.value
|
||||
return self.category == const.AppCategory.remote_app.value
|
||||
|
||||
def get_rdp_remote_app_setting(self):
|
||||
from applications.serializers.attrs import get_serializer_class_by_application_type
|
||||
|
|
|
@ -6,12 +6,12 @@ from django.utils.translation import ugettext_lazy as _
|
|||
from orgs.mixins.serializers import BulkOrgResourceModelSerializer
|
||||
from common.drf.serializers import MethodSerializer
|
||||
from .attrs import category_serializer_classes_mapping, type_serializer_classes_mapping
|
||||
from assets.serializers import SystemUserSerializer
|
||||
from .. import models
|
||||
from .. import const
|
||||
|
||||
__all__ = [
|
||||
'ApplicationSerializer', 'ApplicationSerializerMixin',
|
||||
'ApplicationUserSerializer', 'ApplicationUserWithAuthInfoSerializer'
|
||||
'ApplicationAccountSerializer', 'ApplicationAccountSecretSerializer'
|
||||
]
|
||||
|
||||
|
||||
|
@ -52,9 +52,8 @@ class ApplicationSerializer(ApplicationSerializerMixin, BulkOrgResourceModelSeri
|
|||
model = models.Application
|
||||
fields_mini = ['id', 'name']
|
||||
fields_small = fields_mini + [
|
||||
'category', 'category_display', 'type', 'type_display', 'attrs',
|
||||
'date_created', 'date_updated',
|
||||
'created_by', 'comment'
|
||||
'category', 'category_display', 'type', 'type_display',
|
||||
'attrs', 'date_created', 'date_updated', 'created_by', 'comment'
|
||||
]
|
||||
fields_fk = ['domain']
|
||||
fields = fields_small + fields_fk
|
||||
|
@ -68,41 +67,34 @@ class ApplicationSerializer(ApplicationSerializerMixin, BulkOrgResourceModelSeri
|
|||
return _attrs
|
||||
|
||||
|
||||
class ApplicationUserSerializer(SystemUserSerializer):
|
||||
application_name = serializers.SerializerMethodField(label=_('Application name'))
|
||||
application_category = serializers.SerializerMethodField(label=_('Application category'))
|
||||
application_type = serializers.SerializerMethodField(label=_('Application type'))
|
||||
class ApplicationAccountSerializer(serializers.Serializer):
|
||||
username = serializers.ReadOnlyField(label=_("Username"))
|
||||
password = serializers.CharField(write_only=True, label=_("Password"))
|
||||
systemuser = serializers.ReadOnlyField(label=_('System user'))
|
||||
systemuser_display = serializers.ReadOnlyField(label=_("System user display"))
|
||||
app = serializers.ReadOnlyField(label=_('App'))
|
||||
uid = serializers.ReadOnlyField(label=_("Union id"))
|
||||
app_name = serializers.ReadOnlyField(label=_("Application name"), read_only=True)
|
||||
app_category = serializers.ChoiceField(label=_('Category'), choices=const.AppCategory.choices, read_only=True)
|
||||
app_category_display = serializers.SerializerMethodField(label=_('Category'))
|
||||
app_type = serializers.ChoiceField(label=_('Type'), choices=const.AppType.choices, read_only=True)
|
||||
app_type_display = serializers.SerializerMethodField(label=_('Type'))
|
||||
|
||||
class Meta(SystemUserSerializer.Meta):
|
||||
model = models.ApplicationUser
|
||||
fields_mini = [
|
||||
'id', 'application_name', 'application_category', 'application_type', 'name', 'username'
|
||||
]
|
||||
fields_small = fields_mini + [
|
||||
'protocol', 'login_mode', 'login_mode_display', 'priority',
|
||||
"username_same_with_user", 'comment',
|
||||
]
|
||||
fields = fields_small
|
||||
extra_kwargs = {
|
||||
'login_mode_display': {'label': _('Login mode display')},
|
||||
'created_by': {'read_only': True},
|
||||
}
|
||||
category_mapper = dict(const.AppCategory.choices)
|
||||
type_mapper = dict(const.AppType.choices)
|
||||
|
||||
@property
|
||||
def application(self):
|
||||
return self.context['application']
|
||||
def create(self, validated_data):
|
||||
pass
|
||||
|
||||
def get_application_name(self, obj):
|
||||
return self.application.name
|
||||
def update(self, instance, validated_data):
|
||||
pass
|
||||
|
||||
def get_application_category(self, obj):
|
||||
return self.application.get_category_display()
|
||||
def get_app_category_display(self, obj):
|
||||
return self.category_mapper.get(obj['app_category'])
|
||||
|
||||
def get_application_type(self, obj):
|
||||
return self.application.get_type_display()
|
||||
def get_app_type_display(self, obj):
|
||||
return self.type_mapper.get(obj['app_type'])
|
||||
|
||||
|
||||
class ApplicationUserWithAuthInfoSerializer(ApplicationUserSerializer):
|
||||
|
||||
class Meta(ApplicationUserSerializer.Meta):
|
||||
fields = ApplicationUserSerializer.Meta.fields + ['password', 'token']
|
||||
class ApplicationAccountSecretSerializer(ApplicationAccountSerializer):
|
||||
password = serializers.CharField(write_only=False, label=_("Password"))
|
||||
|
|
|
@ -14,9 +14,9 @@ __all__ = [
|
|||
# ---------------------------------------------------
|
||||
|
||||
category_serializer_classes_mapping = {
|
||||
const.ApplicationCategoryChoices.db.value: application_category.DBSerializer,
|
||||
const.ApplicationCategoryChoices.remote_app.value: application_category.RemoteAppSerializer,
|
||||
const.ApplicationCategoryChoices.cloud.value: application_category.CloudSerializer,
|
||||
const.AppCategory.db.value: application_category.DBSerializer,
|
||||
const.AppCategory.remote_app.value: application_category.RemoteAppSerializer,
|
||||
const.AppCategory.cloud.value: application_category.CloudSerializer,
|
||||
}
|
||||
|
||||
# define `attrs` field `type serializers mapping`
|
||||
|
@ -24,17 +24,17 @@ category_serializer_classes_mapping = {
|
|||
|
||||
type_serializer_classes_mapping = {
|
||||
# db
|
||||
const.ApplicationTypeChoices.mysql.value: application_type.MySQLSerializer,
|
||||
const.ApplicationTypeChoices.mariadb.value: application_type.MariaDBSerializer,
|
||||
const.ApplicationTypeChoices.oracle.value: application_type.OracleSerializer,
|
||||
const.ApplicationTypeChoices.pgsql.value: application_type.PostgreSerializer,
|
||||
const.AppType.mysql.value: application_type.MySQLSerializer,
|
||||
const.AppType.mariadb.value: application_type.MariaDBSerializer,
|
||||
const.AppType.oracle.value: application_type.OracleSerializer,
|
||||
const.AppType.pgsql.value: application_type.PostgreSerializer,
|
||||
# remote-app
|
||||
const.ApplicationTypeChoices.chrome.value: application_type.ChromeSerializer,
|
||||
const.ApplicationTypeChoices.mysql_workbench.value: application_type.MySQLWorkbenchSerializer,
|
||||
const.ApplicationTypeChoices.vmware_client.value: application_type.VMwareClientSerializer,
|
||||
const.ApplicationTypeChoices.custom.value: application_type.CustomSerializer,
|
||||
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.ApplicationTypeChoices.k8s.value: application_type.K8SSerializer
|
||||
const.AppType.k8s.value: application_type.K8SSerializer
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -10,12 +10,14 @@ app_name = 'applications'
|
|||
|
||||
router = BulkRouter()
|
||||
router.register(r'applications', api.ApplicationViewSet, 'application')
|
||||
router.register(r'accounts', api.ApplicationAccountViewSet, 'application-account')
|
||||
router.register(r'account-secrets', api.ApplicationAccountSecretViewSet, 'application-account-secret')
|
||||
|
||||
|
||||
urlpatterns = [
|
||||
path('remote-apps/<uuid:pk>/connection-info/', api.RemoteAppConnectionInfoApi.as_view(), name='remote-app-connection-info'),
|
||||
path('application-users/', api.ApplicationUserListApi.as_view(), name='application-user'),
|
||||
path('application-user-auth-infos/', api.ApplicationUserAuthInfoListApi.as_view(), name='application-user-auth-info')
|
||||
# path('accounts/', api.ApplicationAccountViewSet.as_view(), name='application-account'),
|
||||
# path('account-secrets/', api.ApplicationAccountSecretViewSet.as_view(), name='application-account-secret')
|
||||
]
|
||||
|
||||
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
from django.db.models import F, Q
|
||||
from django.conf import settings
|
||||
from rest_framework.decorators import action
|
||||
from django_filters import rest_framework as filters
|
||||
from rest_framework.response import Response
|
||||
|
@ -87,11 +86,6 @@ class AccountSecretsViewSet(AccountViewSet):
|
|||
permission_classes = (IsOrgAdmin, NeedMFAVerify)
|
||||
http_method_names = ['get']
|
||||
|
||||
def get_permissions(self):
|
||||
if not settings.SECURITY_VIEW_AUTH_NEED_MFA:
|
||||
self.permission_classes = [IsOrgAdminOrAppUser]
|
||||
return super().get_permissions()
|
||||
|
||||
|
||||
class AccountTaskCreateAPI(CreateAPIView):
|
||||
permission_classes = (IsOrgAdminOrAppUser,)
|
||||
|
|
|
@ -60,10 +60,10 @@ class ProtocolMixin:
|
|||
|
||||
@classmethod
|
||||
def get_protocol_by_application_type(cls, app_type):
|
||||
from applications.const import ApplicationTypeChoices
|
||||
from applications.const import AppType
|
||||
if app_type in cls.APPLICATION_CATEGORY_PROTOCOLS:
|
||||
protocol = app_type
|
||||
elif app_type in ApplicationTypeChoices.remote_app_types():
|
||||
elif app_type in AppType.remote_app_types():
|
||||
protocol = cls.Protocol.rdp
|
||||
else:
|
||||
protocol = None
|
||||
|
|
|
@ -28,6 +28,7 @@ class AccountSerializer(AuthSerializerMixin, BulkOrgResourceModelSerializer):
|
|||
},
|
||||
'private_key': {'write_only': True},
|
||||
'public_key': {'write_only': True},
|
||||
'systemuser_display': {'label': _('System user display')}
|
||||
}
|
||||
ref_name = 'AssetAccountSerializer'
|
||||
|
||||
|
|
|
@ -11,7 +11,7 @@ from rest_framework.permissions import AllowAny
|
|||
|
||||
from common.utils.timezone import utcnow
|
||||
from common.const.http import POST, GET
|
||||
from common.drf.api import JmsGenericViewSet
|
||||
from common.drf.api import JMSGenericViewSet
|
||||
from common.drf.serializers import EmptySerializer
|
||||
from common.permissions import IsSuperUser
|
||||
from common.utils import reverse
|
||||
|
@ -26,7 +26,7 @@ NEXT_URL = 'next'
|
|||
AUTH_KEY = 'authkey'
|
||||
|
||||
|
||||
class SSOViewSet(AuthMixin, JmsGenericViewSet):
|
||||
class SSOViewSet(AuthMixin, JMSGenericViewSet):
|
||||
queryset = SSOToken.objects.all()
|
||||
serializer_classes = {
|
||||
'login_url': SSOTokenSerializer,
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
from rest_framework.viewsets import GenericViewSet, ModelViewSet
|
||||
from rest_framework.viewsets import GenericViewSet, ModelViewSet, ReadOnlyModelViewSet, ViewSet
|
||||
from rest_framework_bulk import BulkModelViewSet
|
||||
|
||||
from ..mixins.api import (
|
||||
|
@ -15,19 +15,23 @@ class CommonMixin(SerializerMixin,
|
|||
pass
|
||||
|
||||
|
||||
class JmsGenericViewSet(CommonMixin,
|
||||
GenericViewSet):
|
||||
class JMSGenericViewSet(CommonMixin, GenericViewSet):
|
||||
pass
|
||||
|
||||
|
||||
class JMSModelViewSet(CommonMixin,
|
||||
ModelViewSet):
|
||||
class JMSViewSet(CommonMixin, ViewSet):
|
||||
pass
|
||||
|
||||
|
||||
class JMSBulkModelViewSet(CommonMixin,
|
||||
AllowBulkDestroyMixin,
|
||||
BulkModelViewSet):
|
||||
class JMSModelViewSet(CommonMixin, ModelViewSet):
|
||||
pass
|
||||
|
||||
|
||||
class JMSReadOnlyModelViewSet(CommonMixin, ReadOnlyModelViewSet):
|
||||
pass
|
||||
|
||||
|
||||
class JMSBulkModelViewSet(CommonMixin, AllowBulkDestroyMixin, BulkModelViewSet):
|
||||
pass
|
||||
|
||||
|
||||
|
|
|
@ -24,6 +24,7 @@ from ..utils import lazyproperty
|
|||
__all__ = [
|
||||
'JSONResponseMixin', 'CommonApiMixin', 'AsyncApiMixin', 'RelationMixin',
|
||||
'QuerySetMixin', 'ExtraFilterFieldsMixin', 'RenderToJsonMixin',
|
||||
'SerializerMixin', 'AllowBulkDestroyMixin', 'PaginatedResponseMixin'
|
||||
]
|
||||
|
||||
|
||||
|
|
|
@ -112,6 +112,9 @@ class UserCanUpdateSSHKey(permissions.BasePermission):
|
|||
|
||||
class NeedMFAVerify(permissions.BasePermission):
|
||||
def has_permission(self, request, view):
|
||||
if not settings.SECURITY_VIEW_AUTH_NEED_MFA:
|
||||
return True
|
||||
|
||||
mfa_verify_time = request.session.get('MFA_VERIFY_TIME', 0)
|
||||
if time.time() - mfa_verify_time < settings.SECURITY_MFA_VERIFY_TTL:
|
||||
return True
|
||||
|
|
|
@ -273,3 +273,17 @@ def bulk_get(d, *keys, default=None):
|
|||
for key in keys:
|
||||
values.append(d.get(key, default))
|
||||
return values
|
||||
|
||||
|
||||
def unique(objects, key=None):
|
||||
seen = OrderedDict()
|
||||
|
||||
if key is None:
|
||||
key = lambda item: item
|
||||
|
||||
for obj in objects:
|
||||
v = key(obj)
|
||||
if v not in seen:
|
||||
seen[v] = obj
|
||||
return list(seen.values())
|
||||
|
||||
|
|
|
@ -20,7 +20,7 @@ def celery_flower_view(request, path):
|
|||
try:
|
||||
response = proxy_view(request, remote_url)
|
||||
except Exception as e:
|
||||
msg = _("<h1>Flow service unavailable, check it</h1>") + \
|
||||
msg = _("<h1>Flower service unavailable, check it</h1>") + \
|
||||
'<br><br> <div>{}</div>'.format(e)
|
||||
response = HttpResponse(msg)
|
||||
return response
|
||||
|
|
|
@ -7,7 +7,7 @@ msgid ""
|
|||
msgstr ""
|
||||
"Project-Id-Version: JumpServer 0.3.3\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2021-07-26 19:02+0800\n"
|
||||
"POT-Creation-Date: 2021-07-27 15:57+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"
|
||||
|
@ -18,11 +18,11 @@ msgstr ""
|
|||
"X-Generator: Poedit 2.4.3\n"
|
||||
|
||||
#: acls/models/base.py:25 acls/serializers/login_asset_acl.py:47
|
||||
#: applications/models/application.py:11 assets/models/asset.py:139
|
||||
#: applications/models/application.py:162 assets/models/asset.py:139
|
||||
#: assets/models/base.py:175 assets/models/cluster.py:18
|
||||
#: assets/models/cmd_filter.py:21 assets/models/domain.py:21
|
||||
#: assets/models/group.py:20 assets/models/label.py:18 ops/mixin.py:24
|
||||
#: orgs/models.py:23 perms/models/base.py:49 settings/models.py:29
|
||||
#: orgs/models.py:24 perms/models/base.py:49 settings/models.py:29
|
||||
#: terminal/models/storage.py:23 terminal/models/task.py:16
|
||||
#: terminal/models/terminal.py:100 users/forms/profile.py:32
|
||||
#: users/models/group.py:15 users/models/user.py:551
|
||||
|
@ -52,17 +52,17 @@ msgstr "激活中"
|
|||
|
||||
# msgid "Date created"
|
||||
# msgstr "创建日期"
|
||||
#: acls/models/base.py:32 applications/models/application.py:24
|
||||
#: acls/models/base.py:32 applications/models/application.py:175
|
||||
#: assets/models/asset.py:144 assets/models/asset.py:220
|
||||
#: assets/models/base.py:180 assets/models/cluster.py:29
|
||||
#: assets/models/cmd_filter.py:23 assets/models/cmd_filter.py:64
|
||||
#: assets/models/domain.py:22 assets/models/domain.py:53
|
||||
#: assets/models/group.py:23 assets/models/label.py:23 ops/models/adhoc.py:37
|
||||
#: orgs/models.py:26 perms/models/base.py:57 settings/models.py:34
|
||||
#: orgs/models.py:27 perms/models/base.py:57 settings/models.py:34
|
||||
#: terminal/models/storage.py:26 terminal/models/terminal.py:114
|
||||
#: tickets/models/ticket.py:73 users/models/group.py:16
|
||||
#: users/models/user.py:584 xpack/plugins/change_auth_plan/models.py:77
|
||||
#: xpack/plugins/cloud/models.py:35 xpack/plugins/cloud/models.py:108
|
||||
#: xpack/plugins/cloud/models.py:35 xpack/plugins/cloud/models.py:98
|
||||
#: xpack/plugins/gathered_user/models.py:26
|
||||
msgid "Comment"
|
||||
msgstr "备注"
|
||||
|
@ -94,7 +94,7 @@ msgstr "动作"
|
|||
#: acls/serializers/login_acl.py:33 assets/models/label.py:15
|
||||
#: audits/models.py:36 audits/models.py:56 audits/models.py:69
|
||||
#: audits/serializers.py:93 authentication/models.py:44
|
||||
#: authentication/models.py:97 orgs/models.py:18 orgs/models.py:418
|
||||
#: authentication/models.py:97 orgs/models.py:19 orgs/models.py:433
|
||||
#: perms/models/base.py:50 templates/index.html:78
|
||||
#: terminal/backends/command/models.py:18
|
||||
#: terminal/backends/command/serializers.py:12 terminal/models/session.py:38
|
||||
|
@ -126,8 +126,8 @@ msgstr "系统用户"
|
|||
#: terminal/backends/command/serializers.py:13 terminal/models/session.py:40
|
||||
#: users/templates/users/user_asset_permission.html:40
|
||||
#: users/templates/users/user_asset_permission.html:70
|
||||
#: xpack/plugins/change_auth_plan/models.py:290
|
||||
#: xpack/plugins/cloud/models.py:212
|
||||
#: xpack/plugins/change_auth_plan/models.py:282
|
||||
#: xpack/plugins/cloud/models.py:202
|
||||
msgid "Asset"
|
||||
msgstr "资产"
|
||||
|
||||
|
@ -140,7 +140,7 @@ msgstr "审批人"
|
|||
msgid "Login asset confirm"
|
||||
msgstr "登录资产复核"
|
||||
|
||||
#: acls/serializers/login_acl.py:18 xpack/plugins/cloud/serializers.py:165
|
||||
#: acls/serializers/login_acl.py:18
|
||||
msgid "IP address invalid: `{}`"
|
||||
msgstr "IP 地址无效: `{}`"
|
||||
|
||||
|
@ -172,6 +172,7 @@ msgstr "格式为逗号分隔的字符串, * 表示匹配所有. "
|
|||
|
||||
#: acls/serializers/login_asset_acl.py:17
|
||||
#: acls/serializers/login_asset_acl.py:51
|
||||
#: applications/serializers/application.py:71
|
||||
#: 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
|
||||
|
@ -181,8 +182,8 @@ msgstr "格式为逗号分隔的字符串, * 表示匹配所有. "
|
|||
#: ops/models/adhoc.py:148 users/forms/profile.py:31 users/models/user.py:549
|
||||
#: users/templates/users/_select_user_modal.html:14
|
||||
#: xpack/plugins/change_auth_plan/models.py:47
|
||||
#: xpack/plugins/change_auth_plan/models.py:286
|
||||
#: xpack/plugins/cloud/serializers.py:67
|
||||
#: xpack/plugins/change_auth_plan/models.py:278
|
||||
#: xpack/plugins/cloud/serializers.py:65
|
||||
msgid "Username"
|
||||
msgstr "用户名"
|
||||
|
||||
|
@ -228,21 +229,31 @@ msgstr "组织 `{}` 不存在"
|
|||
msgid "None of the reviewers belong to Organization `{}`"
|
||||
msgstr "所有复核人都不属于组织 `{}`"
|
||||
|
||||
#: applications/const.py:9
|
||||
#: applications/api/mixin.py:19 templates/_nav_user.html:10
|
||||
msgid "My applications"
|
||||
msgstr "我的应用"
|
||||
|
||||
#: applications/const.py:8
|
||||
#: applications/serializers/attrs/application_category/db.py:14
|
||||
#: applications/serializers/attrs/application_type/mysql_workbench.py:26
|
||||
msgid "Database"
|
||||
msgstr "数据库"
|
||||
|
||||
#: applications/const.py:10
|
||||
#: applications/const.py:9
|
||||
msgid "Remote app"
|
||||
msgstr "远程应用"
|
||||
|
||||
#: applications/const.py:29
|
||||
#: applications/const.py:28
|
||||
msgid "Custom"
|
||||
msgstr "自定义"
|
||||
|
||||
#: applications/models/application.py:13 assets/models/label.py:21
|
||||
#: applications/models/application.py:46 templates/_nav.html:60
|
||||
msgid "Applications"
|
||||
msgstr "应用管理"
|
||||
|
||||
#: applications/models/application.py:164
|
||||
#: applications/serializers/application.py:78
|
||||
#: applications/serializers/application.py:79 assets/models/label.py:21
|
||||
#: perms/models/application_permission.py:20
|
||||
#: perms/serializers/application/permission.py:16
|
||||
#: perms/serializers/application/user_permission.py:33
|
||||
|
@ -250,7 +261,9 @@ msgstr "自定义"
|
|||
msgid "Category"
|
||||
msgstr "类别"
|
||||
|
||||
#: applications/models/application.py:16 assets/models/cmd_filter.py:53
|
||||
#: applications/models/application.py:167
|
||||
#: applications/serializers/application.py:80
|
||||
#: applications/serializers/application.py:81 assets/models/cmd_filter.py:53
|
||||
#: assets/models/user.py:202 perms/models/application_permission.py:23
|
||||
#: perms/serializers/application/permission.py:17
|
||||
#: perms/serializers/application/user_permission.py:34
|
||||
|
@ -260,12 +273,12 @@ msgstr "类别"
|
|||
msgid "Type"
|
||||
msgstr "类型"
|
||||
|
||||
#: applications/models/application.py:20 assets/models/asset.py:188
|
||||
#: applications/models/application.py:171 assets/models/asset.py:188
|
||||
#: assets/models/domain.py:27 assets/models/domain.py:52
|
||||
msgid "Domain"
|
||||
msgstr "网域"
|
||||
|
||||
#: applications/models/application.py:22 xpack/plugins/cloud/models.py:33
|
||||
#: applications/models/application.py:173 xpack/plugins/cloud/models.py:33
|
||||
msgid "Attrs"
|
||||
msgstr ""
|
||||
|
||||
|
@ -278,29 +291,63 @@ msgid "Type(Dispaly)"
|
|||
msgstr "类型 (显示名称)"
|
||||
|
||||
#: applications/serializers/application.py:72
|
||||
#: applications/serializers/application.py:100
|
||||
#: 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:63
|
||||
#: authentication/forms.py:22
|
||||
#: authentication/templates/authentication/login.html:164
|
||||
#: settings/serializers/settings.py:94 users/forms/profile.py:21
|
||||
#: users/templates/users/user_otp_check_password.html:13
|
||||
#: users/templates/users/user_password_update.html:43
|
||||
#: users/templates/users/user_password_verify.html:18
|
||||
#: xpack/plugins/change_auth_plan/models.py:68
|
||||
#: xpack/plugins/change_auth_plan/models.py:190
|
||||
#: xpack/plugins/change_auth_plan/models.py:285
|
||||
#: xpack/plugins/cloud/serializers.py:67
|
||||
msgid "Password"
|
||||
msgstr "密码"
|
||||
|
||||
#: applications/serializers/application.py:73 assets/models/authbook.py:16
|
||||
#: assets/models/user.py:277 audits/models.py:39
|
||||
#: perms/models/application_permission.py:31
|
||||
#: perms/models/asset_permission.py:101 templates/_nav.html:45
|
||||
#: terminal/backends/command/models.py:20
|
||||
#: terminal/backends/command/serializers.py:14 terminal/models/session.py:42
|
||||
#: users/templates/users/_granted_assets.html:27
|
||||
#: users/templates/users/user_asset_permission.html:42
|
||||
#: users/templates/users/user_asset_permission.html:76
|
||||
#: users/templates/users/user_asset_permission.html:159
|
||||
#: users/templates/users/user_database_app_permission.html:40
|
||||
#: users/templates/users/user_database_app_permission.html:67
|
||||
msgid "System user"
|
||||
msgstr "系统用户"
|
||||
|
||||
#: applications/serializers/application.py:74 assets/serializers/account.py:31
|
||||
msgid "System user display"
|
||||
msgstr "系统用户显示"
|
||||
|
||||
#: applications/serializers/application.py:75
|
||||
msgid "App"
|
||||
msgstr "应用"
|
||||
|
||||
#: applications/serializers/application.py:76
|
||||
msgid "Union id"
|
||||
msgstr "联合ID"
|
||||
|
||||
#: applications/serializers/application.py:77
|
||||
msgid "Application name"
|
||||
msgstr "应用名称"
|
||||
|
||||
#: applications/serializers/application.py:73
|
||||
msgid "Application category"
|
||||
msgstr "应用类别"
|
||||
|
||||
#: applications/serializers/application.py:74
|
||||
msgid "Application type"
|
||||
msgstr "应用类型"
|
||||
|
||||
#: applications/serializers/application.py:87
|
||||
#: assets/serializers/system_user.py:53 assets/serializers/system_user.py:201
|
||||
msgid "Login mode display"
|
||||
msgstr "认证方式(显示名称)"
|
||||
|
||||
#: applications/serializers/attrs/application_category/cloud.py:9
|
||||
#: assets/models/cluster.py:40
|
||||
msgid "Cluster"
|
||||
msgstr "集群"
|
||||
|
||||
#: applications/serializers/attrs/application_category/db.py:11
|
||||
#: ops/models/adhoc.py:146 xpack/plugins/cloud/serializers.py:65
|
||||
#: ops/models/adhoc.py:146 xpack/plugins/cloud/serializers.py:63
|
||||
msgid "Host"
|
||||
msgstr "主机"
|
||||
|
||||
|
@ -310,7 +357,7 @@ msgstr "主机"
|
|||
#: applications/serializers/attrs/application_type/oracle.py:11
|
||||
#: applications/serializers/attrs/application_type/pgsql.py:11
|
||||
#: assets/models/asset.py:185 assets/models/domain.py:50
|
||||
#: xpack/plugins/cloud/serializers.py:66
|
||||
#: xpack/plugins/cloud/serializers.py:64
|
||||
msgid "Port"
|
||||
msgstr "端口"
|
||||
|
||||
|
@ -326,24 +373,6 @@ msgstr "应用路径"
|
|||
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:63
|
||||
#: authentication/forms.py:22
|
||||
#: authentication/templates/authentication/login.html:164
|
||||
#: settings/serializers/settings.py:94 users/forms/profile.py:21
|
||||
#: users/templates/users/user_otp_check_password.html:13
|
||||
#: users/templates/users/user_password_update.html:43
|
||||
#: users/templates/users/user_password_verify.html:18
|
||||
#: xpack/plugins/change_auth_plan/models.py:68
|
||||
#: xpack/plugins/change_auth_plan/models.py:194
|
||||
#: xpack/plugins/change_auth_plan/models.py:293
|
||||
#: xpack/plugins/cloud/serializers.py:69
|
||||
msgid "Password"
|
||||
msgstr "密码"
|
||||
|
||||
#: applications/serializers/attrs/application_type/custom.py:13
|
||||
msgid "Operating parameter"
|
||||
msgstr "运行参数"
|
||||
|
@ -392,7 +421,6 @@ msgstr "系统平台"
|
|||
|
||||
#: assets/models/asset.py:186 assets/serializers/asset.py:65
|
||||
#: perms/serializers/asset/user_permission.py:41
|
||||
#: xpack/plugins/cloud/models.py:99 xpack/plugins/cloud/serializers.py:183
|
||||
msgid "Protocols"
|
||||
msgstr "协议组"
|
||||
|
||||
|
@ -411,7 +439,7 @@ msgstr "激活"
|
|||
|
||||
#: assets/models/asset.py:193 assets/models/cluster.py:19
|
||||
#: assets/models/user.py:191 assets/models/user.py:326 templates/_nav.html:44
|
||||
#: xpack/plugins/cloud/models.py:96 xpack/plugins/cloud/serializers.py:205
|
||||
#: xpack/plugins/cloud/models.py:92 xpack/plugins/cloud/serializers.py:179
|
||||
msgid "Admin user"
|
||||
msgstr "特权用户"
|
||||
|
||||
|
@ -486,10 +514,10 @@ msgstr "标签管理"
|
|||
#: assets/models/asset.py:218 assets/models/base.py:183
|
||||
#: assets/models/cluster.py:28 assets/models/cmd_filter.py:26
|
||||
#: assets/models/cmd_filter.py:67 assets/models/group.py:21
|
||||
#: common/db/models.py:70 common/mixins/models.py:49 orgs/models.py:24
|
||||
#: orgs/models.py:422 perms/models/base.py:55 users/models/user.py:592
|
||||
#: common/db/models.py:70 common/mixins/models.py:49 orgs/models.py:25
|
||||
#: orgs/models.py:437 perms/models/base.py:55 users/models/user.py:592
|
||||
#: users/serializers/group.py:33 xpack/plugins/change_auth_plan/models.py:81
|
||||
#: xpack/plugins/cloud/models.py:114 xpack/plugins/gathered_user/models.py:30
|
||||
#: xpack/plugins/cloud/models.py:104 xpack/plugins/gathered_user/models.py:30
|
||||
msgid "Created by"
|
||||
msgstr "创建者"
|
||||
|
||||
|
@ -499,26 +527,12 @@ msgstr "创建者"
|
|||
#: assets/models/cluster.py:26 assets/models/domain.py:24
|
||||
#: assets/models/gathered_user.py:19 assets/models/group.py:22
|
||||
#: assets/models/label.py:25 common/db/models.py:72 common/mixins/models.py:50
|
||||
#: ops/models/adhoc.py:38 ops/models/command.py:29 orgs/models.py:25
|
||||
#: orgs/models.py:420 perms/models/base.py:56 users/models/group.py:18
|
||||
#: users/models/user.py:774 xpack/plugins/cloud/models.py:117
|
||||
#: ops/models/adhoc.py:38 ops/models/command.py:29 orgs/models.py:26
|
||||
#: orgs/models.py:435 perms/models/base.py:56 users/models/group.py:18
|
||||
#: users/models/user.py:774 xpack/plugins/cloud/models.py:107
|
||||
msgid "Date created"
|
||||
msgstr "创建日期"
|
||||
|
||||
#: assets/models/authbook.py:16 assets/models/user.py:277 audits/models.py:39
|
||||
#: perms/models/application_permission.py:31
|
||||
#: perms/models/asset_permission.py:101 templates/_nav.html:45
|
||||
#: terminal/backends/command/models.py:20
|
||||
#: terminal/backends/command/serializers.py:14 terminal/models/session.py:42
|
||||
#: users/templates/users/_granted_assets.html:27
|
||||
#: users/templates/users/user_asset_permission.html:42
|
||||
#: users/templates/users/user_asset_permission.html:76
|
||||
#: users/templates/users/user_asset_permission.html:159
|
||||
#: users/templates/users/user_database_app_permission.html:40
|
||||
#: users/templates/users/user_database_app_permission.html:67
|
||||
msgid "System user"
|
||||
msgstr "系统用户"
|
||||
|
||||
#: assets/models/authbook.py:17
|
||||
msgid "Version"
|
||||
msgstr "版本"
|
||||
|
@ -548,20 +562,20 @@ msgid "Date verified"
|
|||
msgstr "校验日期"
|
||||
|
||||
#: assets/models/base.py:178 xpack/plugins/change_auth_plan/models.py:72
|
||||
#: xpack/plugins/change_auth_plan/models.py:201
|
||||
#: xpack/plugins/change_auth_plan/models.py:300
|
||||
#: xpack/plugins/change_auth_plan/models.py:197
|
||||
#: xpack/plugins/change_auth_plan/models.py:292
|
||||
msgid "SSH private key"
|
||||
msgstr "SSH密钥"
|
||||
|
||||
#: assets/models/base.py:179 xpack/plugins/change_auth_plan/models.py:75
|
||||
#: xpack/plugins/change_auth_plan/models.py:197
|
||||
#: xpack/plugins/change_auth_plan/models.py:296
|
||||
#: xpack/plugins/change_auth_plan/models.py:193
|
||||
#: xpack/plugins/change_auth_plan/models.py:288
|
||||
msgid "SSH public key"
|
||||
msgstr "SSH公钥"
|
||||
|
||||
#: assets/models/base.py:182 assets/models/gathered_user.py:20
|
||||
#: common/db/models.py:73 common/mixins/models.py:51 ops/models/adhoc.py:39
|
||||
#: orgs/models.py:421
|
||||
#: orgs/models.py:436
|
||||
msgid "Date updated"
|
||||
msgstr "更新日期"
|
||||
|
||||
|
@ -713,7 +727,7 @@ msgstr "ssh私钥"
|
|||
#: users/templates/users/user_asset_permission.html:41
|
||||
#: users/templates/users/user_asset_permission.html:73
|
||||
#: users/templates/users/user_asset_permission.html:158
|
||||
#: xpack/plugins/cloud/models.py:93 xpack/plugins/cloud/serializers.py:206
|
||||
#: xpack/plugins/cloud/models.py:89 xpack/plugins/cloud/serializers.py:180
|
||||
msgid "Node"
|
||||
msgstr "节点"
|
||||
|
||||
|
@ -866,6 +880,10 @@ msgstr "密钥指纹"
|
|||
msgid "Nodes amount"
|
||||
msgstr "节点数量"
|
||||
|
||||
#: assets/serializers/system_user.py:53 assets/serializers/system_user.py:201
|
||||
msgid "Login mode display"
|
||||
msgstr "认证方式(显示名称)"
|
||||
|
||||
#: assets/serializers/system_user.py:55
|
||||
msgid "Ad domain"
|
||||
msgstr "Ad 网域"
|
||||
|
@ -1066,8 +1084,8 @@ msgstr "成功"
|
|||
#: tickets/serializers/ticket/meta/ticket_type/apply_application.py:74
|
||||
#: tickets/serializers/ticket/meta/ticket_type/apply_asset.py:40
|
||||
#: tickets/serializers/ticket/meta/ticket_type/apply_asset.py:78
|
||||
#: xpack/plugins/change_auth_plan/models.py:181
|
||||
#: xpack/plugins/change_auth_plan/models.py:315
|
||||
#: xpack/plugins/change_auth_plan/models.py:177
|
||||
#: xpack/plugins/change_auth_plan/models.py:307
|
||||
#: xpack/plugins/gathered_user/models.py:76
|
||||
msgid "Date start"
|
||||
msgstr "开始日期"
|
||||
|
@ -1138,13 +1156,13 @@ msgstr "用户代理"
|
|||
msgid "MFA"
|
||||
msgstr "多因子认证"
|
||||
|
||||
#: audits/models.py:106 xpack/plugins/change_auth_plan/models.py:311
|
||||
#: xpack/plugins/cloud/models.py:171
|
||||
#: audits/models.py:106 xpack/plugins/change_auth_plan/models.py:303
|
||||
#: xpack/plugins/cloud/models.py:161
|
||||
msgid "Reason"
|
||||
msgstr "原因"
|
||||
|
||||
#: audits/models.py:107 tickets/models/ticket.py:47
|
||||
#: xpack/plugins/cloud/models.py:167 xpack/plugins/cloud/models.py:216
|
||||
#: xpack/plugins/cloud/models.py:157 xpack/plugins/cloud/models.py:206
|
||||
msgid "Status"
|
||||
msgstr "状态"
|
||||
|
||||
|
@ -1178,7 +1196,7 @@ msgid "Hosts display"
|
|||
msgstr "主机名称"
|
||||
|
||||
#: audits/serializers.py:89 ops/models/command.py:26
|
||||
#: xpack/plugins/cloud/models.py:165
|
||||
#: xpack/plugins/cloud/models.py:155
|
||||
msgid "Result"
|
||||
msgstr "结果"
|
||||
|
||||
|
@ -1391,7 +1409,7 @@ msgstr "{ApplicationPermission} *添加了* {SystemUser}"
|
|||
msgid "{ApplicationPermission} *REMOVE* {SystemUser}"
|
||||
msgstr "{ApplicationPermission} *移除了* {SystemUser}"
|
||||
|
||||
#: authentication/api/connection_token.py:258
|
||||
#: authentication/api/connection_token.py:268
|
||||
msgid "Invalid token"
|
||||
msgstr "无效的令牌"
|
||||
|
||||
|
@ -1694,7 +1712,7 @@ msgstr "OpenID"
|
|||
|
||||
#: authentication/templates/authentication/login.html:205
|
||||
msgid "CAS"
|
||||
msgstr ""
|
||||
msgstr "CAS"
|
||||
|
||||
#: authentication/templates/authentication/login_otp.html:17
|
||||
msgid "One-time password"
|
||||
|
@ -1896,7 +1914,7 @@ msgstr "您的请求超时了"
|
|||
|
||||
#: common/exceptions.py:35
|
||||
msgid "M2M reverse not allowed"
|
||||
msgstr ""
|
||||
msgstr "多对多反向是不被允许的"
|
||||
|
||||
#: common/exceptions.py:41
|
||||
msgid "Is referenced by other objects and cannot be deleted"
|
||||
|
@ -1904,35 +1922,35 @@ msgstr "被其他对象关联,不能删除"
|
|||
|
||||
#: common/exceptions.py:47
|
||||
msgid "This action require verify your MFA"
|
||||
msgstr ""
|
||||
msgstr "这个操作需要验证 MFA"
|
||||
|
||||
#: common/fields/model.py:80
|
||||
msgid "Marshal dict data to char field"
|
||||
msgstr ""
|
||||
msgstr "编码 dict 为 char"
|
||||
|
||||
#: common/fields/model.py:84
|
||||
msgid "Marshal dict data to text field"
|
||||
msgstr ""
|
||||
msgstr "编码 dict 为 text"
|
||||
|
||||
#: common/fields/model.py:96
|
||||
msgid "Marshal list data to char field"
|
||||
msgstr ""
|
||||
msgstr "编码 list 为 char"
|
||||
|
||||
#: common/fields/model.py:100
|
||||
msgid "Marshal list data to text field"
|
||||
msgstr ""
|
||||
msgstr "编码 list 为 text"
|
||||
|
||||
#: common/fields/model.py:104
|
||||
msgid "Marshal data to char field"
|
||||
msgstr ""
|
||||
msgstr "编码数据为 char"
|
||||
|
||||
#: common/fields/model.py:108
|
||||
msgid "Marshal data to text field"
|
||||
msgstr ""
|
||||
msgstr "编码数据为 text"
|
||||
|
||||
#: common/fields/model.py:150
|
||||
msgid "Encrypt field using Secret Key"
|
||||
msgstr ""
|
||||
msgstr "加密的字段"
|
||||
|
||||
#: common/message/backends/exceptions.py:23
|
||||
msgid "Network error, please contact system administrator"
|
||||
|
@ -1942,17 +1960,17 @@ msgstr "网络错误,请联系系统管理员"
|
|||
msgid "WeCom error, please contact system administrator"
|
||||
msgstr "企业微信错误,请联系系统管理员"
|
||||
|
||||
#: common/mixins/api.py:57
|
||||
#: common/mixins/api.py:58
|
||||
msgid "Request file format may be wrong"
|
||||
msgstr "上传的文件格式错误 或 其它类型资源的文件"
|
||||
|
||||
#: common/mixins/models.py:33
|
||||
msgid "is discard"
|
||||
msgstr ""
|
||||
msgstr "忽略的"
|
||||
|
||||
#: common/mixins/models.py:34
|
||||
msgid "discard time"
|
||||
msgstr ""
|
||||
msgstr "忽略时间"
|
||||
|
||||
#: common/utils/ipip/utils.py:15
|
||||
msgid "Invalid ip"
|
||||
|
@ -1975,8 +1993,8 @@ msgid "JumpServer Open Source Bastion Host"
|
|||
msgstr "JumpServer 开源堡垒机"
|
||||
|
||||
#: jumpserver/views/celery_flower.py:23
|
||||
msgid "<h1>Flow service unavailable, check it</h1>"
|
||||
msgstr ""
|
||||
msgid "<h1>Flower service unavailable, check it</h1>"
|
||||
msgstr "Flower 服务不可用,请检查"
|
||||
|
||||
#: jumpserver/views/other.py:25
|
||||
msgid ""
|
||||
|
@ -2031,7 +2049,7 @@ msgid "Regularly perform"
|
|||
msgstr "定期执行"
|
||||
|
||||
#: ops/mixin.py:106 ops/mixin.py:147
|
||||
#: xpack/plugins/change_auth_plan/serializers.py:55
|
||||
#: xpack/plugins/change_auth_plan/serializers.py:51
|
||||
msgid "Periodic perform"
|
||||
msgstr "定时执行"
|
||||
|
||||
|
@ -2110,8 +2128,8 @@ msgstr "开始时间"
|
|||
msgid "End time"
|
||||
msgstr "完成时间"
|
||||
|
||||
#: ops/models/adhoc.py:246 xpack/plugins/change_auth_plan/models.py:184
|
||||
#: xpack/plugins/change_auth_plan/models.py:318
|
||||
#: ops/models/adhoc.py:246 xpack/plugins/change_auth_plan/models.py:180
|
||||
#: xpack/plugins/change_auth_plan/models.py:310
|
||||
#: xpack/plugins/gathered_user/models.py:79
|
||||
msgid "Time"
|
||||
msgstr "时间"
|
||||
|
@ -2177,25 +2195,25 @@ msgstr "当前组织 ({}) 不能被删除"
|
|||
msgid "The organization have resource ({}) cannot be deleted"
|
||||
msgstr "组织存在资源 ({}) 不能被删除"
|
||||
|
||||
#: orgs/mixins/models.py:45 orgs/mixins/serializers.py:25 orgs/models.py:36
|
||||
#: orgs/models.py:417 orgs/serializers.py:106
|
||||
#: orgs/mixins/models.py:45 orgs/mixins/serializers.py:25 orgs/models.py:37
|
||||
#: orgs/models.py:432 orgs/serializers.py:106
|
||||
#: tickets/serializers/ticket/ticket.py:83
|
||||
msgid "Organization"
|
||||
msgstr "组织"
|
||||
|
||||
#: orgs/models.py:16
|
||||
#: orgs/models.py:17
|
||||
msgid "Organization administrator"
|
||||
msgstr "组织管理员"
|
||||
|
||||
#: orgs/models.py:17
|
||||
#: orgs/models.py:18
|
||||
msgid "Organization auditor"
|
||||
msgstr "组织审计员"
|
||||
|
||||
#: orgs/models.py:30
|
||||
#: orgs/models.py:31
|
||||
msgid "GLOBAL"
|
||||
msgstr "全局组织"
|
||||
|
||||
#: orgs/models.py:419 users/models/user.py:561
|
||||
#: orgs/models.py:434 users/models/user.py:561
|
||||
#: users/templates/users/_select_user_modal.html:15
|
||||
msgid "Role"
|
||||
msgstr "角色"
|
||||
|
@ -2476,7 +2494,7 @@ msgstr "LDAP 地址"
|
|||
|
||||
#: settings/serializers/settings.py:91
|
||||
msgid "eg: ldap://localhost:389"
|
||||
msgstr ""
|
||||
msgstr "如: ldap://localhost:389"
|
||||
|
||||
#: settings/serializers/settings.py:93
|
||||
msgid "Bind DN"
|
||||
|
@ -2845,7 +2863,7 @@ msgstr "用户页面"
|
|||
|
||||
#: templates/_header_bar.html:78
|
||||
msgid "API Key"
|
||||
msgstr ""
|
||||
msgstr "API Key"
|
||||
|
||||
#: templates/_header_bar.html:79
|
||||
msgid "Logout"
|
||||
|
@ -2948,10 +2966,6 @@ msgstr "命令过滤"
|
|||
msgid "Platform list"
|
||||
msgstr "平台列表"
|
||||
|
||||
#: templates/_nav.html:60
|
||||
msgid "Applications"
|
||||
msgstr "应用管理"
|
||||
|
||||
#: templates/_nav.html:64 templates/_nav.html:82 templates/_nav_user.html:16
|
||||
msgid "RemoteApp"
|
||||
msgstr "远程应用"
|
||||
|
@ -3037,7 +3051,7 @@ msgstr "改密日志"
|
|||
|
||||
#: templates/_nav.html:163
|
||||
msgid "XPack"
|
||||
msgstr ""
|
||||
msgstr "XPack"
|
||||
|
||||
#: templates/_nav.html:171
|
||||
msgid "Account list"
|
||||
|
@ -3055,10 +3069,6 @@ msgstr "系统设置"
|
|||
msgid "My assets"
|
||||
msgstr "我的资产"
|
||||
|
||||
#: templates/_nav_user.html:10
|
||||
msgid "My Applications"
|
||||
msgstr "我的应用"
|
||||
|
||||
#: templates/_nav_user.html:31
|
||||
msgid "Command execution"
|
||||
msgstr "命令执行"
|
||||
|
@ -3540,18 +3550,18 @@ msgstr "桶名称"
|
|||
|
||||
#: terminal/serializers/storage.py:30
|
||||
msgid "Access key"
|
||||
msgstr ""
|
||||
msgstr "Access key"
|
||||
|
||||
#: terminal/serializers/storage.py:34
|
||||
msgid "Secret key"
|
||||
msgstr ""
|
||||
msgstr "Secret key"
|
||||
|
||||
#: terminal/serializers/storage.py:39 terminal/serializers/storage.py:51
|
||||
#: terminal/serializers/storage.py:81 terminal/serializers/storage.py:91
|
||||
msgid "Endpoint"
|
||||
msgstr "端点"
|
||||
|
||||
#: terminal/serializers/storage.py:66 xpack/plugins/cloud/models.py:209
|
||||
#: terminal/serializers/storage.py:66 xpack/plugins/cloud/models.py:199
|
||||
msgid "Region"
|
||||
msgstr "地域"
|
||||
|
||||
|
@ -3595,7 +3605,7 @@ msgstr "文档类型"
|
|||
msgid "Ignore Certificate Verification"
|
||||
msgstr "忽略证书认证"
|
||||
|
||||
#: terminal/serializers/terminal.py:73 terminal/serializers/terminal.py:81
|
||||
#: terminal/serializers/terminal.py:78 terminal/serializers/terminal.py:86
|
||||
msgid "Not found"
|
||||
msgstr "没有发现"
|
||||
|
||||
|
@ -4141,7 +4151,7 @@ msgid "Set password"
|
|||
msgstr "设置密码"
|
||||
|
||||
#: users/serializers/user.py:27 xpack/plugins/change_auth_plan/models.py:61
|
||||
#: xpack/plugins/change_auth_plan/serializers.py:30
|
||||
#: xpack/plugins/change_auth_plan/serializers.py:29
|
||||
msgid "Password strategy"
|
||||
msgstr "密码策略"
|
||||
|
||||
|
@ -4773,7 +4783,7 @@ msgstr "重置密码成功,返回到登录页面"
|
|||
|
||||
#: xpack/plugins/change_auth_plan/meta.py:9
|
||||
#: xpack/plugins/change_auth_plan/models.py:89
|
||||
#: xpack/plugins/change_auth_plan/models.py:188
|
||||
#: xpack/plugins/change_auth_plan/models.py:184
|
||||
msgid "Change auth plan"
|
||||
msgstr "改密计划"
|
||||
|
||||
|
@ -4793,73 +4803,60 @@ msgstr "所有资产使用不同的随机密码"
|
|||
msgid "Password rules"
|
||||
msgstr "密码规则"
|
||||
|
||||
#: xpack/plugins/change_auth_plan/models.py:176
|
||||
msgid "Manual trigger"
|
||||
msgstr "手动触发"
|
||||
|
||||
#: xpack/plugins/change_auth_plan/models.py:177
|
||||
msgid "Timing trigger"
|
||||
msgstr "定时触发"
|
||||
|
||||
#: xpack/plugins/change_auth_plan/models.py:191
|
||||
#: xpack/plugins/change_auth_plan/models.py:187
|
||||
msgid "Change auth plan snapshot"
|
||||
msgstr "改密计划快照"
|
||||
|
||||
#: xpack/plugins/change_auth_plan/models.py:205
|
||||
#: xpack/plugins/change_auth_plan/serializers.py:132
|
||||
msgid "Trigger mode"
|
||||
msgstr "触发模式"
|
||||
|
||||
#: xpack/plugins/change_auth_plan/models.py:210
|
||||
#: xpack/plugins/change_auth_plan/models.py:304
|
||||
#: xpack/plugins/change_auth_plan/models.py:202
|
||||
#: xpack/plugins/change_auth_plan/models.py:296
|
||||
msgid "Change auth plan execution"
|
||||
msgstr "改密计划执行"
|
||||
|
||||
#: xpack/plugins/change_auth_plan/models.py:277
|
||||
#: xpack/plugins/change_auth_plan/models.py:269
|
||||
msgid "Ready"
|
||||
msgstr ""
|
||||
|
||||
#: xpack/plugins/change_auth_plan/models.py:278
|
||||
#: xpack/plugins/change_auth_plan/models.py:270
|
||||
msgid "Preflight check"
|
||||
msgstr ""
|
||||
|
||||
#: xpack/plugins/change_auth_plan/models.py:279
|
||||
#: xpack/plugins/change_auth_plan/models.py:271
|
||||
msgid "Change auth"
|
||||
msgstr ""
|
||||
|
||||
#: xpack/plugins/change_auth_plan/models.py:280
|
||||
#: xpack/plugins/change_auth_plan/models.py:272
|
||||
msgid "Verify auth"
|
||||
msgstr ""
|
||||
|
||||
#: xpack/plugins/change_auth_plan/models.py:281
|
||||
#: xpack/plugins/change_auth_plan/models.py:273
|
||||
msgid "Keep auth"
|
||||
msgstr ""
|
||||
|
||||
#: xpack/plugins/change_auth_plan/models.py:282
|
||||
#: xpack/plugins/change_auth_plan/models.py:274
|
||||
msgid "Finished"
|
||||
msgstr "结束"
|
||||
|
||||
#: xpack/plugins/change_auth_plan/models.py:308
|
||||
#: xpack/plugins/change_auth_plan/models.py:300
|
||||
msgid "Step"
|
||||
msgstr "步骤"
|
||||
|
||||
#: xpack/plugins/change_auth_plan/models.py:325
|
||||
#: xpack/plugins/change_auth_plan/models.py:317
|
||||
msgid "Change auth plan task"
|
||||
msgstr "改密计划任务"
|
||||
|
||||
#: xpack/plugins/change_auth_plan/serializers.py:56
|
||||
#: xpack/plugins/change_auth_plan/serializers.py:52
|
||||
msgid "Run times"
|
||||
msgstr "执行次数"
|
||||
|
||||
#: xpack/plugins/change_auth_plan/serializers.py:72
|
||||
#: xpack/plugins/change_auth_plan/serializers.py:68
|
||||
msgid "* Please enter custom password"
|
||||
msgstr "* 请输入自定义密码"
|
||||
|
||||
#: xpack/plugins/change_auth_plan/serializers.py:82
|
||||
#: xpack/plugins/change_auth_plan/serializers.py:78
|
||||
msgid "* Please enter the correct password length"
|
||||
msgstr "* 请输入正确的密码长度"
|
||||
|
||||
#: xpack/plugins/change_auth_plan/serializers.py:85
|
||||
#: xpack/plugins/change_auth_plan/serializers.py:81
|
||||
msgid "* Password length range 6-30 bits"
|
||||
msgstr "* 密码长度范围 6-30 位"
|
||||
|
||||
|
@ -4967,47 +4964,43 @@ msgstr "云服务商"
|
|||
msgid "Cloud account"
|
||||
msgstr "云账号"
|
||||
|
||||
#: xpack/plugins/cloud/models.py:82 xpack/plugins/cloud/serializers.py:204
|
||||
#: xpack/plugins/cloud/models.py:78 xpack/plugins/cloud/serializers.py:178
|
||||
msgid "Account"
|
||||
msgstr "账户"
|
||||
|
||||
#: xpack/plugins/cloud/models.py:85 xpack/plugins/cloud/serializers.py:179
|
||||
#: xpack/plugins/cloud/models.py:81 xpack/plugins/cloud/serializers.py:160
|
||||
msgid "Regions"
|
||||
msgstr "地域"
|
||||
|
||||
#: xpack/plugins/cloud/models.py:88
|
||||
#: xpack/plugins/cloud/models.py:84
|
||||
msgid "Hostname strategy"
|
||||
msgstr "主机名策略"
|
||||
|
||||
#: xpack/plugins/cloud/models.py:102 xpack/plugins/cloud/serializers.py:186
|
||||
msgid "IP network segment group"
|
||||
msgstr "IP网段组"
|
||||
|
||||
#: xpack/plugins/cloud/models.py:105 xpack/plugins/cloud/serializers.py:208
|
||||
#: xpack/plugins/cloud/models.py:95 xpack/plugins/cloud/serializers.py:182
|
||||
msgid "Always update"
|
||||
msgstr "总是更新"
|
||||
|
||||
#: xpack/plugins/cloud/models.py:111
|
||||
#: xpack/plugins/cloud/models.py:101
|
||||
msgid "Date last sync"
|
||||
msgstr "最后同步日期"
|
||||
|
||||
#: xpack/plugins/cloud/models.py:122 xpack/plugins/cloud/models.py:163
|
||||
#: xpack/plugins/cloud/models.py:112 xpack/plugins/cloud/models.py:153
|
||||
msgid "Sync instance task"
|
||||
msgstr "同步实例任务"
|
||||
|
||||
#: xpack/plugins/cloud/models.py:174 xpack/plugins/cloud/models.py:219
|
||||
#: xpack/plugins/cloud/models.py:164 xpack/plugins/cloud/models.py:209
|
||||
msgid "Date sync"
|
||||
msgstr "同步日期"
|
||||
|
||||
#: xpack/plugins/cloud/models.py:199
|
||||
#: xpack/plugins/cloud/models.py:189
|
||||
msgid "Sync task"
|
||||
msgstr "同步任务"
|
||||
|
||||
#: xpack/plugins/cloud/models.py:203
|
||||
#: xpack/plugins/cloud/models.py:193
|
||||
msgid "Sync instance task history"
|
||||
msgstr "同步实例任务历史"
|
||||
|
||||
#: xpack/plugins/cloud/models.py:206
|
||||
#: xpack/plugins/cloud/models.py:196
|
||||
msgid "Instance"
|
||||
msgstr "实例"
|
||||
|
||||
|
@ -5151,56 +5144,47 @@ msgstr "西南-贵阳1"
|
|||
msgid "EU-Paris"
|
||||
msgstr "欧洲-巴黎"
|
||||
|
||||
#: xpack/plugins/cloud/serializers.py:21
|
||||
#: xpack/plugins/cloud/serializers.py:19
|
||||
msgid "AccessKey ID"
|
||||
msgstr ""
|
||||
|
||||
#: xpack/plugins/cloud/serializers.py:24
|
||||
#: xpack/plugins/cloud/serializers.py:22
|
||||
msgid "AccessKey Secret"
|
||||
msgstr ""
|
||||
|
||||
#: xpack/plugins/cloud/serializers.py:30
|
||||
#: xpack/plugins/cloud/serializers.py:28
|
||||
msgid "Client ID"
|
||||
msgstr ""
|
||||
|
||||
#: xpack/plugins/cloud/serializers.py:33
|
||||
#: xpack/plugins/cloud/serializers.py:31
|
||||
msgid "Client Secret"
|
||||
msgstr ""
|
||||
|
||||
#: xpack/plugins/cloud/serializers.py:36
|
||||
#: xpack/plugins/cloud/serializers.py:34
|
||||
msgid "Tenant ID"
|
||||
msgstr ""
|
||||
|
||||
#: xpack/plugins/cloud/serializers.py:39
|
||||
#: xpack/plugins/cloud/serializers.py:37
|
||||
msgid "Subscription ID"
|
||||
msgstr ""
|
||||
|
||||
#: xpack/plugins/cloud/serializers.py:51
|
||||
#: xpack/plugins/cloud/serializers.py:49
|
||||
msgid "This field is required"
|
||||
msgstr "这个字段是必填项"
|
||||
|
||||
#: xpack/plugins/cloud/serializers.py:85 xpack/plugins/cloud/serializers.py:89
|
||||
#: xpack/plugins/cloud/serializers.py:83 xpack/plugins/cloud/serializers.py:87
|
||||
msgid "API Endpoint"
|
||||
msgstr "API 端点"
|
||||
|
||||
#: xpack/plugins/cloud/serializers.py:171
|
||||
msgid ""
|
||||
"The IP address that is first matched to will be used as the IP of the "
|
||||
"created asset. <br>The default * indicates a random match. <br>Format for "
|
||||
"comma-delimited string, Such as: 192.168.1.0/24, 10.1.1.1-10.1.1.20"
|
||||
msgstr ""
|
||||
"第一个匹配到的 IP 地址将被用作创建的资产的 IP。<br> 默认值 * 表示随机匹配。"
|
||||
"<br> 格式为以逗号分隔的字符串,例如:192.168.1.0/24,10.1.1.1-10.1.1.20"
|
||||
|
||||
#: xpack/plugins/cloud/serializers.py:177
|
||||
#: xpack/plugins/cloud/serializers.py:158
|
||||
msgid "History count"
|
||||
msgstr "执行次数"
|
||||
|
||||
#: xpack/plugins/cloud/serializers.py:178
|
||||
#: xpack/plugins/cloud/serializers.py:159
|
||||
msgid "Instance count"
|
||||
msgstr "实例个数"
|
||||
|
||||
#: xpack/plugins/cloud/serializers.py:207
|
||||
#: xpack/plugins/cloud/serializers.py:181
|
||||
#: xpack/plugins/gathered_user/serializers.py:20
|
||||
msgid "Periodic display"
|
||||
msgstr "定时执行"
|
||||
|
@ -5293,5 +5277,31 @@ msgstr "旗舰版"
|
|||
msgid "Community edition"
|
||||
msgstr "社区版"
|
||||
|
||||
#~ msgid "Application category"
|
||||
#~ msgstr "应用类别"
|
||||
|
||||
#~ msgid "Application type"
|
||||
#~ msgstr "应用类型"
|
||||
|
||||
#~ msgid "Manual trigger"
|
||||
#~ msgstr "手动触发"
|
||||
|
||||
#~ msgid "Timing trigger"
|
||||
#~ msgstr "定时触发"
|
||||
|
||||
#~ msgid "Trigger mode"
|
||||
#~ msgstr "触发模式"
|
||||
|
||||
#~ msgid "IP network segment group"
|
||||
#~ msgstr "IP网段组"
|
||||
|
||||
#~ msgid ""
|
||||
#~ "The IP address that is first matched to will be used as the IP of the "
|
||||
#~ "created asset. <br>The default * indicates a random match. <br>Format for "
|
||||
#~ "comma-delimited string, Such as: 192.168.1.0/24, 10.1.1.1-10.1.1.20"
|
||||
#~ msgstr ""
|
||||
#~ "第一个匹配到的 IP 地址将被用作创建的资产的 IP。<br> 默认值 * 表示随机匹"
|
||||
#~ "配。<br> 格式为以逗号分隔的字符串,例如:192.168.1.0/24,10.1.1.1-10.1.1.20"
|
||||
|
||||
#~ msgid "Trigger"
|
||||
#~ msgstr "触发"
|
||||
|
|
|
@ -4,7 +4,7 @@ from rest_framework.views import APIView
|
|||
from rest_framework.response import Response
|
||||
from rest_framework import status
|
||||
|
||||
from common.drf.api import JmsGenericViewSet
|
||||
from common.drf.api import JMSGenericViewSet
|
||||
from notifications.notifications import system_msgs
|
||||
from notifications.models import SystemMsgSubscription
|
||||
from notifications.backends import BACKEND
|
||||
|
@ -30,7 +30,7 @@ class BackendListView(APIView):
|
|||
|
||||
class SystemMsgSubscriptionViewSet(ListModelMixin,
|
||||
UpdateModelMixin,
|
||||
JmsGenericViewSet):
|
||||
JMSGenericViewSet):
|
||||
lookup_field = 'message_type'
|
||||
queryset = SystemMsgSubscription.objects.all()
|
||||
serializer_classes = {
|
||||
|
|
|
@ -5,7 +5,7 @@ from rest_framework.decorators import action
|
|||
from common.http import is_true
|
||||
from common.permissions import IsValidUser
|
||||
from common.const.http import GET, PATCH, POST
|
||||
from common.drf.api import JmsGenericViewSet
|
||||
from common.drf.api import JMSGenericViewSet
|
||||
from ..serializers import (
|
||||
SiteMessageDetailSerializer, SiteMessageIdsSerializer,
|
||||
SiteMessageSendSerializer,
|
||||
|
@ -16,7 +16,7 @@ from ..filters import SiteMsgFilter
|
|||
__all__ = ('SiteMessageViewSet', )
|
||||
|
||||
|
||||
class SiteMessageViewSet(ListModelMixin, RetrieveModelMixin, JmsGenericViewSet):
|
||||
class SiteMessageViewSet(ListModelMixin, RetrieveModelMixin, JMSGenericViewSet):
|
||||
permission_classes = (IsValidUser,)
|
||||
serializer_classes = {
|
||||
'default': SiteMessageDetailSerializer,
|
||||
|
|
|
@ -9,6 +9,7 @@ from django.utils.translation import ugettext_lazy as _
|
|||
|
||||
from common.utils import lazyproperty, settings
|
||||
from common.const import choices
|
||||
from common.tree import TreeNode
|
||||
from common.db.models import TextChoices
|
||||
|
||||
|
||||
|
@ -233,6 +234,20 @@ class Organization(models.Model):
|
|||
with tmp_to_org(self):
|
||||
return resource_model.objects.all().count()
|
||||
|
||||
def as_tree_node(self, pid, opened=True):
|
||||
node = TreeNode(**{
|
||||
'id': self.id,
|
||||
'name': self.name,
|
||||
'title': self.name,
|
||||
'pId': pid,
|
||||
'open': opened,
|
||||
'isParent': True,
|
||||
'meta': {
|
||||
'type': 'org'
|
||||
}
|
||||
})
|
||||
return node
|
||||
|
||||
|
||||
def _convert_to_uuid_set(users):
|
||||
rst = set()
|
||||
|
|
|
@ -4,6 +4,7 @@ from rest_framework.generics import ListAPIView
|
|||
from rest_framework.response import Response
|
||||
|
||||
from common.mixins.api import CommonApiMixin
|
||||
from common.tree import TreeNodeSerializer
|
||||
from applications.api.mixin import (
|
||||
SerializeApplicationToTreeNodeMixin
|
||||
)
|
||||
|
@ -52,11 +53,13 @@ class ApplicationsAsTreeMixin(SerializeApplicationToTreeNodeMixin):
|
|||
"""
|
||||
将应用序列化成树的结构返回
|
||||
"""
|
||||
serializer_class = TreeNodeSerializer
|
||||
|
||||
def list(self, request, *args, **kwargs):
|
||||
queryset = self.filter_queryset(self.get_queryset())
|
||||
data = self.serialize_applications_with_org(queryset)
|
||||
return Response(data=data)
|
||||
tree_nodes = self.serialize_applications_with_org(queryset)
|
||||
serializer = self.get_serializer(tree_nodes, many=True)
|
||||
return Response(data=serializer.data)
|
||||
|
||||
|
||||
class UserAllGrantedApplicationsAsTreeApi(ApplicationsAsTreeMixin, UserAllGrantedApplicationsApi):
|
||||
|
|
|
@ -8,7 +8,7 @@ from django.utils.translation import ugettext_lazy as _
|
|||
from common.utils import lazyproperty
|
||||
from .base import BasePermission
|
||||
from users.models import User
|
||||
from applications.const import ApplicationCategoryChoices, ApplicationTypeChoices
|
||||
from applications.const import AppCategory, AppType
|
||||
|
||||
__all__ = [
|
||||
'ApplicationPermission',
|
||||
|
@ -17,10 +17,10 @@ __all__ = [
|
|||
|
||||
class ApplicationPermission(BasePermission):
|
||||
category = models.CharField(
|
||||
max_length=16, choices=ApplicationCategoryChoices.choices, verbose_name=_('Category')
|
||||
max_length=16, choices=AppCategory.choices, verbose_name=_('Category')
|
||||
)
|
||||
type = models.CharField(
|
||||
max_length=16, choices=ApplicationTypeChoices.choices, verbose_name=_('Type')
|
||||
max_length=16, choices=AppType.choices, verbose_name=_('Type')
|
||||
)
|
||||
applications = models.ManyToManyField(
|
||||
'applications.Application', related_name='granted_by_permissions', blank=True,
|
||||
|
@ -38,15 +38,15 @@ class ApplicationPermission(BasePermission):
|
|||
|
||||
@property
|
||||
def category_remote_app(self):
|
||||
return self.category == ApplicationCategoryChoices.remote_app.value
|
||||
return self.category == AppCategory.remote_app.value
|
||||
|
||||
@property
|
||||
def category_db(self):
|
||||
return self.category == ApplicationCategoryChoices.db.value
|
||||
return self.category == AppCategory.db.value
|
||||
|
||||
@property
|
||||
def category_cloud(self):
|
||||
return self.category == ApplicationCategoryChoices.cloud.value
|
||||
return self.category == AppCategory.cloud.value
|
||||
|
||||
@lazyproperty
|
||||
def users_amount(self):
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
|
||||
<li id="applications">
|
||||
<a>
|
||||
<i class="fa fa-th" style="width: 14px"></i> <span class="nav-label">{% trans 'My Applications' %}</span><span class="fa arrow"></span>
|
||||
<i class="fa fa-th" style="width: 14px"></i> <span class="nav-label">{% trans 'My applications' %}</span><span class="fa arrow"></span>
|
||||
</a>
|
||||
<ul class="nav nav-second-level">
|
||||
{% if LICENSE_VALID %}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
from django.utils.translation import ugettext as _
|
||||
from orgs.utils import tmp_to_org, tmp_to_root_org
|
||||
from applications.models import Application
|
||||
from applications.const import ApplicationCategoryChoices, ApplicationTypeChoices
|
||||
from applications.const import AppCategory, AppType
|
||||
from assets.models import SystemUser
|
||||
from perms.models import ApplicationPermission
|
||||
from .base import BaseHandler
|
||||
|
@ -17,9 +17,9 @@ class Handler(BaseHandler):
|
|||
def _construct_meta_display_of_open(self):
|
||||
meta_display_fields = ['apply_category_display', 'apply_type_display']
|
||||
apply_category = self.ticket.meta.get('apply_category')
|
||||
apply_category_display = ApplicationCategoryChoices.get_label(apply_category)
|
||||
apply_category_display = AppCategory.get_label(apply_category)
|
||||
apply_type = self.ticket.meta.get('apply_type')
|
||||
apply_type_display = ApplicationTypeChoices.get_label(apply_type)
|
||||
apply_type_display = AppType.get_label(apply_type)
|
||||
meta_display_values = [apply_category_display, apply_type_display]
|
||||
meta_display = dict(zip(meta_display_fields, meta_display_values))
|
||||
return meta_display
|
||||
|
|
|
@ -3,7 +3,7 @@ from django.utils.translation import ugettext_lazy as _
|
|||
from django.db.models import Q
|
||||
from perms.models import ApplicationPermission
|
||||
from applications.models import Application
|
||||
from applications.const import ApplicationCategoryChoices, ApplicationTypeChoices
|
||||
from applications.const import AppCategory, AppType
|
||||
from assets.models import SystemUser
|
||||
from orgs.utils import tmp_to_org
|
||||
from tickets.models import Ticket
|
||||
|
@ -17,14 +17,14 @@ __all__ = [
|
|||
class ApplySerializer(serializers.Serializer):
|
||||
# 申请信息
|
||||
apply_category = serializers.ChoiceField(
|
||||
required=True, choices=ApplicationCategoryChoices.choices, label=_('Category'),
|
||||
required=True, choices=AppCategory.choices, label=_('Category'),
|
||||
allow_null=True,
|
||||
)
|
||||
apply_category_display = serializers.CharField(
|
||||
read_only=True, label=_('Category display'), allow_null=True,
|
||||
)
|
||||
apply_type = serializers.ChoiceField(
|
||||
required=True, choices=ApplicationTypeChoices.choices, label=_('Type'),
|
||||
required=True, choices=AppType.choices, label=_('Type'),
|
||||
allow_null=True
|
||||
)
|
||||
apply_type_display = serializers.CharField(
|
||||
|
|
Loading…
Reference in New Issue