diff --git a/apps/authentication/api/login_confirm.py b/apps/authentication/api/login_confirm.py index 62350e5c9..57c6dff3f 100644 --- a/apps/authentication/api/login_confirm.py +++ b/apps/authentication/api/login_confirm.py @@ -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) + return Response(e.as_data(), status=400) - -class UserTicketCancelAuthApi(APIView): - permission_classes = () - - 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) diff --git a/apps/authentication/errors.py b/apps/authentication/errors.py index a6a73240f..5e7506e90 100644 --- a/apps/authentication/errors.py +++ b/apps/authentication/errors.py @@ -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) diff --git a/apps/authentication/mixins.py b/apps/authentication/mixins.py index b33d0fdae..3d6ca0321 100644 --- a/apps/authentication/mixins.py +++ b/apps/authentication/mixins.py @@ -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) diff --git a/apps/authentication/models.py b/apps/authentication/models.py index 913e04f73..cb4f97e4d 100644 --- a/apps/authentication/models.py +++ b/apps/authentication/models.py @@ -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) diff --git a/apps/authentication/serializers.py b/apps/authentication/serializers.py index 029c51c1e..f000c3438 100644 --- a/apps/authentication/serializers.py +++ b/apps/authentication/serializers.py @@ -1,10 +1,10 @@ # -*- 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 diff --git a/apps/authentication/templates/authentication/login_wait_confirm.html b/apps/authentication/templates/authentication/login_wait_confirm.html index 0b22cbd1c..e653fb072 100644 --- a/apps/authentication/templates/authentication/login_wait_confirm.html +++ b/apps/authentication/templates/authentication/login_wait_confirm.html @@ -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() { diff --git a/apps/authentication/urls/api_urls.py b/apps/authentication/urls/api_urls.py index a7e15053b..2da89a19f 100644 --- a/apps/authentication/urls/api_urls.py +++ b/apps/authentication/urls/api_urls.py @@ -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//', api.LoginConfirmSettingUpdateApi.as_view(), name='login-confirm-setting-update') ] diff --git a/apps/locale/zh/LC_MESSAGES/django.mo b/apps/locale/zh/LC_MESSAGES/django.mo index 2f408e224..b591fda3c 100644 Binary files a/apps/locale/zh/LC_MESSAGES/django.mo and b/apps/locale/zh/LC_MESSAGES/django.mo differ diff --git a/apps/locale/zh/LC_MESSAGES/django.po b/apps/locale/zh/LC_MESSAGES/django.po index fb4a66a48..00b3ac1c4 100644 --- a/apps/locale/zh/LC_MESSAGES/django.po +++ b/apps/locale/zh/LC_MESSAGES/django.po @@ -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 \n" "Language-Team: Jumpserver team\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 {} confirm, You also can copy link to her/him
\n" " Don't close this page" @@ -2732,15 +2715,15 @@ msgstr "" "等待 {} 确认, 你也可以复制链接发给他/她
\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" -"
\n" -"

Your has a new order

\n" -"
\n" -" Title: {order.title}\n" -"
\n" -" User: {user}\n" -"
\n" -" Assignees: {order.assignees_display}\n" -"
\n" -" City: {order.city}\n" -"
\n" -" IP: {order.ip}\n" -"
\n" -" click here to review \n" -"
\n" -"
\n" -" " -msgstr "" -"\n" -"
\n" -"

您有一个新工单

\n" -"
\n" -" 标题: {order.title}\n" -"
\n" -" 用户: {user}\n" -"
\n" -" 待处理人: {order.assignees_display}\n" -"
\n" -" 城市: {order.city}\n" -"
\n" -" IP: {order.ip}\n" -"
\n" -" 点我查看 \n" -"
\n" -"
\n" -" " - -#: orders/utils.py:48 -msgid "Order has been reply" -msgstr "工单已被回复" - -#: orders/utils.py:49 -#, python-brace-format -msgid "" -"\n" -"
\n" -"

Your order has been replay

\n" -"
\n" -" Title: {order.title}\n" -"
\n" -" Assignee: {order.assignee_display}\n" -"
\n" -" Status: {order.status_display}\n" -"
\n" -"
\n" -"
\n" -" " -msgstr "" -"\n" -"
\n" -"

您的工单已被回复

\n" -"
\n" -" 标题: {order.title}\n" -"
\n" -" 处理人: {order.assignee_display}\n" -"
\n" -" 状态: {order.status_display}\n" -"
\n" -"
\n" -"
\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: {}

{}" 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" +"
\n" +"

Your has a new ticket

\n" +"
\n" +" Title: {ticket.title}\n" +"
\n" +" User: {user}\n" +"
\n" +" Assignees: {ticket.assignees_display}\n" +"
\n" +" City: {ticket.city}\n" +"
\n" +" IP: {ticket.ip}\n" +"
\n" +" click here to review \n" +"
\n" +"
\n" +" " +msgstr "" +"\n" +"
\n" +"

您有一个新工单

\n" +"
\n" +" 标题: {ticket.title}\n" +"
\n" +" 用户: {user}\n" +"
\n" +" 待处理人: {ticket.assignees_display}\n" +"
\n" +" 城市: {ticket.city}\n" +"
\n" +" IP: {ticket.ip}\n" +"
\n" +" 点我查看 \n" +"
\n" +"
\n" +" " + +#: tickets/utils.py:48 +msgid "Ticket has been reply" +msgstr "工单已被回复" + +#: tickets/utils.py:49 +#, python-brace-format +msgid "" +"\n" +"
\n" +"

Your ticket has been replay

\n" +"
\n" +" Title: {ticket.title}\n" +"
\n" +" Assignee: {ticket.assignee_display}\n" +"
\n" +" Status: {ticket.status_display}\n" +"
\n" +"
\n" +"
\n" +" " +msgstr "" +"\n" +"
\n" +"

您的工单已被回复

\n" +"
\n" +" 标题: {ticket.title}\n" +"
\n" +" 处理人: {ticket.assignee_display}\n" +"
\n" +" 状态: {ticket.status_display}\n" +"
\n" +"
\n" +"
\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 "" "具)
注意: 如果同时设置了定期执行和周期执行,优先使用定期执行" #: 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 "您输入的用户名或密码不正确,请重新输入。" diff --git a/apps/static/js/jumpserver.js b/apps/static/js/jumpserver.js index aea8254f6..fdbbfd1f9 100644 --- a/apps/static/js/jumpserver.js +++ b/apps/static/js/jumpserver.js @@ -1317,3 +1317,7 @@ function initDateRangePicker(selector, options) { options = Object.assign(defaultOption, options); return $(selector).daterangepicker(options); } + +function reloadPage() { + window.location.reload(); +} diff --git a/apps/templates/_nav.html b/apps/templates/_nav.html index 2aecd2fd6..794d9b243 100644 --- a/apps/templates/_nav.html +++ b/apps/templates/_nav.html @@ -127,7 +127,7 @@ {% trans 'Tickets' %} {% endif %} diff --git a/apps/tickets/api/base.py b/apps/tickets/api/base.py index a029d8fab..266f9f855 100644 --- a/apps/tickets/api/base.py +++ b/apps/tickets/api/base.py @@ -1,21 +1,38 @@ # -*- 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): +class TicketViewSet(mixins.TicketMixin, viewsets.ModelViewSet): serializer_class = serializers.TicketSerializer - - def get_queryset(self): - queryset = models.Ticket.objects.all().none() - return queryset + 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 diff --git a/apps/tickets/api/login_confirm.py b/apps/tickets/api/login_confirm.py index c990ee339..599931a52 100644 --- a/apps/tickets/api/login_confirm.py +++ b/apps/tickets/api/login_confirm.py @@ -1,39 +1,30 @@ # -*- 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) diff --git a/apps/tickets/apps.py b/apps/tickets/apps.py index 3ea742ac3..d155cbde2 100644 --- a/apps/tickets/apps.py +++ b/apps/tickets/apps.py @@ -3,3 +3,7 @@ from django.apps import AppConfig class TicketsConfig(AppConfig): name = 'tickets' + + def ready(self): + from . import signals_handler + return super().ready() diff --git a/apps/tickets/mixins.py b/apps/tickets/mixins.py new file mode 100644 index 000000000..d648ffe19 --- /dev/null +++ b/apps/tickets/mixins.py @@ -0,0 +1,18 @@ +# -*- 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 diff --git a/apps/tickets/models/base.py b/apps/tickets/models/base.py index 3fcb702cf..d369e534f 100644 --- a/apps/tickets/models/base.py +++ b/apps/tickets/models/base.py @@ -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', '
') @@ -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', ) - - diff --git a/apps/tickets/models/login_confirm.py b/apps/tickets/models/login_confirm.py index 0781d20be..87baaefee 100644 --- a/apps/tickets/models/login_confirm.py +++ b/apps/tickets/models/login_confirm.py @@ -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() diff --git a/apps/tickets/permissions.py b/apps/tickets/permissions.py new file mode 100644 index 000000000..5bc7be14d --- /dev/null +++ b/apps/tickets/permissions.py @@ -0,0 +1,6 @@ +# -*- coding: utf-8 -*- +# + +from rest_framework.permissions import BasePermission + + diff --git a/apps/tickets/serializers/base.py b/apps/tickets/serializers/base.py index eb4040f59..e465fa691 100644 --- a/apps/tickets/serializers/base.py +++ b/apps/tickets/serializers/base.py @@ -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 = [ diff --git a/apps/tickets/serializers/login_confirm.py b/apps/tickets/serializers/login_confirm.py index 4649294ec..e7e224c1e 100644 --- a/apps/tickets/serializers/login_confirm.py +++ b/apps/tickets/serializers/login_confirm.py @@ -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 diff --git a/apps/tickets/signals_handler.py b/apps/tickets/signals_handler.py index a1b9dbbec..212892d0c 100644 --- a/apps/tickets/signals_handler.py +++ b/apps/tickets/signals_handler.py @@ -1,10 +1,10 @@ # -*- 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) diff --git a/apps/tickets/templates/tickets/login_confirm_ticket_detail.html b/apps/tickets/templates/tickets/login_confirm_ticket_detail.html index 2b43da756..e0cc25b0d 100644 --- a/apps/tickets/templates/tickets/login_confirm_ticket_detail.html +++ b/apps/tickets/templates/tickets/login_confirm_ticket_detail.html @@ -1,137 +1,34 @@ -{% extends 'base.html' %} +{% extends 'tickets/ticket_detail.html' %} {% load static %} {% load i18n %} -{% block content %} -
-
-
-
-
-
- {{ object.title }} -
- -
-
-
-
-
-
-
-
{% trans 'User' %}:
{{ object.user_display }}
-
{% trans 'IP' %}:
{{ object.ip }}
-
{% trans 'Assignees' %}:
{{ object.assignees_display }}
-
{% trans 'Status' %}:
-
- {% if object.status == "accpeted" %} - - {{ object.get_status_display }} - - {% endif %} - {% if object.status == "rejected" %} - - {{ object.get_status_display }} - - {% endif %} - {% if object.status == "pending" %} - - {{ object.get_status_display }} - - {% endif %} -
-
-
-
-
-

-
{% trans 'City' %}:
{{ object.city }}
-
{% trans 'Assignee' %}:
{{ object.assignee_display | default_if_none:"" }}
-
{% trans 'Date created' %}:
{{ object.date_created }}
-
-
-
-
-
-
-
-
- {% for comment in object.comments %} -
- - image - -
- {{ comment.user_display }} {{ comment.date_created|timesince}} {% trans 'ago' %} -
- {{ comment.date_created }} -
- {{ comment.body }} -
-
-
- {% endfor %} -
-
- - image - -
- -
-
- -
-
-
-
-
-
-
-
-
-
-
-
-
- +{% block status %} {% endblock %} + +{% block action %} + {% trans 'Approve' %} + {% trans 'Reject' %} +{% endblock %} + {% block custom_foot_js %} +{{ block.super }} {% endblock %} + + + diff --git a/apps/tickets/templates/tickets/login_confirm_ticket_list.html b/apps/tickets/templates/tickets/login_confirm_ticket_list.html index dd5129ea9..51a14b3ae 100644 --- a/apps/tickets/templates/tickets/login_confirm_ticket_list.html +++ b/apps/tickets/templates/tickets/login_confirm_ticket_list.html @@ -13,7 +13,6 @@ {% trans 'Title' %} {% trans 'User' %} - {% trans 'IP' %} {% trans 'Status' %} {% trans 'Datetime' %} {% trans 'Action' %} @@ -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('') } else if (cellData === "rejected") { @@ -53,12 +48,12 @@ function initTable() { $(td).html('') } }}, - {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 = '{% trans "Accept" %} '; + {targets: 5, createdCell: function (td, cellData, rowData) { + var acceptBtn = '{% trans "Approve" %} '; var rejectBtn = '{% trans "Reject" %}'; 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); }) diff --git a/apps/tickets/templates/tickets/ticket_detail.html b/apps/tickets/templates/tickets/ticket_detail.html new file mode 100644 index 000000000..320010398 --- /dev/null +++ b/apps/tickets/templates/tickets/ticket_detail.html @@ -0,0 +1,162 @@ +{% extends 'base.html' %} +{% load static %} +{% load i18n %} + +{% block content %} +
+
+
+
+
+
+ {{ object.title }} +
+ +
+
+
+
+
+
+
+
{% trans 'User' %}:
{{ object.user_display }}
+
{% trans 'Type' %}:
{{ object.get_type_display | default_if_none:"" }}
+
{% trans 'Status' %}:
+
+ {% if object.status == "open" %} + + {{ object.get_status_display }} + + {% elif object.status == "closed" %} + + {{ object.get_status_display }} + + {% endif %} +
+
+
+
+
+
{% trans 'Assignees' %}:
{{ object.assignees_display }}
+
{% trans 'Assignee' %}:
{{ object.assignee_display | default_if_none:"" }}
+
{% trans 'Date created' %}:
{{ object.date_created }}
+
+
+
+
+
+
+
+
+
+ + image + +
+ {{ object.user_display }} {{ object.date_created|timesince}} {% trans 'ago' %} +
+ {{ object.date_created }} +
+ {{ object.body_as_html | safe }} +
+
+
+ {% for comment in object.comments.all %} + +
+ + image + +
+ {{ comment.user_display }} {{ comment.date_created|timesince}} {% trans 'ago' %} +
+ {{ comment.date_created }} +
+ {{ comment.body }} +
+
+
+ {% endfor %} +
+
+ + image + +
+ +
+
+
+ {% block action %} + {% endblock %} + {% block status %} + {% trans 'Close' %} + {% endblock %} + {% block comment %} + {% trans 'Comment' %} + {% endblock %} +
+
+
+
+
+
+
+
+
+
+
+
+
+
+ +{% endblock %} +{% block custom_foot_js %} + +{% endblock %} diff --git a/apps/tickets/urls/api_urls.py b/apps/tickets/urls/api_urls.py index c998efda6..1972fb539 100644 --- a/apps/tickets/urls/api_urls.py +++ b/apps/tickets/urls/api_urls.py @@ -9,15 +9,11 @@ app_name = 'tickets' router = DefaultRouter() router.register('tickets', api.TicketViewSet, 'ticket') +router.register('tickets/(?P[0-9a-zA-Z\-]{36})/comments', api.TicketCommentViewSet, 'ticket-comment') router.register('login-confirm-tickets', api.LoginConfirmTicketViewSet, 'login-confirm-ticket') -router.register('tickets//comments/', api.CommentViewSet, 'ticket-comment') urlpatterns = [ - path('login-confirm-tickets//actions/', - api.LoginConfirmTicketsCreateActionApi.as_view(), - name='login-confirm-ticket-create-action' - ), ] urlpatterns += router.urls diff --git a/apps/tickets/views.py b/apps/tickets/views.py index 3816f8097..aac6b136c 100644 --- a/apps/tickets/views.py +++ b/apps/tickets/views.py @@ -1,13 +1,14 @@ 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) diff --git a/apps/users/serializers/user.py b/apps/users/serializers/user.py index 7e25112d6..b7ec186b7 100644 --- a/apps/users/serializers/user.py +++ b/apps/users/serializers/user.py @@ -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' + ]