mirror of https://github.com/jumpserver/jumpserver
commit
ee22006683
|
@ -18,22 +18,28 @@ class ApplicationSerializerMixin(serializers.Serializer):
|
||||||
attrs = MethodSerializer()
|
attrs = MethodSerializer()
|
||||||
|
|
||||||
def get_attrs_serializer(self):
|
def get_attrs_serializer(self):
|
||||||
serializer_class = None
|
default_serializer = serializers.Serializer(read_only=True)
|
||||||
if isinstance(self.instance, models.Application):
|
if isinstance(self.instance, models.Application):
|
||||||
instance_type = self.instance.type
|
_type = self.instance.type
|
||||||
serializer_class = type_serializer_classes_mapping.get(instance_type)
|
_category = self.instance.category
|
||||||
else:
|
else:
|
||||||
request = self.context['request']
|
_type = self.context['request'].query_params.get('type')
|
||||||
query_type = request.query_params.get('type')
|
_category = self.context['request'].query_params.get('category')
|
||||||
query_category = request.query_params.get('category')
|
|
||||||
if query_type:
|
|
||||||
serializer_class = type_serializer_classes_mapping.get(query_type)
|
|
||||||
elif query_category:
|
|
||||||
serializer_class = category_serializer_classes_mapping.get(query_category)
|
|
||||||
|
|
||||||
if serializer_class is None:
|
if _type:
|
||||||
serializer_class = serializers.Serializer
|
serializer_class = type_serializer_classes_mapping.get(_type)
|
||||||
|
elif _category:
|
||||||
|
serializer_class = category_serializer_classes_mapping.get(_category)
|
||||||
|
else:
|
||||||
|
serializer_class = default_serializer
|
||||||
|
|
||||||
|
if not serializer_class:
|
||||||
|
serializer_class = default_serializer
|
||||||
|
|
||||||
|
if isinstance(serializer_class, type):
|
||||||
serializer = serializer_class()
|
serializer = serializer_class()
|
||||||
|
else:
|
||||||
|
serializer = serializer_class
|
||||||
return serializer
|
return serializer
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -28,7 +28,7 @@ logger = get_logger(__name__)
|
||||||
class AssetUserFilterBackend(filters.BaseFilterBackend):
|
class AssetUserFilterBackend(filters.BaseFilterBackend):
|
||||||
def filter_queryset(self, request, queryset, view):
|
def filter_queryset(self, request, queryset, view):
|
||||||
kwargs = {}
|
kwargs = {}
|
||||||
for field in view.filter_fields:
|
for field in view.filterset_fields:
|
||||||
value = request.GET.get(field)
|
value = request.GET.get(field)
|
||||||
if not value:
|
if not value:
|
||||||
continue
|
continue
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
#
|
#
|
||||||
from rest_framework import serializers
|
from rest_framework import serializers
|
||||||
from django.db.models import F
|
from django.db.models import F
|
||||||
|
from django.core.validators import RegexValidator
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
|
||||||
from orgs.mixins.serializers import BulkOrgResourceModelSerializer
|
from orgs.mixins.serializers import BulkOrgResourceModelSerializer
|
||||||
|
@ -177,6 +177,14 @@ class AssetDisplaySerializer(AssetSerializer):
|
||||||
class PlatformSerializer(serializers.ModelSerializer):
|
class PlatformSerializer(serializers.ModelSerializer):
|
||||||
meta = serializers.DictField(required=False, allow_null=True)
|
meta = serializers.DictField(required=False, allow_null=True)
|
||||||
|
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
super().__init__(*args, **kwargs)
|
||||||
|
|
||||||
|
# TODO 修复 drf SlugField RegexValidator bug,之后记得删除
|
||||||
|
validators = self.fields['name'].validators
|
||||||
|
if isinstance(validators[-1], RegexValidator):
|
||||||
|
validators.pop()
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Platform
|
model = Platform
|
||||||
fields = [
|
fields = [
|
||||||
|
|
|
@ -86,8 +86,7 @@ class CommandExecutionSerializer(serializers.ModelSerializer):
|
||||||
@classmethod
|
@classmethod
|
||||||
def setup_eager_loading(cls, queryset):
|
def setup_eager_loading(cls, queryset):
|
||||||
""" Perform necessary eager loading of data. """
|
""" Perform necessary eager loading of data. """
|
||||||
queryset = queryset.annotate(user_display=F('user__name'))\
|
queryset = queryset.prefetch_related('user', 'run_as', 'hosts')
|
||||||
.annotate(run_as_display=F('run_as__name'))
|
|
||||||
return queryset
|
return queryset
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -4,7 +4,6 @@ import uuid
|
||||||
|
|
||||||
from django.core.cache import cache
|
from django.core.cache import cache
|
||||||
from django.shortcuts import get_object_or_404
|
from django.shortcuts import get_object_or_404
|
||||||
from rest_framework.permissions import AllowAny
|
|
||||||
from rest_framework.response import Response
|
from rest_framework.response import Response
|
||||||
from rest_framework.views import APIView
|
from rest_framework.views import APIView
|
||||||
|
|
||||||
|
|
|
@ -73,9 +73,8 @@ class SimpleMetadataWithFilters(SimpleMetadata):
|
||||||
elif getattr(field, 'fields', None):
|
elif getattr(field, 'fields', None):
|
||||||
field_info['children'] = self.get_serializer_info(field)
|
field_info['children'] = self.get_serializer_info(field)
|
||||||
|
|
||||||
if (not field_info.get('read_only') and
|
if not isinstance(field, (serializers.RelatedField, serializers.ManyRelatedField)) \
|
||||||
not isinstance(field, (serializers.RelatedField, serializers.ManyRelatedField)) and
|
and hasattr(field, 'choices'):
|
||||||
hasattr(field, 'choices')):
|
|
||||||
field_info['choices'] = [
|
field_info['choices'] = [
|
||||||
{
|
{
|
||||||
'value': choice_value,
|
'value': choice_value,
|
||||||
|
|
Binary file not shown.
|
@ -8,7 +8,7 @@ msgid ""
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"Project-Id-Version: JumpServer 0.3.3\n"
|
"Project-Id-Version: JumpServer 0.3.3\n"
|
||||||
"Report-Msgid-Bugs-To: \n"
|
"Report-Msgid-Bugs-To: \n"
|
||||||
"POT-Creation-Date: 2021-01-17 16:12+0800\n"
|
"POT-Creation-Date: 2021-01-19 20:03+0800\n"
|
||||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||||
"Last-Translator: ibuler <ibuler@qq.com>\n"
|
"Last-Translator: ibuler <ibuler@qq.com>\n"
|
||||||
"Language-Team: JumpServer team<ibuler@qq.com>\n"
|
"Language-Team: JumpServer team<ibuler@qq.com>\n"
|
||||||
|
@ -58,23 +58,22 @@ msgid "Name"
|
||||||
msgstr "名称"
|
msgstr "名称"
|
||||||
|
|
||||||
#: applications/models/application.py:12
|
#: applications/models/application.py:12
|
||||||
#: applications/serializers/application.py:41 assets/models/label.py:21
|
#: applications/serializers/application.py:47 assets/models/label.py:21
|
||||||
#: perms/models/application_permission.py:20
|
#: perms/models/application_permission.py:20
|
||||||
#: perms/serializers/application/permission.py:16
|
#: perms/serializers/application/permission.py:16
|
||||||
#: perms/serializers/application/user_permission.py:33
|
#: perms/serializers/application/user_permission.py:33
|
||||||
#: tickets/serializers/ticket/meta/ticket_type/apply_application.py:18
|
#: tickets/serializers/ticket/meta/ticket_type/apply_application.py:20
|
||||||
msgid "Category"
|
msgid "Category"
|
||||||
msgstr "种类"
|
msgstr "种类"
|
||||||
|
|
||||||
#: applications/models/application.py:15
|
#: applications/models/application.py:15
|
||||||
#: applications/serializers/application.py:42 assets/models/cmd_filter.py:52
|
#: applications/serializers/application.py:48 assets/models/cmd_filter.py:52
|
||||||
#: perms/models/application_permission.py:23
|
#: perms/models/application_permission.py:23
|
||||||
#: perms/serializers/application/permission.py:17
|
#: perms/serializers/application/permission.py:17
|
||||||
#: perms/serializers/application/user_permission.py:34
|
#: perms/serializers/application/user_permission.py:34
|
||||||
#: terminal/models/storage.py:18 terminal/models/storage.py:58
|
#: terminal/models/storage.py:18 terminal/models/storage.py:58
|
||||||
#: tickets/models/ticket.py:38
|
#: tickets/models/ticket.py:38
|
||||||
#: tickets/serializers/ticket/meta/ticket_type/apply_application.py:25
|
#: tickets/serializers/ticket/meta/ticket_type/apply_application.py:27
|
||||||
#: tickets/serializers/ticket/ticket.py:19
|
|
||||||
#: users/templates/users/user_granted_database_app.html:35
|
#: users/templates/users/user_granted_database_app.html:35
|
||||||
msgid "Type"
|
msgid "Type"
|
||||||
msgstr "类型"
|
msgstr "类型"
|
||||||
|
@ -269,7 +268,7 @@ msgstr "主机名"
|
||||||
|
|
||||||
#: assets/models/asset.py:194 assets/models/domain.py:54
|
#: assets/models/asset.py:194 assets/models/domain.py:54
|
||||||
#: assets/models/user.py:120 terminal/serializers/session.py:29
|
#: assets/models/user.py:120 terminal/serializers/session.py:29
|
||||||
#: terminal/serializers/storage.py:59
|
#: terminal/serializers/storage.py:68
|
||||||
msgid "Protocol"
|
msgid "Protocol"
|
||||||
msgstr "协议"
|
msgstr "协议"
|
||||||
|
|
||||||
|
@ -293,7 +292,7 @@ msgstr "激活"
|
||||||
|
|
||||||
#: assets/models/asset.py:203 assets/models/cluster.py:19
|
#: assets/models/asset.py:203 assets/models/cluster.py:19
|
||||||
#: assets/models/user.py:66 templates/_nav.html:44
|
#: assets/models/user.py:66 templates/_nav.html:44
|
||||||
#: xpack/plugins/cloud/models.py:143 xpack/plugins/cloud/serializers.py:126
|
#: xpack/plugins/cloud/models.py:143 xpack/plugins/cloud/serializers.py:137
|
||||||
msgid "Admin user"
|
msgid "Admin user"
|
||||||
msgstr "管理用户"
|
msgstr "管理用户"
|
||||||
|
|
||||||
|
@ -508,8 +507,7 @@ msgstr "每行一个命令"
|
||||||
|
|
||||||
#: assets/models/cmd_filter.py:56 audits/models.py:57
|
#: assets/models/cmd_filter.py:56 audits/models.py:57
|
||||||
#: authentication/templates/authentication/_access_key_modal.html:34
|
#: authentication/templates/authentication/_access_key_modal.html:34
|
||||||
#: tickets/models/ticket.py:43 tickets/serializers/ticket/ticket.py:20
|
#: tickets/models/ticket.py:43 users/templates/users/_granted_assets.html:29
|
||||||
#: users/templates/users/_granted_assets.html:29
|
|
||||||
#: users/templates/users/user_asset_permission.html:44
|
#: users/templates/users/user_asset_permission.html:44
|
||||||
#: users/templates/users/user_asset_permission.html:79
|
#: users/templates/users/user_asset_permission.html:79
|
||||||
#: users/templates/users/user_database_app_permission.html:42
|
#: users/templates/users/user_database_app_permission.html:42
|
||||||
|
@ -604,7 +602,7 @@ msgstr "ssh私钥"
|
||||||
#: users/templates/users/user_asset_permission.html:41
|
#: users/templates/users/user_asset_permission.html:41
|
||||||
#: users/templates/users/user_asset_permission.html:73
|
#: users/templates/users/user_asset_permission.html:73
|
||||||
#: users/templates/users/user_asset_permission.html:158
|
#: users/templates/users/user_asset_permission.html:158
|
||||||
#: xpack/plugins/cloud/models.py:139 xpack/plugins/cloud/serializers.py:127
|
#: xpack/plugins/cloud/models.py:139 xpack/plugins/cloud/serializers.py:138
|
||||||
msgid "Node"
|
msgid "Node"
|
||||||
msgstr "节点"
|
msgstr "节点"
|
||||||
|
|
||||||
|
@ -996,10 +994,10 @@ msgstr "成功"
|
||||||
|
|
||||||
#: audits/models.py:43 ops/models/command.py:28 perms/models/base.py:52
|
#: audits/models.py:43 ops/models/command.py:28 perms/models/base.py:52
|
||||||
#: terminal/models/session.py:51
|
#: terminal/models/session.py:51
|
||||||
#: tickets/serializers/ticket/meta/ticket_type/apply_application.py:41
|
#: tickets/serializers/ticket/meta/ticket_type/apply_application.py:43
|
||||||
#: tickets/serializers/ticket/meta/ticket_type/apply_application.py:69
|
#: tickets/serializers/ticket/meta/ticket_type/apply_application.py:74
|
||||||
#: tickets/serializers/ticket/meta/ticket_type/apply_asset.py:38
|
#: tickets/serializers/ticket/meta/ticket_type/apply_asset.py:40
|
||||||
#: tickets/serializers/ticket/meta/ticket_type/apply_asset.py:73
|
#: tickets/serializers/ticket/meta/ticket_type/apply_asset.py:78
|
||||||
#: xpack/plugins/change_auth_plan/models.py:177
|
#: xpack/plugins/change_auth_plan/models.py:177
|
||||||
#: xpack/plugins/change_auth_plan/models.py:307
|
#: xpack/plugins/change_auth_plan/models.py:307
|
||||||
#: xpack/plugins/gathered_user/models.py:76
|
#: xpack/plugins/gathered_user/models.py:76
|
||||||
|
@ -1093,8 +1091,7 @@ msgid "Reason"
|
||||||
msgstr "原因"
|
msgstr "原因"
|
||||||
|
|
||||||
#: audits/models.py:106 tickets/models/ticket.py:47
|
#: audits/models.py:106 tickets/models/ticket.py:47
|
||||||
#: tickets/serializers/ticket/ticket.py:21 xpack/plugins/cloud/models.py:224
|
#: xpack/plugins/cloud/models.py:224 xpack/plugins/cloud/models.py:282
|
||||||
#: xpack/plugins/cloud/models.py:282
|
|
||||||
msgid "Status"
|
msgid "Status"
|
||||||
msgstr "状态"
|
msgstr "状态"
|
||||||
|
|
||||||
|
@ -1132,7 +1129,7 @@ msgstr "是否成功"
|
||||||
msgid "Result"
|
msgid "Result"
|
||||||
msgstr "结果"
|
msgstr "结果"
|
||||||
|
|
||||||
#: audits/serializers.py:79 terminal/serializers/storage.py:157
|
#: audits/serializers.py:79 terminal/serializers/storage.py:177
|
||||||
msgid "Hosts"
|
msgid "Hosts"
|
||||||
msgstr "主机"
|
msgstr "主机"
|
||||||
|
|
||||||
|
@ -1786,11 +1783,11 @@ msgstr "定期清除Celery日志"
|
||||||
msgid "Task log"
|
msgid "Task log"
|
||||||
msgstr "任务列表"
|
msgstr "任务列表"
|
||||||
|
|
||||||
#: ops/utils.py:62
|
#: ops/utils.py:64
|
||||||
msgid "Update task content: {}"
|
msgid "Update task content: {}"
|
||||||
msgstr "更新任务内容: {}"
|
msgstr "更新任务内容: {}"
|
||||||
|
|
||||||
#: ops/utils.py:72
|
#: ops/utils.py:74
|
||||||
msgid "Disk used more than 80%: {} => {}"
|
msgid "Disk used more than 80%: {} => {}"
|
||||||
msgstr "磁盘使用率超过 80%: {} => {}"
|
msgstr "磁盘使用率超过 80%: {} => {}"
|
||||||
|
|
||||||
|
@ -1804,7 +1801,7 @@ msgstr "当前组织不能被删除"
|
||||||
|
|
||||||
#: orgs/mixins/models.py:56 orgs/mixins/serializers.py:25 orgs/models.py:41
|
#: orgs/mixins/models.py:56 orgs/mixins/serializers.py:25 orgs/models.py:41
|
||||||
#: orgs/models.py:422 orgs/serializers.py:100
|
#: orgs/models.py:422 orgs/serializers.py:100
|
||||||
#: tickets/serializers/ticket/ticket.py:74
|
#: tickets/serializers/ticket/ticket.py:81
|
||||||
msgid "Organization"
|
msgid "Organization"
|
||||||
msgstr "组织"
|
msgstr "组织"
|
||||||
|
|
||||||
|
@ -1904,10 +1901,10 @@ msgid "User group"
|
||||||
msgstr "用户组"
|
msgstr "用户组"
|
||||||
|
|
||||||
#: perms/models/base.py:53
|
#: perms/models/base.py:53
|
||||||
#: tickets/serializers/ticket/meta/ticket_type/apply_application.py:44
|
#: tickets/serializers/ticket/meta/ticket_type/apply_application.py:46
|
||||||
#: tickets/serializers/ticket/meta/ticket_type/apply_application.py:72
|
#: tickets/serializers/ticket/meta/ticket_type/apply_application.py:77
|
||||||
#: tickets/serializers/ticket/meta/ticket_type/apply_asset.py:41
|
#: tickets/serializers/ticket/meta/ticket_type/apply_asset.py:43
|
||||||
#: tickets/serializers/ticket/meta/ticket_type/apply_asset.py:76
|
#: tickets/serializers/ticket/meta/ticket_type/apply_asset.py:81
|
||||||
#: users/models/user.py:556 users/templates/users/user_detail.html:93
|
#: users/models/user.py:556 users/templates/users/user_detail.html:93
|
||||||
#: users/templates/users/user_profile.html:120
|
#: users/templates/users/user_profile.html:120
|
||||||
msgid "Date expired"
|
msgid "Date expired"
|
||||||
|
@ -2717,60 +2714,64 @@ msgstr "是否可重放"
|
||||||
msgid "Can join"
|
msgid "Can join"
|
||||||
msgstr "是否可加入"
|
msgstr "是否可加入"
|
||||||
|
|
||||||
#: terminal/serializers/storage.py:19
|
#: terminal/serializers/storage.py:20
|
||||||
|
msgid "Endpoint invalid: remove path `{}`"
|
||||||
|
msgstr "端点无效: 移除路径 `{}`"
|
||||||
|
|
||||||
|
#: terminal/serializers/storage.py:26
|
||||||
msgid "Bucket"
|
msgid "Bucket"
|
||||||
msgstr "桶名称"
|
msgstr "桶名称"
|
||||||
|
|
||||||
#: terminal/serializers/storage.py:22
|
#: terminal/serializers/storage.py:29
|
||||||
msgid "Access key"
|
msgid "Access key"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: terminal/serializers/storage.py:26
|
#: terminal/serializers/storage.py:33
|
||||||
msgid "Secret key"
|
msgid "Secret key"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: terminal/serializers/storage.py:30 terminal/serializers/storage.py:41
|
#: terminal/serializers/storage.py:38 terminal/serializers/storage.py:50
|
||||||
#: terminal/serializers/storage.py:70
|
#: terminal/serializers/storage.py:80
|
||||||
msgid "Endpoint"
|
msgid "Endpoint"
|
||||||
msgstr "端点"
|
msgstr "端点"
|
||||||
|
|
||||||
#: terminal/serializers/storage.py:56 xpack/plugins/cloud/models.py:276
|
#: terminal/serializers/storage.py:65 xpack/plugins/cloud/models.py:276
|
||||||
msgid "Region"
|
msgid "Region"
|
||||||
msgstr "地域"
|
msgstr "地域"
|
||||||
|
|
||||||
#: terminal/serializers/storage.py:80
|
#: terminal/serializers/storage.py:90
|
||||||
msgid "Container name"
|
msgid "Container name"
|
||||||
msgstr "容器名称"
|
msgstr "容器名称"
|
||||||
|
|
||||||
#: terminal/serializers/storage.py:82
|
#: terminal/serializers/storage.py:92
|
||||||
msgid "Account name"
|
msgid "Account name"
|
||||||
msgstr "账户名称"
|
msgstr "账户名称"
|
||||||
|
|
||||||
#: terminal/serializers/storage.py:83
|
#: terminal/serializers/storage.py:93
|
||||||
msgid "Account key"
|
msgid "Account key"
|
||||||
msgstr "账户密钥"
|
msgstr "账户密钥"
|
||||||
|
|
||||||
#: terminal/serializers/storage.py:86
|
#: terminal/serializers/storage.py:96
|
||||||
msgid "Endpoint suffix"
|
msgid "Endpoint suffix"
|
||||||
msgstr "端点后缀"
|
msgstr "端点后缀"
|
||||||
|
|
||||||
#: terminal/serializers/storage.py:135
|
#: terminal/serializers/storage.py:154
|
||||||
msgid "The address format is incorrect"
|
msgid "The address format is incorrect"
|
||||||
msgstr "地址格式不正确"
|
msgstr "地址格式不正确"
|
||||||
|
|
||||||
#: terminal/serializers/storage.py:142
|
#: terminal/serializers/storage.py:161
|
||||||
msgid "Host invalid"
|
msgid "Host invalid"
|
||||||
msgstr "主机无效"
|
msgstr "主机无效"
|
||||||
|
|
||||||
#: terminal/serializers/storage.py:145
|
#: terminal/serializers/storage.py:164
|
||||||
msgid "Port invalid"
|
msgid "Port invalid"
|
||||||
msgstr "端口无效"
|
msgstr "端口无效"
|
||||||
|
|
||||||
#: terminal/serializers/storage.py:161
|
#: terminal/serializers/storage.py:180
|
||||||
msgid "Index"
|
msgid "Index"
|
||||||
msgstr "索引"
|
msgstr "索引"
|
||||||
|
|
||||||
#: terminal/serializers/storage.py:164
|
#: terminal/serializers/storage.py:183
|
||||||
msgid "Doc type"
|
msgid "Doc type"
|
||||||
msgstr "文档类型"
|
msgstr "文档类型"
|
||||||
|
|
||||||
|
@ -2778,14 +2779,14 @@ msgstr "文档类型"
|
||||||
msgid "Not found"
|
msgid "Not found"
|
||||||
msgstr "没有发现"
|
msgstr "没有发现"
|
||||||
|
|
||||||
#: terminal/utils.py:74
|
#: terminal/utils.py:79
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid ""
|
msgid ""
|
||||||
"Insecure Command Alert: [%(name)s->%(login_from)s@%(remote_addr)s] $"
|
"Insecure Command Alert: [%(name)s->%(login_from)s@%(remote_addr)s] $"
|
||||||
"%(command)s"
|
"%(command)s"
|
||||||
msgstr "危险命令告警: [%(name)s->%(login_from)s@%(remote_addr)s] $%(command)s"
|
msgstr "危险命令告警: [%(name)s->%(login_from)s@%(remote_addr)s] $%(command)s"
|
||||||
|
|
||||||
#: terminal/utils.py:81
|
#: terminal/utils.py:87
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid ""
|
msgid ""
|
||||||
"\n"
|
"\n"
|
||||||
|
@ -2842,6 +2843,129 @@ msgstr "拒绝"
|
||||||
msgid "Closed"
|
msgid "Closed"
|
||||||
msgstr "关闭"
|
msgstr "关闭"
|
||||||
|
|
||||||
|
#: tickets/handler/apply_application.py:55
|
||||||
|
msgid "Applied category"
|
||||||
|
msgstr "申请的种类"
|
||||||
|
|
||||||
|
#: tickets/handler/apply_application.py:56
|
||||||
|
msgid "Applied type"
|
||||||
|
msgstr "申请的类型"
|
||||||
|
|
||||||
|
#: tickets/handler/apply_application.py:57
|
||||||
|
msgid "Applied application group"
|
||||||
|
msgstr "申请的应用组"
|
||||||
|
|
||||||
|
#: tickets/handler/apply_application.py:58 tickets/handler/apply_asset.py:59
|
||||||
|
msgid "Applied system user group"
|
||||||
|
msgstr "申请的系统用户组"
|
||||||
|
|
||||||
|
#: tickets/handler/apply_application.py:59 tickets/handler/apply_asset.py:61
|
||||||
|
msgid "Applied date start"
|
||||||
|
msgstr "申请的开始日期"
|
||||||
|
|
||||||
|
#: tickets/handler/apply_application.py:60 tickets/handler/apply_asset.py:62
|
||||||
|
msgid "Applied date expired"
|
||||||
|
msgstr "申请的失效日期"
|
||||||
|
|
||||||
|
#: tickets/handler/apply_application.py:75
|
||||||
|
msgid "Approved applications"
|
||||||
|
msgstr "批准的应用"
|
||||||
|
|
||||||
|
#: tickets/handler/apply_application.py:76 tickets/handler/apply_asset.py:79
|
||||||
|
msgid "Approved system users"
|
||||||
|
msgstr "批准的系统用户"
|
||||||
|
|
||||||
|
#: tickets/handler/apply_application.py:77 tickets/handler/apply_asset.py:81
|
||||||
|
msgid "Approved date start"
|
||||||
|
msgstr "批准的开始日期"
|
||||||
|
|
||||||
|
#: tickets/handler/apply_application.py:78 tickets/handler/apply_asset.py:82
|
||||||
|
msgid "Approved date expired"
|
||||||
|
msgstr "批准的失效日期"
|
||||||
|
|
||||||
|
#: tickets/handler/apply_application.py:100 tickets/handler/apply_asset.py:103
|
||||||
|
msgid ""
|
||||||
|
"Created by the ticket, ticket title: {}, ticket applicant: {}, ticket "
|
||||||
|
"processor: {}, ticket ID: {}"
|
||||||
|
msgstr ""
|
||||||
|
"通过工单创建, 工单标题: {}, 工单申请人: {}, 工单处理人: {}, 工单 ID: {}"
|
||||||
|
|
||||||
|
#: tickets/handler/apply_asset.py:57
|
||||||
|
msgid "Applied IP group"
|
||||||
|
msgstr "申请的IP组"
|
||||||
|
|
||||||
|
#: tickets/handler/apply_asset.py:58
|
||||||
|
msgid "Applied hostname group"
|
||||||
|
msgstr "申请的主机名组"
|
||||||
|
|
||||||
|
#: tickets/handler/apply_asset.py:60
|
||||||
|
msgid "Applied actions"
|
||||||
|
msgstr "申请的动作"
|
||||||
|
|
||||||
|
#: tickets/handler/apply_asset.py:78
|
||||||
|
msgid "Approved assets"
|
||||||
|
msgstr "批准的资产"
|
||||||
|
|
||||||
|
#: tickets/handler/apply_asset.py:80
|
||||||
|
msgid "Approved actions"
|
||||||
|
msgstr "批准的动作"
|
||||||
|
|
||||||
|
#: tickets/handler/base.py:62
|
||||||
|
msgid "User {} {} the ticket"
|
||||||
|
msgstr "用户 {} {} 这个工单"
|
||||||
|
|
||||||
|
#: tickets/handler/base.py:91
|
||||||
|
msgid "Ticket title"
|
||||||
|
msgstr "工单标题"
|
||||||
|
|
||||||
|
#: tickets/handler/base.py:92
|
||||||
|
msgid "Ticket type"
|
||||||
|
msgstr "工单类型"
|
||||||
|
|
||||||
|
#: tickets/handler/base.py:93
|
||||||
|
msgid "Ticket status"
|
||||||
|
msgstr "工单状态"
|
||||||
|
|
||||||
|
#: tickets/handler/base.py:94
|
||||||
|
msgid "Ticket action"
|
||||||
|
msgstr "工单动作"
|
||||||
|
|
||||||
|
#: tickets/handler/base.py:95
|
||||||
|
msgid "Ticket applicant"
|
||||||
|
msgstr "工单申请人"
|
||||||
|
|
||||||
|
#: tickets/handler/base.py:96
|
||||||
|
msgid "Ticket assignees"
|
||||||
|
msgstr "工单受理人"
|
||||||
|
|
||||||
|
#: tickets/handler/base.py:99
|
||||||
|
msgid "Ticket processor"
|
||||||
|
msgstr "工单处理人"
|
||||||
|
|
||||||
|
#: tickets/handler/base.py:100
|
||||||
|
msgid "Ticket basic info"
|
||||||
|
msgstr "工单基本信息"
|
||||||
|
|
||||||
|
#: tickets/handler/base.py:114
|
||||||
|
msgid "Ticket applied info"
|
||||||
|
msgstr "工单申请信息"
|
||||||
|
|
||||||
|
#: tickets/handler/base.py:119
|
||||||
|
msgid "Ticket approved info"
|
||||||
|
msgstr "工单批准信息"
|
||||||
|
|
||||||
|
#: tickets/handler/login_confirm.py:16
|
||||||
|
msgid "Applied login IP"
|
||||||
|
msgstr "申请的登录IP"
|
||||||
|
|
||||||
|
#: tickets/handler/login_confirm.py:17
|
||||||
|
msgid "Applied login city"
|
||||||
|
msgstr "申请的登录城市"
|
||||||
|
|
||||||
|
#: tickets/handler/login_confirm.py:18
|
||||||
|
msgid "Applied login datetime"
|
||||||
|
msgstr "申请的登录日期"
|
||||||
|
|
||||||
#: tickets/models/comment.py:19
|
#: tickets/models/comment.py:19
|
||||||
msgid "User display name"
|
msgid "User display name"
|
||||||
msgstr "用户显示名称"
|
msgstr "用户显示名称"
|
||||||
|
@ -2864,7 +2988,7 @@ msgstr "申请人"
|
||||||
|
|
||||||
#: tickets/models/ticket.py:55
|
#: tickets/models/ticket.py:55
|
||||||
msgid "Applicant display"
|
msgid "Applicant display"
|
||||||
msgstr "申请人"
|
msgstr "申请人 (显示名称)"
|
||||||
|
|
||||||
#: tickets/models/ticket.py:60
|
#: tickets/models/ticket.py:60
|
||||||
msgid "Processor"
|
msgid "Processor"
|
||||||
|
@ -2872,7 +2996,7 @@ msgstr "处理人"
|
||||||
|
|
||||||
#: tickets/models/ticket.py:63
|
#: tickets/models/ticket.py:63
|
||||||
msgid "Processor display"
|
msgid "Processor display"
|
||||||
msgstr "处理人"
|
msgstr "处理人 (显示名称)"
|
||||||
|
|
||||||
#: tickets/models/ticket.py:67
|
#: tickets/models/ticket.py:67
|
||||||
msgid "Assignees"
|
msgid "Assignees"
|
||||||
|
@ -2880,153 +3004,134 @@ msgstr "受理人"
|
||||||
|
|
||||||
#: tickets/models/ticket.py:70
|
#: tickets/models/ticket.py:70
|
||||||
msgid "Assignees display"
|
msgid "Assignees display"
|
||||||
msgstr "受理人"
|
msgstr "受理人 (显示名称)"
|
||||||
|
|
||||||
#: tickets/serializers/ticket/meta/ticket_type/apply_application.py:22
|
#: tickets/serializers/ticket/meta/ticket_type/apply_application.py:24
|
||||||
msgid "Category display"
|
msgid "Category display"
|
||||||
msgstr "种类"
|
msgstr "种类 (显示名称)"
|
||||||
|
|
||||||
#: tickets/serializers/ticket/meta/ticket_type/apply_application.py:29
|
#: tickets/serializers/ticket/meta/ticket_type/apply_application.py:31
|
||||||
|
#: tickets/serializers/ticket/ticket.py:19
|
||||||
msgid "Type display"
|
msgid "Type display"
|
||||||
msgstr "类型"
|
msgstr "类型 (显示名称)"
|
||||||
|
|
||||||
#: tickets/serializers/ticket/meta/ticket_type/apply_application.py:33
|
#: tickets/serializers/ticket/meta/ticket_type/apply_application.py:35
|
||||||
msgid "Application group"
|
msgid "Application group"
|
||||||
msgstr "应用组"
|
msgstr "应用组"
|
||||||
|
|
||||||
#: tickets/serializers/ticket/meta/ticket_type/apply_application.py:37
|
#: tickets/serializers/ticket/meta/ticket_type/apply_application.py:39
|
||||||
#: tickets/serializers/ticket/meta/ticket_type/apply_asset.py:26
|
#: tickets/serializers/ticket/meta/ticket_type/apply_asset.py:28
|
||||||
msgid "System user group"
|
msgid "System user group"
|
||||||
msgstr "系统用户组"
|
msgstr "系统用户组"
|
||||||
|
|
||||||
#: tickets/serializers/ticket/meta/ticket_type/apply_application.py:51
|
#: tickets/serializers/ticket/meta/ticket_type/apply_application.py:53
|
||||||
|
#: tickets/serializers/ticket/meta/ticket_type/apply_asset.py:50
|
||||||
|
msgid "Permission name"
|
||||||
|
msgstr "授权名称"
|
||||||
|
|
||||||
|
#: tickets/serializers/ticket/meta/ticket_type/apply_application.py:56
|
||||||
msgid "Approve applications"
|
msgid "Approve applications"
|
||||||
msgstr "批准的应用"
|
msgstr "批准的应用"
|
||||||
|
|
||||||
#: tickets/serializers/ticket/meta/ticket_type/apply_application.py:56
|
#: tickets/serializers/ticket/meta/ticket_type/apply_application.py:61
|
||||||
msgid "Approve applications display"
|
msgid "Approve applications display"
|
||||||
msgstr "批准的应用"
|
msgstr "批准的应用 (显示名称)"
|
||||||
|
|
||||||
#: tickets/serializers/ticket/meta/ticket_type/apply_application.py:60
|
#: tickets/serializers/ticket/meta/ticket_type/apply_application.py:65
|
||||||
#: tickets/serializers/ticket/meta/ticket_type/apply_asset.py:57
|
#: tickets/serializers/ticket/meta/ticket_type/apply_asset.py:62
|
||||||
msgid "Approve system users"
|
msgid "Approve system users"
|
||||||
msgstr "批准的系统用户"
|
msgstr "批准的系统用户"
|
||||||
|
|
||||||
#: tickets/serializers/ticket/meta/ticket_type/apply_application.py:65
|
#: tickets/serializers/ticket/meta/ticket_type/apply_application.py:70
|
||||||
msgid "Approve system user display"
|
msgid "Approve system user display"
|
||||||
msgstr "批准的系统用户"
|
msgstr "批准的系统用户 (显示名称)"
|
||||||
|
|
||||||
#: tickets/serializers/ticket/meta/ticket_type/apply_application.py:89
|
#: tickets/serializers/ticket/meta/ticket_type/apply_application.py:90
|
||||||
|
#: tickets/serializers/ticket/meta/ticket_type/apply_asset.py:94
|
||||||
|
msgid "Permission named `{}` already exists"
|
||||||
|
msgstr "授权名称 `{}` 已存在"
|
||||||
|
|
||||||
|
#: tickets/serializers/ticket/meta/ticket_type/apply_application.py:107
|
||||||
msgid "No `Application` are found under Organization `{}`"
|
msgid "No `Application` are found under Organization `{}`"
|
||||||
msgstr "在组织 `{}` 下没有发现 `应用`"
|
msgstr "在组织 `{}` 下没有发现 `应用`"
|
||||||
|
|
||||||
#: tickets/serializers/ticket/meta/ticket_type/apply_application.py:107
|
#: tickets/serializers/ticket/meta/ticket_type/apply_application.py:125
|
||||||
#: tickets/serializers/ticket/meta/ticket_type/apply_asset.py:106
|
#: tickets/serializers/ticket/meta/ticket_type/apply_asset.py:124
|
||||||
msgid "No `SystemUser` are found under Organization `{}`"
|
msgid "No `SystemUser` are found under Organization `{}`"
|
||||||
msgstr "在组织 `{}` 下没有发现 `系统用户`"
|
msgstr "在组织 `{}` 下没有发现 `系统用户`"
|
||||||
|
|
||||||
#: tickets/serializers/ticket/meta/ticket_type/apply_asset.py:18
|
#: tickets/serializers/ticket/meta/ticket_type/apply_asset.py:20
|
||||||
msgid "IP group"
|
msgid "IP group"
|
||||||
msgstr "IP组"
|
msgstr "IP组"
|
||||||
|
|
||||||
#: tickets/serializers/ticket/meta/ticket_type/apply_asset.py:22
|
#: tickets/serializers/ticket/meta/ticket_type/apply_asset.py:24
|
||||||
msgid "Hostname group"
|
msgid "Hostname group"
|
||||||
msgstr "主机名组"
|
msgstr "主机名组"
|
||||||
|
|
||||||
#: tickets/serializers/ticket/meta/ticket_type/apply_asset.py:34
|
#: tickets/serializers/ticket/meta/ticket_type/apply_asset.py:36
|
||||||
#: tickets/serializers/ticket/meta/ticket_type/apply_asset.py:52
|
#: tickets/serializers/ticket/meta/ticket_type/apply_asset.py:57
|
||||||
#: tickets/serializers/ticket/meta/ticket_type/apply_asset.py:61
|
#: tickets/serializers/ticket/meta/ticket_type/apply_asset.py:66
|
||||||
#: tickets/serializers/ticket/meta/ticket_type/apply_asset.py:69
|
#: tickets/serializers/ticket/meta/ticket_type/apply_asset.py:74
|
||||||
msgid "Approve assets display"
|
msgid "Approve assets display"
|
||||||
msgstr "批准的资产"
|
msgstr "批准的资产 (显示名称)"
|
||||||
|
|
||||||
#: tickets/serializers/ticket/meta/ticket_type/apply_asset.py:48
|
#: tickets/serializers/ticket/meta/ticket_type/apply_asset.py:53
|
||||||
msgid "Approve assets"
|
msgid "Approve assets"
|
||||||
msgstr "批准的资产"
|
msgstr "批准的资产"
|
||||||
|
|
||||||
#: tickets/serializers/ticket/meta/ticket_type/apply_asset.py:90
|
#: tickets/serializers/ticket/meta/ticket_type/apply_asset.py:108
|
||||||
msgid "No `Asset` are found under Organization `{}`"
|
msgid "No `Asset` are found under Organization `{}`"
|
||||||
msgstr "在组织 `{}` 下没有发现 `资产`"
|
msgstr "在组织 `{}` 下没有发现 `资产`"
|
||||||
|
|
||||||
|
#: tickets/serializers/ticket/meta/ticket_type/common.py:11
|
||||||
|
msgid "Created by ticket ({}-{})"
|
||||||
|
msgstr "通过工单创建 ({}-{})"
|
||||||
|
|
||||||
#: tickets/serializers/ticket/meta/ticket_type/login_confirm.py:20
|
#: tickets/serializers/ticket/meta/ticket_type/login_confirm.py:20
|
||||||
msgid "Login datetime"
|
msgid "Login datetime"
|
||||||
msgstr "登录日期"
|
msgstr "登录日期"
|
||||||
|
|
||||||
#: tickets/serializers/ticket/ticket.py:92
|
#: tickets/serializers/ticket/ticket.py:21
|
||||||
|
msgid "Action display"
|
||||||
|
msgstr "动作 (显示名称)"
|
||||||
|
|
||||||
|
#: tickets/serializers/ticket/ticket.py:24
|
||||||
|
msgid "Status display"
|
||||||
|
msgstr "状态(显示名称)"
|
||||||
|
|
||||||
|
#: tickets/serializers/ticket/ticket.py:99
|
||||||
msgid ""
|
msgid ""
|
||||||
"The `type` in the submission data (`{}`) is different from the type in the "
|
"The `type` in the submission data (`{}`) is different from the type in the "
|
||||||
"request url (`{}`)"
|
"request url (`{}`)"
|
||||||
msgstr "提交数据中的类型 (`{}`) 与请求URL地址中的类型 (`{}`) 不一致"
|
msgstr "提交数据中的类型 (`{}`) 与请求URL地址中的类型 (`{}`) 不一致"
|
||||||
|
|
||||||
#: tickets/serializers/ticket/ticket.py:102
|
#: tickets/serializers/ticket/ticket.py:109
|
||||||
msgid "The organization `{}` does not exist"
|
msgid "The organization `{}` does not exist"
|
||||||
msgstr "组织 `{}` 不存在"
|
msgstr "组织 `{}` 不存在"
|
||||||
|
|
||||||
#: tickets/serializers/ticket/ticket.py:113
|
#: tickets/serializers/ticket/ticket.py:120
|
||||||
msgid "None of the assignees belong to Organization `{}` admins"
|
msgid "None of the assignees belong to Organization `{}` admins"
|
||||||
msgstr "所有受理人都不属于组织 `{}` 下的管理员"
|
msgstr "所有受理人都不属于组织 `{}` 下的管理员"
|
||||||
|
|
||||||
#: tickets/utils.py:21
|
#: tickets/utils.py:36
|
||||||
msgid "New Ticket: {} ({})"
|
msgid "New Ticket - {} ({})"
|
||||||
msgstr "新建工单: {} ({})"
|
msgstr "新工单 - {} ({})"
|
||||||
|
|
||||||
#: tickets/utils.py:26
|
#: tickets/utils.py:38
|
||||||
#, python-brace-format
|
msgid "Your has a new ticket, applicant - {}"
|
||||||
msgid ""
|
msgstr "你有一个新的工单, 申请人 - {}"
|
||||||
"<div>\n"
|
|
||||||
" <p>Your has a new ticket</p>\n"
|
|
||||||
" <div>\n"
|
|
||||||
" <b>Ticket:</b> \n"
|
|
||||||
" <br/>\n"
|
|
||||||
" {body}\n"
|
|
||||||
" <br/>\n"
|
|
||||||
" <a href={ticket_detail_url}>click here to review</a> \n"
|
|
||||||
" </div>\n"
|
|
||||||
" </div>\n"
|
|
||||||
" "
|
|
||||||
msgstr ""
|
|
||||||
"<div>\n"
|
|
||||||
" <p>你有一个新工单</p>\n"
|
|
||||||
" <div>\n"
|
|
||||||
" <b>工单:</b> \n"
|
|
||||||
" <br/>\n"
|
|
||||||
" {body}\n"
|
|
||||||
" <br/>\n"
|
|
||||||
" <a href={ticket_detail_url}>点击查看</a> \n"
|
|
||||||
" </div>\n"
|
|
||||||
" </div>\n"
|
|
||||||
" "
|
|
||||||
|
|
||||||
#: tickets/utils.py:51
|
#: tickets/utils.py:40 tickets/utils.py:59
|
||||||
msgid "Ticket has processed: {} ({})"
|
msgid "click here to review"
|
||||||
msgstr "工单已处理: {} ({})"
|
msgstr "点击查看"
|
||||||
|
|
||||||
#: tickets/utils.py:53
|
#: tickets/utils.py:55
|
||||||
#, python-brace-format
|
msgid "Ticket has processed - {} ({})"
|
||||||
msgid ""
|
msgstr "工单已处理 - {} ({})"
|
||||||
"\n"
|
|
||||||
" <div>\n"
|
#: tickets/utils.py:57
|
||||||
" <p>Your ticket has been processed</p>\n"
|
msgid "Your ticket has been processed, processor - {}"
|
||||||
" <div>\n"
|
msgstr "你的工单已被处理, 处理人 - {}"
|
||||||
" <b>Ticket:</b> \n"
|
|
||||||
" <br/>\n"
|
|
||||||
" {body}\n"
|
|
||||||
" <br/>\n"
|
|
||||||
" </div>\n"
|
|
||||||
" </div>\n"
|
|
||||||
" "
|
|
||||||
msgstr ""
|
|
||||||
"\n"
|
|
||||||
" <div>\n"
|
|
||||||
" <p>你的工单已被处理</p>\n"
|
|
||||||
" <div>\n"
|
|
||||||
" <b>工单:</b> \n"
|
|
||||||
" <br/>\n"
|
|
||||||
" {body}\n"
|
|
||||||
" <br/>\n"
|
|
||||||
" </div>\n"
|
|
||||||
" </div>\n"
|
|
||||||
" "
|
|
||||||
|
|
||||||
#: users/api/user.py:199
|
#: users/api/user.py:199
|
||||||
msgid "Could not reset self otp, use profile reset instead"
|
msgid "Could not reset self otp, use profile reset instead"
|
||||||
|
@ -3263,7 +3368,7 @@ msgstr "安全令牌验证"
|
||||||
|
|
||||||
#: users/templates/users/_base_otp.html:14 users/templates/users/_user.html:13
|
#: users/templates/users/_base_otp.html:14 users/templates/users/_user.html:13
|
||||||
#: users/templates/users/user_profile_update.html:55
|
#: users/templates/users/user_profile_update.html:55
|
||||||
#: xpack/plugins/cloud/models.py:125 xpack/plugins/cloud/serializers.py:125
|
#: xpack/plugins/cloud/models.py:125 xpack/plugins/cloud/serializers.py:136
|
||||||
msgid "Account"
|
msgid "Account"
|
||||||
msgstr "账户"
|
msgstr "账户"
|
||||||
|
|
||||||
|
@ -4079,7 +4184,7 @@ msgstr "邮箱地址错误,重新输入"
|
||||||
|
|
||||||
#: users/views/profile/reset.py:51
|
#: users/views/profile/reset.py:51
|
||||||
msgid ""
|
msgid ""
|
||||||
"The user is from A, please go to the corresponding system to change the "
|
"The user is from {}, please go to the corresponding system to change the "
|
||||||
"password"
|
"password"
|
||||||
msgstr "用户来自 {} 请去相应系统修改密码"
|
msgstr "用户来自 {} 请去相应系统修改密码"
|
||||||
|
|
||||||
|
@ -4227,7 +4332,7 @@ msgstr "实例名称"
|
||||||
msgid "Instance name and Partial IP"
|
msgid "Instance name and Partial IP"
|
||||||
msgstr "实例名称和部分IP"
|
msgstr "实例名称和部分IP"
|
||||||
|
|
||||||
#: xpack/plugins/cloud/models.py:128 xpack/plugins/cloud/serializers.py:101
|
#: xpack/plugins/cloud/models.py:128 xpack/plugins/cloud/serializers.py:112
|
||||||
msgid "Regions"
|
msgid "Regions"
|
||||||
msgstr "地域"
|
msgstr "地域"
|
||||||
|
|
||||||
|
@ -4239,7 +4344,7 @@ msgstr "实例"
|
||||||
msgid "Hostname strategy"
|
msgid "Hostname strategy"
|
||||||
msgstr "主机名策略"
|
msgstr "主机名策略"
|
||||||
|
|
||||||
#: xpack/plugins/cloud/models.py:147 xpack/plugins/cloud/serializers.py:129
|
#: xpack/plugins/cloud/models.py:147 xpack/plugins/cloud/serializers.py:140
|
||||||
msgid "Always update"
|
msgid "Always update"
|
||||||
msgstr "总是更新"
|
msgstr "总是更新"
|
||||||
|
|
||||||
|
@ -4371,15 +4476,15 @@ msgstr "租户ID"
|
||||||
msgid "Subscription ID"
|
msgid "Subscription ID"
|
||||||
msgstr "订阅ID"
|
msgstr "订阅ID"
|
||||||
|
|
||||||
#: xpack/plugins/cloud/serializers.py:99
|
#: xpack/plugins/cloud/serializers.py:110
|
||||||
msgid "History count"
|
msgid "History count"
|
||||||
msgstr "执行次数"
|
msgstr "执行次数"
|
||||||
|
|
||||||
#: xpack/plugins/cloud/serializers.py:100
|
#: xpack/plugins/cloud/serializers.py:111
|
||||||
msgid "Instance count"
|
msgid "Instance count"
|
||||||
msgstr "实例个数"
|
msgstr "实例个数"
|
||||||
|
|
||||||
#: xpack/plugins/cloud/serializers.py:128
|
#: xpack/plugins/cloud/serializers.py:139
|
||||||
#: xpack/plugins/gathered_user/serializers.py:20
|
#: xpack/plugins/gathered_user/serializers.py:20
|
||||||
msgid "Periodic display"
|
msgid "Periodic display"
|
||||||
msgstr "定时执行"
|
msgstr "定时执行"
|
||||||
|
@ -4471,3 +4576,6 @@ msgstr "旗舰版"
|
||||||
#: xpack/plugins/license/models.py:77
|
#: xpack/plugins/license/models.py:77
|
||||||
msgid "Community edition"
|
msgid "Community edition"
|
||||||
msgstr "社区版"
|
msgstr "社区版"
|
||||||
|
|
||||||
|
#~ msgid "No"
|
||||||
|
#~ msgstr "无"
|
||||||
|
|
|
@ -1,11 +1,13 @@
|
||||||
# ~*~ coding: utf-8 ~*~
|
# ~*~ coding: utf-8 ~*~
|
||||||
import os
|
import os
|
||||||
|
import uuid
|
||||||
|
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
|
||||||
from common.utils import get_logger, get_object_or_none
|
from common.utils import get_logger, get_object_or_none
|
||||||
from common.tasks import send_mail_async
|
from common.tasks import send_mail_async
|
||||||
from orgs.utils import org_aware_func
|
from orgs.utils import org_aware_func
|
||||||
|
from jumpserver.const import PROJECT_DIR
|
||||||
|
|
||||||
from .models import Task, AdHoc
|
from .models import Task, AdHoc
|
||||||
|
|
||||||
|
@ -79,8 +81,12 @@ def send_server_performance_mail(path, usage, usages):
|
||||||
|
|
||||||
def get_task_log_path(base_path, task_id, level=2):
|
def get_task_log_path(base_path, task_id, level=2):
|
||||||
task_id = str(task_id)
|
task_id = str(task_id)
|
||||||
|
try:
|
||||||
|
uuid.UUID(task_id)
|
||||||
|
except:
|
||||||
|
return os.path.join(PROJECT_DIR, 'data', 'caution.txt')
|
||||||
|
|
||||||
rel_path = os.path.join(*task_id[:level], task_id + '.log')
|
rel_path = os.path.join(*task_id[:level], task_id + '.log')
|
||||||
path = os.path.join(base_path, rel_path)
|
path = os.path.join(base_path, rel_path)
|
||||||
os.makedirs(os.path.dirname(path), exist_ok=True)
|
os.makedirs(os.path.dirname(path), exist_ok=True)
|
||||||
return path
|
return path
|
||||||
|
|
||||||
|
|
|
@ -22,7 +22,7 @@ class TaskLogWebsocket(JsonWebsocketConsumer):
|
||||||
|
|
||||||
def connect(self):
|
def connect(self):
|
||||||
user = self.scope["user"]
|
user = self.scope["user"]
|
||||||
if user.is_authenticated and user.is_org_admin:
|
if user.is_authenticated:
|
||||||
self.accept()
|
self.accept()
|
||||||
else:
|
else:
|
||||||
self.close()
|
self.close()
|
||||||
|
|
|
@ -122,18 +122,18 @@ def refresh_user_amount_on_user_create_or_delete(user_id):
|
||||||
|
|
||||||
|
|
||||||
@receiver(post_save, sender=User)
|
@receiver(post_save, sender=User)
|
||||||
def on_user_create(sender, instance, created, **kwargs):
|
def on_user_create_refresh_cache(sender, instance, created, **kwargs):
|
||||||
if created:
|
if created:
|
||||||
refresh_user_amount_on_user_create_or_delete(instance.id)
|
refresh_user_amount_on_user_create_or_delete(instance.id)
|
||||||
|
|
||||||
|
|
||||||
@receiver(pre_delete, sender=User)
|
@receiver(pre_delete, sender=User)
|
||||||
def on_user_delete(sender, instance, **kwargs):
|
def on_user_delete_refresh_cache(sender, instance, **kwargs):
|
||||||
refresh_user_amount_on_user_create_or_delete(instance.id)
|
refresh_user_amount_on_user_create_or_delete(instance.id)
|
||||||
|
|
||||||
|
|
||||||
@receiver(m2m_changed, sender=OrganizationMember)
|
@receiver(m2m_changed, sender=OrganizationMember)
|
||||||
def on_org_user_changed(sender, action, instance, reverse, pk_set, **kwargs):
|
def on_org_user_changed_refresh_cache(sender, action, instance, reverse, pk_set, **kwargs):
|
||||||
if not action.startswith(POST_PREFIX):
|
if not action.startswith(POST_PREFIX):
|
||||||
return
|
return
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
#
|
#
|
||||||
from collections import OrderedDict
|
|
||||||
import logging
|
import logging
|
||||||
import uuid
|
import uuid
|
||||||
|
|
||||||
|
@ -8,7 +7,6 @@ from django.core.cache import cache
|
||||||
from django.shortcuts import get_object_or_404
|
from django.shortcuts import get_object_or_404
|
||||||
from rest_framework import viewsets
|
from rest_framework import viewsets
|
||||||
from rest_framework.views import APIView, Response
|
from rest_framework.views import APIView, Response
|
||||||
from rest_framework.permissions import AllowAny
|
|
||||||
|
|
||||||
from common.drf.api import JMSBulkModelViewSet
|
from common.drf.api import JMSBulkModelViewSet
|
||||||
from common.utils import get_object_or_none
|
from common.utils import get_object_or_none
|
||||||
|
@ -18,7 +16,7 @@ from .. import serializers
|
||||||
from .. import exceptions
|
from .. import exceptions
|
||||||
|
|
||||||
__all__ = [
|
__all__ = [
|
||||||
'TerminalViewSet', 'TerminalTokenApi', 'StatusViewSet', 'TerminalConfig',
|
'TerminalViewSet', 'StatusViewSet', 'TerminalConfig',
|
||||||
]
|
]
|
||||||
logger = logging.getLogger(__file__)
|
logger = logging.getLogger(__file__)
|
||||||
|
|
||||||
|
@ -69,41 +67,6 @@ class TerminalViewSet(JMSBulkModelViewSet):
|
||||||
queryset = queryset.filter(id__in=filtered_queryset_id)
|
queryset = queryset.filter(id__in=filtered_queryset_id)
|
||||||
return queryset
|
return queryset
|
||||||
|
|
||||||
def get_permissions(self):
|
|
||||||
if self.action == "create":
|
|
||||||
self.permission_classes = (AllowAny,)
|
|
||||||
return super().get_permissions()
|
|
||||||
|
|
||||||
|
|
||||||
class TerminalTokenApi(APIView):
|
|
||||||
permission_classes = (AllowAny,)
|
|
||||||
queryset = Terminal.objects.filter(is_deleted=False)
|
|
||||||
|
|
||||||
def get(self, request, *args, **kwargs):
|
|
||||||
try:
|
|
||||||
terminal = self.queryset.get(id=kwargs.get('terminal'))
|
|
||||||
except Terminal.DoesNotExist:
|
|
||||||
terminal = None
|
|
||||||
|
|
||||||
token = request.query_params.get("token")
|
|
||||||
|
|
||||||
if terminal is None:
|
|
||||||
return Response('May be reject by administrator', status=401)
|
|
||||||
|
|
||||||
if token is None or cache.get(token, "") != str(terminal.id):
|
|
||||||
return Response('Token is not valid', status=401)
|
|
||||||
|
|
||||||
if not terminal.is_accepted:
|
|
||||||
return Response("Terminal was not accepted yet", status=400)
|
|
||||||
|
|
||||||
if not terminal.user or not terminal.user.access_key:
|
|
||||||
return Response("No access key generate", status=401)
|
|
||||||
|
|
||||||
access_key = terminal.user.access_key()
|
|
||||||
data = OrderedDict()
|
|
||||||
data['access_key'] = {'id': access_key.id, 'secret': access_key.secret}
|
|
||||||
return Response(data, status=200)
|
|
||||||
|
|
||||||
|
|
||||||
class StatusViewSet(viewsets.ModelViewSet):
|
class StatusViewSet(viewsets.ModelViewSet):
|
||||||
queryset = Status.objects.all()
|
queryset = Status.objects.all()
|
||||||
|
|
|
@ -14,6 +14,13 @@ from .. import const
|
||||||
# --------------------------
|
# --------------------------
|
||||||
|
|
||||||
|
|
||||||
|
def replay_storage_endpoint_format_validator(endpoint):
|
||||||
|
h = urlparse(endpoint)
|
||||||
|
if h.path:
|
||||||
|
raise serializers.ValidationError(_('Endpoint invalid: remove path `{}`').format(h.path))
|
||||||
|
return endpoint
|
||||||
|
|
||||||
|
|
||||||
class ReplayStorageTypeBaseSerializer(serializers.Serializer):
|
class ReplayStorageTypeBaseSerializer(serializers.Serializer):
|
||||||
BUCKET = serializers.CharField(
|
BUCKET = serializers.CharField(
|
||||||
required=True, max_length=1024, label=_('Bucket'), allow_null=True
|
required=True, max_length=1024, label=_('Bucket'), allow_null=True
|
||||||
|
@ -27,6 +34,7 @@ class ReplayStorageTypeBaseSerializer(serializers.Serializer):
|
||||||
allow_null=True,
|
allow_null=True,
|
||||||
)
|
)
|
||||||
ENDPOINT = serializers.CharField(
|
ENDPOINT = serializers.CharField(
|
||||||
|
validators=[replay_storage_endpoint_format_validator],
|
||||||
required=True, max_length=1024, label=_('Endpoint'), allow_null=True,
|
required=True, max_length=1024, label=_('Endpoint'), allow_null=True,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -38,6 +46,7 @@ class ReplayStorageTypeS3Serializer(ReplayStorageTypeBaseSerializer):
|
||||||
Such as: http://s3.cn-north-1.amazonaws.com.cn
|
Such as: http://s3.cn-north-1.amazonaws.com.cn
|
||||||
'''
|
'''
|
||||||
ENDPOINT = serializers.CharField(
|
ENDPOINT = serializers.CharField(
|
||||||
|
validators=[replay_storage_endpoint_format_validator],
|
||||||
required=True, max_length=1024, label=_('Endpoint'), help_text=_(endpoint_help_text),
|
required=True, max_length=1024, label=_('Endpoint'), help_text=_(endpoint_help_text),
|
||||||
allow_null=True,
|
allow_null=True,
|
||||||
)
|
)
|
||||||
|
@ -67,6 +76,7 @@ class ReplayStorageTypeOSSSerializer(ReplayStorageTypeBaseSerializer):
|
||||||
Such as: http://oss-cn-hangzhou.aliyuncs.com
|
Such as: http://oss-cn-hangzhou.aliyuncs.com
|
||||||
'''
|
'''
|
||||||
ENDPOINT = serializers.CharField(
|
ENDPOINT = serializers.CharField(
|
||||||
|
validators=[replay_storage_endpoint_format_validator],
|
||||||
max_length=1024, label=_('Endpoint'), help_text=_(endpoint_help_text), allow_null=True,
|
max_length=1024, label=_('Endpoint'), help_text=_(endpoint_help_text), allow_null=True,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -113,16 +123,25 @@ class ReplayStorageSerializer(serializers.ModelSerializer):
|
||||||
return _meta
|
return _meta
|
||||||
|
|
||||||
def get_meta_serializer(self):
|
def get_meta_serializer(self):
|
||||||
serializer_class = None
|
default_serializer = serializers.Serializer(read_only=True)
|
||||||
query_type = self.context['request'].query_params.get('type')
|
|
||||||
if query_type:
|
|
||||||
serializer_class = replay_storage_type_serializer_classes_mapping.get(query_type)
|
|
||||||
if isinstance(self.instance, ReplayStorage):
|
if isinstance(self.instance, ReplayStorage):
|
||||||
instance_type = self.instance.type
|
_type = self.instance.type
|
||||||
serializer_class = replay_storage_type_serializer_classes_mapping.get(instance_type)
|
else:
|
||||||
if serializer_class is None:
|
_type = self.context['request'].query_params.get('type')
|
||||||
serializer_class = serializers.Serializer
|
|
||||||
|
if _type:
|
||||||
|
serializer_class = replay_storage_type_serializer_classes_mapping.get(_type)
|
||||||
|
else:
|
||||||
|
serializer_class = default_serializer
|
||||||
|
|
||||||
|
if not serializer_class:
|
||||||
|
serializer_class = default_serializer
|
||||||
|
|
||||||
|
if isinstance(serializer_class, type):
|
||||||
serializer = serializer_class()
|
serializer = serializer_class()
|
||||||
|
else:
|
||||||
|
serializer = serializer_class
|
||||||
return serializer
|
return serializer
|
||||||
|
|
||||||
|
|
||||||
|
@ -130,7 +149,7 @@ class ReplayStorageSerializer(serializers.ModelSerializer):
|
||||||
# ---------------------------
|
# ---------------------------
|
||||||
|
|
||||||
|
|
||||||
def es_host_format_validator(host):
|
def command_storage_es_host_format_validator(host):
|
||||||
h = urlparse(host)
|
h = urlparse(host)
|
||||||
default_error_msg = _('The address format is incorrect')
|
default_error_msg = _('The address format is incorrect')
|
||||||
if h.scheme not in ['http', 'https']:
|
if h.scheme not in ['http', 'https']:
|
||||||
|
@ -154,8 +173,8 @@ class CommandStorageTypeESSerializer(serializers.Serializer):
|
||||||
(eg: http://www.jumpserver.a.com, http://www.jumpserver.b.com)
|
(eg: http://www.jumpserver.a.com, http://www.jumpserver.b.com)
|
||||||
'''
|
'''
|
||||||
HOSTS = serializers.ListField(
|
HOSTS = serializers.ListField(
|
||||||
child=serializers.CharField(validators=[es_host_format_validator]), label=_('Hosts'),
|
child=serializers.CharField(validators=[command_storage_es_host_format_validator]),
|
||||||
help_text=_(hosts_help_text), allow_null=True
|
label=_('Hosts'), help_text=_(hosts_help_text), allow_null=True
|
||||||
)
|
)
|
||||||
INDEX = serializers.CharField(
|
INDEX = serializers.CharField(
|
||||||
max_length=1024, default='jumpserver', label=_('Index'), allow_null=True
|
max_length=1024, default='jumpserver', label=_('Index'), allow_null=True
|
||||||
|
@ -187,14 +206,23 @@ class CommandStorageSerializer(serializers.ModelSerializer):
|
||||||
return _meta
|
return _meta
|
||||||
|
|
||||||
def get_meta_serializer(self):
|
def get_meta_serializer(self):
|
||||||
serializer_class = None
|
default_serializer = serializers.Serializer(read_only=True)
|
||||||
query_type = self.context['request'].query_params.get('type')
|
|
||||||
if query_type:
|
|
||||||
serializer_class = command_storage_type_serializer_classes_mapping.get(query_type)
|
|
||||||
if isinstance(self.instance, CommandStorage):
|
if isinstance(self.instance, CommandStorage):
|
||||||
instance_type = self.instance.type
|
_type = self.instance.type
|
||||||
serializer_class = command_storage_type_serializer_classes_mapping.get(instance_type)
|
else:
|
||||||
if serializer_class is None:
|
_type = self.context['request'].query_params.get('type')
|
||||||
serializer_class = serializers.Serializer
|
|
||||||
|
if _type:
|
||||||
|
serializer_class = command_storage_type_serializer_classes_mapping.get(_type)
|
||||||
|
else:
|
||||||
|
serializer_class = default_serializer
|
||||||
|
|
||||||
|
if not serializer_class:
|
||||||
|
serializer_class = default_serializer
|
||||||
|
|
||||||
|
if isinstance(serializer_class, type):
|
||||||
serializer = serializer_class()
|
serializer = serializer_class()
|
||||||
|
else:
|
||||||
|
serializer = serializer_class
|
||||||
return serializer
|
return serializer
|
||||||
|
|
|
@ -27,8 +27,6 @@ urlpatterns = [
|
||||||
api.SessionReplayViewSet.as_view({'get': 'retrieve', 'post': 'create'}),
|
api.SessionReplayViewSet.as_view({'get': 'retrieve', 'post': 'create'}),
|
||||||
name='session-replay'),
|
name='session-replay'),
|
||||||
path('tasks/kill-session/', api.KillSessionAPI.as_view(), name='kill-session'),
|
path('tasks/kill-session/', api.KillSessionAPI.as_view(), name='kill-session'),
|
||||||
path('terminals/<uuid:terminal>/access-key/', api.TerminalTokenApi.as_view(),
|
|
||||||
name='terminal-access-key'),
|
|
||||||
path('terminals/config/', api.TerminalConfig.as_view(), name='terminal-config'),
|
path('terminals/config/', api.TerminalConfig.as_view(), name='terminal-config'),
|
||||||
path('commands/export/', api.CommandExportApi.as_view(), name="command-export"),
|
path('commands/export/', api.CommandExportApi.as_view(), name="command-export"),
|
||||||
path('commands/insecure-command/', api.InsecureCommandAlertAPI.as_view(), name="command-alert"),
|
path('commands/insecure-command/', api.InsecureCommandAlertAPI.as_view(), name="command-alert"),
|
||||||
|
|
|
@ -71,12 +71,18 @@ def get_session_replay_url(session):
|
||||||
|
|
||||||
def send_command_alert_mail(command):
|
def send_command_alert_mail(command):
|
||||||
session_obj = Session.objects.get(id=command['session'])
|
session_obj = Session.objects.get(id=command['session'])
|
||||||
|
|
||||||
|
input = command['input']
|
||||||
|
if isinstance(input, str):
|
||||||
|
input = input.replace('\r\n', ' ').replace('\r', ' ').replace('\n', ' ')
|
||||||
|
|
||||||
subject = _("Insecure Command Alert: [%(name)s->%(login_from)s@%(remote_addr)s] $%(command)s") % {
|
subject = _("Insecure Command Alert: [%(name)s->%(login_from)s@%(remote_addr)s] $%(command)s") % {
|
||||||
'name': command['user'],
|
'name': command['user'],
|
||||||
'login_from': session_obj.get_login_from_display(),
|
'login_from': session_obj.get_login_from_display(),
|
||||||
'remote_addr': session_obj.remote_addr,
|
'remote_addr': session_obj.remote_addr,
|
||||||
'command': command['input']
|
'command': input
|
||||||
}
|
}
|
||||||
|
|
||||||
recipient_list = settings.SECURITY_INSECURE_COMMAND_EMAIL_RECEIVER.split(',')
|
recipient_list = settings.SECURITY_INSECURE_COMMAND_EMAIL_RECEIVER.split(',')
|
||||||
message = _("""
|
message = _("""
|
||||||
Command: %(command)s
|
Command: %(command)s
|
||||||
|
|
|
@ -12,7 +12,7 @@ from common.permissions import IsValidUser, IsOrgAdmin
|
||||||
|
|
||||||
from tickets import serializers
|
from tickets import serializers
|
||||||
from tickets.models import Ticket
|
from tickets.models import Ticket
|
||||||
from tickets.permissions.ticket import IsAssignee, NotClosed
|
from tickets.permissions.ticket import IsAssignee, IsAssigneeOrApplicant, NotClosed
|
||||||
|
|
||||||
|
|
||||||
__all__ = ['TicketViewSet']
|
__all__ = ['TicketViewSet']
|
||||||
|
@ -50,7 +50,7 @@ class TicketViewSet(CommonApiMixin, viewsets.ModelViewSet):
|
||||||
instance = serializer.save()
|
instance = serializer.save()
|
||||||
instance.open(applicant=self.request.user)
|
instance.open(applicant=self.request.user)
|
||||||
|
|
||||||
@action(detail=False, methods=[POST])
|
@action(detail=False, methods=[POST], permission_classes=[IsValidUser, ])
|
||||||
def open(self, request, *args, **kwargs):
|
def open(self, request, *args, **kwargs):
|
||||||
return super().create(request, *args, **kwargs)
|
return super().create(request, *args, **kwargs)
|
||||||
|
|
||||||
|
@ -68,7 +68,7 @@ class TicketViewSet(CommonApiMixin, viewsets.ModelViewSet):
|
||||||
instance.reject(processor=request.user)
|
instance.reject(processor=request.user)
|
||||||
return Response(serializer.data)
|
return Response(serializer.data)
|
||||||
|
|
||||||
@action(detail=True, methods=[PUT], permission_classes=[IsOrgAdmin, IsAssignee, NotClosed])
|
@action(detail=True, methods=[PUT], permission_classes=[IsAssigneeOrApplicant, NotClosed])
|
||||||
def close(self, request, *args, **kwargs):
|
def close(self, request, *args, **kwargs):
|
||||||
instance = self.get_object()
|
instance = self.get_object()
|
||||||
serializer = self.get_serializer(instance)
|
serializer = self.get_serializer(instance)
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
from django.utils.translation import ugettext as __
|
from django.utils.translation import ugettext as _
|
||||||
from orgs.utils import tmp_to_org, tmp_to_root_org
|
from orgs.utils import tmp_to_org, tmp_to_root_org
|
||||||
from applications.models import Application
|
from applications.models import Application
|
||||||
from applications.const import ApplicationCategoryChoices, ApplicationTypeChoices
|
from applications.const import ApplicationCategoryChoices, ApplicationTypeChoices
|
||||||
|
@ -52,12 +52,12 @@ class Handler(BaseHandler):
|
||||||
{}: {},
|
{}: {},
|
||||||
{}: {},
|
{}: {},
|
||||||
'''.format(
|
'''.format(
|
||||||
__('Applied category'), apply_category_display,
|
_('Applied category'), apply_category_display,
|
||||||
__('Applied type'), apply_type_display,
|
_('Applied type'), apply_type_display,
|
||||||
__('Applied application group'), apply_application_group,
|
_('Applied application group'), apply_application_group,
|
||||||
__('Applied system user group'), apply_system_user_group,
|
_('Applied system user group'), apply_system_user_group,
|
||||||
__('Applied date start'), apply_date_start,
|
_('Applied date start'), apply_date_start,
|
||||||
__('Applied date expired'), apply_date_expired,
|
_('Applied date expired'), apply_date_expired,
|
||||||
)
|
)
|
||||||
return applied_body
|
return applied_body
|
||||||
|
|
||||||
|
@ -72,10 +72,10 @@ class Handler(BaseHandler):
|
||||||
{}: {},
|
{}: {},
|
||||||
{}: {},
|
{}: {},
|
||||||
'''.format(
|
'''.format(
|
||||||
__('Approved applications'), approve_applications_display,
|
_('Approved applications'), approve_applications_display,
|
||||||
__('Approved system users'), approve_system_users_display,
|
_('Approved system users'), approve_system_users_display,
|
||||||
__('Approved date start'), approve_date_start,
|
_('Approved date start'), approve_date_start,
|
||||||
__('Approved date expired'), approve_date_expired
|
_('Approved date expired'), approve_date_expired
|
||||||
)
|
)
|
||||||
return approved_body
|
return approved_body
|
||||||
|
|
||||||
|
@ -88,31 +88,33 @@ class Handler(BaseHandler):
|
||||||
|
|
||||||
apply_category = self.ticket.meta.get('apply_category')
|
apply_category = self.ticket.meta.get('apply_category')
|
||||||
apply_type = self.ticket.meta.get('apply_type')
|
apply_type = self.ticket.meta.get('apply_type')
|
||||||
|
approve_permission_name = self.ticket.meta.get('approve_permission_name', '')
|
||||||
approved_applications_id = self.ticket.meta.get('approve_applications', [])
|
approved_applications_id = self.ticket.meta.get('approve_applications', [])
|
||||||
approve_system_users_id = self.ticket.meta.get('approve_system_users', [])
|
approve_system_users_id = self.ticket.meta.get('approve_system_users', [])
|
||||||
approve_date_start = self.ticket.meta.get('approve_date_start')
|
approve_date_start = self.ticket.meta.get('approve_date_start')
|
||||||
approve_date_expired = self.ticket.meta.get('approve_date_expired')
|
approve_date_expired = self.ticket.meta.get('approve_date_expired')
|
||||||
permission_name = '{}({})'.format(
|
permission_created_by = '{}:{}'.format(
|
||||||
__('Created by ticket ({})'.format(self.ticket.title)), str(self.ticket.id)[:4]
|
str(self.ticket.__class__.__name__), str(self.ticket.id)
|
||||||
)
|
)
|
||||||
permission_comment = __(
|
permission_comment = _(
|
||||||
'Created by the ticket, '
|
'Created by the ticket, '
|
||||||
'ticket title: {}, '
|
'ticket title: {}, '
|
||||||
'ticket applicant: {}, '
|
'ticket applicant: {}, '
|
||||||
'ticket processor: {}, '
|
'ticket processor: {}, '
|
||||||
'ticket ID: {}'
|
'ticket ID: {}'
|
||||||
''.format(
|
).format(
|
||||||
self.ticket.title, self.ticket.applicant_display,
|
self.ticket.title,
|
||||||
self.ticket.processor_display, str(self.ticket.id)
|
self.ticket.applicant_display,
|
||||||
)
|
self.ticket.processor_display,
|
||||||
|
str(self.ticket.id)
|
||||||
)
|
)
|
||||||
permissions_data = {
|
permissions_data = {
|
||||||
'id': self.ticket.id,
|
'id': self.ticket.id,
|
||||||
'name': permission_name,
|
'name': approve_permission_name,
|
||||||
'category': apply_category,
|
'category': apply_category,
|
||||||
'type': apply_type,
|
'type': apply_type,
|
||||||
'comment': permission_comment,
|
'comment': str(permission_comment),
|
||||||
'created_by': '{}:{}'.format(str(self.__class__.__name__), str(self.ticket.id)),
|
'created_by': permission_created_by,
|
||||||
'date_start': approve_date_start,
|
'date_start': approve_date_start,
|
||||||
'date_expired': approve_date_expired,
|
'date_expired': approve_date_expired,
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
from .base import BaseHandler
|
from .base import BaseHandler
|
||||||
from django.utils.translation import ugettext as __
|
from django.utils.translation import ugettext as _
|
||||||
|
|
||||||
from perms.models import AssetPermission, Action
|
from perms.models import AssetPermission, Action
|
||||||
from assets.models import Asset, SystemUser
|
from assets.models import Asset, SystemUser
|
||||||
|
@ -54,12 +54,12 @@ class Handler(BaseHandler):
|
||||||
{}: {},
|
{}: {},
|
||||||
{}: {}
|
{}: {}
|
||||||
'''.format(
|
'''.format(
|
||||||
__('Applied IP group'), apply_ip_group,
|
_('Applied IP group'), apply_ip_group,
|
||||||
__("Applied hostname group"), apply_hostname_group,
|
_("Applied hostname group"), apply_hostname_group,
|
||||||
__("Applied system user group"), apply_system_user_group,
|
_("Applied system user group"), apply_system_user_group,
|
||||||
__("Applied actions"), apply_actions_display,
|
_("Applied actions"), apply_actions_display,
|
||||||
__('Applied date start'), apply_date_start,
|
_('Applied date start'), apply_date_start,
|
||||||
__('Applied date expired'), apply_date_expired,
|
_('Applied date expired'), apply_date_expired,
|
||||||
)
|
)
|
||||||
return applied_body
|
return applied_body
|
||||||
|
|
||||||
|
@ -75,11 +75,11 @@ class Handler(BaseHandler):
|
||||||
{}: {},
|
{}: {},
|
||||||
{}: {}
|
{}: {}
|
||||||
'''.format(
|
'''.format(
|
||||||
__('Approved assets'), approve_assets_display,
|
_('Approved assets'), approve_assets_display,
|
||||||
__('Approved system users'), approve_system_users_display,
|
_('Approved system users'), approve_system_users_display,
|
||||||
__('Approved actions'), ', '.join(approve_actions_display),
|
_('Approved actions'), ', '.join(approve_actions_display),
|
||||||
__('Approved date start'), approve_date_start,
|
_('Approved date start'), approve_date_start,
|
||||||
__('Approved date expired'), approve_date_expired,
|
_('Approved date expired'), approve_date_expired,
|
||||||
)
|
)
|
||||||
return approved_body
|
return approved_body
|
||||||
|
|
||||||
|
@ -90,30 +90,33 @@ class Handler(BaseHandler):
|
||||||
if asset_permission:
|
if asset_permission:
|
||||||
return asset_permission
|
return asset_permission
|
||||||
|
|
||||||
|
approve_permission_name = self.ticket.meta.get('approve_permission_name', )
|
||||||
approve_assets_id = self.ticket.meta.get('approve_assets', [])
|
approve_assets_id = self.ticket.meta.get('approve_assets', [])
|
||||||
approve_system_users_id = self.ticket.meta.get('approve_system_users', [])
|
approve_system_users_id = self.ticket.meta.get('approve_system_users', [])
|
||||||
approve_actions = self.ticket.meta.get('approve_actions', Action.NONE)
|
approve_actions = self.ticket.meta.get('approve_actions', Action.NONE)
|
||||||
approve_date_start = self.ticket.meta.get('approve_date_start')
|
approve_date_start = self.ticket.meta.get('approve_date_start')
|
||||||
approve_date_expired = self.ticket.meta.get('approve_date_expired')
|
approve_date_expired = self.ticket.meta.get('approve_date_expired')
|
||||||
permission_name = '{}({})'.format(
|
permission_created_by = '{}:{}'.format(
|
||||||
__('Created by ticket ({})'.format(self.ticket.title)), str(self.ticket.id)[:4]
|
str(self.ticket.__class__.__name__), str(self.ticket.id)
|
||||||
)
|
)
|
||||||
permission_comment = __(
|
permission_comment = _(
|
||||||
'Created by the ticket, '
|
'Created by the ticket, '
|
||||||
'ticket title: {}, '
|
'ticket title: {}, '
|
||||||
'ticket applicant: {}, '
|
'ticket applicant: {}, '
|
||||||
'ticket processor: {}, '
|
'ticket processor: {}, '
|
||||||
'ticket ID: {}'
|
'ticket ID: {}'
|
||||||
''.format(
|
).format(
|
||||||
self.ticket.title, self.ticket.applicant_display, self.ticket.processor_display,
|
self.ticket.title,
|
||||||
|
self.ticket.applicant_display,
|
||||||
|
self.ticket.processor_display,
|
||||||
str(self.ticket.id)
|
str(self.ticket.id)
|
||||||
)
|
)
|
||||||
)
|
|
||||||
permission_data = {
|
permission_data = {
|
||||||
'id': self.ticket.id,
|
'id': self.ticket.id,
|
||||||
'name': permission_name,
|
'name': approve_permission_name,
|
||||||
'comment': permission_comment,
|
'comment': str(permission_comment),
|
||||||
'created_by': '{}:{}'.format(str(self.__class__.__name__), str(self.ticket.id)),
|
'created_by': permission_created_by,
|
||||||
'actions': approve_actions,
|
'actions': approve_actions,
|
||||||
'date_start': approve_date_start,
|
'date_start': approve_date_start,
|
||||||
'date_expired': approve_date_expired,
|
'date_expired': approve_date_expired,
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
from django.utils.translation import ugettext as __
|
from django.utils.translation import ugettext as _
|
||||||
from common.utils import get_logger
|
from common.utils import get_logger
|
||||||
from tickets.utils import send_ticket_processed_mail_to_applicant
|
from tickets.utils import (
|
||||||
|
send_ticket_processed_mail_to_applicant, send_ticket_applied_mail_to_assignees
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
logger = get_logger(__name__)
|
logger = get_logger(__name__)
|
||||||
|
@ -14,9 +16,11 @@ class BaseHandler(object):
|
||||||
# on action
|
# on action
|
||||||
def _on_open(self):
|
def _on_open(self):
|
||||||
self.ticket.applicant_display = str(self.ticket.applicant)
|
self.ticket.applicant_display = str(self.ticket.applicant)
|
||||||
|
self.ticket.assignees_display = [str(assignee) for assignee in self.ticket.assignees.all()]
|
||||||
meta_display = getattr(self, '_construct_meta_display_of_open', lambda: {})()
|
meta_display = getattr(self, '_construct_meta_display_of_open', lambda: {})()
|
||||||
self.ticket.meta.update(meta_display)
|
self.ticket.meta.update(meta_display)
|
||||||
self.ticket.save()
|
self.ticket.save()
|
||||||
|
self._send_applied_mail_to_assignees()
|
||||||
|
|
||||||
def _on_approve(self):
|
def _on_approve(self):
|
||||||
meta_display = getattr(self, '_construct_meta_display_of_approve', lambda: {})()
|
meta_display = getattr(self, '_construct_meta_display_of_approve', lambda: {})()
|
||||||
|
@ -41,11 +45,12 @@ class BaseHandler(object):
|
||||||
return method()
|
return method()
|
||||||
|
|
||||||
# email
|
# email
|
||||||
|
def _send_applied_mail_to_assignees(self):
|
||||||
|
logger.debug('Send applied email to assignees: {}'.format(self.ticket.assignees_display))
|
||||||
|
send_ticket_applied_mail_to_assignees(self.ticket)
|
||||||
|
|
||||||
def _send_processed_mail_to_applicant(self):
|
def _send_processed_mail_to_applicant(self):
|
||||||
msg = 'Ticket ({}) has processed, send mail to applicant ({})'.format(
|
logger.debug('Send processed mail to applicant: {}'.format(self.ticket.applicant_display))
|
||||||
self.ticket.title, self.ticket.applicant_display
|
|
||||||
)
|
|
||||||
logger.debug(msg)
|
|
||||||
send_ticket_processed_mail_to_applicant(self.ticket)
|
send_ticket_processed_mail_to_applicant(self.ticket)
|
||||||
|
|
||||||
# comments
|
# comments
|
||||||
|
@ -54,13 +59,18 @@ class BaseHandler(object):
|
||||||
user_display = str(user)
|
user_display = str(user)
|
||||||
action_display = self.ticket.get_action_display()
|
action_display = self.ticket.get_action_display()
|
||||||
data = {
|
data = {
|
||||||
'body': __('User {} {} the ticket'.format(user_display, action_display)),
|
'body': _('User {} {} the ticket'.format(user_display, action_display)),
|
||||||
'user': user,
|
'user': user,
|
||||||
'user_display': user_display
|
'user_display': user_display
|
||||||
}
|
}
|
||||||
return self.ticket.comments.create(**data)
|
return self.ticket.comments.create(**data)
|
||||||
|
|
||||||
# body
|
# body
|
||||||
|
body_html_format = '''
|
||||||
|
{}:
|
||||||
|
<div style="margin-left: 20px;">{}</div>
|
||||||
|
'''
|
||||||
|
|
||||||
def get_body(self):
|
def get_body(self):
|
||||||
old_body = self.ticket.meta.get('body')
|
old_body = self.ticket.meta.get('body')
|
||||||
if old_body:
|
if old_body:
|
||||||
|
@ -71,25 +81,23 @@ class BaseHandler(object):
|
||||||
return basic_body + meta_body
|
return basic_body + meta_body
|
||||||
|
|
||||||
def _construct_basic_body(self):
|
def _construct_basic_body(self):
|
||||||
body = '''
|
basic_body = '''{}: {},
|
||||||
{}:
|
|
||||||
{}: {},
|
|
||||||
{}: {},
|
|
||||||
{}: {},
|
{}: {},
|
||||||
{}: {},
|
{}: {},
|
||||||
{}: {},
|
{}: {},
|
||||||
{}: {},
|
{}: {},
|
||||||
{}: {}
|
{}: {}
|
||||||
'''.format(
|
'''.format(
|
||||||
__("Ticket basic info"),
|
_('Ticket title'), self.ticket.title,
|
||||||
__('Ticket title'), self.ticket.title,
|
_('Ticket type'), self.ticket.get_type_display(),
|
||||||
__('Ticket type'), self.ticket.get_type_display(),
|
_('Ticket status'), self.ticket.get_status_display(),
|
||||||
__('Ticket applicant'), self.ticket.applicant_display,
|
_('Ticket action'), self.ticket.get_action_display(),
|
||||||
__('Ticket assignees'), self.ticket.assignees_display,
|
_('Ticket applicant'), self.ticket.applicant_display,
|
||||||
__('Ticket processor'), self.ticket.processor_display,
|
_('Ticket assignees'), self.ticket.assignees_display,
|
||||||
__('Ticket action'), self.ticket.get_action_display(),
|
|
||||||
__('Ticket status'), self.ticket.get_status_display()
|
|
||||||
)
|
)
|
||||||
|
if self.ticket.status_closed:
|
||||||
|
basic_body += '''{}: {}'''.format(_('Ticket processor'), self.ticket.processor_display)
|
||||||
|
body = self.body_html_format.format(_("Ticket basic info"), basic_body)
|
||||||
return body
|
return body
|
||||||
|
|
||||||
def _construct_meta_body(self):
|
def _construct_meta_body(self):
|
||||||
|
@ -102,21 +110,11 @@ class BaseHandler(object):
|
||||||
return body
|
return body
|
||||||
|
|
||||||
def _base_construct_meta_body_of_open(self):
|
def _base_construct_meta_body_of_open(self):
|
||||||
open_body = '''
|
meta_body_of_open = getattr(self, '_construct_meta_body_of_open', lambda: 'No')()
|
||||||
{}:
|
body = self.body_html_format.format(_('Ticket applied info'), meta_body_of_open)
|
||||||
{}
|
return body
|
||||||
'''.format(
|
|
||||||
__('Ticket applied info'),
|
|
||||||
getattr(self, '_construct_meta_body_of_open', lambda: 'No')()
|
|
||||||
)
|
|
||||||
return open_body
|
|
||||||
|
|
||||||
def _base_construct_meta_body_of_approve(self):
|
def _base_construct_meta_body_of_approve(self):
|
||||||
approve_body = '''
|
meta_body_of_approve = getattr(self, '_construct_meta_body_of_approve', lambda: 'No')()
|
||||||
{}:
|
body = self.body_html_format.format(_('Ticket approved info'), meta_body_of_approve)
|
||||||
{}
|
return body
|
||||||
'''.format(
|
|
||||||
__('Ticket approved info'),
|
|
||||||
getattr(self, '_construct_meta_body_of_approve', lambda: 'No')()
|
|
||||||
)
|
|
||||||
return approve_body
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
from django.utils.translation import ugettext as __
|
from django.utils.translation import ugettext as _
|
||||||
from .base import BaseHandler
|
from .base import BaseHandler
|
||||||
|
|
||||||
|
|
||||||
|
@ -13,8 +13,8 @@ class Handler(BaseHandler):
|
||||||
{}: {},
|
{}: {},
|
||||||
{}: {}
|
{}: {}
|
||||||
'''.format(
|
'''.format(
|
||||||
__("Applied login IP"), apply_login_ip,
|
_("Applied login IP"), apply_login_ip,
|
||||||
__("Applied login city"), apply_login_city,
|
_("Applied login city"), apply_login_city,
|
||||||
__("Applied login datetime"), apply_login_datetime,
|
_("Applied login datetime"), apply_login_datetime,
|
||||||
)
|
)
|
||||||
return applied_body
|
return applied_body
|
||||||
|
|
|
@ -148,35 +148,9 @@ class Ticket(CommonModelMixin, OrgModelMixin):
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def get_user_related_tickets(cls, user):
|
def get_user_related_tickets(cls, user):
|
||||||
queries = None
|
queries = Q(applicant=user) | Q(assignees=user)
|
||||||
tickets = cls.all()
|
tickets = cls.all().filter(queries).distinct()
|
||||||
if user.is_superuser:
|
return tickets
|
||||||
pass
|
|
||||||
elif user.is_super_auditor:
|
|
||||||
pass
|
|
||||||
elif user.is_org_admin:
|
|
||||||
admin_orgs_id = [
|
|
||||||
str(org_id) for org_id in user.admin_orgs.values_list('id', flat=True)
|
|
||||||
]
|
|
||||||
assigned_tickets_id = [
|
|
||||||
str(ticket_id) for ticket_id in user.assigned_tickets.values_list('id', flat=True)
|
|
||||||
]
|
|
||||||
queries = Q(applicant=user)
|
|
||||||
queries |= Q(processor=user)
|
|
||||||
queries |= Q(org_id__in=admin_orgs_id)
|
|
||||||
queries |= Q(id__in=assigned_tickets_id)
|
|
||||||
elif user.is_org_auditor:
|
|
||||||
audit_orgs_id = [
|
|
||||||
str(org_id) for org_id in user.audit_orgs.values_list('id', flat=True)
|
|
||||||
]
|
|
||||||
queries = Q(org_id__in=audit_orgs_id)
|
|
||||||
elif user.is_common_user:
|
|
||||||
queries = Q(applicant=user)
|
|
||||||
else:
|
|
||||||
tickets = cls.objects.none()
|
|
||||||
if queries:
|
|
||||||
tickets = tickets.filter(queries)
|
|
||||||
return tickets.distinct()
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def all(cls):
|
def all(cls):
|
||||||
|
|
|
@ -7,6 +7,12 @@ class IsAssignee(permissions.BasePermission):
|
||||||
return obj.has_assignee(request.user)
|
return obj.has_assignee(request.user)
|
||||||
|
|
||||||
|
|
||||||
|
class IsAssigneeOrApplicant(IsAssignee):
|
||||||
|
|
||||||
|
def has_object_permission(self, request, view, obj):
|
||||||
|
return super().has_object_permission(request, view, obj) or obj.applicant == request.user
|
||||||
|
|
||||||
|
|
||||||
class NotClosed(permissions.BasePermission):
|
class NotClosed(permissions.BasePermission):
|
||||||
def has_object_permission(self, request, view, obj):
|
def has_object_permission(self, request, view, obj):
|
||||||
return not obj.status_closed
|
return not obj.status_closed
|
||||||
|
|
|
@ -29,5 +29,6 @@ type_serializer_classes_mapping = {
|
||||||
const.TicketTypeChoices.login_confirm.value: {
|
const.TicketTypeChoices.login_confirm.value: {
|
||||||
'default': login_confirm.LoginConfirmSerializer,
|
'default': login_confirm.LoginConfirmSerializer,
|
||||||
action_open: login_confirm.ApplySerializer,
|
action_open: login_confirm.ApplySerializer,
|
||||||
|
action_approve: login_confirm.LoginConfirmSerializer(read_only=True),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,11 +1,13 @@
|
||||||
from rest_framework import serializers
|
from rest_framework import serializers
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
from django.db.models import Q
|
from django.db.models import Q
|
||||||
|
from perms.models import ApplicationPermission
|
||||||
from applications.models import Application
|
from applications.models import Application
|
||||||
from applications.const import ApplicationCategoryChoices, ApplicationTypeChoices
|
from applications.const import ApplicationCategoryChoices, ApplicationTypeChoices
|
||||||
from assets.models import SystemUser
|
from assets.models import SystemUser
|
||||||
from orgs.utils import tmp_to_org
|
from orgs.utils import tmp_to_org
|
||||||
from tickets.models import Ticket
|
from tickets.models import Ticket
|
||||||
|
from .common import DefaultPermissionName
|
||||||
|
|
||||||
__all__ = [
|
__all__ = [
|
||||||
'ApplyApplicationSerializer', 'ApplySerializer', 'ApproveSerializer',
|
'ApplyApplicationSerializer', 'ApplySerializer', 'ApproveSerializer',
|
||||||
|
@ -47,6 +49,9 @@ class ApplySerializer(serializers.Serializer):
|
||||||
|
|
||||||
class ApproveSerializer(serializers.Serializer):
|
class ApproveSerializer(serializers.Serializer):
|
||||||
# 审批信息
|
# 审批信息
|
||||||
|
approve_permission_name = serializers.CharField(
|
||||||
|
max_length=128, default=DefaultPermissionName(), label=_('Permission name')
|
||||||
|
)
|
||||||
approve_applications = serializers.ListField(
|
approve_applications = serializers.ListField(
|
||||||
required=True, child=serializers.UUIDField(), label=_('Approve applications'),
|
required=True, child=serializers.UUIDField(), label=_('Approve applications'),
|
||||||
allow_null=True
|
allow_null=True
|
||||||
|
@ -72,6 +77,19 @@ class ApproveSerializer(serializers.Serializer):
|
||||||
required=True, label=_('Date expired'), allow_null=True
|
required=True, label=_('Date expired'), allow_null=True
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def validate_approve_permission_name(self, permission_name):
|
||||||
|
if not isinstance(self.root.instance, Ticket):
|
||||||
|
return permission_name
|
||||||
|
|
||||||
|
with tmp_to_org(self.root.instance.org_id):
|
||||||
|
already_exists = ApplicationPermission.objects.filter(name=permission_name).exists()
|
||||||
|
if not already_exists:
|
||||||
|
return permission_name
|
||||||
|
|
||||||
|
raise serializers.ValidationError(_(
|
||||||
|
'Permission named `{}` already exists'.format(permission_name)
|
||||||
|
))
|
||||||
|
|
||||||
def validate_approve_applications(self, approve_applications):
|
def validate_approve_applications(self, approve_applications):
|
||||||
if not isinstance(self.root.instance, Ticket):
|
if not isinstance(self.root.instance, Ticket):
|
||||||
return []
|
return []
|
||||||
|
@ -118,6 +136,9 @@ class ApplyApplicationSerializer(ApplySerializer, ApproveSerializer):
|
||||||
return []
|
return []
|
||||||
|
|
||||||
apply_application_group = value.get('apply_application_group', [])
|
apply_application_group = value.get('apply_application_group', [])
|
||||||
|
if not apply_application_group:
|
||||||
|
return []
|
||||||
|
|
||||||
apply_type = value.get('apply_type')
|
apply_type = value.get('apply_type')
|
||||||
queries = Q()
|
queries = Q()
|
||||||
for application in apply_application_group:
|
for application in apply_application_group:
|
||||||
|
@ -133,8 +154,11 @@ class ApplyApplicationSerializer(ApplySerializer, ApproveSerializer):
|
||||||
if not isinstance(self.root.instance, Ticket):
|
if not isinstance(self.root.instance, Ticket):
|
||||||
return []
|
return []
|
||||||
|
|
||||||
apply_type = value.get('apply_type')
|
|
||||||
apply_system_user_group = value.get('apply_system_user_group', [])
|
apply_system_user_group = value.get('apply_system_user_group', [])
|
||||||
|
if not apply_system_user_group:
|
||||||
|
return []
|
||||||
|
|
||||||
|
apply_type = value.get('apply_type')
|
||||||
protocol = SystemUser.get_protocol_by_application_type(apply_type)
|
protocol = SystemUser.get_protocol_by_application_type(apply_type)
|
||||||
queries = Q()
|
queries = Q()
|
||||||
for system_user in apply_system_user_group:
|
for system_user in apply_system_user_group:
|
||||||
|
|
|
@ -2,9 +2,11 @@ from django.utils.translation import ugettext_lazy as _
|
||||||
from django.db.models import Q
|
from django.db.models import Q
|
||||||
from rest_framework import serializers
|
from rest_framework import serializers
|
||||||
from perms.serializers import ActionsField
|
from perms.serializers import ActionsField
|
||||||
|
from perms.models import AssetPermission
|
||||||
from assets.models import Asset, SystemUser
|
from assets.models import Asset, SystemUser
|
||||||
from orgs.utils import tmp_to_org
|
from orgs.utils import tmp_to_org
|
||||||
from tickets.models import Ticket
|
from tickets.models import Ticket
|
||||||
|
from .common import DefaultPermissionName
|
||||||
|
|
||||||
|
|
||||||
__all__ = [
|
__all__ = [
|
||||||
|
@ -44,6 +46,9 @@ class ApplySerializer(serializers.Serializer):
|
||||||
|
|
||||||
class ApproveSerializer(serializers.Serializer):
|
class ApproveSerializer(serializers.Serializer):
|
||||||
# 审批信息
|
# 审批信息
|
||||||
|
approve_permission_name = serializers.CharField(
|
||||||
|
max_length=128, default=DefaultPermissionName(), label=_('Permission name')
|
||||||
|
)
|
||||||
approve_assets = serializers.ListField(
|
approve_assets = serializers.ListField(
|
||||||
required=True, allow_null=True, child=serializers.UUIDField(), label=_('Approve assets')
|
required=True, allow_null=True, child=serializers.UUIDField(), label=_('Approve assets')
|
||||||
)
|
)
|
||||||
|
@ -76,6 +81,19 @@ class ApproveSerializer(serializers.Serializer):
|
||||||
required=True, label=_('Date expired'), allow_null=True
|
required=True, label=_('Date expired'), allow_null=True
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def validate_approve_permission_name(self, permission_name):
|
||||||
|
if not isinstance(self.root.instance, Ticket):
|
||||||
|
return permission_name
|
||||||
|
|
||||||
|
with tmp_to_org(self.root.instance.org_id):
|
||||||
|
already_exists = AssetPermission.objects.filter(name=permission_name).exists()
|
||||||
|
if not already_exists:
|
||||||
|
return permission_name
|
||||||
|
|
||||||
|
raise serializers.ValidationError(_(
|
||||||
|
'Permission named `{}` already exists'.format(permission_name)
|
||||||
|
))
|
||||||
|
|
||||||
def validate_approve_assets(self, approve_assets):
|
def validate_approve_assets(self, approve_assets):
|
||||||
if not isinstance(self.root.instance, Ticket):
|
if not isinstance(self.root.instance, Ticket):
|
||||||
return []
|
return []
|
||||||
|
@ -118,10 +136,13 @@ class ApplyAssetSerializer(ApplySerializer, ApproveSerializer):
|
||||||
|
|
||||||
apply_ip_group = value.get('apply_ip_group', [])
|
apply_ip_group = value.get('apply_ip_group', [])
|
||||||
apply_hostname_group = value.get('apply_hostname_group', [])
|
apply_hostname_group = value.get('apply_hostname_group', [])
|
||||||
queries = Q(ip__in=apply_ip_group)
|
queries = Q()
|
||||||
|
if apply_ip_group:
|
||||||
|
queries |= Q(ip__in=apply_ip_group)
|
||||||
for hostname in apply_hostname_group:
|
for hostname in apply_hostname_group:
|
||||||
queries |= Q(hostname__icontains=hostname)
|
queries |= Q(hostname__icontains=hostname)
|
||||||
|
if not queries:
|
||||||
|
return []
|
||||||
with tmp_to_org(self.root.instance.org_id):
|
with tmp_to_org(self.root.instance.org_id):
|
||||||
assets_id = Asset.objects.filter(queries).values_list('id', flat=True)[:5]
|
assets_id = Asset.objects.filter(queries).values_list('id', flat=True)[:5]
|
||||||
assets_id = [str(asset_id) for asset_id in assets_id]
|
assets_id = [str(asset_id) for asset_id in assets_id]
|
||||||
|
@ -132,6 +153,9 @@ class ApplyAssetSerializer(ApplySerializer, ApproveSerializer):
|
||||||
return []
|
return []
|
||||||
|
|
||||||
apply_system_user_group = value.get('apply_system_user_group', [])
|
apply_system_user_group = value.get('apply_system_user_group', [])
|
||||||
|
if not apply_system_user_group:
|
||||||
|
return []
|
||||||
|
|
||||||
queries = Q()
|
queries = Q()
|
||||||
for system_user in apply_system_user_group:
|
for system_user in apply_system_user_group:
|
||||||
queries |= Q(username__icontains=system_user)
|
queries |= Q(username__icontains=system_user)
|
||||||
|
|
|
@ -0,0 +1,30 @@
|
||||||
|
from django.utils.translation import ugettext as _
|
||||||
|
from tickets.models import Ticket
|
||||||
|
|
||||||
|
|
||||||
|
__all__ = ['DefaultPermissionName', 'get_default_permission_name']
|
||||||
|
|
||||||
|
|
||||||
|
def get_default_permission_name(ticket):
|
||||||
|
name = ''
|
||||||
|
if isinstance(ticket, Ticket):
|
||||||
|
name = _('Created by ticket ({}-{})').format(ticket.title, str(ticket.id)[:4])
|
||||||
|
return name
|
||||||
|
|
||||||
|
|
||||||
|
class DefaultPermissionName(object):
|
||||||
|
default = None
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _construct_default_permission_name(serializer_field):
|
||||||
|
permission_name = ''
|
||||||
|
ticket = serializer_field.root.instance
|
||||||
|
if isinstance(ticket, Ticket):
|
||||||
|
permission_name = get_default_permission_name(ticket)
|
||||||
|
return permission_name
|
||||||
|
|
||||||
|
def set_context(self, serializer_field):
|
||||||
|
self.default = self._construct_default_permission_name(serializer_field)
|
||||||
|
|
||||||
|
def __call__(self):
|
||||||
|
return self.default
|
|
@ -16,9 +16,13 @@ __all__ = [
|
||||||
|
|
||||||
|
|
||||||
class TicketSerializer(OrgResourceModelSerializerMixin):
|
class TicketSerializer(OrgResourceModelSerializerMixin):
|
||||||
type_display = serializers.ReadOnlyField(source='get_type_display', label=_('Type'))
|
type_display = serializers.ReadOnlyField(source='get_type_display', label=_('Type display'))
|
||||||
action_display = serializers.ReadOnlyField(source='get_action_display', label=_('Action'))
|
action_display = serializers.ReadOnlyField(
|
||||||
status_display = serializers.ReadOnlyField(source='get_status_display', label=_('Status'))
|
source='get_action_display', label=_('Action display')
|
||||||
|
)
|
||||||
|
status_display = serializers.ReadOnlyField(
|
||||||
|
source='get_status_display', label=_('Status display')
|
||||||
|
)
|
||||||
meta = MethodSerializer()
|
meta = MethodSerializer()
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
|
@ -34,31 +38,34 @@ class TicketSerializer(OrgResourceModelSerializerMixin):
|
||||||
]
|
]
|
||||||
|
|
||||||
def get_meta_serializer(self):
|
def get_meta_serializer(self):
|
||||||
request = self.context['request']
|
default_serializer = serializers.Serializer(read_only=True)
|
||||||
default_serializer_class = serializers.Serializer
|
|
||||||
if isinstance(self.instance, Ticket):
|
if isinstance(self.instance, Ticket):
|
||||||
_type = self.instance.type
|
_type = self.instance.type
|
||||||
else:
|
else:
|
||||||
_type = request.query_params.get('type')
|
_type = self.context['request'].query_params.get('type')
|
||||||
|
|
||||||
if not _type:
|
|
||||||
return default_serializer_class()
|
|
||||||
|
|
||||||
|
if _type:
|
||||||
action_serializer_classes_mapping = type_serializer_classes_mapping.get(_type)
|
action_serializer_classes_mapping = type_serializer_classes_mapping.get(_type)
|
||||||
if not action_serializer_classes_mapping:
|
if action_serializer_classes_mapping:
|
||||||
return default_serializer_class()
|
query_action = self.context['request'].query_params.get('action')
|
||||||
|
action = query_action if query_action else self.context['view'].action
|
||||||
query_action = request.query_params.get('action')
|
serializer_class = action_serializer_classes_mapping.get(action)
|
||||||
_action = query_action if query_action else self.context['view'].action
|
if not serializer_class:
|
||||||
serializer_class = action_serializer_classes_mapping.get(_action)
|
|
||||||
if serializer_class:
|
|
||||||
return serializer_class()
|
|
||||||
|
|
||||||
serializer_class = action_serializer_classes_mapping.get('default')
|
serializer_class = action_serializer_classes_mapping.get('default')
|
||||||
if serializer_class:
|
else:
|
||||||
return serializer_class()
|
serializer_class = default_serializer
|
||||||
|
else:
|
||||||
|
serializer_class = default_serializer
|
||||||
|
|
||||||
return default_serializer_class()
|
if not serializer_class:
|
||||||
|
serializer_class = default_serializer
|
||||||
|
|
||||||
|
if isinstance(serializer_class, type):
|
||||||
|
serializer = serializer_class()
|
||||||
|
else:
|
||||||
|
serializer = serializer_class
|
||||||
|
|
||||||
|
return serializer
|
||||||
|
|
||||||
|
|
||||||
class TicketDisplaySerializer(TicketSerializer):
|
class TicketDisplaySerializer(TicketSerializer):
|
||||||
|
|
|
@ -1,11 +1,9 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
#
|
#
|
||||||
from django.dispatch import receiver
|
from django.dispatch import receiver
|
||||||
from django.db.models.signals import m2m_changed
|
|
||||||
|
|
||||||
from common.utils import get_logger
|
from common.utils import get_logger
|
||||||
from tickets.models import Ticket
|
from tickets.models import Ticket
|
||||||
from tickets.utils import send_ticket_applied_mail_to_assignees
|
|
||||||
from ..signals import post_change_ticket_action
|
from ..signals import post_change_ticket_action
|
||||||
|
|
||||||
|
|
||||||
|
@ -15,19 +13,3 @@ logger = get_logger(__name__)
|
||||||
@receiver(post_change_ticket_action, sender=Ticket)
|
@receiver(post_change_ticket_action, sender=Ticket)
|
||||||
def on_post_change_ticket_action(sender, ticket, action, **kwargs):
|
def on_post_change_ticket_action(sender, ticket, action, **kwargs):
|
||||||
ticket.handler.dispatch(action)
|
ticket.handler.dispatch(action)
|
||||||
|
|
||||||
|
|
||||||
@receiver(m2m_changed, sender=Ticket.assignees.through)
|
|
||||||
def on_ticket_assignees_changed(sender, instance, action, reverse, model, pk_set, **kwargs):
|
|
||||||
if reverse:
|
|
||||||
return
|
|
||||||
if action != 'post_add':
|
|
||||||
return
|
|
||||||
logger.debug('Receives ticket and assignees changed signal, ticket: {}'.format(instance.title))
|
|
||||||
instance.assignees_display = [str(assignee) for assignee in instance.assignees.all()]
|
|
||||||
instance.save()
|
|
||||||
assignees = model.objects.filter(pk__in=pk_set)
|
|
||||||
assignees_display = [str(assignee) for assignee in assignees]
|
|
||||||
logger.debug('Send applied email to assignees: {}'.format(assignees_display))
|
|
||||||
send_ticket_applied_mail_to_assignees(instance, assignees)
|
|
||||||
|
|
||||||
|
|
|
@ -10,37 +10,39 @@ from . import const
|
||||||
|
|
||||||
logger = get_logger(__file__)
|
logger = get_logger(__file__)
|
||||||
|
|
||||||
|
EMAIL_TEMPLATE = '''
|
||||||
|
<div>
|
||||||
|
<p>
|
||||||
|
{title}
|
||||||
|
<a href={ticket_detail_url}>
|
||||||
|
<strong>{ticket_detail_url_description}</strong>
|
||||||
|
</a>
|
||||||
|
</p>
|
||||||
|
<div>
|
||||||
|
{body}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
'''
|
||||||
|
|
||||||
def send_ticket_applied_mail_to_assignees(ticket, assignees):
|
|
||||||
if not assignees:
|
def send_ticket_applied_mail_to_assignees(ticket):
|
||||||
|
if not ticket.assignees:
|
||||||
logger.debug("Not found assignees, ticket: {}({}), assignees: {}".format(
|
logger.debug("Not found assignees, ticket: {}({}), assignees: {}".format(
|
||||||
ticket, str(ticket.id), assignees)
|
ticket, str(ticket.id), ticket.assignees)
|
||||||
)
|
)
|
||||||
return
|
return
|
||||||
|
|
||||||
subject = _('New Ticket: {} ({})'.format(ticket.title, ticket.get_type_display()))
|
ticket_detail_url = urljoin(settings.SITE_URL, const.TICKET_DETAIL_URL.format(id=str(ticket.id)))
|
||||||
ticket_detail_url = urljoin(
|
subject = _('New Ticket - {} ({})').format(ticket.title, ticket.get_type_display())
|
||||||
settings.SITE_URL, const.TICKET_DETAIL_URL.format(id=str(ticket.id))
|
message = EMAIL_TEMPLATE.format(
|
||||||
)
|
title=_('Your has a new ticket, applicant - {}').format(str(ticket.applicant_display)),
|
||||||
message = _(
|
ticket_detail_url=ticket_detail_url,
|
||||||
"""<div>
|
ticket_detail_url_description=_('click here to review'),
|
||||||
<p>Your has a new ticket</p>
|
|
||||||
<div>
|
|
||||||
<b>Ticket:</b>
|
|
||||||
<br/>
|
|
||||||
{body}
|
|
||||||
<br/>
|
|
||||||
<a href={ticket_detail_url}>click here to review</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
""".format(
|
|
||||||
body=ticket.body.replace('\n', '<br/>'),
|
body=ticket.body.replace('\n', '<br/>'),
|
||||||
ticket_detail_url=ticket_detail_url
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
if settings.DEBUG:
|
if settings.DEBUG:
|
||||||
logger.debug(message)
|
logger.debug(message)
|
||||||
recipient_list = [assignee.email for assignee in assignees]
|
recipient_list = [assignee.email for assignee in ticket.assignees.all()]
|
||||||
send_mail_async.delay(subject, message, recipient_list, html_message=message)
|
send_mail_async.delay(subject, message, recipient_list, html_message=message)
|
||||||
|
|
||||||
|
|
||||||
|
@ -48,22 +50,15 @@ def send_ticket_processed_mail_to_applicant(ticket):
|
||||||
if not ticket.applicant:
|
if not ticket.applicant:
|
||||||
logger.error("Not found applicant: {}({})".format(ticket.title, ticket.id))
|
logger.error("Not found applicant: {}({})".format(ticket.title, ticket.id))
|
||||||
return
|
return
|
||||||
subject = _('Ticket has processed: {} ({})').format(ticket.title, ticket.get_type_display())
|
|
||||||
message = _(
|
ticket_detail_url = urljoin(settings.SITE_URL, const.TICKET_DETAIL_URL.format(id=str(ticket.id)))
|
||||||
"""
|
subject = _('Ticket has processed - {} ({})').format(ticket.title, ticket.processor_display)
|
||||||
<div>
|
message = EMAIL_TEMPLATE.format(
|
||||||
<p>Your ticket has been processed</p>
|
title=_('Your ticket has been processed, processor - {}').format(ticket.processor_display),
|
||||||
<div>
|
ticket_detail_url=ticket_detail_url,
|
||||||
<b>Ticket:</b>
|
ticket_detail_url_description=_('click here to review'),
|
||||||
<br/>
|
|
||||||
{body}
|
|
||||||
<br/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
""".format(
|
|
||||||
body=ticket.body.replace('\n', '<br/>'),
|
body=ticket.body.replace('\n', '<br/>'),
|
||||||
)
|
)
|
||||||
)
|
|
||||||
if settings.DEBUG:
|
if settings.DEBUG:
|
||||||
logger.debug(message)
|
logger.debug(message)
|
||||||
recipient_list = [ticket.applicant.email]
|
recipient_list = [ticket.applicant.email]
|
||||||
|
|
|
@ -48,7 +48,7 @@ class UserForgotPasswordView(FormView):
|
||||||
|
|
||||||
if not user.is_local:
|
if not user.is_local:
|
||||||
error = _(
|
error = _(
|
||||||
'The user is from A, please go to the corresponding system to change the password'
|
'The user is from {}, please go to the corresponding system to change the password'
|
||||||
''.format(user.get_source_display())
|
''.format(user.get_source_display())
|
||||||
)
|
)
|
||||||
form.add_error('email', error)
|
form.add_error('email', error)
|
||||||
|
|
|
@ -0,0 +1,2 @@
|
||||||
|
[1;31m 你想偷看啥 !!!
|
||||||
|
[1;31m What are you trying to peek at !!!
|
Loading…
Reference in New Issue