From c244cf5f430ccd328d09e5660cf49eca84f08af7 Mon Sep 17 00:00:00 2001 From: fit2bot <68588906+fit2bot@users.noreply.github.com> Date: Fri, 22 Oct 2021 20:06:16 +0800 Subject: [PATCH] =?UTF-8?q?pref:=20=E4=BF=AE=E6=94=B9=E4=BD=BF=E7=94=A8?= =?UTF-8?q?=E7=9A=84=E6=B6=88=E6=81=AF=E5=86=85=E5=AE=B9=20(#7061)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * perf: 再次优化通知 * pref: 修改使用的消息内容 * perf: 修复url地址 Co-authored-by: ibuler --- .../authentication/_msg_different_city.html | 4 +- .../authentication/_msg_reset_password.html | 30 +- .../_msg_rest_password_success.html | 16 +- apps/common/utils/timezone.py | 4 + apps/jumpserver/api.py | 1 - apps/jumpserver/context_processor.py | 36 +- apps/locale/zh/LC_MESSAGES/django.mo | 4 +- apps/locale/zh/LC_MESSAGES/django.po | 370 ++++++++++-------- apps/notifications/api/notifications.py | 45 ++- apps/notifications/notifications.py | 71 +++- apps/notifications/urls/api_urls.py | 6 + apps/ops/notifications.py | 85 ++-- .../ops/_msg_terminal_performance.html | 10 + apps/perms/notifications.py | 11 +- apps/perms/tasks.py | 4 +- .../perms/_msg_permed_items_expire.html | 5 +- apps/settings/api/public.py | 29 +- apps/settings/utils/common.py | 13 + apps/terminal/models/terminal.py | 7 +- apps/terminal/notifications.py | 45 ++- .../terminal/_msg_command_alert.html | 29 +- .../terminal/_msg_command_execute_alert.html | 18 +- apps/tickets/notifications.py | 1 + apps/users/notifications.py | 43 +- .../users/_msg_password_expire_reminder.html | 6 +- .../users/templates/users/_msg_reset_mfa.html | 10 +- .../templates/users/_msg_reset_ssh_key.html | 10 +- .../templates/users/_msg_user_created.html | 20 + apps/users/utils.py | 55 +-- 29 files changed, 569 insertions(+), 419 deletions(-) create mode 100644 apps/ops/templates/ops/_msg_terminal_performance.html create mode 100644 apps/users/templates/users/_msg_user_created.html diff --git a/apps/authentication/templates/authentication/_msg_different_city.html b/apps/authentication/templates/authentication/_msg_different_city.html index 16ff90fbc..b12e9aab6 100644 --- a/apps/authentication/templates/authentication/_msg_different_city.html +++ b/apps/authentication/templates/authentication/_msg_different_city.html @@ -12,7 +12,5 @@

- - {% trans 'If you suspect that the login behavior is abnormal, please modify the account password in time.' %} - + {% trans 'If you suspect that the login behavior is abnormal, please modify the account password in time.' %}

\ No newline at end of file diff --git a/apps/authentication/templates/authentication/_msg_reset_password.html b/apps/authentication/templates/authentication/_msg_reset_password.html index 9388f2388..036eea542 100644 --- a/apps/authentication/templates/authentication/_msg_reset_password.html +++ b/apps/authentication/templates/authentication/_msg_reset_password.html @@ -1,15 +1,17 @@ {% load i18n %} -{% trans 'Hello' %} {{ user.name }}, -
-{% trans 'Please click the link below to reset your password, if not your request, concern your account security' %} -
-
-{% trans 'Click here reset password' %} -
-
-{% trans 'This link is valid for 1 hour. After it expires,' %} {% trans 'request new one' %} -
---- -
-{% trans 'Login direct' %} -
+

+ {% trans 'Hello' %} {{ user.name }}, +

+

+ {% trans 'Please click the link below to reset your password, if not your request, concern your account security' %} +
+
+ {% trans 'Click here reset password' %} +

+ +

+ {% trans 'This link is valid for 1 hour. After it expires' %} + + {% trans 'request new one' %} + +

diff --git a/apps/authentication/templates/authentication/_msg_rest_password_success.html b/apps/authentication/templates/authentication/_msg_rest_password_success.html index 424396473..a12d611f7 100644 --- a/apps/authentication/templates/authentication/_msg_rest_password_success.html +++ b/apps/authentication/templates/authentication/_msg_rest_password_success.html @@ -1,18 +1,14 @@ {% load i18n %} - -

{% trans 'Hello' %}: {{ name }},

+

{% trans 'Hello' %} {{ name }},

- {% trans 'Your password has just been successfully updated.' %} + {% trans 'Your password has just been successfully updated' %}

- {% trans 'IP' %}: {{ ip_address }}
- {% trans 'Browser' %}: {{ browser }} + {% trans 'IP' %}: {{ ip_address }}
+ {% trans 'Browser' %}: {{ browser }}

-

---

- - {% trans 'If the password update was not initiated by you, your account may have security issues.' %}
- {% trans 'If you have any questions, you can contact the administrator.' %} -
+ {% trans 'If the password update was not initiated by you, your account may have security issues' %}
+ {% trans 'If you have any questions, you can contact the administrator' %}

diff --git a/apps/common/utils/timezone.py b/apps/common/utils/timezone.py index 037aad62c..6848e7428 100644 --- a/apps/common/utils/timezone.py +++ b/apps/common/utils/timezone.py @@ -28,6 +28,10 @@ def local_now(): return as_current_tz(utc_now()) +def local_now_display(fmt='%Y-%m-%d %H:%M:%S'): + return local_now().strftime(fmt) + + _rest_dt_field = DateTimeField() dt_parser = _rest_dt_field.to_internal_value dt_formatter = _rest_dt_field.to_representation diff --git a/apps/jumpserver/api.py b/apps/jumpserver/api.py index e67bff1cd..3e7c4f19c 100644 --- a/apps/jumpserver/api.py +++ b/apps/jumpserver/api.py @@ -8,7 +8,6 @@ from django.http.response import JsonResponse, HttpResponse from rest_framework.views import APIView from rest_framework.permissions import AllowAny from collections import Counter -from django.conf import settings from rest_framework.response import Response from users.models import User diff --git a/apps/jumpserver/context_processor.py b/apps/jumpserver/context_processor.py index 93c20c6f3..51a077cca 100644 --- a/apps/jumpserver/context_processor.py +++ b/apps/jumpserver/context_processor.py @@ -4,27 +4,39 @@ from django.templatetags.static import static from django.conf import settings from django.utils.translation import ugettext as _ +default_context = { + 'DEFAULT_PK': '00000000-0000-0000-0000-000000000000', + 'LOGO_URL': static('img/logo.png'), + 'LOGO_TEXT_URL': static('img/logo_text.png'), + 'LOGIN_IMAGE_URL': static('img/login_image.jpg'), + 'FAVICON_URL': static('img/facio.ico'), + 'LOGIN_CAS_LOGO_URL': static('img/login_cas_logo.png'), + 'LOGIN_WECOM_LOGO_URL': static('img/login_wecom_logo.png'), + 'LOGIN_DINGTALK_LOGO_URL': static('img/login_dingtalk_logo.png'), + 'LOGIN_FEISHU_LOGO_URL': static('img/login_feishu_logo.png'), + 'JMS_TITLE': _('JumpServer Open Source Bastion Host'), +} + +default_interface = { + 'login_title': default_context['JMS_TITLE'], + 'logo_logout': default_context['LOGO_URL'], + 'logo_index': default_context['LOGO_TEXT_URL'], + 'login_image': default_context['LOGIN_IMAGE_URL'], + 'favicon': default_context['FAVICON_URL'], +} + def jumpserver_processor(request): # Setting default pk - context = { - 'DEFAULT_PK': '00000000-0000-0000-0000-000000000000', - 'LOGO_URL': static('img/logo.png'), - 'LOGO_TEXT_URL': static('img/logo_text.png'), - 'LOGIN_IMAGE_URL': static('img/login_image.jpg'), - 'FAVICON_URL': static('img/facio.ico'), - 'LOGIN_CAS_LOGO_URL': static('img/login_cas_logo.png'), - 'LOGIN_WECOM_LOGO_URL': static('img/login_wecom_logo.png'), - 'LOGIN_DINGTALK_LOGO_URL': static('img/login_dingtalk_logo.png'), - 'LOGIN_FEISHU_LOGO_URL': static('img/login_feishu_logo.png'), - 'JMS_TITLE': _('JumpServer Open Source Bastion Host'), + context = default_context + context.update({ 'VERSION': settings.VERSION, 'COPYRIGHT': 'FIT2CLOUD 飞致云' + ' © 2014-2021', 'SECURITY_COMMAND_EXECUTION': settings.SECURITY_COMMAND_EXECUTION, 'SECURITY_MFA_VERIFY_TTL': settings.SECURITY_MFA_VERIFY_TTL, 'FORCE_SCRIPT_NAME': settings.FORCE_SCRIPT_NAME, 'SECURITY_VIEW_AUTH_NEED_MFA': settings.SECURITY_VIEW_AUTH_NEED_MFA, - } + }) return context diff --git a/apps/locale/zh/LC_MESSAGES/django.mo b/apps/locale/zh/LC_MESSAGES/django.mo index 4944330cb..555ebb9f5 100644 --- a/apps/locale/zh/LC_MESSAGES/django.mo +++ b/apps/locale/zh/LC_MESSAGES/django.mo @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:b5a90a96a51ef2f38d4530f3e5b79cd59b07dbca39f60bc35cde5ee0e364b210 -size 91089 +oid sha256:b2663d90ceadd419c40abd472a3b5030b007b1e1db1fcaeda9893ff90cd7cdd6 +size 89955 diff --git a/apps/locale/zh/LC_MESSAGES/django.po b/apps/locale/zh/LC_MESSAGES/django.po index ae72cdc87..c2945ae51 100644 --- a/apps/locale/zh/LC_MESSAGES/django.po +++ b/apps/locale/zh/LC_MESSAGES/django.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: JumpServer 0.3.3\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2021-10-21 18:50+0800\n" +"POT-Creation-Date: 2021-10-22 19:15+0800\n" "PO-Revision-Date: 2021-05-20 10:54+0800\n" "Last-Translator: ibuler \n" "Language-Team: JumpServer team\n" @@ -85,8 +85,7 @@ msgstr "登录复核" #: orgs/models.py:19 orgs/models.py:433 perms/models/base.py:45 #: templates/index.html:78 terminal/backends/command/models.py:18 #: terminal/backends/command/serializers.py:12 terminal/models/session.py:38 -#: terminal/templates/terminal/_msg_command_alert.html:10 -#: terminal/templates/terminal/_msg_command_execute_alert.html:4 +#: terminal/notifications.py:91 terminal/notifications.py:136 #: tickets/models/comment.py:17 users/const.py:14 users/models/user.py:173 #: users/models/user.py:801 users/models/user.py:827 #: users/serializers/group.py:19 @@ -128,7 +127,7 @@ msgstr "系统用户" #: audits/models.py:38 perms/models/asset_permission.py:99 #: templates/index.html:82 terminal/backends/command/models.py:19 #: terminal/backends/command/serializers.py:13 terminal/models/session.py:40 -#: terminal/templates/terminal/_msg_command_alert.html:7 +#: terminal/notifications.py:90 #: users/templates/users/user_asset_permission.html:40 #: users/templates/users/user_asset_permission.html:70 #: xpack/plugins/change_auth_plan/models/asset.py:195 @@ -154,6 +153,7 @@ msgstr "格式为逗号分隔的字符串, * 表示匹配所有. " #: audits/models.py:105 authentication/forms.py:15 authentication/forms.py:17 #: authentication/templates/authentication/_msg_different_city.html:9 #: ops/models/adhoc.py:148 users/forms/profile.py:31 users/models/user.py:595 +#: users/templates/users/_msg_user_created.html:10 #: users/templates/users/_select_user_modal.html:14 #: xpack/plugins/change_auth_plan/models/asset.py:35 #: xpack/plugins/change_auth_plan/models/asset.py:191 @@ -174,7 +174,7 @@ msgstr "" #: applications/serializers/attrs/application_type/mysql_workbench.py:18 #: assets/models/asset.py:180 assets/models/domain.py:61 #: assets/serializers/account.py:12 -#: authentication/templates/authentication/_msg_rest_password_success.html:9 +#: authentication/templates/authentication/_msg_rest_password_success.html:8 #: settings/serializers/terminal.py:8 #: users/templates/users/_granted_assets.html:26 #: users/templates/users/user_asset_permission.html:156 @@ -384,6 +384,7 @@ msgstr "目标URL" #: authentication/forms.py:22 #: authentication/templates/authentication/login.html:151 #: settings/serializers/auth/ldap.py:44 users/forms/profile.py:21 +#: users/templates/users/_msg_user_created.html:11 #: users/templates/users/user_otp_check_password.html:13 #: users/templates/users/user_password_update.html:43 #: users/templates/users/user_password_verify.html:18 @@ -647,7 +648,7 @@ msgstr "正则表达式" #: assets/models/cmd_filter.py:41 ops/models/command.py:25 #: terminal/backends/command/serializers.py:15 terminal/models/session.py:49 -#: terminal/templates/terminal/_msg_command_alert.html:4 +#: terminal/templates/terminal/_msg_command_alert.html:12 #: terminal/templates/terminal/_msg_command_execute_alert.html:10 msgid "Command" msgstr "命令" @@ -1445,7 +1446,7 @@ msgstr "{ApplicationPermission} 添加 {SystemUser}" msgid "{ApplicationPermission} REMOVE {SystemUser}" msgstr "{ApplicationPermission} 移除 {SystemUser}" -#: authentication/api/connection_token.py:235 +#: authentication/api/connection_token.py:239 msgid "Invalid token" msgstr "无效的令牌" @@ -1731,6 +1732,7 @@ msgid "Secret" msgstr "密钥" #: authentication/templates/authentication/_access_key_modal.html:33 +#: terminal/notifications.py:93 terminal/notifications.py:138 msgid "Date" msgstr "日期" @@ -1788,8 +1790,8 @@ msgid "Code error" msgstr "代码错误" #: authentication/templates/authentication/_msg_different_city.html:3 -#: authentication/templates/authentication/_msg_reset_password.html:2 -#: authentication/templates/authentication/_msg_rest_password_success.html:3 +#: authentication/templates/authentication/_msg_reset_password.html:3 +#: authentication/templates/authentication/_msg_rest_password_success.html:2 #: perms/templates/perms/_msg_item_permissions_expire.html:3 #: perms/templates/perms/_msg_permed_items_expire.html:3 #: users/templates/users/_msg_account_expire_reminder.html:4 @@ -1807,53 +1809,48 @@ msgstr "你的账号存在异地登录行为,请关注。" msgid "Login time" msgstr "登录日期" -#: authentication/templates/authentication/_msg_different_city.html:16 +#: authentication/templates/authentication/_msg_different_city.html:15 msgid "" "If you suspect that the login behavior is abnormal, please modify the " "account password in time." msgstr "若怀疑此次登录行为异常,请及时修改账号密码" -#: authentication/templates/authentication/_msg_reset_password.html:4 +#: authentication/templates/authentication/_msg_reset_password.html:6 msgid "" "Please click the link below to reset your password, if not your request, " "concern your account security" msgstr "请点击下面链接重置密码, 如果不是您申请的,请关注账号安全" -#: authentication/templates/authentication/_msg_reset_password.html:7 +#: authentication/templates/authentication/_msg_reset_password.html:9 msgid "Click here reset password" msgstr "点击这里重置密码" -#: authentication/templates/authentication/_msg_reset_password.html:10 -msgid "This link is valid for 1 hour. After it expires," +#: authentication/templates/authentication/_msg_reset_password.html:13 +#: users/templates/users/_msg_user_created.html:17 +msgid "This link is valid for 1 hour. After it expires" msgstr "这个链接有效期1小时, 超过时间您可以" -#: authentication/templates/authentication/_msg_reset_password.html:10 +#: authentication/templates/authentication/_msg_reset_password.html:15 +#: users/templates/users/_msg_user_created.html:18 msgid "request new one" msgstr "重新申请" -#: authentication/templates/authentication/_msg_reset_password.html:14 -#: users/templates/users/_msg_password_expire_reminder.html:22 -#: users/templates/users/_msg_reset_mfa.html:11 -#: users/templates/users/_msg_reset_ssh_key.html:11 -msgid "Login direct" -msgstr "直接登录" - -#: authentication/templates/authentication/_msg_rest_password_success.html:6 -msgid "Your password has just been successfully updated." -msgstr "你的密码刚刚已经成功更新。" +#: authentication/templates/authentication/_msg_rest_password_success.html:5 +msgid "Your password has just been successfully updated" +msgstr "你的密码刚刚成功更新" -#: authentication/templates/authentication/_msg_rest_password_success.html:10 +#: authentication/templates/authentication/_msg_rest_password_success.html:9 msgid "Browser" msgstr "浏览器" -#: authentication/templates/authentication/_msg_rest_password_success.html:15 +#: authentication/templates/authentication/_msg_rest_password_success.html:12 msgid "" "If the password update was not initiated by you, your account may have " -"security issues." -msgstr "如果这次密码更新不是由你发起的,那么你的账号可能存在安全问题。" +"security issues" +msgstr "如果这次密码更新不是由你发起的,那么你的账号可能存在安全问题" -#: authentication/templates/authentication/_msg_rest_password_success.html:16 -msgid "If you have any questions, you can contact the administrator." +#: authentication/templates/authentication/_msg_rest_password_success.html:13 +msgid "If you have any questions, you can contact the administrator" msgstr "如果有疑问或需求,请联系系统管理员" #: authentication/templates/authentication/login.html:143 @@ -2206,7 +2203,7 @@ msgstr "不能包含特殊字符" msgid "The mobile phone number format is incorrect" msgstr "手机号格式不正确" -#: jumpserver/context_processor.py:20 +#: jumpserver/context_processor.py:17 msgid "JumpServer Open Source Bastion Host" msgstr "JumpServer 开源堡垒机" @@ -2246,7 +2243,7 @@ msgstr "邮件" msgid "Site message" msgstr "站内信" -#: notifications/notifications.py:148 ops/models/adhoc.py:246 +#: notifications/notifications.py:167 ops/models/adhoc.py:246 #: xpack/plugins/change_auth_plan/models/base.py:108 #: xpack/plugins/change_auth_plan/models/base.py:190 #: xpack/plugins/gathered_user/models.py:79 @@ -2261,7 +2258,7 @@ msgstr "等待任务开始" msgid "Not has host {} permission" msgstr "没有该主机 {} 权限" -#: ops/apps.py:9 ops/notifications.py:14 +#: ops/apps.py:9 ops/notifications.py:15 msgid "Operations" msgstr "运维" @@ -2383,29 +2380,33 @@ msgstr "命令 `{}` 不允许被执行 ......." msgid "Task end" msgstr "任务结束" -#: ops/notifications.py:15 +#: ops/notifications.py:16 msgid "Server performance" msgstr "监控告警" -#: ops/notifications.py:54 +#: ops/notifications.py:22 +msgid "Terminal health check warning" +msgstr "" + +#: ops/notifications.py:63 #, python-brace-format msgid "The terminal is offline: {name}" msgstr "终端已离线: {name}" -#: ops/notifications.py:60 +#: ops/notifications.py:68 #, python-brace-format -msgid "Disk used more than {max_threshold}%: => {value} ({name})" -msgstr "硬盘使用率超过 {max_threshold}%: => {value} ({name})" +msgid "Disk used more than {max_threshold}%: => {value}" +msgstr "硬盘使用率超过 {max_threshold}%: => {value}" -#: ops/notifications.py:67 +#: ops/notifications.py:73 #, python-brace-format -msgid "Memory used more than {max_threshold}%: => {value} ({name})" -msgstr "内存使用率超过 {max_threshold}%: => {value} ({name})" +msgid "Memory used more than {max_threshold}%: => {value}" +msgstr "内存使用率超过 {max_threshold}%: => {value}" -#: ops/notifications.py:74 +#: ops/notifications.py:78 #, python-brace-format -msgid "CPU load more than {max_threshold}: => {value} ({name})" -msgstr "CPU 使用率超过 {max_threshold}: => {value} ({name})" +msgid "CPU load more than {max_threshold}: => {value}" +msgstr "CPU 使用率超过 {max_threshold}: => {value}" #: ops/tasks.py:71 msgid "Clean task history period" @@ -2533,35 +2534,35 @@ msgstr "失效日期" msgid "From ticket" msgstr "来自工单" -#: perms/notifications.py:19 +#: perms/notifications.py:15 msgid "You permed assets is about to expire" msgstr "你授权的资产即将到期" -#: perms/notifications.py:23 +#: perms/notifications.py:19 msgid "permed assets" msgstr "授权的资产" -#: perms/notifications.py:61 +#: perms/notifications.py:57 msgid "Asset permissions is about to expire" msgstr "资产授权规则将要过期" -#: perms/notifications.py:65 +#: perms/notifications.py:61 msgid "asset permissions of organization {}" msgstr "组织 ({}) 的资产授权" -#: perms/notifications.py:91 +#: perms/notifications.py:87 msgid "Your permed applications is about to expire" msgstr "你授权的应用即将过期" -#: perms/notifications.py:94 +#: perms/notifications.py:90 msgid "permed applications" msgstr "授权的应用" -#: perms/notifications.py:132 +#: perms/notifications.py:128 msgid "Application permissions is about to expire" msgstr "应用授权规则即将过期" -#: perms/notifications.py:135 +#: perms/notifications.py:131 msgid "application permissions of organization {}" msgstr "组织 ({}) 的应用授权" @@ -2633,7 +2634,7 @@ msgstr "" " 以下 %(item_type)s 即将在 3 天后过期\n" " " -#: perms/templates/perms/_msg_permed_items_expire.html:22 +#: perms/templates/perms/_msg_permed_items_expire.html:20 msgid "If you have any question, please contact the administrator" msgstr "如果有疑问或需求,请联系系统管理员" @@ -2671,11 +2672,6 @@ msgstr "获取 LDAP 用户为 None" msgid "Imported {} users successfully (Organization: {})" msgstr "成功导入 {} 个用户 ( 组织: {} )" -#: settings/api/public.py:41 xpack/plugins/interface/api.py:18 -#: xpack/plugins/interface/models.py:36 -msgid "Welcome to the JumpServer open source Bastion Host" -msgstr "欢迎使用JumpServer开源堡垒机" - #: settings/models.py:191 users/templates/users/reset_password.html:29 msgid "Setting" msgstr "设置" @@ -3725,7 +3721,7 @@ msgstr "数据库应用" msgid "Perms" msgstr "权限管理" -#: templates/_nav.html:97 terminal/notifications.py:22 +#: templates/_nav.html:97 terminal/notifications.py:24 msgid "Sessions" msgstr "会话管理" @@ -4068,7 +4064,7 @@ msgstr "输出" #: terminal/backends/command/models.py:23 terminal/models/sharing.py:15 #: terminal/models/sharing.py:58 -#: terminal/templates/terminal/_msg_command_alert.html:16 +#: terminal/templates/terminal/_msg_command_alert.html:10 msgid "Session" msgstr "会话" @@ -4235,11 +4231,15 @@ msgstr "命令存储" msgid "Replay storage" msgstr "录像存储" -#: terminal/notifications.py:68 +#: terminal/notifications.py:70 msgid "Danger command alert" msgstr "危险命令告警" -#: terminal/notifications.py:106 +#: terminal/notifications.py:92 terminal/notifications.py:137 +msgid "Level" +msgstr "级别" + +#: terminal/notifications.py:110 msgid "Batch danger command alert" msgstr "批量危险命令告警" @@ -4348,12 +4348,7 @@ msgstr "" msgid "Not found" msgstr "没有发现" -#: terminal/templates/terminal/_msg_command_alert.html:13 -#: terminal/templates/terminal/_msg_command_execute_alert.html:7 -msgid "Level" -msgstr "级别" - -#: terminal/templates/terminal/_msg_command_alert.html:16 +#: terminal/templates/terminal/_msg_command_alert.html:10 msgid "view" msgstr "查看" @@ -4445,7 +4440,7 @@ msgstr "申请的开始日期" msgid "Applied date expired" msgstr "申请的失效日期" -#: tickets/handler/apply_application.py:79 tickets/handler/apply_asset.py:72 +#: tickets/handler/apply_application.py:79 msgid "" "Created by the ticket, ticket title: {}, ticket applicant: {}, ticket " "processor: {}, ticket ID: {}" @@ -4460,87 +4455,94 @@ msgstr "申请的主机名组" msgid "Applied actions" msgstr "申请的动作" +#: tickets/handler/apply_asset.py:72 +msgid "" +"Created by the ticket ticket title: {} ticket applicant: {} ticket " +"processor: {} ticket ID: {}" +msgstr "" +"通过工单创建, 工单标题: {}, 工单申请人: {}, 工单处理人: {}, 工单 ID: {}" + #: tickets/handler/base.py:86 msgid "{} {} the ticket" -msgstr "{} {}工单" +msgstr "{} {} 工单" -#: tickets/handler/base.py:113 +#: tickets/handler/base.py:114 msgid "Ticket title" msgstr "工单标题" -#: tickets/handler/base.py:114 +#: tickets/handler/base.py:115 msgid "Ticket type" msgstr "工单类型" -#: tickets/handler/base.py:115 +#: tickets/handler/base.py:116 msgid "Ticket status" msgstr "工单状态" -#: tickets/handler/base.py:116 +#: tickets/handler/base.py:117 msgid "Ticket applicant" msgstr "工单申请人" -#: tickets/handler/base.py:118 +#: tickets/handler/base.py:119 msgid "Ticket basic info" msgstr "工单基本信息" -#: tickets/handler/base.py:129 +#: tickets/handler/base.py:130 msgid "No content" msgstr "无内容" -#: tickets/handler/base.py:131 +#: tickets/handler/base.py:132 msgid "Ticket applied info" msgstr "工单申请信息" -#: tickets/handler/command_confirm.py:24 +#: tickets/handler/command_confirm.py:25 msgid "Applied run user" msgstr "申请运行的用户" -#: tickets/handler/command_confirm.py:25 +#: tickets/handler/command_confirm.py:26 msgid "Applied run asset" msgstr "申请运行的资产" -#: tickets/handler/command_confirm.py:26 +#: tickets/handler/command_confirm.py:27 msgid "Applied run system user" msgstr "申请运行的系统用户" -#: tickets/handler/command_confirm.py:27 +#: tickets/handler/command_confirm.py:28 msgid "Applied run command" msgstr "申请运行的命令" -#: tickets/handler/command_confirm.py:28 +#: tickets/handler/command_confirm.py:29 msgid "Applied from session" msgstr "申请来自会话" -#: tickets/handler/command_confirm.py:29 +#: tickets/handler/command_confirm.py:30 msgid "Applied from command filter rules" msgstr "申请来自命令过滤规则" -#: tickets/handler/command_confirm.py:30 +#: tickets/handler/command_confirm.py:31 msgid "Applied from command filter" msgstr "申请来自命令过滤规则" -#: tickets/handler/login_asset_confirm.py:16 +#: tickets/handler/login_asset_confirm.py:17 msgid "Applied login user" msgstr "申请登录的用户" -#: tickets/handler/login_asset_confirm.py:17 +#: tickets/handler/login_asset_confirm.py:18 msgid "Applied login asset" msgstr "申请登录的资产" -#: tickets/handler/login_asset_confirm.py:18 +#: tickets/handler/login_asset_confirm.py:19 msgid "Applied login system user" msgstr "申请登录的系统用户" -#: tickets/handler/login_confirm.py:16 +#: tickets/handler/login_confirm.py:17 msgid "Applied login IP" msgstr "申请登录的IP" -#: tickets/handler/login_confirm.py:17 +#: tickets/handler/login_confirm.py:18 msgid "Applied login city" msgstr "申请登录的城市" -#: tickets/handler/login_confirm.py:18 +#: tickets/handler/login_confirm.py:19 msgid "Applied login datetime" msgstr "申请登录的日期" @@ -4741,7 +4743,8 @@ msgstr "请选择受理人" msgid "The current organization type already exists" msgstr "当前组织已存在该类型" -#: tickets/templates/tickets/_msg_ticket.html:11 +#: tickets/templates/tickets/_base_ticket_body.html:17 +#: tickets/templates/tickets/_msg_ticket.html:12 msgid "Click here to review" msgstr "点击查看" @@ -4901,30 +4904,38 @@ msgstr "管理员" msgid "Administrator is the super user of system" msgstr "Administrator是初始的超级管理员" -#: users/notifications.py:20 +#: users/notifications.py:15 +msgid "Create account successfully" +msgstr "创建账户成功" + +#: users/notifications.py:19 +msgid "Hello {}" +msgstr "你好" + +#: users/notifications.py:51 #: users/templates/users/_msg_password_expire_reminder.html:17 #: users/templates/users/reset_password.html:5 #: users/templates/users/reset_password.html:6 msgid "Reset password" msgstr "重置密码" -#: users/notifications.py:50 users/views/profile/reset.py:127 +#: users/notifications.py:81 users/views/profile/reset.py:127 msgid "Reset password success" msgstr "重置密码成功" -#: users/notifications.py:78 +#: users/notifications.py:110 msgid "Password is about expire" msgstr "密码即将过期" -#: users/notifications.py:105 +#: users/notifications.py:137 msgid "Account is about expire" msgstr "账号即将过期" -#: users/notifications.py:127 +#: users/notifications.py:161 msgid "Reset SSH Key" msgstr "重置 SSH 密钥" -#: users/notifications.py:147 +#: users/notifications.py:181 msgid "Reset MFA" msgstr "重置 MFA" @@ -5073,28 +5084,34 @@ msgid "Click here update password" msgstr "点击这里更新密码" #: users/templates/users/_msg_password_expire_reminder.html:16 -msgid "If your password has expired, please click" -msgstr "如果你的密码已过期,先点击" - -#: users/templates/users/_msg_password_expire_reminder.html:18 -msgid "to apply for a password reset email." -msgstr "申请重置" +msgid "If your password has expired, please click the link below to" +msgstr "如果你的密码已过期,请点击" #: users/templates/users/_msg_reset_mfa.html:7 -msgid "Your MFA has been reset by site administrator." -msgstr "你的 MFA 已经被管理员重置。" +msgid "Your MFA has been reset by site administrator" +msgstr "你的 MFA 已经被管理员重置" #: users/templates/users/_msg_reset_mfa.html:8 -msgid "Please login and reset your MFA." -msgstr "请登录并重新设置你的 MFA" +#: users/templates/users/_msg_reset_ssh_key.html:8 +msgid "Please click the link below to set" +msgstr "请点击下面链接设置" + +#: users/templates/users/_msg_reset_mfa.html:11 +#: users/templates/users/_msg_reset_ssh_key.html:11 +msgid "Click here set" +msgstr "点击这里设置" #: users/templates/users/_msg_reset_ssh_key.html:7 -msgid "Your ssh public key has been reset by site administrator." -msgstr "你的 SSH 密钥已经被管理员重置。" +msgid "Your ssh public key has been reset by site administrator" +msgstr "你的 SSH 密钥已经被管理员重置" -#: users/templates/users/_msg_reset_ssh_key.html:8 -msgid "Please login and reset your ssh public key." -msgstr "请登录并重新设置你的密钥" +#: users/templates/users/_msg_user_created.html:8 +msgid "Your account has been created successfully" +msgstr "您的账户已创建成功" + +#: users/templates/users/_msg_user_created.html:13 +msgid "click here to set your password" +msgstr "点击这里设置密码" #: users/templates/users/_select_user_modal.html:5 msgid "Please Select User" @@ -5279,56 +5296,6 @@ msgstr "账号保护已开启,请根据提示完成以下操作" msgid "Open MFA Authenticator and enter the 6-bit dynamic code" msgstr "请打开MFA验证器,输入6位动态码" -# msgid "Update user" -# msgstr "更新用户" -#: users/utils.py:23 -#, python-format -msgid "" -"\n" -"
\n" -"

Your account has been created successfully

\n" -"
\n" -" Username: %(username)s\n" -"
\n" -" Password: \n" -" click here to set your password \n" -" (This link is valid for 1 hour. After it expires, request new one)\n" -"
\n" -"
\n" -"

---

\n" -" Login direct\n" -"
\n" -"
\n" -" " -msgstr "" -"\n" -"
\n" -"

您的账户已创建成功

\n" -"
\n" -" 用户名: %(username)s\n" -"
\n" -" 密码: 请点击这里设置密码 (这个链接有效期1小时, 超过时" -"间您可以 重新申请)\n" -"
\n" -"
\n" -"

---

\n" -" 直接登录\n" -"
\n" -"
\n" -" " - -#: users/utils.py:57 -msgid "Create account successfully" -msgstr "创建账户成功" - -#: users/utils.py:61 -#, python-format -msgid "Hello %(name)s" -msgstr "你好 %(name)s" - #: users/views/profile/otp.py:122 users/views/profile/otp.py:161 #: users/views/profile/otp.py:181 msgid "MFA code invalid, or ntp sync server time" @@ -5923,11 +5890,11 @@ msgstr "资产为空,请更改节点" msgid "Executed times" msgstr "执行次数" -#: xpack/plugins/interface/api.py:68 +#: xpack/plugins/interface/api.py:43 msgid "It is already in the default setting state!" msgstr "当前已经是初始化状态!" -#: xpack/plugins/interface/api.py:72 +#: xpack/plugins/interface/api.py:46 msgid "Restore default successfully." msgstr "恢复默认成功!" @@ -5935,23 +5902,23 @@ msgstr "恢复默认成功!" msgid "Interface settings" msgstr "界面设置" -#: xpack/plugins/interface/models.py:15 +#: xpack/plugins/interface/models.py:16 msgid "Title of login page" msgstr "登录页面标题" -#: xpack/plugins/interface/models.py:19 +#: xpack/plugins/interface/models.py:20 msgid "Image of login page" msgstr "登录页面图片" -#: xpack/plugins/interface/models.py:23 +#: xpack/plugins/interface/models.py:24 msgid "Website icon" msgstr "网站图标" -#: xpack/plugins/interface/models.py:27 +#: xpack/plugins/interface/models.py:28 msgid "Logo of management page" msgstr "管理页面logo" -#: xpack/plugins/interface/models.py:31 +#: xpack/plugins/interface/models.py:32 msgid "Logo of logout page" msgstr "退出页面logo" @@ -5983,6 +5950,69 @@ msgstr "旗舰版" msgid "Community edition" msgstr "社区版" +#~ msgid "Login direct" +#~ msgstr "直接登录" + +#~ msgid "to apply for a password reset email." +#~ msgstr "申请重置" + +#~ msgid "Please login and reset your MFA." +#~ msgstr "请登录并重新设置你的 MFA" + +#~ msgid "Please login and reset your ssh public key." +#~ msgstr "请登录并重新设置你的密钥" + +# msgid "Update user" +# msgstr "更新用户" +#, python-format +#~ msgid "" +#~ "\n" +#~ "
\n" +#~ "

Your account has been created successfully

\n" +#~ "
\n" +#~ " Username: %(username)s\n" +#~ "
\n" +#~ " Password: \n" +#~ " click here to set your password \n" +#~ " (This link is valid for 1 hour. After it expires, request new one)\n" +#~ "
\n" +#~ "
\n" +#~ "

---

\n" +#~ " Login direct\n" +#~ "
\n" +#~ "
\n" +#~ " " +#~ msgstr "" +#~ "\n" +#~ "
\n" +#~ "

您的账户已创建成功

\n" +#~ "
\n" +#~ " 用户名: %(username)s\n" +#~ "
\n" +#~ " 密码: 请点击这里设置密码 (这个链接有效期1小时, 超" +#~ "过时间您可以 重新申请" +#~ ")\n" +#~ "
\n" +#~ "
\n" +#~ "

---

\n" +#~ " 直接登录\n" +#~ "
\n" +#~ "
\n" +#~ " " + +#, python-format +#~ msgid "Hello %(name)s" +#~ msgstr "你好 %(name)s" + +#~ msgid "This link is valid for 1 hour. After it expires," +#~ msgstr "这个链接有效期1小时, 超过时间您可以" + +#~ msgid "Welcome to the JumpServer open source Bastion Host" +#~ msgstr "欢迎使用JumpServer开源堡垒机" + #~ msgid "Login IP" #~ msgstr "登录IP" diff --git a/apps/notifications/api/notifications.py b/apps/notifications/api/notifications.py index 3b58d3edc..e1ad87a43 100644 --- a/apps/notifications/api/notifications.py +++ b/apps/notifications/api/notifications.py @@ -12,7 +12,10 @@ from notifications.serializers import ( UserMsgSubscriptionSerializer, ) -__all__ = ('BackendListView', 'SystemMsgSubscriptionViewSet', 'UserMsgSubscriptionViewSet') +__all__ = ( + 'BackendListView', 'SystemMsgSubscriptionViewSet', + 'UserMsgSubscriptionViewSet', 'get_all_test_messages' +) class BackendListView(APIView): @@ -80,3 +83,43 @@ class UserMsgSubscriptionViewSet(ListModelMixin, queryset = UserMsgSubscription.objects.all() serializer_class = UserMsgSubscriptionSerializer permission_classes = (IsObjectOwner | IsSuperUser, OnlySuperUserCanList) + + +def get_all_test_messages(request): + import textwrap + from ..notifications import Message + from django.shortcuts import HttpResponse + + msgs_cls = Message.get_all_sub_messages() + html_data = '

HTML 格式

' + text_data = '

Text 格式

' + + for msg_cls in msgs_cls: + try: + msg = msg_cls.gen_test_msg() + if not msg: + continue + msg_html = msg.html_msg_with_sign['message'] + msg_text = msg.text_msg_with_sign['message'] + except NotImplementedError: + msg_html = msg_text = '没有实现方法' + except Exception as e: + msg_html = msg_text = 'Error: ' + str(e) + + html_data += """ +

{}

+ {} +
+ """.format(msg_cls.__name__, msg_html) + + text_data += textwrap.dedent(""" +

{}

+
+        {}
+        
+
+
+ """).format(msg_cls.__name__, msg_text) + return HttpResponse(html_data + text_data) + + diff --git a/apps/notifications/notifications.py b/apps/notifications/notifications.py index 3bb8866c2..cb2d5144f 100644 --- a/apps/notifications/notifications.py +++ b/apps/notifications/notifications.py @@ -2,22 +2,23 @@ import traceback from html2text import HTML2Text from typing import Iterable from itertools import chain +import textwrap from celery import shared_task from django.utils.translation import gettext_lazy as _ from common.utils.timezone import local_now from common.utils import lazyproperty +from settings.utils import get_login_title from users.models import User from notifications.backends import BACKEND from .models import SystemMsgSubscription, UserMsgSubscription -__all__ = ('SystemMessage', 'UserMessage', 'system_msgs') +__all__ = ('SystemMessage', 'UserMessage', 'system_msgs', 'Message') system_msgs = [] user_msgs = [] -all_msgs = [] class MessageType(type): @@ -55,7 +56,6 @@ class Message(metaclass=MessageType): - publish 该方法的实现与消息订阅的表结构有关 - send_msg """ - message_type_label: str category: str category_label: str @@ -84,16 +84,13 @@ class Message(metaclass=MessageType): backend = BACKEND(backend) if not backend.is_enable: continue - get_msg_method = getattr(self, f'get_{backend}_msg', self.get_common_msg) - try: - msg = get_msg_method() - except NotImplementedError: - continue - + msg = get_msg_method() client = backend.client() client.send_msg(users, **msg) - except Exception: + except NotImplementedError: + continue + except: traceback.print_exc() @classmethod @@ -111,10 +108,7 @@ class Message(metaclass=MessageType): @staticmethod def get_common_msg() -> dict: - return { - 'subject': '', - 'message': '' - } + return {'subject': '', 'message': ''} def get_html_msg(self) -> dict: return self.get_common_msg() @@ -133,11 +127,39 @@ class Message(metaclass=MessageType): @lazyproperty def text_msg(self) -> dict: - return self.get_text_msg() + msg = self.get_text_msg() + return msg @lazyproperty def html_msg(self) -> dict: - return self.get_html_msg() + msg = self.get_html_msg() + return msg + + @lazyproperty + def html_msg_with_sign(self): + msg = self.get_html_msg() + msg['message'] = textwrap.dedent(""" + {} +
+ — +
+ {} + """).format(msg['message'], self.signature) + return msg + + @lazyproperty + def text_msg_with_sign(self): + msg = self.get_text_msg() + msg['message'] = textwrap.dedent(""" + {} + — + {} + """).format(msg['message'], self.signature) + return msg + + @lazyproperty + def signature(self): + return get_login_title() # -------------------------------------------------------------- # 支持不同发送消息的方式定义自己的消息内容,比如有些支持 html 标签 @@ -159,16 +181,16 @@ class Message(metaclass=MessageType): return self.text_msg def get_email_msg(self) -> dict: - return self.html_msg + return self.html_msg_with_sign def get_site_msg_msg(self) -> dict: return self.html_msg def get_sms_msg(self) -> dict: - return self.text_msg + return self.text_msg_with_sign @classmethod - def test_all_messages(cls): + def get_all_sub_messages(cls): def get_subclasses(cls): """returns all subclasses of argument, cls""" if issubclass(cls, type): @@ -180,6 +202,12 @@ class Message(metaclass=MessageType): return subclasses messages_cls = get_subclasses(cls) + return messages_cls + + @classmethod + def test_all_messages(cls): + messages_cls = cls.get_all_sub_messages() + for _cls in messages_cls: try: msg = _cls.send_test_msg() @@ -225,6 +253,11 @@ class UserMessage(Message): sub = UserMsgSubscription.objects.get(user=self.user) self.send_msg([self.user], sub.receive_backends) + @classmethod + def get_test_user(cls): + from users.models import User + return User.objects.all().first() + @classmethod def gen_test_msg(cls): raise NotImplementedError diff --git a/apps/notifications/urls/api_urls.py b/apps/notifications/urls/api_urls.py index 14ed78e52..a65dd7b79 100644 --- a/apps/notifications/urls/api_urls.py +++ b/apps/notifications/urls/api_urls.py @@ -1,6 +1,7 @@ from rest_framework_bulk.routes import BulkRouter from django.urls import path +from django.conf import settings from notifications import api @@ -14,3 +15,8 @@ router.register('site-message', api.SiteMessageViewSet, 'site-message') urlpatterns = [ path('backends/', api.BackendListView.as_view(), name='backends') ] + router.urls + +if settings.DEBUG: + urlpatterns += [ + path('debug-msgs/', api.get_all_test_messages, name='debug-all-msgs') + ] diff --git a/apps/ops/notifications.py b/apps/ops/notifications.py index 7e0643dcc..442a81cf6 100644 --- a/apps/ops/notifications.py +++ b/apps/ops/notifications.py @@ -1,4 +1,5 @@ from django.utils.translation import gettext_lazy as _ +from django.template.loader import render_to_string from notifications.notifications import SystemMessage from notifications.models import SystemMsgSubscription @@ -14,14 +15,18 @@ class ServerPerformanceMessage(SystemMessage): category_label = _('Operations') message_type_label = _('Server performance') - def __init__(self, msg): - self._msg = msg + def __init__(self, terms_with_errors): + self.terms_with_errors = terms_with_errors def get_html_msg(self) -> dict: - subject = self._msg[:80] + subject = _("Terminal health check warning") + context = { + 'terms_with_errors': self.terms_with_errors + } + message = render_to_string('ops/_msg_terminal_performance.html', context) return { - 'subject': subject.replace('
', '; '), - 'message': self._msg + 'subject': subject, + 'message': message } @classmethod @@ -33,17 +38,21 @@ class ServerPerformanceMessage(SystemMessage): @classmethod def gen_test_msg(cls): - alarm_messages = [] + from terminal.models import Terminal items_mapper = ServerPerformanceCheckUtil.items_mapper - for item, data in items_mapper.items(): - msg = data['alarm_msg_format'] - max_threshold = data['max_threshold'] - value = 123 - msg = msg.format(max_threshold=max_threshold, value=value, name='Fake terminal') - alarm_messages.append(msg) + terms_with_errors = [] + terms = Terminal.objects.all()[:5] - msg = '
'.join(alarm_messages) - return cls(msg) + for i, term in enumerate(terms, 1): + errors = [] + for item, data in items_mapper.items(): + msg = data['alarm_msg_format'] + max_threshold = data['max_threshold'] + value = 123 // i+1 + msg = msg.format(max_threshold=max_threshold, value=value, name=term.name) + errors.append(msg) + terms_with_errors.append([term, errors]) + return cls(terms_with_errors) class ServerPerformanceCheckUtil(object): @@ -56,59 +65,65 @@ class ServerPerformanceCheckUtil(object): 'disk_used': { 'default': 0, 'max_threshold': 80, - 'alarm_msg_format': _( - 'Disk used more than {max_threshold}%: => {value} ({name})' - ) + 'alarm_msg_format': _('Disk used more than {max_threshold}%: => {value}') }, 'memory_used': { 'default': 0, 'max_threshold': 85, - 'alarm_msg_format': _( - 'Memory used more than {max_threshold}%: => {value} ({name})' - ), + 'alarm_msg_format': _('Memory used more than {max_threshold}%: => {value}'), }, 'cpu_load': { 'default': 0, 'max_threshold': 5, - 'alarm_msg_format': _( - 'CPU load more than {max_threshold}: => {value} ({name})' - ), + 'alarm_msg_format': _('CPU load more than {max_threshold}: => {value}'), }, } def __init__(self): - self.alarm_messages = [] + self.terms_with_errors = [] self._terminals = [] - self._terminal = None def check_and_publish(self): self.check() self.publish() def check(self): - self.alarm_messages = [] + self.terms_with_errors = [] self.initial_terminals() + + for term in self._terminals: + errors = self.check_terminal(term) + if not errors: + continue + self.terms_with_errors.append((term, errors)) + + def check_terminal(self, term): + errors = [] for item, data in self.items_mapper.items(): - for self._terminal in self._terminals: - self.check_item(item, data) + error = self.check_item(term, item, data) + if not error: + continue + errors.append(error) + return errors - def check_item(self, item, data): + @staticmethod + def check_item(term, item, data): default = data['default'] max_threshold = data['max_threshold'] - value = getattr(self._terminal.stat, item, default) + value = getattr(term.stat, item, default) + if isinstance(value, bool) and value != max_threshold: return elif isinstance(value, (int, float)) and value < max_threshold: return msg = data['alarm_msg_format'] - msg = msg.format(max_threshold=max_threshold, value=value, name=self._terminal.name) - self.alarm_messages.append(msg) + error = msg.format(max_threshold=max_threshold, value=value, name=term.name) + return error def publish(self): - if not self.alarm_messages: + if not self.terms_with_errors: return - msg = '
'.join(self.alarm_messages) - ServerPerformanceMessage(msg).publish() + ServerPerformanceMessage(self.terms_with_errors).publish() def initial_terminals(self): terminals = [] diff --git a/apps/ops/templates/ops/_msg_terminal_performance.html b/apps/ops/templates/ops/_msg_terminal_performance.html new file mode 100644 index 000000000..8fa86ec11 --- /dev/null +++ b/apps/ops/templates/ops/_msg_terminal_performance.html @@ -0,0 +1,10 @@ +
+ {% for term, errors in terms_with_errors %} +

{{ term.name }}

+
    + {% for error in errors %} +
  • {{ error }}
  • + {% endfor %} +
+ {% endfor %} +
\ No newline at end of file diff --git a/apps/perms/notifications.py b/apps/perms/notifications.py index 4971e7134..54d3f188e 100644 --- a/apps/perms/notifications.py +++ b/apps/perms/notifications.py @@ -1,4 +1,6 @@ +from urllib.parse import urljoin +from django.conf import settings from django.utils.translation import ugettext as _ from django.template.loader import render_to_string @@ -6,7 +8,7 @@ from common.utils import reverse as js_reverse from notifications.notifications import UserMessage -class PermedWillExpireUserMsg(UserMessage): +class PermedAssetsWillExpireUserMsg(UserMessage): def __init__(self, user, assets): super().__init__(user) self.assets = assets @@ -114,12 +116,9 @@ class AppPermsWillExpireForOrgAdminMsg(UserMessage): def get_items_with_url(self): items_with_url = [] + perm_detail_url = urljoin(settings.SITE_URL, '/ui/#/perms/app-permissions/{}') for perm in self.perms: - url = js_reverse( - 'perms:application-permission-detail', - kwargs={'pk': perm.id}, external=True, - api_to_ui=True - ) + f'?oid={perm.org_id}' + url = perm_detail_url.format(perm.id) + f'?oid={perm.org_id}' items_with_url.append([perm.name, url]) return items_with_url diff --git a/apps/perms/tasks.py b/apps/perms/tasks.py index 023913a41..2f5edafed 100644 --- a/apps/perms/tasks.py +++ b/apps/perms/tasks.py @@ -12,7 +12,7 @@ from common.utils import get_logger from common.utils.timezone import local_now, dt_formatter, dt_parser from ops.celery.decorator import register_as_period_task from perms.notifications import ( - PermedWillExpireUserMsg, AssetPermsWillExpireForOrgAdminMsg, + PermedAssetsWillExpireUserMsg, AssetPermsWillExpireForOrgAdminMsg, PermedAppsWillExpireUserMsg, AppPermsWillExpireForOrgAdminMsg ) from perms.models import AssetPermission, ApplicationPermission @@ -83,7 +83,7 @@ def check_asset_permission_will_expired(): user_asset_mapper[u].update(assets) for user, assets in user_asset_mapper.items(): - PermedWillExpireUserMsg(user, assets).publish_async() + PermedAssetsWillExpireUserMsg(user, assets).publish_async() for org, perms in org_perm_mapper.items(): org_admins = org.admins.all() diff --git a/apps/perms/templates/perms/_msg_permed_items_expire.html b/apps/perms/templates/perms/_msg_permed_items_expire.html index 6a73f1b5f..74df4556d 100644 --- a/apps/perms/templates/perms/_msg_permed_items_expire.html +++ b/apps/perms/templates/perms/_msg_permed_items_expire.html @@ -17,8 +17,5 @@

- ---
- - {% trans 'If you have any question, please contact the administrator' %} - + {% trans 'If you have any question, please contact the administrator' %}

diff --git a/apps/settings/api/public.py b/apps/settings/api/public.py index a94ff7e49..ad9c1fbe6 100644 --- a/apps/settings/api/public.py +++ b/apps/settings/api/public.py @@ -1,12 +1,11 @@ from rest_framework import generics from rest_framework.permissions import AllowAny from django.conf import settings -from django.utils.translation import ugettext_lazy as _ -from django.templatetags.static import static from jumpserver.utils import has_valid_xpack_license from common.utils import get_logger from .. import serializers +from ..utils import get_interface_setting logger = get_logger(__name__) @@ -19,30 +18,14 @@ class PublicSettingApi(generics.RetrieveAPIView): @staticmethod def get_logo_urls(): - logo_urls = { - 'logo_logout': static('img/logo.png'), - 'logo_index': static('img/logo_text.png'), - 'login_image': static('img/login_image.jpg'), - 'favicon': static('img/facio.ico') - } - if not settings.XPACK_ENABLED: - return logo_urls - from xpack.plugins.interface.models import Interface - obj = Interface.interface() - if not obj: - return logo_urls - for attr in ['logo_logout', 'logo_index', 'login_image', 'favicon']: - if getattr(obj, attr, '') and getattr(obj, attr).url: - logo_urls.update({attr: getattr(obj, attr).url}) - return logo_urls + interface = get_interface_setting() + keys = ['logo_logout', 'logo_index', 'login_image', 'favicon'] + return {k: interface[k] for k in keys} @staticmethod def get_login_title(): - default_title = _('Welcome to the JumpServer open source Bastion Host') - if not settings.XPACK_ENABLED: - return default_title - from xpack.plugins.interface.models import Interface - return Interface.get_login_title() + interface = get_interface_setting() + return interface['login_title'] def get_object(self): instance = { diff --git a/apps/settings/utils/common.py b/apps/settings/utils/common.py index e64ceaf7f..6289b2711 100644 --- a/apps/settings/utils/common.py +++ b/apps/settings/utils/common.py @@ -1,4 +1,6 @@ # coding: utf-8 +from jumpserver.context_processor import default_interface +from django.conf import settings class ObjectDict(dict): @@ -16,3 +18,14 @@ class ObjectDict(dict): del self[name] else: raise AttributeError("No such attribute: " + name) + + +def get_interface_setting(): + if not settings.XPACK_ENABLED: + return default_interface + from xpack.plugins.interface.models import Interface + return Interface.get_interface_setting() + + +def get_login_title(): + return get_interface_setting()['login_title'] diff --git a/apps/terminal/models/terminal.py b/apps/terminal/models/terminal.py index d15010fef..7ae262603 100644 --- a/apps/terminal/models/terminal.py +++ b/apps/terminal/models/terminal.py @@ -134,11 +134,8 @@ class Terminal(StorageMixin, TerminalStatusMixin, models.Model): @staticmethod def get_login_title_setting(): - login_title = None - if settings.XPACK_ENABLED: - from xpack.plugins.interface.models import Interface - login_title = Interface.get_login_title() - return {'TERMINAL_HEADER_TITLE': login_title} + from settings.utils import get_login_title + return {'TERMINAL_HEADER_TITLE': get_login_title()} @property def config(self): diff --git a/apps/terminal/notifications.py b/apps/terminal/notifications.py index 461bc94a1..8aa7ef0b5 100644 --- a/apps/terminal/notifications.py +++ b/apps/terminal/notifications.py @@ -13,6 +13,8 @@ from notifications.models import SystemMsgSubscription from notifications.backends import BACKEND from orgs.utils import tmp_to_root_org from common.utils import lazyproperty +from common.utils.timezone import local_now_display + logger = get_logger(__name__) @@ -73,25 +75,26 @@ class CommandAlertMessage(CommandAlertMixin, SystemMessage): @classmethod def gen_test_msg(cls): command = Command.objects.first().to_dict() + command['session'] = Session.objects.first().id return cls(command) def get_html_msg(self) -> dict: command = self.command - - with tmp_to_root_org(): - session = Session.objects.get(id=command['session']) session_detail_url = reverse( 'api-terminal:session-detail', kwargs={'pk': command['session']}, external=True, api_to_ui=True - ) + ) + '?oid={}'.format(self.command['org_id']) + level = Command.get_risk_level_str(command['risk_level']) + items = { + _("Asset"): command['asset'], + _("User"): command['user'], + _("Level"): level, + _("Date"): local_now_display(), + } context = { - 'command': command['input'], - 'hostname': command['asset'], - 'host_ip': session.asset_obj.ip, - 'user': command['user'], - 'risk_level': Command.get_risk_level_str(command['risk_level']), - 'session_detail_url': session_detail_url, - 'oid': session.org_id + 'items': items, + 'session_url': session_detail_url, + "command": command['input'], } message = render_to_string('terminal/_msg_command_alert.html', context) return { @@ -122,19 +125,25 @@ class CommandExecutionAlert(CommandAlertMixin, SystemMessage): def get_html_msg(self) -> dict: command = self.command - _input = command['input'] - _input = _input.replace('\n', '
') - assets_with_url = [] for asset in command['assets']: - url = reverse('assets:asset-detail', kwargs={'pk': asset.id}, api_to_ui=True, external=True) + url = reverse( + 'assets:asset-detail', kwargs={'pk': asset.id}, + api_to_ui=True, external=True + ) + '?oid={}'.format(asset.org_id) assets_with_url.append([asset, url]) + level = Command.get_risk_level_str(command['risk_level']) + items = { + _("User"): command['user'], + _("Level"): level, + _("Date"): local_now_display(), + } + context = { - 'command': _input, + 'items': items, 'assets_with_url': assets_with_url, - 'user': command['user'], - 'risk_level': Command.get_risk_level_str(command['risk_level']) + 'command': command['input'], } message = render_to_string('terminal/_msg_command_execute_alert.html', context) return { diff --git a/apps/terminal/templates/terminal/_msg_command_alert.html b/apps/terminal/templates/terminal/_msg_command_alert.html index b980e87bc..4b7eb26ed 100644 --- a/apps/terminal/templates/terminal/_msg_command_alert.html +++ b/apps/terminal/templates/terminal/_msg_command_alert.html @@ -1,17 +1,16 @@ {% load i18n %} -

- {% trans 'Command' %}: {{ command }} -

-

- {% trans 'Asset' %}: {{ hostname }}({{ host_ip }}) -

-

- {% trans 'User' %}: {{ user }} -

-

- {% trans 'Level' %}: {{ risk_level }} -

-

- {% trans 'Session' %}: {% trans 'view' %} -

+
+ {% for item, value in items.items %} + + {{ item }}: {{ value }} + +
+ {% endfor %} + {% trans 'Session' %}: {% trans 'view' %} +
+ {% trans 'Command' %}:
+
+{{ command }}
+    
+
diff --git a/apps/terminal/templates/terminal/_msg_command_execute_alert.html b/apps/terminal/templates/terminal/_msg_command_execute_alert.html index e9136c6a1..61022d156 100644 --- a/apps/terminal/templates/terminal/_msg_command_execute_alert.html +++ b/apps/terminal/templates/terminal/_msg_command_execute_alert.html @@ -1,16 +1,16 @@ {% load i18n %} -

- {% trans 'User' %}: {{ user }} -

-

- {% trans 'Level' %}: {{ risk_level }} -

- {% trans 'Command' %}:
-
+{% for item, value in items.items %}
+    
+        {{ item }}: {{ value }}
+    
+    
+{% endfor %} + {% trans 'Command' %}:
+
 {{ command }}
-
+
{% trans 'Assets' %}:
diff --git a/apps/tickets/notifications.py b/apps/tickets/notifications.py index 2df08845f..a70e6d497 100644 --- a/apps/tickets/notifications.py +++ b/apps/tickets/notifications.py @@ -19,6 +19,7 @@ class BaseTicketMessage(UserMessage): @property def ticket_detail_url(self): + tp = self.ticket.type return urljoin(settings.SITE_URL, const.TICKET_DETAIL_URL.format(id=str(self.ticket.id))) @property diff --git a/apps/users/notifications.py b/apps/users/notifications.py index ab04b6fae..83ab9c532 100644 --- a/apps/users/notifications.py +++ b/apps/users/notifications.py @@ -1,4 +1,3 @@ -from datetime import datetime from urllib.parse import urljoin from django.utils import timezone @@ -10,6 +9,38 @@ from common.utils import reverse, get_request_ip_or_data, get_request_user_agent from notifications.notifications import UserMessage +class UserCreatedMsg(UserMessage): + def get_html_msg(self) -> dict: + user = self.user + subject = _('Create account successfully') + if settings.EMAIL_CUSTOM_USER_CREATED_SUBJECT: + subject = settings.EMAIL_CUSTOM_USER_CREATED_SUBJECT + + honorific = settings.EMAIL_CUSTOM_USER_CREATED_HONORIFIC or _('Hello {}').format(user.name) + signature = settings.EMAIL_CUSTOM_USER_CREATED_SIGNATURE or 'JumpServer' + + context = { + 'honorific': honorific, + 'signature': signature, + 'username': user.username, + 'rest_password_url': reverse('authentication:reset-password', external=True), + 'rest_password_token': user.generate_reset_token(), + 'forget_password_url': reverse('authentication:forgot-password', external=True), + 'email': user.email, + 'login_url': reverse('authentication:login', external=True), + } + message = render_to_string('users/_msg_user_created.html', context) + return { + 'subject': subject, + 'message': message + } + + @classmethod + def gen_test_msg(cls): + user = cls.get_test_user() + return cls(user) + + class ResetPasswordMsg(UserMessage): def __init__(self, user): super().__init__(user) @@ -71,18 +102,17 @@ class ResetPasswordSuccessMsg(UserMessage): class PasswordExpirationReminderMsg(UserMessage): - update_password_url = urljoin(settings.SITE_URL, '/ui/#/users/profile/?activeTab=PasswordUpdate') - def get_html_msg(self) -> dict: user = self.user subject = _('Password is about expire') date_password_expired_local = timezone.localtime(user.date_password_expired) + update_password_url = urljoin(settings.SITE_URL, '/ui/#/users/profile/?activeTab=PasswordUpdate') date_password_expired = date_password_expired_local.strftime('%Y-%m-%d %H:%M:%S') context = { 'name': user.name, 'date_password_expired': date_password_expired, - 'update_password_url': self.update_password_url, + 'update_password_url': update_password_url, 'forget_password_url': reverse('authentication:forgot-password', external=True), 'email': user.email, 'login_url': reverse('authentication:login', external=True), @@ -125,9 +155,10 @@ class UserExpirationReminderMsg(UserMessage): class ResetSSHKeyMsg(UserMessage): def get_html_msg(self) -> dict: subject = _('Reset SSH Key') + update_url = urljoin(settings.SITE_URL, '/ui/#/users/profile/?activeTab=SSHUpdate') context = { 'name': self.user.name, - 'login_url': reverse('authentication:login', external=True), + 'url': update_url, } message = render_to_string('users/_msg_reset_ssh_key.html', context) return { @@ -147,7 +178,7 @@ class ResetMFAMsg(UserMessage): subject = _('Reset MFA') context = { 'name': self.user.name, - 'login_url': reverse('authentication:login', external=True), + 'url': reverse('authentication:user-otp-enable-start', external=True), } message = render_to_string('users/_msg_reset_mfa.html', context) return { diff --git a/apps/users/templates/users/_msg_password_expire_reminder.html b/apps/users/templates/users/_msg_password_expire_reminder.html index 5037a332f..0c8175c23 100644 --- a/apps/users/templates/users/_msg_password_expire_reminder.html +++ b/apps/users/templates/users/_msg_password_expire_reminder.html @@ -13,10 +13,6 @@

- {% trans 'If your password has expired, please click' %} + {% trans 'If your password has expired, please click the link below to' %} {% trans 'Reset password' %} - {% trans 'to apply for a password reset email.' %}

---- -
-{% trans 'Login direct' %} \ No newline at end of file diff --git a/apps/users/templates/users/_msg_reset_mfa.html b/apps/users/templates/users/_msg_reset_mfa.html index 073d97ccb..c214adef2 100644 --- a/apps/users/templates/users/_msg_reset_mfa.html +++ b/apps/users/templates/users/_msg_reset_mfa.html @@ -4,9 +4,9 @@ {% trans 'Hello' %} {{ name }},

- {% trans 'Your MFA has been reset by site administrator.' %}
- {% trans 'Please login and reset your MFA.' %} + {% trans 'Your MFA has been reset by site administrator' %}
+ {% trans 'Please click the link below to set' %} +
+
+ {% trans 'Click here set' %}

-

- {% trans 'Login direct' %} -

\ No newline at end of file diff --git a/apps/users/templates/users/_msg_reset_ssh_key.html b/apps/users/templates/users/_msg_reset_ssh_key.html index ab705aca7..58f97524a 100644 --- a/apps/users/templates/users/_msg_reset_ssh_key.html +++ b/apps/users/templates/users/_msg_reset_ssh_key.html @@ -4,9 +4,9 @@ {% trans 'Hello' %} {{ name }},

- {% trans 'Your ssh public key has been reset by site administrator.' %}
- {% trans 'Please login and reset your ssh public key.' %} + {% trans 'Your ssh public key has been reset by site administrator' %}
+ {% trans 'Please click the link below to set' %} +
+
+ {% trans 'Click here set' %}

-

- {% trans 'Login direct' %} -

\ No newline at end of file diff --git a/apps/users/templates/users/_msg_user_created.html b/apps/users/templates/users/_msg_user_created.html new file mode 100644 index 000000000..c0515e9f6 --- /dev/null +++ b/apps/users/templates/users/_msg_user_created.html @@ -0,0 +1,20 @@ +{% load i18n %} + +

+ {{ honorific }}: +

+ +
+

{% trans 'Your account has been created successfully' %}

+

+ {% trans 'Username' %}: {{ username }}
+ {% trans 'Password' %}: + + {% trans 'click here to set your password' %} + +

+

+ {% trans 'This link is valid for 1 hour. After it expires' %} + {% trans 'request new one' %} +

+
diff --git a/apps/users/utils.py b/apps/users/utils.py index b89dacdb2..8be6a4928 100644 --- a/apps/users/utils.py +++ b/apps/users/utils.py @@ -8,67 +8,24 @@ import logging import time from django.conf import settings -from django.utils.translation import ugettext as _ from django.core.cache import cache from common.tasks import send_mail_async -from common.utils import reverse, get_object_or_none, get_request_ip_or_data, get_request_user_agent +from common.utils import reverse, get_object_or_none from .models import User logger = logging.getLogger('jumpserver') -def construct_user_created_email_body(user): - default_body = _(""" -
-

Your account has been created successfully

-
- Username: %(username)s -
- Password: - click here to set your password - (This link is valid for 1 hour. After it expires, request new one) -
-
-

---

- Login direct -
-
- """) % { - 'username': user.username, - 'rest_password_url': reverse('authentication:reset-password', external=True), - 'rest_password_token': user.generate_reset_token(), - 'forget_password_url': reverse('authentication:forgot-password', external=True), - 'email': user.email, - 'login_url': reverse('authentication:login', external=True), - } - - if settings.EMAIL_CUSTOM_USER_CREATED_BODY: - custom_body = '

' + settings.EMAIL_CUSTOM_USER_CREATED_BODY + '

' - else: - custom_body = '' - body = custom_body + default_body - return body - - def send_user_created_mail(user): - recipient_list = [user.email] - subject = _('Create account successfully') - if settings.EMAIL_CUSTOM_USER_CREATED_SUBJECT: - subject = settings.EMAIL_CUSTOM_USER_CREATED_SUBJECT + from .notifications import UserCreatedMsg - honorific = '

' + _('Hello %(name)s') % {'name': user.name} + ':

' - if settings.EMAIL_CUSTOM_USER_CREATED_HONORIFIC: - honorific = '

' + settings.EMAIL_CUSTOM_USER_CREATED_HONORIFIC + ':

' - - body = construct_user_created_email_body(user) - - signature = '

jumpserver

' - if settings.EMAIL_CUSTOM_USER_CREATED_SIGNATURE: - signature = '

' + settings.EMAIL_CUSTOM_USER_CREATED_SIGNATURE + '

' + recipient_list = [user.email] + msg = UserCreatedMsg.html_msg + subject = msg['subject'] + message = msg['message'] - message = honorific + body + signature if settings.DEBUG: try: print(message)