Commit f9e41d71 authored by ibuler's avatar ibuler

[Update] 修改登录工单

parent 08775551
......@@ -4,6 +4,7 @@ from rest_framework.generics import UpdateAPIView
from rest_framework.response import Response
from rest_framework.views import APIView
from django.shortcuts import get_object_or_404
from django.utils.translation import ugettext as _
from common.utils import get_logger, get_object_or_none
from common.permissions import IsOrgAdmin
......@@ -11,7 +12,7 @@ from ..models import LoginConfirmSetting
from ..serializers import LoginConfirmSettingSerializer
from .. import errors
__all__ = ['LoginConfirmSettingUpdateApi', 'UserTicketAcceptAuthApi']
__all__ = ['LoginConfirmSettingUpdateApi', 'LoginConfirmTicketStatusApi']
logger = get_logger(__name__)
......@@ -30,10 +31,10 @@ class LoginConfirmSettingUpdateApi(UpdateAPIView):
return s
class UserTicketAcceptAuthApi(APIView):
class LoginConfirmTicketStatusApi(APIView):
permission_classes = ()
def get(self, request, *args, **kwargs):
def get_ticket(self):
from tickets.models import LoginConfirmTicket
ticket_id = self.request.session.get("auth_ticket_id")
logger.debug('Login confirm ticket id: {}'.format(ticket_id))
......@@ -41,31 +42,32 @@ class UserTicketAcceptAuthApi(APIView):
ticket = None
else:
ticket = get_object_or_none(LoginConfirmTicket, pk=ticket_id)
return ticket
def get(self, request, *args, **kwargs):
ticket_id = self.request.session.get("auth_ticket_id")
ticket = self.get_ticket()
try:
if not ticket:
raise errors.LoginConfirmTicketNotFound(ticket_id)
if ticket.action == LoginConfirmTicket.ACTION_APPROVE:
raise errors.LoginConfirmOtherError(ticket_id, _("not found"))
if ticket.status == 'open':
raise errors.LoginConfirmWaitError(ticket_id)
elif ticket.action == ticket.ACTION_APPROVE:
self.request.session["auth_confirm"] = "1"
return Response({"msg": "ok"})
elif ticket.action == LoginConfirmTicket.ACTION_REJECT:
raise errors.LoginConfirmRejectedError(ticket_id)
elif ticket.action == ticket.ACTION_REJECT:
raise errors.LoginConfirmOtherError(
ticket_id, ticket.get_action_display()
)
else:
raise errors.LoginConfirmWaitError(ticket_id)
raise errors.LoginConfirmOtherError(
ticket_id, ticket.get_status_display()
)
except errors.AuthFailedError as e:
data = e.as_data()
return Response(data, status=400)
class UserTicketCancelAuthApi(APIView):
permission_classes = ()
return Response(e.as_data(), status=400)
def get(self, request, *args, **kwargs):
from tickets.models import LoginConfirmTicket
ticket_id = self.request.session.get("auth_ticket_id")
logger.debug('Login confirm ticket id: {}'.format(ticket_id))
if not ticket_id:
ticket = None
else:
ticket = get_object_or_none(LoginConfirmTicket, pk=ticket_id)
if not ticket:
ticket.status = "close"
def delete(self, request, *args, **kwargs):
ticket = self.get_ticket()
if ticket:
ticket.perform_status('closed', request.user)
return Response('', status=200)
......@@ -48,8 +48,7 @@ mfa_failed_msg = _("MFA code invalid, or ntp sync server time")
mfa_required_msg = _("MFA required")
login_confirm_required_msg = _("Login confirm required")
login_confirm_wait_msg = _("Wait login confirm ticket for accept")
login_confirm_rejected_msg = _("Login confirm ticket was rejected")
login_confirm_ticket_not_found_msg = _("Ticket not found")
login_confirm_error_msg = _("Login confirm ticket was {}")
class AuthFailedNeedLogMixin:
......@@ -174,11 +173,9 @@ class LoginConfirmWaitError(LoginConfirmError):
error = 'login_confirm_wait'
class LoginConfirmRejectedError(LoginConfirmError):
msg = login_confirm_rejected_msg
error = 'login_confirm_rejected'
class LoginConfirmOtherError(LoginConfirmError):
error = 'login_confirm_error'
class LoginConfirmTicketNotFound(LoginConfirmError):
msg = login_confirm_ticket_not_found_msg
error = 'login_confirm_ticket_not_found'
def __init__(self, ticket_id, status):
msg = login_confirm_error_msg.format(status)
super().__init__(ticket_id=ticket_id, msg=msg)
......@@ -106,7 +106,7 @@ class AuthMixin:
if ticket.status == "accepted":
return
elif ticket.status == "rejected":
raise errors.LoginConfirmRejectedError(ticket.id)
raise errors.LoginConfirmOtherError(ticket.id)
else:
raise errors.LoginConfirmWaitError(ticket.id)
......
......@@ -62,12 +62,9 @@ class LoginConfirmSetting(CommonModelMixin):
remote_addr = '127.0.0.1'
body = ''
reviewer = self.reviewers.all()
reviewer_names = ','.join([u.name for u in reviewer])
ticket = LoginConfirmTicket.objects.create(
user=self.user, user_display=str(self.user),
title=title, body=body,
user=self.user, title=title, body=body,
city=city, ip=remote_addr,
assignees_display=reviewer_names,
type=LoginConfirmTicket.TYPE_LOGIN_CONFIRM,
)
ticket.assignees.set(reviewer)
......
# -*- coding: utf-8 -*-
#
from django.core.cache import cache
from rest_framework import serializers
from common.utils import get_object_or_none
from users.models import User
from users.serializers import UserProfileSerializer
from .models import AccessKey, LoginConfirmSetting
......@@ -26,14 +26,15 @@ class OtpVerifySerializer(serializers.Serializer):
class BearerTokenSerializer(serializers.Serializer):
username = serializers.CharField(allow_null=True, required=False)
username = serializers.CharField(allow_null=True, required=False, write_only=True)
password = serializers.CharField(write_only=True, allow_null=True,
required=False)
required=False, allow_blank=True)
public_key = serializers.CharField(write_only=True, allow_null=True,
required=False)
allow_blank=True, required=False)
token = serializers.CharField(read_only=True)
keyword = serializers.SerializerMethodField()
date_expired = serializers.DateTimeField(read_only=True)
user = UserProfileSerializer(read_only=True)
@staticmethod
def get_keyword(obj):
......@@ -52,9 +53,9 @@ class BearerTokenSerializer(serializers.Serializer):
)
token, date_expired = user.create_bearer_token(request)
instance = {
"username": user.username,
"token": token,
"date_expired": date_expired,
"user": user
}
return instance
......
......@@ -73,7 +73,7 @@ var infoMsgRef = $(".info-messages");
var timestamp = '{{ timestamp }}';
var progressBarRef = $(".progress-bar");
var interval, checkInterval;
var url = "{% url 'api-auth:user-order-auth' %}";
var url = "{% url 'api-auth:login-confirm-ticket-status' %}";
var successUrl = "{% url 'authentication:login-guard' %}";
function doRequestAuth() {
......
......@@ -18,7 +18,7 @@ urlpatterns = [
path('connection-token/',
api.UserConnectionTokenApi.as_view(), name='connection-token'),
path('otp/verify/', api.UserOtpVerifyApi.as_view(), name='user-otp-verify'),
path('order/auth/', api.UserTicketAcceptAuthApi.as_view(), name='user-order-auth'),
path('login-confirm-ticket/status/', api.LoginConfirmTicketStatusApi.as_view(), name='login-confirm-ticket-status'),
path('login-confirm-settings/<uuid:user_id>/', api.LoginConfirmSettingUpdateApi.as_view(), name='login-confirm-setting-update')
]
......
......@@ -8,7 +8,7 @@ msgid ""
msgstr ""
"Project-Id-Version: Jumpserver 0.3.3\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2019-11-05 15:00+0800\n"
"POT-Creation-Date: 2019-11-08 15:42+0800\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: ibuler <ibuler@qq.com>\n"
"Language-Team: Jumpserver team<ibuler@qq.com>\n"
......@@ -96,7 +96,7 @@ msgstr "运行参数"
#: terminal/templates/terminal/session_list.html:28
#: terminal/templates/terminal/session_list.html:72
#: xpack/plugins/change_auth_plan/forms.py:73
#: xpack/plugins/change_auth_plan/models.py:419
#: xpack/plugins/change_auth_plan/models.py:412
#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_create_update.html:46
#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_execution_list.html:54
#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_execution_subtask_list.html:13
......@@ -152,7 +152,7 @@ msgstr "资产"
#: users/templates/users/user_profile.html:51
#: users/templates/users/user_pubkey_update.html:57
#: xpack/plugins/change_auth_plan/forms.py:56
#: xpack/plugins/change_auth_plan/models.py:64
#: xpack/plugins/change_auth_plan/models.py:63
#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_detail.html:61
#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_list.html:12
#: xpack/plugins/cloud/models.py:59 xpack/plugins/cloud/models.py:144
......@@ -199,7 +199,7 @@ msgstr "参数"
#: perms/templates/perms/remote_app_permission_detail.html:90
#: users/models/user.py:423 users/serializers/group.py:32
#: users/templates/users/user_detail.html:112
#: xpack/plugins/change_auth_plan/models.py:109
#: xpack/plugins/change_auth_plan/models.py:108
#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_detail.html:113
#: xpack/plugins/cloud/models.py:80 xpack/plugins/cloud/models.py:179
#: xpack/plugins/gathered_user/models.py:46
......@@ -219,11 +219,11 @@ msgstr "创建者"
#: assets/templates/assets/system_user_detail.html:96
#: common/mixins/models.py:51 ops/models/adhoc.py:45
#: ops/templates/ops/adhoc_detail.html:90 ops/templates/ops/task_detail.html:64
#: orders/templates/orders/login_confirm_order_detail.html:60 orgs/models.py:17
#: perms/models/base.py:55
#: orgs/models.py:17 perms/models/base.py:55
#: perms/templates/perms/asset_permission_detail.html:94
#: perms/templates/perms/remote_app_permission_detail.html:86
#: terminal/templates/terminal/terminal_detail.html:59 users/models/group.py:17
#: terminal/templates/terminal/terminal_detail.html:59
#: tickets/templates/tickets/ticket_detail.html:52 users/models/group.py:17
#: users/templates/users/user_group_detail.html:63
#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_detail.html:105
#: xpack/plugins/cloud/models.py:83 xpack/plugins/cloud/models.py:182
......@@ -254,18 +254,17 @@ msgstr "创建日期"
#: assets/templates/assets/domain_list.html:28
#: assets/templates/assets/system_user_detail.html:104
#: assets/templates/assets/system_user_list.html:55 ops/models/adhoc.py:43
#: orders/serializers.py:23
#: orders/templates/orders/login_confirm_order_detail.html:96 orgs/models.py:18
#: perms/models/base.py:56
#: orgs/models.py:18 perms/models/base.py:56
#: perms/templates/perms/asset_permission_detail.html:102
#: perms/templates/perms/remote_app_permission_detail.html:94
#: settings/models.py:34 terminal/models.py:33
#: terminal/templates/terminal/terminal_detail.html:63 users/models/group.py:15
#: terminal/templates/terminal/terminal_detail.html:63
#: tickets/templates/tickets/ticket_detail.html:106 users/models/group.py:15
#: users/models/user.py:415 users/templates/users/user_detail.html:130
#: users/templates/users/user_group_detail.html:67
#: users/templates/users/user_group_list.html:37
#: users/templates/users/user_profile.html:138
#: xpack/plugins/change_auth_plan/models.py:105
#: xpack/plugins/change_auth_plan/models.py:104
#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_detail.html:117
#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_list.html:19
#: xpack/plugins/cloud/models.py:77 xpack/plugins/cloud/models.py:173
......@@ -311,7 +310,7 @@ msgstr "远程应用"
#: settings/templates/settings/security_setting.html:73
#: settings/templates/settings/terminal_setting.html:71
#: terminal/templates/terminal/terminal_update.html:45
#: users/templates/users/_user.html:50
#: users/templates/users/_user.html:51
#: users/templates/users/user_bulk_update.html:23
#: users/templates/users/user_detail.html:179
#: users/templates/users/user_group_create_update.html:31
......@@ -355,7 +354,7 @@ msgstr "重置"
#: terminal/templates/terminal/command_list.html:47
#: terminal/templates/terminal/session_list.html:52
#: terminal/templates/terminal/terminal_update.html:46
#: users/templates/users/_user.html:51
#: users/templates/users/_user.html:52
#: users/templates/users/forgot_password.html:42
#: users/templates/users/user_bulk_update.html:24
#: users/templates/users/user_list.html:57
......@@ -519,7 +518,6 @@ msgstr "创建远程应用"
#: authentication/templates/authentication/_access_key_modal.html:34
#: ops/templates/ops/adhoc_history.html:59 ops/templates/ops/task_adhoc.html:64
#: ops/templates/ops/task_history.html:65 ops/templates/ops/task_list.html:18
#: orders/templates/orders/login_confirm_order_list.html:19
#: perms/forms/asset_permission.py:21
#: perms/templates/perms/asset_permission_create_update.html:50
#: perms/templates/perms/asset_permission_list.html:56
......@@ -529,6 +527,8 @@ msgstr "创建远程应用"
#: settings/templates/settings/terminal_setting.html:107
#: terminal/templates/terminal/session_list.html:36
#: terminal/templates/terminal/terminal_list.html:36
#: tickets/templates/tickets/login_confirm_ticket_list.html:18
#: tickets/templates/tickets/login_confirm_ticket_list.html:92
#: users/templates/users/_granted_assets.html:34
#: users/templates/users/user_group_list.html:38
#: users/templates/users/user_list.html:41
......@@ -606,7 +606,7 @@ msgstr "端口"
#: assets/templates/assets/asset_detail.html:196
#: assets/templates/assets/system_user_assets.html:83
#: perms/models/asset_permission.py:81
#: xpack/plugins/change_auth_plan/models.py:75
#: xpack/plugins/change_auth_plan/models.py:74
#: xpack/plugins/gathered_user/models.py:31
#: xpack/plugins/gathered_user/templates/gathered_user/task_list.html:17
msgid "Nodes"
......@@ -700,21 +700,21 @@ msgstr "SSH网关,支持代理SSH,RDP和VNC"
#: assets/templates/assets/admin_user_list.html:45
#: assets/templates/assets/domain_gateway_list.html:71
#: assets/templates/assets/system_user_detail.html:62
#: assets/templates/assets/system_user_list.html:48 audits/models.py:82
#: assets/templates/assets/system_user_list.html:48 audits/models.py:81
#: audits/templates/audits/login_log_list.html:57 authentication/forms.py:13
#: authentication/templates/authentication/login.html:60
#: authentication/templates/authentication/xpack_login.html:87
#: authentication/templates/authentication/login.html:58
#: authentication/templates/authentication/xpack_login.html:91
#: ops/models/adhoc.py:189 perms/templates/perms/asset_permission_list.html:70
#: perms/templates/perms/asset_permission_user.html:55
#: perms/templates/perms/remote_app_permission_user.html:54
#: settings/templates/settings/_ldap_list_users_modal.html:30 users/forms.py:13
#: settings/templates/settings/_ldap_list_users_modal.html:30 users/forms.py:14
#: users/models/user.py:380 users/templates/users/_select_user_modal.html:14
#: users/templates/users/user_detail.html:68
#: users/templates/users/user_list.html:36
#: users/templates/users/user_profile.html:47
#: xpack/plugins/change_auth_plan/forms.py:58
#: xpack/plugins/change_auth_plan/models.py:66
#: xpack/plugins/change_auth_plan/models.py:415
#: xpack/plugins/change_auth_plan/models.py:65
#: xpack/plugins/change_auth_plan/models.py:408
#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_detail.html:65
#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_execution_list.html:53
#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_execution_subtask_list.html:12
......@@ -732,17 +732,17 @@ msgstr "密码或密钥密码"
#: assets/templates/assets/_asset_user_auth_update_modal.html:21
#: assets/templates/assets/_asset_user_auth_view_modal.html:27
#: authentication/forms.py:15
#: authentication/templates/authentication/login.html:63
#: authentication/templates/authentication/xpack_login.html:90
#: settings/forms.py:114 users/forms.py:15 users/forms.py:27
#: authentication/templates/authentication/login.html:66
#: authentication/templates/authentication/xpack_login.html:99
#: settings/forms.py:114 users/forms.py:16 users/forms.py:42
#: users/templates/users/reset_password.html:53
#: users/templates/users/user_password_authentication.html:18
#: users/templates/users/user_password_update.html:44
#: users/templates/users/user_profile_update.html:41
#: users/templates/users/user_pubkey_update.html:41
#: users/templates/users/user_update.html:20
#: xpack/plugins/change_auth_plan/models.py:96
#: xpack/plugins/change_auth_plan/models.py:264
#: xpack/plugins/change_auth_plan/models.py:95
#: xpack/plugins/change_auth_plan/models.py:263
msgid "Password"
msgstr "密码"
......@@ -797,8 +797,6 @@ msgstr "使用逗号分隔多个命令,如: /bin/whoami,/sbin/ifconfig"
#: assets/templates/assets/domain_gateway_list.html:68
#: assets/templates/assets/user_asset_list.html:76
#: audits/templates/audits/login_log_list.html:60
#: orders/templates/orders/login_confirm_order_detail.html:33
#: orders/templates/orders/login_confirm_order_list.html:16
#: perms/templates/perms/asset_permission_asset.html:58 settings/forms.py:144
#: users/templates/users/_granted_assets.html:31
#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_asset_list.html:54
......@@ -938,13 +936,13 @@ msgstr "版本"
msgid "AuthBook"
msgstr ""
#: assets/models/base.py:31 xpack/plugins/change_auth_plan/models.py:100
#: xpack/plugins/change_auth_plan/models.py:271
#: assets/models/base.py:31 xpack/plugins/change_auth_plan/models.py:99
#: xpack/plugins/change_auth_plan/models.py:270
msgid "SSH private key"
msgstr "ssh密钥"
#: assets/models/base.py:32 xpack/plugins/change_auth_plan/models.py:103
#: xpack/plugins/change_auth_plan/models.py:267
#: assets/models/base.py:32 xpack/plugins/change_auth_plan/models.py:102
#: xpack/plugins/change_auth_plan/models.py:266
msgid "SSH public key"
msgstr "ssh公钥"
......@@ -1041,12 +1039,13 @@ msgstr "过滤器"
#: assets/models/cmd_filter.py:51
#: assets/templates/assets/cmd_filter_rule_list.html:58
#: audits/templates/audits/login_log_list.html:58 orders/models.py:41
#: audits/templates/audits/login_log_list.html:58
#: perms/templates/perms/remote_app_permission_remote_app.html:54
#: settings/templates/settings/command_storage_create.html:31
#: settings/templates/settings/replay_storage_create.html:31
#: settings/templates/settings/terminal_setting.html:84
#: settings/templates/settings/terminal_setting.html:106
#: tickets/models/base.py:34 tickets/templates/tickets/ticket_detail.html:33
msgid "Type"
msgstr "类型"
......@@ -1105,10 +1104,7 @@ msgstr "默认资产组"
#: audits/templates/audits/password_change_log_list.html:39
#: audits/templates/audits/password_change_log_list.html:56
#: authentication/models.py:43 ops/templates/ops/command_execution_list.html:38
#: ops/templates/ops/command_execution_list.html:63 orders/models.py:11
#: orders/models.py:32
#: orders/templates/orders/login_confirm_order_detail.html:32
#: orders/templates/orders/login_confirm_order_list.html:15
#: ops/templates/ops/command_execution_list.html:63
#: perms/forms/asset_permission.py:78 perms/forms/remote_app_permission.py:34
#: perms/models/base.py:49
#: perms/templates/perms/asset_permission_create_update.html:41
......@@ -1120,7 +1116,10 @@ msgstr "默认资产组"
#: terminal/models.py:156 terminal/templates/terminal/command_list.html:29
#: terminal/templates/terminal/command_list.html:65
#: terminal/templates/terminal/session_list.html:27
#: terminal/templates/terminal/session_list.html:71 users/forms.py:319
#: terminal/templates/terminal/session_list.html:71 tickets/models/base.py:25
#: tickets/models/base.py:68
#: tickets/templates/tickets/login_confirm_ticket_list.html:15
#: tickets/templates/tickets/ticket_detail.html:32 users/forms.py:339
#: users/models/user.py:132 users/models/user.py:148 users/models/user.py:509
#: users/serializers/group.py:21
#: users/templates/users/user_group_detail.html:78
......@@ -1187,7 +1186,7 @@ msgstr "手动登录"
#: assets/views/label.py:27 assets/views/label.py:45 assets/views/label.py:73
#: assets/views/system_user.py:29 assets/views/system_user.py:46
#: assets/views/system_user.py:63 assets/views/system_user.py:79
#: templates/_nav.html:39 xpack/plugins/change_auth_plan/models.py:71
#: templates/_nav.html:39 xpack/plugins/change_auth_plan/models.py:70
msgid "Assets"
msgstr "资产管理"
......@@ -1245,7 +1244,7 @@ msgstr "不可达"
msgid "Reachable"
msgstr "可连接"
#: assets/models/utils.py:45 assets/tasks/const.py:89 audits/utils.py:29
#: assets/models/utils.py:45 assets/tasks/const.py:89 audits/utils.py:30
#: xpack/plugins/license/models.py:78
msgid "Unknown"
msgstr "未知"
......@@ -1276,7 +1275,7 @@ msgstr "组织名称"
msgid "Backend"
msgstr "后端"
#: assets/serializers/asset_user.py:67 users/forms.py:262
#: assets/serializers/asset_user.py:67 users/forms.py:282
#: users/models/user.py:412 users/templates/users/first_login.html:42
#: users/templates/users/user_password_update.html:49
#: users/templates/users/user_profile.html:69
......@@ -1332,7 +1331,7 @@ msgstr "测试资产可连接性: {}"
#: assets/tasks/asset_user_connectivity.py:27
#: assets/tasks/push_system_user.py:130
#: xpack/plugins/change_auth_plan/models.py:528
#: xpack/plugins/change_auth_plan/models.py:521
msgid "The asset {} system platform {} does not support run Ansible tasks"
msgstr "资产 {} 系统平台 {} 不支持运行 Ansible 任务"
......@@ -1447,7 +1446,7 @@ msgid "Asset list"
msgstr "资产列表"
#: assets/templates/assets/_asset_list_modal.html:33
#: assets/templates/assets/_node_tree.html:40
#: assets/templates/assets/_node_tree.html:39
#: ops/templates/ops/command_execution_create.html:70
#: ops/templates/ops/command_execution_create.html:127
#: users/templates/users/_granted_assets.html:7
......@@ -1494,7 +1493,8 @@ msgstr "获取认证信息错误"
#: authentication/templates/authentication/_access_key_modal.html:142
#: authentication/templates/authentication/_mfa_confirm_modal.html:53
#: settings/templates/settings/_ldap_list_users_modal.html:92
#: templates/_modal.html:22
#: templates/_modal.html:22 tickets/models/base.py:50
#: tickets/templates/tickets/ticket_detail.html:103
msgid "Close"
msgstr "关闭"
......@@ -1502,9 +1502,9 @@ msgstr "关闭"
#: audits/templates/audits/operate_log_list.html:77
#: audits/templates/audits/password_change_log_list.html:59
#: ops/templates/ops/task_adhoc.html:63
#: orders/templates/orders/login_confirm_order_list.html:18
#: terminal/templates/terminal/command_list.html:33
#: terminal/templates/terminal/session_detail.html:50
#: tickets/templates/tickets/login_confirm_ticket_list.html:17
msgid "Datetime"
msgstr "日期"
......@@ -1544,31 +1544,31 @@ msgstr "SSH端口"
msgid "If use nat, set the ssh real port"
msgstr "如果使用了nat端口映射,请设置为ssh真实监听的端口"
#: assets/templates/assets/_node_tree.html:50
#: assets/templates/assets/_node_tree.html:49
msgid "Add node"
msgstr "新建节点"
#: assets/templates/assets/_node_tree.html:51
#: assets/templates/assets/_node_tree.html:50
msgid "Rename node"
msgstr "重命名节点"
#: assets/templates/assets/_node_tree.html:52
#: assets/templates/assets/_node_tree.html:51
msgid "Delete node"
msgstr "删除节点"
#: assets/templates/assets/_node_tree.html:166
#: assets/templates/assets/_node_tree.html:165
msgid "Create node failed"
msgstr "创建节点失败"
#: assets/templates/assets/_node_tree.html:178
#: assets/templates/assets/_node_tree.html:177
msgid "Have child node, cancel"
msgstr "存在子节点,不能删除"
#: assets/templates/assets/_node_tree.html:180
#: assets/templates/assets/_node_tree.html:179
msgid "Have assets, cancel"
msgstr "存在资产,不能删除"
#: assets/templates/assets/_node_tree.html:255
#: assets/templates/assets/_node_tree.html:254
msgid "Rename success"
msgstr "重命名成功"
......@@ -2214,7 +2214,7 @@ msgstr "操作"
msgid "Filename"
msgstr "文件名"
#: audits/models.py:24 audits/models.py:78
#: audits/models.py:24 audits/models.py:77
#: audits/templates/audits/ftp_log_list.html:79
#: ops/templates/ops/command_execution_list.html:68
#: ops/templates/ops/task_list.html:15
......@@ -2256,53 +2256,53 @@ msgstr "启用"
msgid "-"
msgstr ""
#: audits/models.py:79 xpack/plugins/cloud/models.py:264
#: audits/models.py:78 xpack/plugins/cloud/models.py:264
#: xpack/plugins/cloud/models.py:287
msgid "Failed"
msgstr "失败"
#: audits/models.py:83
#: audits/models.py:82
msgid "Login type"
msgstr "登录方式"
#: audits/models.py:84
#: audits/models.py:83
msgid "Login ip"
msgstr "登录IP"
#: audits/models.py:85
#: audits/models.py:84
msgid "Login city"
msgstr "登录城市"
#: audits/models.py:86
#: audits/models.py:85
msgid "User agent"
msgstr "Agent"
#: audits/models.py:87 audits/templates/audits/login_log_list.html:62
#: audits/models.py:86 audits/templates/audits/login_log_list.html:62
#: authentication/templates/authentication/_mfa_confirm_modal.html:14
#: users/forms.py:174 users/models/user.py:404
#: users/forms.py:194 users/models/user.py:404
#: users/templates/users/first_login.html:45
msgid "MFA"
msgstr "MFA"
#: audits/models.py:88 audits/templates/audits/login_log_list.html:63
#: xpack/plugins/change_auth_plan/models.py:423
#: audits/models.py:87 audits/templates/audits/login_log_list.html:63
#: xpack/plugins/change_auth_plan/models.py:416
#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_execution_subtask_list.html:15
#: xpack/plugins/cloud/models.py:278
#: xpack/plugins/cloud/templates/cloud/sync_instance_task_history.html:69
msgid "Reason"
msgstr "原因"
#: audits/models.py:89 audits/templates/audits/login_log_list.html:64
#: orders/templates/orders/login_confirm_order_detail.html:35
#: orders/templates/orders/login_confirm_order_list.html:17
#: orders/templates/orders/login_confirm_order_list.html:91
#: audits/models.py:88 audits/templates/audits/login_log_list.html:64
#: tickets/templates/tickets/login_confirm_ticket_list.html:16
#: tickets/templates/tickets/login_confirm_ticket_list.html:88
#: tickets/templates/tickets/ticket_detail.html:34
#: xpack/plugins/cloud/models.py:275 xpack/plugins/cloud/models.py:310
#: xpack/plugins/cloud/templates/cloud/sync_instance_task_history.html:70
#: xpack/plugins/cloud/templates/cloud/sync_instance_task_instance.html:65
msgid "Status"
msgstr "状态"
#: audits/models.py:90
#: audits/models.py:89
msgid "Date login"
msgstr "登录日期"
......@@ -2314,8 +2314,8 @@ msgstr "登录日期"
#: perms/templates/perms/asset_permission_detail.html:86
#: perms/templates/perms/remote_app_permission_detail.html:78
#: terminal/models.py:167 terminal/templates/terminal/session_list.html:34
#: xpack/plugins/change_auth_plan/models.py:250
#: xpack/plugins/change_auth_plan/models.py:426
#: xpack/plugins/change_auth_plan/models.py:249
#: xpack/plugins/change_auth_plan/models.py:419
#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_execution_list.html:59
#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_execution_subtask_list.html:17
#: xpack/plugins/gathered_user/models.py:143
......@@ -2356,7 +2356,6 @@ msgid "UA"
msgstr "Agent"
#: audits/templates/audits/login_log_list.html:61
#: orders/templates/orders/login_confirm_order_detail.html:58
msgid "City"
msgstr "城市"
......@@ -2391,21 +2390,9 @@ msgstr "登录日志"
msgid "Command execution log"
msgstr "命令执行"
#: authentication/api/auth.py:58
msgid "Log in frequently and try again later"
msgstr "登录频繁, 稍后重试"
#: authentication/api/auth.py:83
msgid "Please carry seed value and conduct MFA secondary certification"
msgstr "请携带seed值, 进行MFA二次认证"
#: authentication/api/auth.py:173
msgid "Please verify the user name and password first"
msgstr "请先进行用户名和密码验证"
#: authentication/api/auth.py:178
msgid "MFA certification failed"
msgstr "MFA认证失败"
#: authentication/api/login_confirm.py:52
msgid "not found"
msgstr "没有发现"
#: authentication/backends/api.py:53
msgid "Invalid signature header. No credentials provided."
......@@ -2482,11 +2469,11 @@ msgstr "禁用或失效"
msgid "This account is inactive."
msgstr "此账户已禁用"
#: authentication/errors.py:28
#: authentication/errors.py:35
msgid "No session found, check your cookie"
msgstr "会话已变更,刷新页面"
#: authentication/errors.py:30
#: authentication/errors.py:37
#, python-brace-format
msgid ""
"The username or password you entered is incorrect, please enter it again. "
......@@ -2496,37 +2483,33 @@ msgstr ""
"您输入的用户名或密码不正确,请重新输入。 您还可以尝试 {times_try} 次(账号将"
"被临时 锁定 {block_time} 分钟)"
#: authentication/errors.py:36
#: authentication/errors.py:43
msgid ""
"The account has been locked (please contact admin to unlock it or try again "
"after {} minutes)"
msgstr "账号已被锁定(请联系管理员解锁 或 {}分钟后重试)"
#: authentication/errors.py:39 users/views/user.py:393 users/views/user.py:418
#: authentication/errors.py:46 users/views/user.py:393 users/views/user.py:418
msgid "MFA code invalid, or ntp sync server time"
msgstr "MFA验证码不正确,或者服务器端时间不对"
#: authentication/errors.py:41
#: authentication/errors.py:48
msgid "MFA required"
msgstr ""
#: authentication/errors.py:42
#: authentication/errors.py:49
msgid "Login confirm required"
msgstr "需要登录复核"
#: authentication/errors.py:43
msgid "Wait login confirm order for accept"
#: authentication/errors.py:50
msgid "Wait login confirm ticket for accept"
msgstr "等待登录复核处理"
#: authentication/errors.py:44
msgid "Login confirm order was rejected"
msgstr "登录已被拒绝"
#: authentication/errors.py:51
msgid "Login confirm ticket was {}"
msgstr "登录复核 {}"
#: authentication/errors.py:45
msgid "Order not found"
msgstr "没有发现工单"
#: authentication/forms.py:32 users/forms.py:21
#: authentication/forms.py:32 users/forms.py:22
msgid "MFA code"
msgstr "MFA 验证码"
......@@ -2643,30 +2626,30 @@ msgstr ""
msgid "Changes the world, starting with a little bit."
msgstr "改变世界,从一点点开始。"
#: authentication/templates/authentication/login.html:46
#: authentication/templates/authentication/login.html:68
#: authentication/templates/authentication/xpack_login.html:96
#: authentication/templates/authentication/login.html:45
#: authentication/templates/authentication/login.html:76
#: authentication/templates/authentication/xpack_login.html:110
#: templates/_header_bar.html:83
msgid "Login"
msgstr "登录"
#: authentication/templates/authentication/login.html:52
#: authentication/templates/authentication/xpack_login.html:78
#: authentication/templates/authentication/login.html:54
#: authentication/templates/authentication/xpack_login.html:87
msgid "Captcha invalid"
msgstr "验证码错误"
#: authentication/templates/authentication/login.html:79
#: authentication/templates/authentication/xpack_login.html:100
#: authentication/templates/authentication/login.html:87
#: authentication/templates/authentication/xpack_login.html:114
#: users/templates/users/forgot_password.html:10
#: users/templates/users/forgot_password.html:25
msgid "Forgot password"
msgstr "忘记密码"
#: authentication/templates/authentication/login.html:86
#: authentication/templates/authentication/login.html:94
msgid "More login options"
msgstr "更多登录方式"
#: authentication/templates/authentication/login.html:90
#: authentication/templates/authentication/login.html:98
msgid "Keycloak"
msgstr ""
......@@ -2716,15 +2699,15 @@ msgstr "复制链接"
msgid "Return"
msgstr "返回"
#: authentication/templates/authentication/xpack_login.html:67
#: authentication/templates/authentication/xpack_login.html:74
msgid "Welcome back, please enter username and password to login"
msgstr "欢迎回来,请输入用户名和密码登录"
#: authentication/views/login.py:80
#: authentication/views/login.py:73
msgid "Please enable cookies and try again."
msgstr "设置你的浏览器支持cookie"
#: authentication/views/login.py:192
#: authentication/views/login.py:172
msgid ""
"Wait for <b>{}</b> confirm, You also can copy link to her/him <br/>\n"
" Don't close this page"
......@@ -2732,15 +2715,15 @@ msgstr ""
"等待 <b>{}</b> 确认, 你也可以复制链接发给他/她 <br/>\n"
" 不要关闭本页面"
#: authentication/views/login.py:197
msgid "No order found"
#: authentication/views/login.py:177
msgid "No ticket found"
msgstr "没有发现工单"
#: authentication/views/login.py:220
#: authentication/views/login.py:200
msgid "Logout success"
msgstr "退出登录成功"
#: authentication/views/login.py:221
#: authentication/views/login.py:201
msgid "Logout success, return login page"
msgstr "退出登录成功,返回到登录页面"
......@@ -2906,49 +2889,49 @@ msgstr "Become"
msgid "Create by"
msgstr "创建者"
#: ops/models/adhoc.py:251
#: ops/models/adhoc.py:252
msgid "{} Start task: {}"
msgstr "{} 任务开始: {}"
#: ops/models/adhoc.py:263
#: ops/models/adhoc.py:264
msgid "{} Task finish"
msgstr "{} 任务结束"
#: ops/models/adhoc.py:355
#: ops/models/adhoc.py:356
msgid "Start time"
msgstr "开始时间"
#: ops/models/adhoc.py:356
#: ops/models/adhoc.py:357
msgid "End time"
msgstr "完成时间"
#: ops/models/adhoc.py:357 ops/templates/ops/adhoc_history.html:57
#: ops/models/adhoc.py:358 ops/templates/ops/adhoc_history.html:57
#: ops/templates/ops/task_history.html:63 ops/templates/ops/task_list.html:17
#: xpack/plugins/change_auth_plan/models.py:253
#: xpack/plugins/change_auth_plan/models.py:429
#: xpack/plugins/change_auth_plan/models.py:252
#: xpack/plugins/change_auth_plan/models.py:422
#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_execution_list.html:58
#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_execution_subtask_list.html:16
#: xpack/plugins/gathered_user/models.py:146
msgid "Time"
msgstr "时间"
#: ops/models/adhoc.py:358 ops/templates/ops/adhoc_detail.html:106
#: ops/models/adhoc.py:359 ops/templates/ops/adhoc_detail.html:106
#: ops/templates/ops/adhoc_history.html:55
#: ops/templates/ops/adhoc_history_detail.html:69
#: ops/templates/ops/task_detail.html:84 ops/templates/ops/task_history.html:61
msgid "Is finished"
msgstr "是否完成"
#: ops/models/adhoc.py:359 ops/templates/ops/adhoc_history.html:56
#: ops/models/adhoc.py:360 ops/templates/ops/adhoc_history.html:56
#: ops/templates/ops/task_history.html:62
msgid "Is success"
msgstr "是否成功"
#: ops/models/adhoc.py:360
#: ops/models/adhoc.py:361
msgid "Adhoc raw result"
msgstr "结果"
#: ops/models/adhoc.py:361
#: ops/models/adhoc.py:362
msgid "Adhoc result summary"
msgstr "汇总"
......@@ -3108,8 +3091,7 @@ msgstr "没有输入命令"
msgid "No system user was selected"
msgstr "没有选择系统用户"
#: ops/templates/ops/command_execution_create.html:296 orders/models.py:26
#: orders/templates/orders/login_confirm_order_list.html:92
#: ops/templates/ops/command_execution_create.html:296
msgid "Pending"
msgstr "等待"
......@@ -3193,159 +3175,6 @@ msgstr "命令执行列表"
msgid "Command execution"
msgstr "命令执行"
#: orders/models.py:12 orders/models.py:33
msgid "User display name"
msgstr "用户显示名称"
#: orders/models.py:13 orders/models.py:36
msgid "Body"
msgstr "内容"
#: orders/models.py:24 orders/templates/orders/login_confirm_order_list.html:93
msgid "Accepted"
msgstr "已接受"
#: orders/models.py:25 orders/templates/orders/login_confirm_order_list.html:94
msgid "Rejected"
msgstr "已拒绝"
#: orders/models.py:35 orders/templates/orders/login_confirm_order_list.html:14
#: orders/templates/orders/login_confirm_order_list.html:90
msgid "Title"
msgstr "标题"
#: orders/models.py:37
#: orders/templates/orders/login_confirm_order_detail.html:59
msgid "Assignee"
msgstr "处理人"
#: orders/models.py:38
msgid "Assignee display name"
msgstr "处理人名称"
#: orders/models.py:39
#: orders/templates/orders/login_confirm_order_detail.html:34
msgid "Assignees"
msgstr "待处理人"
#: orders/models.py:40
msgid "Assignees display name"
msgstr "待处理人名称"
#: orders/serializers.py:21
#: orders/templates/orders/login_confirm_order_detail.html:94
#: orders/templates/orders/login_confirm_order_list.html:59
#: terminal/templates/terminal/terminal_list.html:78
msgid "Accept"
msgstr "接受"
#: orders/serializers.py:22
#: orders/templates/orders/login_confirm_order_detail.html:95
#: orders/templates/orders/login_confirm_order_list.html:60
#: terminal/templates/terminal/terminal_list.html:80
msgid "Reject"
msgstr "拒绝"
#: orders/serializers.py:43
msgid "this order"
msgstr "这个工单"
#: orders/templates/orders/login_confirm_order_detail.html:75
msgid "ago"
msgstr "前"
#: orders/utils.py:18
msgid "New order"
msgstr "新工单"
#: orders/utils.py:21
#, python-brace-format
msgid ""
"\n"
" <div>\n"
" <p>Your has a new order</p>\n"
" <div>\n"
" <b>Title:</b> {order.title}\n"
" <br/>\n"
" <b>User:</b> {user}\n"
" <br/>\n"
" <b>Assignees:</b> {order.assignees_display}\n"
" <br/>\n"
" <b>City:</b> {order.city}\n"
" <br/>\n"
" <b>IP:</b> {order.ip}\n"
" <br/>\n"
" <a href={url}>click here to review</a> \n"
" </div>\n"
" </div>\n"
" "
msgstr ""
"\n"
" <div>\n"
" <p>您有一个新工单</p>\n"
" <div>\n"
" <b>标题:</b> {order.title}\n"
" <br/>\n"
" <b>用户:</b> {user}\n"
" <br/>\n"
" <b>待处理人:</b> {order.assignees_display}\n"
" <br/>\n"
" <b>城市:</b> {order.city}\n"
" <br/>\n"
" <b>IP:</b> {order.ip}\n"
" <br/>\n"
" <a href={url}>点我查看</a> \n"
" </div>\n"
" </div>\n"
" "
#: orders/utils.py:48
msgid "Order has been reply"
msgstr "工单已被回复"
#: orders/utils.py:49
#, python-brace-format
msgid ""
"\n"
" <div>\n"
" <p>Your order has been replay</p>\n"
" <div>\n"
" <b>Title:</b> {order.title}\n"
" <br/>\n"
" <b>Assignee:</b> {order.assignee_display}\n"
" <br/>\n"
" <b>Status:</b> {order.status_display}\n"
" <br/>\n"
" </div>\n"
" </div>\n"
" "
msgstr ""
"\n"
" <div>\n"
" <p>您的工单已被回复</p>\n"
" <div>\n"
" <b>标题:</b> {order.title}\n"
" <br/>\n"
" <b>处理人:</b> {order.assignee_display}\n"
" <br/>\n"
" <b>状态:</b> {order.status_display}\n"
" <br/>\n"
" </div>\n"
" </div>\n"
" "
#: orders/views.py:15 orders/views.py:31 templates/_nav.html:127
msgid "Orders"
msgstr "工单管理"
#: orders/views.py:16
msgid "Login confirm order list"
msgstr "登录复核工单列表"
#: orders/views.py:32
msgid "Login confirm order detail"
msgstr "登录复核工单详情"
#: orgs/mixins/models.py:44 orgs/mixins/serializers.py:26 orgs/models.py:31
msgid "Organization"
msgstr "组织"
......@@ -3369,7 +3198,7 @@ msgstr "提示:RDP 协议不支持单独控制上传或下载文件"
#: perms/templates/perms/asset_permission_list.html:71
#: perms/templates/perms/asset_permission_list.html:118
#: perms/templates/perms/remote_app_permission_list.html:16
#: templates/_nav.html:21 users/forms.py:293 users/models/group.py:26
#: templates/_nav.html:21 users/forms.py:313 users/models/group.py:26
#: users/models/user.py:388 users/templates/users/_select_user_modal.html:16
#: users/templates/users/user_detail.html:218
#: users/templates/users/user_list.html:38
......@@ -3629,33 +3458,33 @@ msgstr "远程应用授权用户列表"
msgid "RemoteApp permission RemoteApp list"
msgstr "远程应用授权远程应用列表"
#: settings/api.py:28
#: settings/api.py:31
msgid "Test mail sent to {}, please check"
msgstr "邮件已经发送{}, 请检查"
#: settings/api.py:67
#: settings/api.py:70
msgid "Test ldap success"
msgstr "连接LDAP成功"
#: settings/api.py:104
#: settings/api.py:107
msgid "Match {} s users"
msgstr "匹配 {} 个用户"
#: settings/api.py:163
#: settings/api.py:166
msgid "succeed: {} failed: {} total: {}"
msgstr "成功:{} 失败:{} 总数:{}"
#: settings/api.py:185 settings/api.py:221
#: settings/api.py:188 settings/api.py:224
msgid ""
"Error: Account invalid (Please make sure the information such as Access key "
"or Secret key is correct)"
msgstr "错误:账户无效 (请确保 Access key 或 Secret key 等信息正确)"
#: settings/api.py:191 settings/api.py:227
#: settings/api.py:194 settings/api.py:230
msgid "Create succeed"
msgstr "创建成功"
#: settings/api.py:209 settings/api.py:247
#: settings/api.py:212 settings/api.py:250
#: settings/templates/settings/terminal_setting.html:154
msgid "Delete succeed"
msgstr "删除成功"
......@@ -4210,8 +4039,8 @@ msgid "Commercial support"
msgstr "商业支持"
#: templates/_header_bar.html:70 templates/_nav.html:30
#: templates/_nav_user.html:32 users/forms.py:153
#: users/templates/users/_user.html:43
#: templates/_nav_user.html:32 users/forms.py:173
#: users/templates/users/_user.html:44
#: users/templates/users/first_login.html:39
#: users/templates/users/user_password_update.html:40
#: users/templates/users/user_profile.html:17
......@@ -4381,7 +4210,12 @@ msgstr "批量命令"
msgid "Task monitor"
msgstr "任务监控"
#: templates/_nav.html:130 users/templates/users/user_detail.html:257
#: templates/_nav.html:127 tickets/views.py:16 tickets/views.py:30
msgid "Tickets"
msgstr "工单管理"
#: templates/_nav.html:130 tickets/models/base.py:23
#: users/templates/users/user_detail.html:257
msgid "Login confirm"
msgstr "登录复核"
......@@ -4730,6 +4564,18 @@ msgstr "地址"
msgid "Alive"
msgstr "在线"
#: terminal/templates/terminal/terminal_list.html:78
msgid "Accept"
msgstr "接受"
#: terminal/templates/terminal/terminal_list.html:80
#: tickets/models/login_confirm.py:16
#: tickets/templates/tickets/login_confirm_ticket_detail.html:10
#: tickets/templates/tickets/login_confirm_ticket_list.html:57
#: tickets/templates/tickets/login_confirm_ticket_list.html:94
msgid "Reject"
msgstr "拒绝"
#: terminal/templates/terminal/terminal_modal_accept.html:5
msgid "Accept terminal registration"
msgstr "接受终端注册"
......@@ -4763,11 +4609,163 @@ msgid ""
"You should use your ssh client tools connect terminal: {} <br /> <br />{}"
msgstr "你可以使用ssh客户端工具连接终端"
#: tickets/models/base.py:16 tickets/models/base.py:52
#: tickets/templates/tickets/login_confirm_ticket_list.html:89
msgid "Open"
msgstr ""
#: tickets/models/base.py:17
#: tickets/templates/tickets/login_confirm_ticket_list.html:90
msgid "Closed"
msgstr "关闭"
#: tickets/models/base.py:22
msgid "General"
msgstr "一般"
#: tickets/models/base.py:26 tickets/models/base.py:69
msgid "User display name"
msgstr "用户显示名称"
#: tickets/models/base.py:28
#: tickets/templates/tickets/login_confirm_ticket_list.html:14
#: tickets/templates/tickets/login_confirm_ticket_list.html:87
msgid "Title"
msgstr "标题"
#: tickets/models/base.py:29 tickets/models/base.py:70
msgid "Body"
msgstr "内容"
#: tickets/models/base.py:30 tickets/templates/tickets/ticket_detail.html:51
msgid "Assignee"
msgstr "处理人"
#: tickets/models/base.py:31
msgid "Assignee display name"
msgstr "处理人名称"
#: tickets/models/base.py:32 tickets/templates/tickets/ticket_detail.html:50
msgid "Assignees"
msgstr "待处理人"
#: tickets/models/base.py:33
msgid "Assignees display name"
msgstr "待处理人名称"
#: tickets/models/base.py:53
msgid "{} {} this ticket"
msgstr "{} {} 这个工单"
#: tickets/models/login_confirm.py:15
#: tickets/templates/tickets/login_confirm_ticket_detail.html:9
#: tickets/templates/tickets/login_confirm_ticket_list.html:56
#: tickets/templates/tickets/login_confirm_ticket_list.html:93
msgid "Approve"
msgstr "同意"
#: tickets/models/login_confirm.py:24
msgid "this order"
msgstr "这个工单"
#: tickets/templates/tickets/ticket_detail.html:66
#: tickets/templates/tickets/ticket_detail.html:81
msgid "ago"
msgstr "前"
#: tickets/utils.py:18
msgid "New ticket"
msgstr "新工单"
#: tickets/utils.py:21
#, python-brace-format
msgid ""
"\n"
" <div>\n"
" <p>Your has a new ticket</p>\n"
" <div>\n"
" <b>Title:</b> {ticket.title}\n"
" <br/>\n"
" <b>User:</b> {user}\n"
" <br/>\n"
" <b>Assignees:</b> {ticket.assignees_display}\n"
" <br/>\n"
" <b>City:</b> {ticket.city}\n"
" <br/>\n"
" <b>IP:</b> {ticket.ip}\n"
" <br/>\n"
" <a href={url}>click here to review</a> \n"
" </div>\n"
" </div>\n"
" "
msgstr ""
"\n"
" <div>\n"
" <p>您有一个新工单</p>\n"
" <div>\n"
" <b>标题:</b> {ticket.title}\n"
" <br/>\n"
" <b>用户:</b> {user}\n"
" <br/>\n"
" <b>待处理人:</b> {ticket.assignees_display}\n"
" <br/>\n"
" <b>城市:</b> {ticket.city}\n"
" <br/>\n"
" <b>IP:</b> {ticket.ip}\n"
" <br/>\n"
" <a href={url}>点我查看</a> \n"
" </div>\n"
" </div>\n"
" "
#: tickets/utils.py:48
msgid "Ticket has been reply"
msgstr "工单已被回复"
#: tickets/utils.py:49
#, python-brace-format
msgid ""
"\n"
" <div>\n"
" <p>Your ticket has been replay</p>\n"
" <div>\n"
" <b>Title:</b> {ticket.title}\n"
" <br/>\n"
" <b>Assignee:</b> {ticket.assignee_display}\n"
" <br/>\n"
" <b>Status:</b> {ticket.status_display}\n"
" <br/>\n"
" </div>\n"
" </div>\n"
" "
msgstr ""
"\n"
" <div>\n"
" <p>您的工单已被回复</p>\n"
" <div>\n"
" <b>标题:</b> {ticket.title}\n"
" <br/>\n"
" <b>处理人:</b> {ticket.assignee_display}\n"
" <br/>\n"
" <b>状态:</b> {ticket.status_display}\n"
" <br/>\n"
" </div>\n"
" </div>\n"
" "
#: tickets/views.py:17
msgid "Login confirm ticket list"
msgstr "登录复核工单列表"
#: tickets/views.py:31
msgid "Login confirm ticket detail"
msgstr "登录复核工单详情"
#: users/api/user.py:173
msgid "Could not reset self otp, use profile reset instead"
msgstr "不能再该页面重置MFA, 请去个人信息页面重置"
#: users/forms.py:32 users/models/user.py:392
#: users/forms.py:47 users/models/user.py:392
#: users/templates/users/_select_user_modal.html:15
#: users/templates/users/user_detail.html:88
#: users/templates/users/user_list.html:37
......@@ -4775,44 +4773,51 @@ msgstr "不能再该页面重置MFA, 请去个人信息页面重置"
msgid "Role"
msgstr "角色"
#: users/forms.py:35 users/forms.py:232
#: users/forms.py:51 users/models/user.py:427
#: users/templates/users/user_detail.html:104
#: users/templates/users/user_list.html:39
#: users/templates/users/user_profile.html:102
msgid "Source"
msgstr "用户来源"
#: users/forms.py:54 users/forms.py:252
#: users/templates/users/user_update.html:30
msgid "ssh public key"
msgstr "ssh公钥"
#: users/forms.py:36 users/forms.py:233
#: users/forms.py:55 users/forms.py:253
msgid "ssh-rsa AAAA..."
msgstr ""
#: users/forms.py:37
#: users/forms.py:56
msgid "Paste user id_rsa.pub here."
msgstr "复制用户公钥到这里"
#: users/forms.py:51 users/templates/users/user_detail.html:226
#: users/forms.py:71 users/templates/users/user_detail.html:226
msgid "Join user groups"
msgstr "添加到用户组"
#: users/forms.py:86 users/forms.py:247
#: users/forms.py:106 users/forms.py:267
msgid "Public key should not be the same as your old one."
msgstr "不能和原来的密钥相同"
#: users/forms.py:90 users/forms.py:251 users/serializers/user.py:110
#: users/forms.py:110 users/forms.py:271 users/serializers/user.py:109
msgid "Not a valid ssh public key"
msgstr "ssh密钥不合法"
#: users/forms.py:103 users/views/login.py:114 users/views/user.py:287
#: users/forms.py:123 users/views/login.py:114 users/views/user.py:287
msgid "* Your password does not meet the requirements"
msgstr "* 您的密码不符合要求"
#: users/forms.py:124
#: users/forms.py:144
msgid "Reset link will be generated and sent to the user"
msgstr "生成重置密码链接,通过邮件发送给用户"
#: users/forms.py:125
#: users/forms.py:145
msgid "Set password"
msgstr "设置密码"
#: users/forms.py:132 xpack/plugins/change_auth_plan/models.py:89
#: users/forms.py:152 xpack/plugins/change_auth_plan/models.py:88
#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_create_update.html:51
#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_detail.html:69
#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_execution_list.html:57
......@@ -4820,7 +4825,7 @@ msgstr "设置密码"
msgid "Password strategy"
msgstr "密码策略"
#: users/forms.py:159
#: users/forms.py:179
msgid ""
"When enabled, you will enter the MFA binding process the next time you log "
"in. you can also directly bind in \"personal information -> quick "
......@@ -4829,11 +4834,11 @@ msgstr ""
"启用之后您将会在下次登录时进入MFA绑定流程;您也可以在(个人信息->快速修改->更"
"改MFA设置)中直接绑定!"
#: users/forms.py:169
#: users/forms.py:189
msgid "* Enable MFA authentication to make the account more secure."
msgstr "* 启用MFA认证,使账号更加安全。"
#: users/forms.py:179
#: users/forms.py:199
msgid ""
"In order to protect you and your company, please keep your account, password "
"and key sensitive information properly. (for example: setting complex "
......@@ -4842,41 +4847,41 @@ msgstr ""
"为了保护您和公司的安全,请妥善保管您的账户、密码和密钥等重要敏感信息;(如:"
"设置复杂密码,启用MFA认证)"
#: users/forms.py:186 users/templates/users/first_login.html:48
#: users/forms.py:206 users/templates/users/first_login.html:48
#: users/templates/users/first_login.html:110
#: users/templates/users/first_login.html:139
msgid "Finish"
msgstr "完成"
#: users/forms.py:192
#: users/forms.py:212
msgid "Old password"
msgstr "原来密码"
#: users/forms.py:197
#: users/forms.py:217
msgid "New password"
msgstr "新密码"
#: users/forms.py:202
#: users/forms.py:222
msgid "Confirm password"
msgstr "确认密码"
#: users/forms.py:212
#: users/forms.py:232
msgid "Old password error"
msgstr "原来密码错误"
#: users/forms.py:220
#: users/forms.py:240
msgid "Password does not match"
msgstr "密码不一致"
#: users/forms.py:230
#: users/forms.py:250
msgid "Automatically configure and download the SSH key"
msgstr "自动配置并下载SSH密钥"
#: users/forms.py:234
#: users/forms.py:254
msgid "Paste your id_rsa.pub here."
msgstr "复制你的公钥到这里"
#: users/forms.py:268 users/forms.py:273 users/forms.py:323
#: users/forms.py:288 users/forms.py:293 users/forms.py:343
#: xpack/plugins/orgs/forms.py:18
msgid "Select users"
msgstr "选择用户"
......@@ -4919,12 +4924,6 @@ msgstr "头像"
msgid "Wechat"
msgstr "微信"
#: users/models/user.py:427 users/templates/users/user_detail.html:104
#: users/templates/users/user_list.html:39
#: users/templates/users/user_profile.html:102
msgid "Source"
msgstr "用户来源"
#: users/models/user.py:431
msgid "Date password last updated"
msgstr "最后更新密码日期"
......@@ -4965,11 +4964,11 @@ msgstr " 是否过期"
msgid "Avatar url"
msgstr "头像路径"
#: users/serializers/user.py:66
#: users/serializers/user.py:65
msgid "Role limit to {}"
msgstr "角色只能为 {}"
#: users/serializers/user.py:78
#: users/serializers/user.py:77
msgid "Password does not match security rules"
msgstr "密码不满足安全规则"
......@@ -5005,7 +5004,7 @@ msgstr "选择用户"
msgid "Asset num"
msgstr "资产数量"
#: users/templates/users/_user.html:26
#: users/templates/users/_user.html:27
msgid "Security and Role"
msgstr "角色安全"
......@@ -5748,8 +5747,8 @@ msgstr ""
"具</a>) <br>注意: 如果同时设置了定期执行和周期执行,优先使用定期执行"
#: xpack/plugins/change_auth_plan/meta.py:9
#: xpack/plugins/change_auth_plan/models.py:117
#: xpack/plugins/change_auth_plan/models.py:257
#: xpack/plugins/change_auth_plan/models.py:116
#: xpack/plugins/change_auth_plan/models.py:256
#: xpack/plugins/change_auth_plan/views.py:33
#: xpack/plugins/change_auth_plan/views.py:50
#: xpack/plugins/change_auth_plan/views.py:74
......@@ -5760,20 +5759,20 @@ msgstr ""
msgid "Change auth plan"
msgstr "改密计划"
#: xpack/plugins/change_auth_plan/models.py:58
#: xpack/plugins/change_auth_plan/models.py:57
msgid "Custom password"
msgstr "自定义密码"
#: xpack/plugins/change_auth_plan/models.py:59
#: xpack/plugins/change_auth_plan/models.py:58
msgid "All assets use the same random password"
msgstr "所有资产使用相同的随机密码"
#: xpack/plugins/change_auth_plan/models.py:60
#: xpack/plugins/change_auth_plan/models.py:59
msgid "All assets use different random password"
msgstr "所有资产使用不同的随机密码"
#: xpack/plugins/change_auth_plan/models.py:79
#: xpack/plugins/change_auth_plan/models.py:148
#: xpack/plugins/change_auth_plan/models.py:78
#: xpack/plugins/change_auth_plan/models.py:147
#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_detail.html:100
#: xpack/plugins/cloud/models.py:165 xpack/plugins/cloud/models.py:219
#: xpack/plugins/cloud/templates/cloud/sync_instance_task_detail.html:91
......@@ -5782,8 +5781,8 @@ msgstr "所有资产使用不同的随机密码"
msgid "Cycle perform"
msgstr "周期执行"
#: xpack/plugins/change_auth_plan/models.py:84
#: xpack/plugins/change_auth_plan/models.py:146
#: xpack/plugins/change_auth_plan/models.py:83
#: xpack/plugins/change_auth_plan/models.py:145
#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_detail.html:92
#: xpack/plugins/cloud/models.py:170 xpack/plugins/cloud/models.py:217
#: xpack/plugins/cloud/templates/cloud/sync_instance_task_detail.html:83
......@@ -5792,37 +5791,37 @@ msgstr "周期执行"
msgid "Regularly perform"
msgstr "定期执行"
#: xpack/plugins/change_auth_plan/models.py:93
#: xpack/plugins/change_auth_plan/models.py:92
#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_detail.html:74
msgid "Password rules"
msgstr "密码规则"
#: xpack/plugins/change_auth_plan/models.py:213
#: xpack/plugins/change_auth_plan/models.py:212
msgid "* For security, do not change {} user's password"
msgstr "* 为了安全,禁止更改 {} 用户的密码"
#: xpack/plugins/change_auth_plan/models.py:217
#: xpack/plugins/change_auth_plan/models.py:216
msgid "Assets is empty, please add the asset"
msgstr "资产为空,请添加资产"
#: xpack/plugins/change_auth_plan/models.py:261
#: xpack/plugins/change_auth_plan/models.py:260
msgid "Change auth plan snapshot"
msgstr "改密计划快照"
#: xpack/plugins/change_auth_plan/models.py:276
#: xpack/plugins/change_auth_plan/models.py:433
#: xpack/plugins/change_auth_plan/models.py:275
#: xpack/plugins/change_auth_plan/models.py:426
msgid "Change auth plan execution"
msgstr "改密计划执行"
#: xpack/plugins/change_auth_plan/models.py:442
#: xpack/plugins/change_auth_plan/models.py:435
msgid "Change auth plan execution subtask"
msgstr "改密计划执行子任务"
#: xpack/plugins/change_auth_plan/models.py:460
#: xpack/plugins/change_auth_plan/models.py:453
msgid "Authentication failed"
msgstr "认证失败"
#: xpack/plugins/change_auth_plan/models.py:462
#: xpack/plugins/change_auth_plan/models.py:455
msgid "Connection timeout"
msgstr "连接超时"
......@@ -6432,6 +6431,30 @@ msgstr "密码匣子"
msgid "vault create"
msgstr "创建"
#~ msgid "Log in frequently and try again later"
#~ msgstr "登录频繁, 稍后重试"
#~ msgid "Please carry seed value and conduct MFA secondary certification"
#~ msgstr "请携带seed值, 进行MFA二次认证"
#~ msgid "Please verify the user name and password first"
#~ msgstr "请先进行用户名和密码验证"
#~ msgid "MFA certification failed"
#~ msgstr "MFA认证失败"
#~ msgid "Accepted"
#~ msgstr "已接受"
#~ msgid "Rejected"
#~ msgstr "已拒绝"
#~ msgid "New order"
#~ msgstr "新工单"
#~ msgid "Orders"
#~ msgstr "工单管理"
#~ msgid ""
#~ "The username or password you entered is incorrect, please enter it again."
#~ msgstr "您输入的用户名或密码不正确,请重新输入。"
......
......@@ -1317,3 +1317,7 @@ function initDateRangePicker(selector, options) {
options = Object.assign(defaultOption, options);
return $(selector).daterangepicker(options);
}
function reloadPage() {
window.location.reload();
}
......@@ -127,7 +127,7 @@
<i class="fa fa-check-square-o" style="width: 14px"></i> <span class="nav-label">{% trans 'Tickets' %}</span><span class="fa arrow"></span>
</a>
<ul class="nav nav-second-level">
<li id="login-confirm-orders"><a href="{% url 'tickets:login-confirm-ticket-list' %}">{% trans 'Login confirm' %}</a></li>
<li id="login-confirm-tickets"><a href="{% url 'tickets:login-confirm-ticket-list' %}">{% trans 'Login confirm' %}</a></li>
</ul>
</li>
{% endif %}
......
# -*- coding: utf-8 -*-
#
from rest_framework import viewsets, generics
from .. import serializers, models
from rest_framework import viewsets
from django.shortcuts import get_object_or_404
from common.utils import lazyproperty
from .. import serializers, models, mixins
class TicketViewSet(viewsets.ModelViewSet):
serializer_class = serializers.TicketSerializer
def get_queryset(self):
queryset = models.Ticket.objects.all().none()
return queryset
class TicketViewSet(mixins.TicketMixin, viewsets.ModelViewSet):
serializer_class = serializers.TicketSerializer
queryset = models.Ticket.objects.all()
class CommentViewSet(viewsets.ModelViewSet):
class TicketCommentViewSet(viewsets.ModelViewSet):
serializer_class = serializers.CommentSerializer
def check_permissions(self, request):
ticket = self.ticket
if request.user == ticket.user or request.user in ticket.assignees.all():
return True
return False
def get_serializer_context(self):
context = super().get_serializer_context()
context['ticket'] = self.ticket
return context
@lazyproperty
def ticket(self):
ticket_id = self.kwargs.get('ticket_id')
ticket = get_object_or_404(models.Ticket, pk=ticket_id)
return ticket
def get_queryset(self):
queryset = models.Comment.objects.none()
queryset = self.ticket.comments.all()
return queryset
# -*- coding: utf-8 -*-
#
from rest_framework import viewsets, generics
from rest_framework.serializers import ValidationError
from django.shortcuts import get_object_or_404
from common.permissions import IsValidUser
from common.mixins import CommonApiMixin
from .. import serializers
from .. import serializers, mixins
from ..models import LoginConfirmTicket
class LoginConfirmTicketViewSet(CommonApiMixin, viewsets.ModelViewSet):
class LoginConfirmTicketViewSet(CommonApiMixin, mixins.TicketMixin, viewsets.ModelViewSet):
serializer_class = serializers.LoginConfirmTicketSerializer
permission_classes = (IsValidUser,)
filter_fields = ['status', 'title']
queryset = LoginConfirmTicket.objects.all()
filter_fields = ['status', 'title', 'action', 'ip']
search_fields = ['user_display', 'title', 'ip', 'city']
def get_queryset(self):
queryset = LoginConfirmTicket.objects.all()\
.filter(assignees=self.request.user)
return queryset
class LoginConfirmTicketsCreateActionApi(generics.CreateAPIView):
permission_classes = (IsValidUser,)
serializer_class = serializers.LoginConfirmTicketActionSerializer
def get_ticket(self):
ticket_id = self.kwargs.get('pk')
queryset = LoginConfirmTicket.objects.all()\
.filter(assignees=self.request.user)
ticket = get_object_or_404(queryset, id=ticket_id)
return ticket
def get_serializer_context(self):
context = super().get_serializer_context()
ticket = self.get_ticket()
context['ticket'] = ticket
return context
# def check_update_permission(self, serializer):
# data = serializer.validated_data
# action = data.get("action")
# user = self.request.user
# instance = serializer.instance
# if action and user not in instance.assignees.all():
# error = {"action": "Only assignees can update"}
# raise ValidationError(error)
#
# def perform_update(self, serializer):
# self.check_update_permission(serializer)
......@@ -3,3 +3,7 @@ from django.apps import AppConfig
class TicketsConfig(AppConfig):
name = 'tickets'
def ready(self):
from . import signals_handler
return super().ready()
# -*- coding: utf-8 -*-
#
from django.db.models import Q
class TicketMixin:
def get_queryset(self):
queryset = super().get_queryset()
assign = self.request.GET.get('assign', None)
if assign is None:
queryset = queryset.filter(
Q(assignees=self.request.user) | Q(user=self.request.user)
).distinct()
elif assign in ['1']:
queryset = queryset.filter(assignees=self.request.user)
else:
queryset = queryset.filter(user=self.request.user)
return queryset
......@@ -31,16 +31,12 @@ class Ticket(CommonModelMixin):
assignee_display = models.CharField(max_length=128, blank=True, null=True, verbose_name=_("Assignee display name"))
assignees = models.ManyToManyField('users.User', related_name='%(class)s_assigned', verbose_name=_("Assignees"))
assignees_display = models.CharField(max_length=128, verbose_name=_("Assignees display name"), blank=True)
type = models.CharField(max_length=16, default='general', verbose_name=_("Type"))
type = models.CharField(max_length=16, choices=TYPE_CHOICES, default=TYPE_GENERAL, verbose_name=_("Type"))
status = models.CharField(choices=STATUS_CHOICES, max_length=16, default='open')
def __str__(self):
return '{}: {}'.format(self.user_display, self.title)
@property
def comments(self):
return Comment.objects.filter(order_id=self.id)
@property
def body_as_html(self):
return self.body.replace('\n', '<br/>')
......@@ -49,17 +45,29 @@ class Ticket(CommonModelMixin):
def status_display(self):
return self.get_status_display()
def create_status_comment(self, status, user):
if status == self.STATUS_CLOSED:
action = _("Close")
else:
action = _("Open")
body = _('{} {} this ticket').format(self.user, action)
self.comments.create(user=user, body=body)
def perform_status(self, status, user):
if self.status == status:
return
self.status = status
self.save()
class Meta:
ordering = ('-date_created',)
class Comment(CommonModelMixin):
ticket = models.ForeignKey(Ticket, on_delete=models.CASCADE)
ticket = models.ForeignKey(Ticket, on_delete=models.CASCADE, related_name='comments')
user = models.ForeignKey('users.User', on_delete=models.SET_NULL, null=True, verbose_name=_("User"), related_name='comments')
user_display = models.CharField(max_length=128, verbose_name=_("User display name"))
body = models.TextField(verbose_name=_("Body"))
class Meta:
ordering = ('date_created', )
......@@ -18,3 +18,16 @@ class LoginConfirmTicket(Ticket):
ip = models.GenericIPAddressField(blank=True, null=True)
city = models.CharField(max_length=16, blank=True, default='')
action = models.CharField(choices=ACTION_CHOICES, max_length=16, default='', blank=True)
def create_action_comment(self, action, user):
action_display = dict(self.ACTION_CHOICES).get(action)
body = '{} {} {}'.format(user, action_display, _("this order"))
self.comments.create(body=body, user=user, user_display=str(user))
def perform_action(self, action, user):
self.create_action_comment(action, user)
self.action = action
self.status = self.STATUS_CLOSED
self.assignee = user
self.assignees_display = str(user)
self.save()
# -*- coding: utf-8 -*-
#
from rest_framework.permissions import BasePermission
......@@ -21,7 +21,24 @@ class TicketSerializer(serializers.ModelSerializer):
]
class CurrentTicket(object):
ticket = None
def set_context(self, serializer_field):
self.ticket = serializer_field.context['ticket']
def __call__(self):
return self.ticket
class CommentSerializer(serializers.ModelSerializer):
user = serializers.HiddenField(
default=serializers.CurrentUserDefault(),
)
ticket = serializers.HiddenField(
default=CurrentTicket()
)
class Meta:
model = models.Comment
fields = [
......
......@@ -17,13 +17,28 @@ class LoginConfirmTicketSerializer(serializers.ModelSerializer):
]
read_only_fields = TicketSerializer.Meta.read_only_fields
def create(self, validated_data):
validated_data.pop('action')
return super().create(validated_data)
def update(self, instance, validated_data):
action = validated_data.get("action")
user = self.context["request"].user
if action and user not in instance.assignees.all():
error = {"action": "Only assignees can update"}
raise serializers.ValidationError(error)
instance = super().update(instance, validated_data)
if action:
instance.perform_action(action, user)
return instance
class LoginConfirmTicketActionSerializer(serializers.ModelSerializer):
comment = serializers.CharField(allow_blank=True)
class Meta:
model = LoginConfirmTicket
fields = ['action', 'comment']
fields = ['action']
def update(self, instance, validated_data):
pass
......
# -*- coding: utf-8 -*-
#
from django.dispatch import receiver
from django.db.models.signals import m2m_changed, post_save
from django.db.models.signals import m2m_changed, post_save, pre_save
from common.utils import get_logger
from .models import LoginConfirmTicket
from .models import LoginConfirmTicket, Ticket, Comment
from .utils import (
send_login_confirm_ticket_mail_to_assignees,
send_login_confirm_action_mail_to_user
......@@ -16,16 +16,34 @@ logger = get_logger(__name__)
@receiver(m2m_changed, sender=LoginConfirmTicket.assignees.through)
def on_login_confirm_ticket_assignees_set(sender, instance=None, action=None,
model=None, pk_set=None, **kwargs):
reverse=False, model=None,
pk_set=None, **kwargs):
if action == 'post_add':
logger.debug('New ticket create, send mail: {}'.format(instance.id))
assignees = model.objects.filter(pk__in=pk_set)
send_login_confirm_ticket_mail_to_assignees(instance, assignees)
if action.startswith('post') and not reverse:
instance.assignees_display = ', '.join([
str(u) for u in instance.assignees.all()
])
instance.save()
@receiver(post_save, sender=LoginConfirmTicket)
def on_login_confirm_ticket_status_change(sender, instance=None, created=False, **kwargs):
if created or instance.status == "pending":
if created or instance.status == "open":
return
logger.debug('Ticket changed, send mail: {}'.format(instance.id))
send_login_confirm_action_mail_to_user(instance)
@receiver(pre_save, sender=LoginConfirmTicket)
def on_ticket_create(sender, instance=None, **kwargs):
instance.user_display = str(instance.user)
if instance.assignee:
instance.assignee_display = str(instance.assignee)
@receiver(pre_save, sender=Comment)
def on_comment_create(sender, instance=None, **kwargs):
instance.user_display = str(instance.user)
{% extends 'base.html' %}
{% extends 'tickets/ticket_detail.html' %}
{% load static %}
{% load i18n %}
{% block content %}
<div class="wrapper wrapper-content animated fadeInRight">
<div class="row">
<div class="col-sm-12">
<div class="ibox float-e-margins">
<div class="ibox-title">
<h5>
{{ object.title }}
</h5>
<div class="ibox-tools">
<a class="collapse-link">
<i class="fa fa-chevron-up"></i>
</a>
<a class="dropdown-toggle" data-toggle="dropdown" href="#">
<i class="fa fa-wrench"></i>
</a>
<a class="close-link">
<i class="fa fa-times"></i>
</a>
</div>
</div>
<div class="ibox-content">
<div class="row">
<div class="col-lg-11">
<div class="row">
<div class="col-lg-6">
<dl class="dl-horizontal">
<dt>{% trans 'User' %}:</dt> <dd>{{ object.user_display }}</dd>
<dt>{% trans 'IP' %}:</dt> <dd>{{ object.ip }}</dd>
<dt>{% trans 'Assignees' %}:</dt> <dd> {{ object.assignees_display }}</dd>
<dt>{% trans 'Status' %}:</dt>
<dd>
{% if object.status == "accpeted" %}
<span class="label label-primary">
{{ object.get_status_display }}
</span>
{% endif %}
{% if object.status == "rejected" %}
<span class="label label-danger">
{{ object.get_status_display }}
</span>
{% endif %}
{% if object.status == "pending" %}
<span class="label label-info">
{{ object.get_status_display }}
</span>
{% endif %}
</dd>
</dl>
</div>
<div class="col-lg-6">
<dl class="dl-horizontal">
<dt><br></dt><dd></dd>
<dt>{% trans 'City' %}:</dt> <dd>{{ object.city }}</dd>
<dt>{% trans 'Assignee' %}:</dt> <dd>{{ object.assignee_display | default_if_none:"" }}</dd>
<dt>{% trans 'Date created' %}:</dt> <dd> {{ object.date_created }}</dd>
</dl>
</div>
</div>
<div class="row m-t-sm">
<div class="col-lg-12">
<div class="panel blank-panel">
<div class="panel-body">
<div class="feed-activity-list">
{% for comment in object.comments %}
<div class="feed-element">
<a href="#" class="pull-left">
<img alt="image" class="img-circle" src="{% static 'img/avatar/user.png'%}" >
</a>
<div class="media-body ">
<strong>{{ comment.user_display }}</strong> <small class="text-muted"> {{ comment.date_created|timesince}} {% trans 'ago' %}</small>
<br/>
<small class="text-muted">{{ comment.date_created }} </small>
<div style="padding-top: 10px">
{{ comment.body }}
</div>
</div>
</div>
{% endfor %}
</div>
<div class="feed-element">
<a href="" class="pull-left">
<img alt="image" class="img-circle" src="{% static 'img/avatar/user.png'%}" >
</a>
<div class="media-body">
<textarea class="form-control" placeholder="" id="comment"></textarea>
</div>
</div>
<div class="text-right">
<a class="btn btn-sm btn-primary btn-action btn-update" data-action="accept"><i class="fa fa-check"></i> {% trans 'Accept' %}</a>
<a class="btn btn-sm btn-danger btn-action btn-update" data-action="reject"><i class="fa fa-times"></i> {% trans 'Reject' %}</a>
<a class="btn btn-sm btn-info btn-action" data-action="comment"><i class="fa fa-pencil"></i> {% trans 'Comment' %}</a>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="col-lg-1">
</div>
</div>
</div>
</div>
</div>
</div>
</div>
{% block status %}
{% endblock %}
{% block action %}
<a class="btn btn-sm btn-primary btn-update btn-action" data-action="approve"><i class="fa fa-check"></i> {% trans 'Approve' %}</a>
<a class="btn btn-sm btn-danger btn-update btn-action" data-action="reject"><i class="fa fa-times"></i> {% trans 'Reject' %}</a>
{% endblock %}
{% block custom_foot_js %}
{{ block.super }}
<script>
var ticketId = "{{ object.id }}";
var status = "{{ object.status }}";
var actionCreateUrl = "{% url 'api-tickets:login-confirm-ticket-create-action' pk=object.id %}";
$(document).ready(function () {
if (status !== "pending") {
$('.btn-update').attr('disabled', '1')
}
})
.on('click', '.btn-action', function () {
var action = $(this).data('action');
var comment = $("#comment").val();
var data = {
url: actionCreateUrl,
method: 'POST',
body: JSON.stringify({action: action, comment: comment}),
success: function () {
window.location.reload();
}
};
requestApi(data);
})
var ticketDetailUrl = "{% url 'api-tickets:login-confirm-ticket-detail' pk=object.id %}";
$(document).ready(function () {
}).on('click', '.btn-action', function () {
createComment(function () {
});
var action = $(this).data('action');
var data = {
url: ticketDetailUrl,
body: JSON.stringify({action: action}),
method: "PATCH",
success: reloadPage
};
requestApi(data);
})
</script>
{% endblock %}
......@@ -13,7 +13,6 @@
</th>
<th class="text-center">{% trans 'Title' %}</th>
<th class="text-center">{% trans 'User' %}</th>
<th class="text-center">{% trans 'IP' %}</th>
<th class="text-center">{% trans 'Status' %}</th>
<th class="text-center">{% trans 'Datetime' %}</th>
<th class="text-center">{% trans 'Action' %}</th>
......@@ -39,10 +38,6 @@ function initTable() {
$(td).html(detailBtn.replace("{{ DEFAULT_PK }}", rowData.id));
}},
{targets: 3, createdCell: function (td, cellData, rowData) {
var d = cellData + "(" + rowData.city + ")";
$(td).html(d)
}},
{targets: 4, createdCell: function (td, cellData, rowData) {
if (cellData === "approval") {
$(td).html('<i class="fa fa-check text-navy"></i>')
} else if (cellData === "rejected") {
......@@ -53,12 +48,12 @@ function initTable() {
$(td).html('<i class="fa fa-circle text-info"></i>')
}
}},
{targets: 5, createdCell: function (td, cellData) {
{targets: 4, createdCell: function (td, cellData) {
var d = toSafeLocalDateStr(cellData);
$(td).html(d)
}},
{targets: 6, createdCell: function (td, cellData, rowData) {
var acceptBtn = '<a class="btn btn-xs btn-info btn-action" data-action="accept" data-uid="{{ DEFAULT_PK }}" >{% trans "Accept" %}</a> ';
{targets: 5, createdCell: function (td, cellData, rowData) {
var acceptBtn = '<a class="btn btn-xs btn-info btn-action" data-action="approve" data-uid="{{ DEFAULT_PK }}" >{% trans "Approve" %}</a> ';
var rejectBtn = '<a class="btn btn-xs btn-danger btn-action" data-action="reject" data-uid="{{ DEFAULT_PK }}" >{% trans "Reject" %}</a>';
acceptBtn = acceptBtn.replace('{{ DEFAULT_PK }}', cellData);
rejectBtn = rejectBtn.replace('{{ DEFAULT_PK }}', cellData);
......@@ -74,7 +69,7 @@ function initTable() {
ajax_url: '{% url "api-tickets:login-confirm-ticket-list" %}',
columns: [
{data: "id"}, {data: "title"},
{data: "user_display"}, {data: "ip"},
{data: "user_display"},
{data: "status", ticketable: false},
{data: "date_created", width: "120px"},
{data: "id", ticketable: false}
......@@ -101,18 +96,15 @@ $(document).ready(function(){
];
initTableFilterDropdown('#login_confirm_ticket_list_table_filter input', menu)
}).on('click', '.btn-action', function () {
var actionCreateUrl = "{% url 'api-tickets:login-confirm-ticket-create-action' pk=DEFAULT_PK %}";
var ticketId = $(this).data('uid');
actionCreateUrl = actionCreateUrl.replace("{{ DEFAULT_PK }}", ticketId);
var ticketId = $(this).data("uid");
var action = $(this).data('action');
var comment = '';
var ticketDetailUrl = "{% url 'api-tickets:login-confirm-ticket-detail' pk=DEFAULT_PK %}";
ticketDetailUrl = ticketDetailUrl.replace("{{ DEFAULT_PK }}", ticketId);
var data = {
url: actionCreateUrl,
method: 'POST',
body: JSON.stringify({action: action, comment: comment}),
success: function () {
window.location.reload();
}
url: ticketDetailUrl,
body: JSON.stringify({action: action}),
method: "PATCH",
success: reloadPage
};
requestApi(data);
})
......
{% extends 'base.html' %}
{% load static %}
{% load i18n %}
{% block content %}
<div class="wrapper wrapper-content animated fadeInRight">
<div class="row">
<div class="col-sm-12">
<div class="ibox float-e-margins">
<div class="ibox-title">
<h5>
{{ object.title }}
</h5>
<div class="ibox-tools">
<a class="collapse-link">
<i class="fa fa-chevron-up"></i>
</a>
<a class="dropdown-toggle" data-toggle="dropdown" href="#">
<i class="fa fa-wrench"></i>
</a>
<a class="close-link">
<i class="fa fa-times"></i>
</a>
</div>
</div>
<div class="ibox-content">
<div class="row">
<div class="col-lg-11">
<div class="row">
<div class="col-lg-6">
<dl class="dl-horizontal">
<dt>{% trans 'User' %}:</dt> <dd>{{ object.user_display }}</dd>
<dt>{% trans 'Type' %}:</dt> <dd>{{ object.get_type_display | default_if_none:"" }}</dd>
<dt>{% trans 'Status' %}:</dt>
<dd>
{% if object.status == "open" %}
<span class="label label-primary">
{{ object.get_status_display }}
</span>
{% elif object.status == "closed" %}
<span class="label label-danger">
{{ object.get_status_display }}
</span>
{% endif %}
</dd>
</dl>
</div>
<div class="col-lg-6">
<dl class="dl-horizontal">
<dt>{% trans 'Assignees' %}:</dt> <dd> {{ object.assignees_display }}</dd>
<dt>{% trans 'Assignee' %}:</dt> <dd>{{ object.assignee_display | default_if_none:"" }}</dd>
<dt>{% trans 'Date created' %}:</dt> <dd> {{ object.date_created }}</dd>
</dl>
</div>
</div>
<div class="row m-t-sm">
<div class="col-lg-12">
<div class="panel blank-panel">
<div class="panel-body">
<div class="feed-activity-list">
<div class="feed-element">
<a href="#" class="pull-left">
<img alt="image" class="img-circle" src="{% static 'img/avatar/user.png'%}" >
</a>
<div class="media-body ">
<strong>{{ object.user_display }}</strong> <small class="text-muted"> {{ object.date_created|timesince}} {% trans 'ago' %}</small>
<br/>
<small class="text-muted">{{ object.date_created }} </small>
<div style="padding-top: 10px">
{{ object.body_as_html | safe }}
</div>
</div>
</div>
{% for comment in object.comments.all %}
<div class="feed-element">
<a href="#" class="pull-left">
<img alt="image" class="img-circle" src="{% static 'img/avatar/user.png'%}" >
</a>
<div class="media-body ">
<strong>{{ comment.user_display }}</strong> <small class="text-muted"> {{ comment.date_created|timesince}} {% trans 'ago' %}</small>
<br/>
<small class="text-muted">{{ comment.date_created }} </small>
<div style="padding-top: 10px">
{{ comment.body }}
</div>
</div>
</div>
{% endfor %}
</div>
<div class="feed-element">
<a href="" class="pull-left">
<img alt="image" class="img-circle" src="{% static 'img/avatar/user.png'%}" >
</a>
<div class="media-body">
<textarea class="form-control" placeholder="" id="comment"></textarea>
</div>
</div>
<div class="text-right">
{% block action %}
{% endblock %}
{% block status %}
<a class="btn btn-sm btn-danger btn-update btn-status" data-uid="close"><i class="fa fa-times"></i> {% trans 'Close' %}</a>
{% endblock %}
{% block comment %}
<a class="btn btn-sm btn-info btn-update btn-comment" data-uid="comment"><i class="fa fa-pencil"></i> {% trans 'Comment' %}</a>
{% endblock %}
</div>
</div>
</div>
</div>
</div>
</div>
<div class="col-lg-1">
</div>
</div>
</div>
</div>
</div>
</div>
</div>
{% endblock %}
{% block custom_foot_js %}
<script>
var ticketId = "{{ object.id }}";
var status = "{{ object.status }}";
var commentUrl = "{% url 'api-tickets:ticket-comment-list' ticket_id=object.id %}";
function createComment(successCallback) {
var commentText = $("#comment").val();
if (!commentText) {
return
}
var body = {
body: commentText,
ticket: ticketId,
};
var success = function () {
window.location.reload();
};
if (successCallback){
success = successCallback;
}
requestApi({
url: commentUrl,
data: JSON.stringify(body),
method: "POST",
success: success
})
}
$(document).ready(function () {
if (status !== "open") {
$('.btn-update').attr('disabled', '1')
}
})
.on('click', '.btn-comment', function () {
createComment();
})
</script>
{% endblock %}
......@@ -9,15 +9,11 @@ app_name = 'tickets'
router = DefaultRouter()
router.register('tickets', api.TicketViewSet, 'ticket')
router.register('tickets/(?P<ticket_id>[0-9a-zA-Z\-]{36})/comments', api.TicketCommentViewSet, 'ticket-comment')
router.register('login-confirm-tickets', api.LoginConfirmTicketViewSet, 'login-confirm-ticket')
router.register('tickets/<uuid:ticket_id>/comments/', api.CommentViewSet, 'ticket-comment')
urlpatterns = [
path('login-confirm-tickets/<uuid:pk>/actions/',
api.LoginConfirmTicketsCreateActionApi.as_view(),
name='login-confirm-ticket-create-action'
),
]
urlpatterns += router.urls
from django.views.generic import TemplateView, DetailView
from django.utils.translation import ugettext as _
from common.permissions import PermissionsMixin, IsOrgAdmin
from common.permissions import PermissionsMixin, IsValidUser
from .models import LoginConfirmTicket
from . import mixins
class LoginConfirmTicketListView(PermissionsMixin, TemplateView):
template_name = 'tickets/login_confirm_ticket_list.html'
permission_classes = (IsOrgAdmin,)
permission_classes = (IsValidUser,)
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
......@@ -18,12 +19,10 @@ class LoginConfirmTicketListView(PermissionsMixin, TemplateView):
return context
class LoginConfirmTicketDetailView(PermissionsMixin, DetailView):
class LoginConfirmTicketDetailView(PermissionsMixin, mixins.TicketMixin, DetailView):
template_name = 'tickets/login_confirm_ticket_detail.html'
permission_classes = (IsOrgAdmin,)
def get_queryset(self):
return LoginConfirmTicket.objects.filter(assignees=self.request.user)
queryset = LoginConfirmTicket.objects.all()
permission_classes = (IsValidUser,)
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
......
......@@ -13,11 +13,11 @@ from ..models import User, UserGroup
__all__ = [
'UserSerializer', 'UserPKUpdateSerializer', 'UserUpdateGroupSerializer',
'ChangeUserPasswordSerializer', 'ResetOTPSerializer',
'UserProfileSerializer',
]
class UserSerializer(BulkSerializerMixin, serializers.ModelSerializer):
can_update = serializers.SerializerMethodField()
can_delete = serializers.SerializerMethodField()
......@@ -135,3 +135,11 @@ class ResetOTPSerializer(serializers.Serializer):
def update(self, instance, validated_data):
pass
class UserProfileSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = [
'id', 'username', 'name', 'role', 'email'
]
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment