diff --git a/apps/authentication/api/__init__.py b/apps/authentication/api/__init__.py index c2a4a740f..4f6475124 100644 --- a/apps/authentication/api/__init__.py +++ b/apps/authentication/api/__init__.py @@ -5,3 +5,4 @@ from .auth import * from .token import * from .mfa import * from .access_key import * +from .login_confirm import * diff --git a/apps/authentication/api/login_confirm.py b/apps/authentication/api/login_confirm.py new file mode 100644 index 000000000..3ce26f84d --- /dev/null +++ b/apps/authentication/api/login_confirm.py @@ -0,0 +1,25 @@ +# -*- coding: utf-8 -*- +# +from rest_framework.generics import UpdateAPIView +from django.shortcuts import get_object_or_404 + +from common.permissions import IsOrgAdmin +from ..models import LoginConfirmSetting +from ..serializers import LoginConfirmSettingSerializer + +__all__ = ['LoginConfirmSettingUpdateApi'] + + +class LoginConfirmSettingUpdateApi(UpdateAPIView): + permission_classes = (IsOrgAdmin,) + serializer_class = LoginConfirmSettingSerializer + + def get_object(self): + from users.models import User + user_id = self.kwargs.get('user_id') + user = get_object_or_404(User, pk=user_id) + defaults = {'user': user} + s, created = LoginConfirmSetting.objects.get_or_create( + defaults, user=user, + ) + return s diff --git a/apps/authentication/migrations/0003_loginconfirmsetting.py b/apps/authentication/migrations/0003_loginconfirmsetting.py new file mode 100644 index 000000000..c8043bc87 --- /dev/null +++ b/apps/authentication/migrations/0003_loginconfirmsetting.py @@ -0,0 +1,32 @@ +# Generated by Django 2.2.5 on 2019-10-31 10:23 + +from django.conf import settings +from django.db import migrations, models +import django.db.models.deletion +import uuid + + +class Migration(migrations.Migration): + + dependencies = [ + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ('authentication', '0002_auto_20190729_1423'), + ] + + operations = [ + migrations.CreateModel( + name='LoginConfirmSetting', + fields=[ + ('id', models.UUIDField(default=uuid.uuid4, primary_key=True, serialize=False)), + ('created_by', models.CharField(blank=True, max_length=32, null=True, verbose_name='Created by')), + ('date_created', models.DateTimeField(auto_now_add=True, null=True, verbose_name='Date created')), + ('date_updated', models.DateTimeField(auto_now=True, verbose_name='Date updated')), + ('is_active', models.BooleanField(default=True, verbose_name='Is active')), + ('reviewers', models.ManyToManyField(blank=True, related_name='review_login_confirm_settings', to=settings.AUTH_USER_MODEL, verbose_name='Reviewers')), + ('user', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='login_confirm_setting', to=settings.AUTH_USER_MODEL, verbose_name='User')), + ], + options={ + 'abstract': False, + }, + ), + ] diff --git a/apps/authentication/models.py b/apps/authentication/models.py index f50305651..4f0e06fb6 100644 --- a/apps/authentication/models.py +++ b/apps/authentication/models.py @@ -1,7 +1,7 @@ import uuid from django.db import models from django.utils import timezone -from django.utils.translation import ugettext_lazy as _ +from django.utils.translation import ugettext_lazy as _, ugettext as __ from rest_framework.authtoken.models import Token from django.conf import settings @@ -40,8 +40,8 @@ class PrivateToken(Token): class LoginConfirmSetting(CommonModelMixin): - user = models.OneToOneField('users.User', on_delete=models.CASCADE, verbose_name=_("User"), related_name=_("login_confirmation_setting")) - reviewers = models.ManyToManyField('users.User', verbose_name=_("Reviewers"), related_name=_("review_login_confirmation_settings")) + user = models.OneToOneField('users.User', on_delete=models.CASCADE, verbose_name=_("User"), related_name="login_confirm_setting") + reviewers = models.ManyToManyField('users.User', verbose_name=_("Reviewers"), related_name="review_login_confirm_settings", blank=True) is_active = models.BooleanField(default=True, verbose_name=_("Is active")) @classmethod @@ -50,7 +50,7 @@ class LoginConfirmSetting(CommonModelMixin): def create_confirm_order(self, request=None): from orders.models import LoginConfirmOrder - title = _('User login confirm: {}'.format(self.user)) + title = _('User login confirm: {}').format(self.user) if request: remote_addr = get_request_ip(request) city = get_ip_city(remote_addr) diff --git a/apps/authentication/serializers.py b/apps/authentication/serializers.py index 584da768f..7463d30ca 100644 --- a/apps/authentication/serializers.py +++ b/apps/authentication/serializers.py @@ -4,17 +4,16 @@ from django.core.cache import cache from rest_framework import serializers from users.models import User -from .models import AccessKey +from .models import AccessKey, LoginConfirmSetting __all__ = [ 'AccessKeySerializer', 'OtpVerifySerializer', 'BearerTokenSerializer', - 'MFAChallengeSerializer', + 'MFAChallengeSerializer', 'LoginConfirmSettingSerializer', ] class AccessKeySerializer(serializers.ModelSerializer): - class Meta: model = AccessKey fields = ['id', 'secret', 'is_active', 'date_created'] @@ -87,3 +86,9 @@ class MFAChallengeSerializer(BearerTokenMixin, serializers.Serializer): username = self.context["username"] return self.create_response(username) + +class LoginConfirmSettingSerializer(serializers.ModelSerializer): + class Meta: + model = LoginConfirmSetting + fields = ['id', 'user', 'reviewers', 'date_created', 'date_updated'] + read_only_fields = ['date_created', 'date_updated'] diff --git a/apps/authentication/templates/authentication/login_wait_confirm.html b/apps/authentication/templates/authentication/login_wait_confirm.html index 0a14e8515..0167236df 100644 --- a/apps/authentication/templates/authentication/login_wait_confirm.html +++ b/apps/authentication/templates/authentication/login_wait_confirm.html @@ -61,9 +61,6 @@
{% include '_copyright.html' %}
-
- 2014-2019 -
diff --git a/apps/authentication/urls/api_urls.py b/apps/authentication/urls/api_urls.py index a90b328cc..b47e5eb72 100644 --- a/apps/authentication/urls/api_urls.py +++ b/apps/authentication/urls/api_urls.py @@ -19,7 +19,8 @@ urlpatterns = [ api.UserConnectionTokenApi.as_view(), name='connection-token'), path('otp/auth/', api.UserOtpAuthApi.as_view(), name='user-otp-auth'), path('otp/verify/', api.UserOtpVerifyApi.as_view(), name='user-otp-verify'), - path('order/auth/', api.UserOrderAcceptAuthApi.as_view(), name='user-order-auth') + path('order/auth/', api.UserOrderAcceptAuthApi.as_view(), name='user-order-auth'), + path('login-confirm-settings//', api.LoginConfirmSettingUpdateApi.as_view(), name='login-confirm-setting-update') ] urlpatterns += router.urls diff --git a/apps/authentication/views/login.py b/apps/authentication/views/login.py index 646268eea..761e656a1 100644 --- a/apps/authentication/views/login.py +++ b/apps/authentication/views/login.py @@ -179,7 +179,7 @@ class UserLoginGuardView(RedirectView): if user.otp_enabled and user.otp_secret_key and \ not self.request.session.get('auth_otp'): return reverse('authentication:login-otp') - confirm_setting = LoginConfirmSetting.get_user_confirm_setting(user) + confirm_setting = user.get_login_confirm_setting() if confirm_setting and not self.request.session.get('auth_confirm'): order = confirm_setting.create_confirm_order(self.request) self.request.session['auth_order_id'] = str(order.id) diff --git a/apps/locale/zh/LC_MESSAGES/django.mo b/apps/locale/zh/LC_MESSAGES/django.mo index fe18ec6df..7d21e0d68 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 3d36fc15e..f8837acbd 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-10-30 14:51+0800\n" +"POT-Creation-Date: 2019-10-31 16:57+0800\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: ibuler \n" "Language-Team: Jumpserver team\n" @@ -83,7 +83,7 @@ msgstr "运行参数" #: assets/templates/assets/domain_detail.html:60 #: assets/templates/assets/domain_list.html:26 #: assets/templates/assets/label_list.html:16 -#: assets/templates/assets/system_user_list.html:51 audits/models.py:19 +#: assets/templates/assets/system_user_list.html:51 audits/models.py:20 #: audits/templates/audits/ftp_log_list.html:44 #: audits/templates/audits/ftp_log_list.html:74 #: perms/forms/asset_permission.py:84 perms/models/asset_permission.py:80 @@ -144,7 +144,7 @@ msgstr "资产" #: settings/templates/settings/terminal_setting.html:105 terminal/models.py:23 #: terminal/models.py:260 terminal/templates/terminal/terminal_detail.html:43 #: terminal/templates/terminal/terminal_list.html:29 users/models/group.py:14 -#: users/models/user.py:375 users/templates/users/_select_user_modal.html:13 +#: users/models/user.py:382 users/templates/users/_select_user_modal.html:13 #: users/templates/users/user_detail.html:63 #: users/templates/users/user_group_detail.html:55 #: users/templates/users/user_group_list.html:35 @@ -197,7 +197,7 @@ msgstr "参数" #: orgs/models.py:16 perms/models/base.py:54 #: perms/templates/perms/asset_permission_detail.html:98 #: perms/templates/perms/remote_app_permission_detail.html:90 -#: users/models/user.py:416 users/serializers/group.py:32 +#: users/models/user.py:423 users/serializers/group.py:32 #: users/templates/users/user_detail.html:111 #: xpack/plugins/change_auth_plan/models.py:108 #: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_detail.html:113 @@ -261,7 +261,7 @@ msgstr "创建日期" #: 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 -#: users/models/user.py:408 users/templates/users/user_detail.html:129 +#: users/models/user.py:415 users/templates/users/user_detail.html:129 #: users/templates/users/user_group_detail.html:67 #: users/templates/users/user_group_list.html:37 #: users/templates/users/user_profile.html:138 @@ -413,7 +413,7 @@ msgstr "详情" #: assets/templates/assets/label_list.html:39 #: assets/templates/assets/system_user_detail.html:26 #: assets/templates/assets/system_user_list.html:29 -#: assets/templates/assets/system_user_list.html:81 audits/models.py:33 +#: assets/templates/assets/system_user_list.html:81 audits/models.py:34 #: perms/templates/perms/asset_permission_detail.html:30 #: perms/templates/perms/asset_permission_list.html:178 #: perms/templates/perms/remote_app_permission_detail.html:30 @@ -457,7 +457,7 @@ msgstr "更新" #: assets/templates/assets/domain_list.html:55 #: assets/templates/assets/label_list.html:40 #: assets/templates/assets/system_user_detail.html:30 -#: assets/templates/assets/system_user_list.html:82 audits/models.py:34 +#: assets/templates/assets/system_user_list.html:82 audits/models.py:35 #: authentication/templates/authentication/_access_key_modal.html:65 #: ops/templates/ops/task_list.html:69 #: perms/templates/perms/asset_permission_detail.html:34 @@ -513,7 +513,7 @@ msgstr "创建远程应用" #: assets/templates/assets/domain_gateway_list.html:73 #: assets/templates/assets/domain_list.html:29 #: assets/templates/assets/label_list.html:17 -#: assets/templates/assets/system_user_list.html:56 audits/models.py:38 +#: assets/templates/assets/system_user_list.html:56 audits/models.py:39 #: audits/templates/audits/operate_log_list.html:47 #: audits/templates/audits/operate_log_list.html:73 #: authentication/templates/authentication/_access_key_modal.html:34 @@ -700,7 +700,7 @@ 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:80 +#: 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:65 #: authentication/templates/authentication/xpack_login.html:92 @@ -708,7 +708,7 @@ msgstr "SSH网关,支持代理SSH,RDP和VNC" #: 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 -#: users/models/user.py:373 users/templates/users/_select_user_modal.html:14 +#: users/models/user.py:380 users/templates/users/_select_user_modal.html:14 #: users/templates/users/user_detail.html:67 #: users/templates/users/user_list.html:36 #: users/templates/users/user_profile.html:47 @@ -748,7 +748,7 @@ msgstr "密码" #: assets/forms/user.py:30 assets/serializers/asset_user.py:71 #: assets/templates/assets/_asset_user_auth_update_modal.html:27 -#: users/models/user.py:402 +#: users/models/user.py:409 msgid "Private key" msgstr "ssh私钥" @@ -798,7 +798,7 @@ msgstr "使用逗号分隔多个命令,如: /bin/whoami,/sbin/ifconfig" #: 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:15 +#: 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 @@ -964,7 +964,7 @@ msgstr "带宽" msgid "Contact" msgstr "联系人" -#: assets/models/cluster.py:22 users/models/user.py:394 +#: assets/models/cluster.py:22 users/models/user.py:401 #: users/templates/users/user_detail.html:76 msgid "Phone" msgstr "手机" @@ -990,7 +990,7 @@ msgid "Default" msgstr "默认" #: assets/models/cluster.py:36 assets/models/label.py:14 -#: users/models/user.py:514 +#: users/models/user.py:521 msgid "System" msgstr "系统" @@ -1097,8 +1097,8 @@ msgstr "资产组" msgid "Default asset group" msgstr "默认资产组" -#: assets/models/label.py:15 audits/models.py:17 audits/models.py:37 -#: audits/models.py:50 audits/templates/audits/ftp_log_list.html:36 +#: assets/models/label.py:15 audits/models.py:18 audits/models.py:38 +#: audits/models.py:51 audits/templates/audits/ftp_log_list.html:36 #: audits/templates/audits/ftp_log_list.html:73 #: audits/templates/audits/operate_log_list.html:39 #: audits/templates/audits/operate_log_list.html:72 @@ -1108,7 +1108,7 @@ msgstr "默认资产组" #: 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:14 +#: orders/templates/orders/login_confirm_order_list.html:15 #: 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 @@ -1121,7 +1121,7 @@ msgstr "默认资产组" #: 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 -#: users/models/user.py:129 users/models/user.py:145 users/models/user.py:502 +#: users/models/user.py:136 users/models/user.py:152 users/models/user.py:509 #: users/serializers/group.py:21 #: users/templates/users/user_group_detail.html:78 #: users/templates/users/user_group_list.html:36 users/views/user.py:250 @@ -1211,7 +1211,7 @@ msgid "Login mode" msgstr "登录模式" #: assets/models/user.py:166 assets/templates/assets/user_asset_list.html:79 -#: audits/models.py:20 audits/templates/audits/ftp_log_list.html:52 +#: audits/models.py:21 audits/templates/audits/ftp_log_list.html:52 #: audits/templates/audits/ftp_log_list.html:75 #: perms/forms/asset_permission.py:90 perms/forms/remote_app_permission.py:43 #: perms/models/asset_permission.py:82 perms/models/remote_app_permission.py:16 @@ -1277,7 +1277,7 @@ msgid "Backend" msgstr "后端" #: assets/serializers/asset_user.py:67 users/forms.py:262 -#: users/models/user.py:405 users/templates/users/first_login.html:42 +#: 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 #: users/templates/users/user_profile_update.html:46 @@ -1470,8 +1470,8 @@ msgstr "请输入密码" #: assets/templates/assets/_asset_user_auth_update_modal.html:68 #: assets/templates/assets/asset_detail.html:302 -#: users/templates/users/user_detail.html:313 -#: users/templates/users/user_detail.html:340 +#: users/templates/users/user_detail.html:364 +#: users/templates/users/user_detail.html:391 #: xpack/plugins/interface/views.py:35 msgid "Update successfully!" msgstr "更新成功" @@ -1668,10 +1668,11 @@ msgstr "选择节点" #: authentication/templates/authentication/_mfa_confirm_modal.html:20 #: settings/templates/settings/terminal_setting.html:168 #: templates/_modal.html:23 terminal/templates/terminal/session_detail.html:112 -#: users/templates/users/user_detail.html:394 -#: users/templates/users/user_detail.html:420 -#: users/templates/users/user_detail.html:443 -#: users/templates/users/user_detail.html:488 +#: users/templates/users/user_detail.html:271 +#: users/templates/users/user_detail.html:445 +#: users/templates/users/user_detail.html:471 +#: users/templates/users/user_detail.html:494 +#: users/templates/users/user_detail.html:539 #: users/templates/users/user_group_create_update.html:32 #: users/templates/users/user_group_list.html:120 #: users/templates/users/user_list.html:256 @@ -1871,9 +1872,9 @@ msgstr "显示所有子节点资产" #: assets/templates/assets/asset_list.html:417 #: assets/templates/assets/system_user_list.html:129 -#: users/templates/users/user_detail.html:388 -#: users/templates/users/user_detail.html:414 -#: users/templates/users/user_detail.html:482 +#: users/templates/users/user_detail.html:439 +#: users/templates/users/user_detail.html:465 +#: users/templates/users/user_detail.html:533 #: users/templates/users/user_group_list.html:114 #: users/templates/users/user_list.html:250 #: xpack/plugins/interface/templates/interface/interface.html:97 @@ -1887,9 +1888,9 @@ msgstr "删除选择资产" #: assets/templates/assets/asset_list.html:421 #: assets/templates/assets/system_user_list.html:133 #: settings/templates/settings/terminal_setting.html:166 -#: users/templates/users/user_detail.html:392 -#: users/templates/users/user_detail.html:418 -#: users/templates/users/user_detail.html:486 +#: users/templates/users/user_detail.html:443 +#: users/templates/users/user_detail.html:469 +#: users/templates/users/user_detail.html:537 #: users/templates/users/user_group_list.html:118 #: users/templates/users/user_list.html:254 #: xpack/plugins/interface/templates/interface/interface.html:101 @@ -2194,7 +2195,7 @@ msgstr "资产管理" msgid "System user asset" msgstr "系统用户资产" -#: audits/models.py:18 audits/models.py:41 audits/models.py:52 +#: audits/models.py:19 audits/models.py:42 audits/models.py:53 #: audits/templates/audits/ftp_log_list.html:76 #: audits/templates/audits/operate_log_list.html:76 #: audits/templates/audits/password_change_log_list.html:58 @@ -2204,86 +2205,86 @@ msgstr "系统用户资产" msgid "Remote addr" msgstr "远端地址" -#: audits/models.py:21 audits/templates/audits/ftp_log_list.html:77 +#: audits/models.py:22 audits/templates/audits/ftp_log_list.html:77 msgid "Operate" msgstr "操作" -#: audits/models.py:22 audits/templates/audits/ftp_log_list.html:59 +#: audits/models.py:23 audits/templates/audits/ftp_log_list.html:59 #: audits/templates/audits/ftp_log_list.html:78 msgid "Filename" msgstr "文件名" -#: audits/models.py:23 audits/models.py:76 +#: 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 -#: users/templates/users/user_detail.html:464 +#: users/templates/users/user_detail.html:515 #: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_execution_subtask_list.html:14 #: xpack/plugins/cloud/api.py:61 msgid "Success" msgstr "成功" -#: audits/models.py:32 +#: audits/models.py:33 #: authentication/templates/authentication/_access_key_modal.html:22 #: xpack/plugins/vault/templates/vault/vault.html:46 msgid "Create" msgstr "创建" -#: audits/models.py:39 audits/templates/audits/operate_log_list.html:55 +#: audits/models.py:40 audits/templates/audits/operate_log_list.html:55 #: audits/templates/audits/operate_log_list.html:74 msgid "Resource Type" msgstr "资源类型" -#: audits/models.py:40 audits/templates/audits/operate_log_list.html:75 +#: audits/models.py:41 audits/templates/audits/operate_log_list.html:75 msgid "Resource" msgstr "资源" -#: audits/models.py:51 audits/templates/audits/password_change_log_list.html:57 +#: audits/models.py:52 audits/templates/audits/password_change_log_list.html:57 msgid "Change by" msgstr "修改者" -#: audits/models.py:70 users/templates/users/user_detail.html:98 +#: audits/models.py:71 users/templates/users/user_detail.html:98 msgid "Disabled" msgstr "禁用" -#: audits/models.py:71 settings/models.py:33 +#: audits/models.py:72 settings/models.py:33 #: users/templates/users/user_detail.html:96 msgid "Enabled" msgstr "启用" -#: audits/models.py:72 +#: audits/models.py:73 msgid "-" msgstr "" -#: audits/models.py:77 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:81 +#: audits/models.py:82 msgid "Login type" msgstr "登录方式" -#: audits/models.py:82 +#: audits/models.py:83 msgid "Login ip" msgstr "登录IP" -#: audits/models.py:83 +#: audits/models.py:84 msgid "Login city" msgstr "登录城市" -#: audits/models.py:84 +#: audits/models.py:85 msgid "User agent" msgstr "Agent" -#: audits/models.py:85 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:397 +#: users/forms.py:174 users/models/user.py:404 #: users/templates/users/first_login.html:45 msgid "MFA" msgstr "MFA" -#: audits/models.py:86 audits/templates/audits/login_log_list.html:63 +#: 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 @@ -2291,16 +2292,17 @@ msgstr "MFA" msgid "Reason" msgstr "原因" -#: audits/models.py:87 audits/templates/audits/login_log_list.html:64 +#: audits/models.py:88 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 #: 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:88 +#: audits/models.py:89 msgid "Date login" msgstr "登录日期" @@ -2355,7 +2357,6 @@ msgstr "Agent" #: audits/templates/audits/login_log_list.html:61 #: orders/templates/orders/login_confirm_order_detail.html:58 -#: orders/templates/orders/login_confirm_order_list.html:16 msgid "City" msgstr "城市" @@ -2522,15 +2523,15 @@ msgid "Private Token" msgstr "ssh密钥" #: authentication/models.py:43 -msgid "login_confirmation_setting" -msgstr "" +msgid "login_confirm_setting" +msgstr "登录复核设置" -#: authentication/models.py:44 +#: authentication/models.py:44 users/templates/users/user_detail.html:265 msgid "Reviewers" -msgstr "" +msgstr "审批人" #: authentication/models.py:44 -msgid "review_login_confirmation_settings" +msgid "review_login_confirm_settings" msgstr "" #: authentication/models.py:53 @@ -2571,14 +2572,14 @@ msgid "Show" msgstr "显示" #: authentication/templates/authentication/_access_key_modal.html:66 -#: users/models/user.py:332 users/templates/users/user_profile.html:94 +#: users/models/user.py:339 users/templates/users/user_profile.html:94 #: users/templates/users/user_profile.html:163 #: users/templates/users/user_profile.html:166 msgid "Disable" msgstr "禁用" #: authentication/templates/authentication/_access_key_modal.html:67 -#: users/models/user.py:333 users/templates/users/user_profile.html:92 +#: users/models/user.py:340 users/templates/users/user_profile.html:92 #: users/templates/users/user_profile.html:170 msgid "Enable" msgstr "启用" @@ -2838,8 +2839,9 @@ msgid "" "configure nginx for url distribution, If you see this page, " "prove that you are not accessing the nginx listening port. Good luck." msgstr "" -"
Koko是单独部署的一个程序,你需要部署Koko, 并确保nginx配置转发,
如果你看到了" -"这个页面,证明你访问的不是nginx监听的端口,祝你好运
" +"
Koko是单独部署的一个程序,你需要部署Koko, 并确保nginx配置转发,
如果你看到了这个页面,证明你访问的不是nginx监听的端口,祝你好运" #: ops/api/celery.py:54 msgid "Waiting task start" @@ -3113,6 +3115,7 @@ 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 msgid "Pending" msgstr "等待" @@ -3197,26 +3200,23 @@ msgid "Command execution" msgstr "命令执行" #: orders/models.py:12 orders/models.py:33 -#, fuzzy -#| msgid "User is inactive" msgid "User display name" -msgstr "用户已禁用" +msgstr "用户显示名称" #: orders/models.py:13 orders/models.py:36 msgid "Body" -msgstr "" +msgstr "内容" -#: orders/models.py:24 -#, fuzzy -#| msgid "Accept" +#: orders/models.py:24 orders/templates/orders/login_confirm_order_list.html:93 msgid "Accepted" -msgstr "接受" +msgstr "已接受" -#: orders/models.py:25 +#: orders/models.py:25 orders/templates/orders/login_confirm_order_list.html:94 msgid "Rejected" -msgstr "拒绝" +msgstr "已拒绝" -#: orders/models.py:35 orders/templates/orders/login_confirm_order_list.html:13 +#: 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 "标题" @@ -3240,14 +3240,14 @@ msgstr "待处理人名称" #: orders/serializers.py:21 #: orders/templates/orders/login_confirm_order_detail.html:94 -#: orders/templates/orders/login_confirm_order_list.html:53 +#: 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:54 +#: orders/templates/orders/login_confirm_order_list.html:60 #: terminal/templates/terminal/terminal_list.html:80 msgid "Reject" msgstr "拒绝" @@ -3256,16 +3256,16 @@ msgstr "拒绝" msgid "this order" msgstr "这个工单" -#: orders/signals_handler.py:21 -#, fuzzy -#| msgid "New node" -msgid "New order" -msgstr "新节点" +#: orders/templates/orders/login_confirm_order_detail.html:75 +msgid "ago" +msgstr "前" -# msgid "Update user" -# msgstr "更新用户" -#: orders/signals_handler.py:24 -#, fuzzy, python-brace-format +#: orders/utils.py:18 +msgid "New order" +msgstr "新工单" + +#: orders/utils.py:21 +#, python-brace-format msgid "" "\n" "
\n" @@ -3275,6 +3275,8 @@ msgid "" "
\n" " User: {user}\n" "
\n" +" Assignees: {order.assignees_display}\n" +"
\n" " City: {order.city}\n" "
\n" " IP: {order.ip}\n" @@ -3292,6 +3294,8 @@ msgstr "" "
\n" " 用户: {user}\n" "
\n" +" 待处理人: {order.assignees_display}\n" +"
\n" " 城市: {order.city}\n" "
\n" " IP: {order.ip}\n" @@ -3301,19 +3305,40 @@ msgstr "" "
\n" " " -#: orders/templates/orders/login_confirm_order_detail.html:75 -msgid "ago" -msgstr "前" +#: orders/utils.py:52 +msgid "Order has been reply" +msgstr "工单已被回复" -#: orders/templates/orders/login_confirm_order_list.html:83 -#: users/templates/users/user_list.html:327 -msgid "User is expired" -msgstr "用户已失效" - -#: orders/templates/orders/login_confirm_order_list.html:86 -#: users/templates/users/user_list.html:330 -msgid "User is inactive" -msgstr "用户已禁用" +#: orders/utils.py:53 +#, 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" @@ -3351,7 +3376,7 @@ msgstr "提示:RDP 协议不支持单独控制上传或下载文件" #: 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 -#: users/models/user.py:381 users/templates/users/_select_user_modal.html:16 +#: 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 #: xpack/plugins/orgs/templates/orgs/org_list.html:16 @@ -3393,7 +3418,7 @@ msgstr "资产授权" #: perms/models/base.py:53 #: perms/templates/perms/asset_permission_detail.html:90 #: perms/templates/perms/remote_app_permission_detail.html:82 -#: users/models/user.py:413 users/templates/users/user_detail.html:107 +#: users/models/user.py:420 users/templates/users/user_detail.html:107 #: users/templates/users/user_profile.html:120 msgid "Date expired" msgstr "失效日期" @@ -3957,7 +3982,7 @@ msgid "Please submit the LDAP configuration before import" msgstr "请先提交LDAP配置再进行导入" #: settings/templates/settings/_ldap_list_users_modal.html:32 -#: users/models/user.py:377 users/templates/users/user_detail.html:71 +#: users/models/user.py:384 users/templates/users/user_detail.html:71 #: users/templates/users/user_profile.html:59 msgid "Email" msgstr "邮件" @@ -4362,7 +4387,7 @@ msgstr "批量命令" msgid "Task monitor" msgstr "任务监控" -#: templates/_nav.html:130 +#: templates/_nav.html:130 users/templates/users/user_detail.html:257 msgid "Login confirm" msgstr "登录复核" @@ -4748,7 +4773,7 @@ msgstr "你可以使用ssh客户端工具连接终端" msgid "Could not reset self otp, use profile reset instead" msgstr "不能再该页面重置MFA, 请去个人信息页面重置" -#: users/forms.py:32 users/models/user.py:385 +#: users/forms.py:32 users/models/user.py:392 #: users/templates/users/_select_user_modal.html:15 #: users/templates/users/user_detail.html:87 #: users/templates/users/user_list.html:37 @@ -4867,50 +4892,50 @@ msgstr "选择用户" msgid "User auth from {}, go there change password" msgstr "用户认证源来自 {}, 请去相应系统修改密码" -#: users/models/user.py:128 users/models/user.py:510 +#: users/models/user.py:135 users/models/user.py:517 msgid "Administrator" msgstr "管理员" -#: users/models/user.py:130 +#: users/models/user.py:137 msgid "Application" msgstr "应用程序" -#: users/models/user.py:131 xpack/plugins/orgs/forms.py:30 +#: users/models/user.py:138 xpack/plugins/orgs/forms.py:30 #: xpack/plugins/orgs/templates/orgs/org_list.html:14 msgid "Auditor" msgstr "审计员" -#: users/models/user.py:141 +#: users/models/user.py:148 msgid "Org admin" msgstr "组织管理员" -#: users/models/user.py:143 +#: users/models/user.py:150 msgid "Org auditor" msgstr "组织审计员" -#: users/models/user.py:334 users/templates/users/user_profile.html:90 +#: users/models/user.py:341 users/templates/users/user_profile.html:90 msgid "Force enable" msgstr "强制启用" -#: users/models/user.py:388 +#: users/models/user.py:395 msgid "Avatar" msgstr "头像" -#: users/models/user.py:391 users/templates/users/user_detail.html:82 +#: users/models/user.py:398 users/templates/users/user_detail.html:82 msgid "Wechat" msgstr "微信" -#: users/models/user.py:420 users/templates/users/user_detail.html:103 +#: users/models/user.py:427 users/templates/users/user_detail.html:103 #: users/templates/users/user_list.html:39 #: users/templates/users/user_profile.html:102 msgid "Source" msgstr "用户来源" -#: users/models/user.py:424 +#: users/models/user.py:431 msgid "Date password last updated" msgstr "最后更新密码日期" -#: users/models/user.py:513 +#: users/models/user.py:520 msgid "Administrator is the super user of system" msgstr "Administrator是初始的超级管理员" @@ -5082,7 +5107,7 @@ msgid "Always young, always with tears in my eyes. Stay foolish Stay hungry" msgstr "永远年轻,永远热泪盈眶 stay foolish stay hungry" #: users/templates/users/reset_password.html:46 -#: users/templates/users/user_detail.html:379 users/utils.py:83 +#: users/templates/users/user_detail.html:430 users/utils.py:83 msgid "Reset password" msgstr "重置密码" @@ -5199,7 +5224,7 @@ msgid "Send reset ssh key mail" msgstr "发送重置密钥邮件" #: users/templates/users/user_detail.html:203 -#: users/templates/users/user_detail.html:467 +#: users/templates/users/user_detail.html:518 msgid "Unblock user" msgstr "解除登录限制" @@ -5207,46 +5232,46 @@ msgstr "解除登录限制" msgid "Unblock" msgstr "解除" -#: users/templates/users/user_detail.html:322 +#: users/templates/users/user_detail.html:373 msgid "Goto profile page enable MFA" msgstr "请去个人信息页面启用自己的MFA" -#: users/templates/users/user_detail.html:378 +#: users/templates/users/user_detail.html:429 msgid "An e-mail has been sent to the user`s mailbox." msgstr "已发送邮件到用户邮箱" -#: users/templates/users/user_detail.html:389 +#: users/templates/users/user_detail.html:440 msgid "This will reset the user password and send a reset mail" msgstr "将失效用户当前密码,并发送重设密码邮件到用户邮箱" -#: users/templates/users/user_detail.html:404 +#: users/templates/users/user_detail.html:455 msgid "" "The reset-ssh-public-key E-mail has been sent successfully. Please inform " "the user to update his new ssh public key." msgstr "重设密钥邮件将会发送到用户邮箱" -#: users/templates/users/user_detail.html:405 +#: users/templates/users/user_detail.html:456 msgid "Reset SSH public key" msgstr "重置SSH密钥" -#: users/templates/users/user_detail.html:415 +#: users/templates/users/user_detail.html:466 msgid "This will reset the user public key and send a reset mail" msgstr "将会失效用户当前密钥,并发送重置邮件到用户邮箱" -#: users/templates/users/user_detail.html:433 +#: users/templates/users/user_detail.html:484 msgid "Successfully updated the SSH public key." msgstr "更新ssh密钥成功" -#: users/templates/users/user_detail.html:434 -#: users/templates/users/user_detail.html:438 +#: users/templates/users/user_detail.html:485 +#: users/templates/users/user_detail.html:489 msgid "User SSH public key update" msgstr "ssh密钥" -#: users/templates/users/user_detail.html:483 +#: users/templates/users/user_detail.html:534 msgid "After unlocking the user, the user can log in normally." msgstr "解除用户登录限制后,此用户即可正常登录" -#: users/templates/users/user_detail.html:497 +#: users/templates/users/user_detail.html:548 msgid "Reset user MFA success" msgstr "重置用户MFA成功" @@ -5299,6 +5324,14 @@ msgstr "删除" msgid "User Deleting failed." msgstr "用户删除失败" +#: users/templates/users/user_list.html:327 +msgid "User is expired" +msgstr "用户已失效" + +#: users/templates/users/user_list.html:330 +msgid "User is inactive" +msgstr "用户已禁用" + #: users/templates/users/user_otp_authentication.html:6 #: users/templates/users/user_password_authentication.html:6 msgid "Authenticate" diff --git a/apps/orders/migrations/0001_initial.py b/apps/orders/migrations/0001_initial.py new file mode 100644 index 000000000..9b1099965 --- /dev/null +++ b/apps/orders/migrations/0001_initial.py @@ -0,0 +1,59 @@ +# Generated by Django 2.2.5 on 2019-10-31 10:23 + +from django.conf import settings +from django.db import migrations, models +import django.db.models.deletion +import uuid + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ] + + operations = [ + migrations.CreateModel( + name='LoginConfirmOrder', + fields=[ + ('id', models.UUIDField(default=uuid.uuid4, primary_key=True, serialize=False)), + ('created_by', models.CharField(blank=True, max_length=32, null=True, verbose_name='Created by')), + ('date_created', models.DateTimeField(auto_now_add=True, null=True, verbose_name='Date created')), + ('date_updated', models.DateTimeField(auto_now=True, verbose_name='Date updated')), + ('user_display', models.CharField(max_length=128, verbose_name='User display name')), + ('title', models.CharField(max_length=256, verbose_name='Title')), + ('body', models.TextField(verbose_name='Body')), + ('assignee_display', models.CharField(blank=True, max_length=128, null=True, verbose_name='Assignee display name')), + ('assignees_display', models.CharField(blank=True, max_length=128, verbose_name='Assignees display name')), + ('type', models.CharField(choices=[('login_confirm', 'Login confirm')], max_length=16, verbose_name='Type')), + ('status', models.CharField(choices=[('accepted', 'Accepted'), ('rejected', 'Rejected'), ('pending', 'Pending')], default='pending', max_length=16)), + ('ip', models.GenericIPAddressField(blank=True, null=True)), + ('city', models.CharField(blank=True, default='', max_length=16)), + ('assignee', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='loginconfirmorder_handled', to=settings.AUTH_USER_MODEL, verbose_name='Assignee')), + ('assignees', models.ManyToManyField(related_name='loginconfirmorder_assigned', to=settings.AUTH_USER_MODEL, verbose_name='Assignees')), + ('user', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='loginconfirmorder_requested', to=settings.AUTH_USER_MODEL, verbose_name='User')), + ], + options={ + 'ordering': ('-date_created',), + 'abstract': False, + }, + ), + migrations.CreateModel( + name='Comment', + fields=[ + ('id', models.UUIDField(default=uuid.uuid4, primary_key=True, serialize=False)), + ('created_by', models.CharField(blank=True, max_length=32, null=True, verbose_name='Created by')), + ('date_created', models.DateTimeField(auto_now_add=True, null=True, verbose_name='Date created')), + ('date_updated', models.DateTimeField(auto_now=True, verbose_name='Date updated')), + ('order_id', models.UUIDField()), + ('user_display', models.CharField(max_length=128, verbose_name='User display name')), + ('body', models.TextField(verbose_name='Body')), + ('user', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='comments', to=settings.AUTH_USER_MODEL, verbose_name='User')), + ], + options={ + 'ordering': ('date_created',), + }, + ), + ] diff --git a/apps/orders/models.py b/apps/orders/models.py index c574cacb9..3ed12c8c8 100644 --- a/apps/orders/models.py +++ b/apps/orders/models.py @@ -48,6 +48,14 @@ class BaseOrder(CommonModelMixin): def comments(self): return Comment.objects.filter(order_id=self.id) + @property + def body_as_html(self): + return self.body.replace('\n', '
') + + @property + def status_display(self): + return self.get_status_display() + class Meta: abstract = True ordering = ('-date_created',) diff --git a/apps/orders/signals_handler.py b/apps/orders/signals_handler.py index 9e2cdd2e7..60db0c5e5 100644 --- a/apps/orders/signals_handler.py +++ b/apps/orders/signals_handler.py @@ -1,59 +1,32 @@ # -*- coding: utf-8 -*- # -from django.utils.translation import ugettext as _ from django.dispatch import receiver -from django.db.models.signals import m2m_changed -from django.conf import settings +from django.db.models.signals import m2m_changed, post_save -from common.tasks import send_mail_async -from common.utils import get_logger, reverse +from common.utils import get_logger from .models import LoginConfirmOrder +from .utils import ( + send_login_confirm_order_mail_to_assignees, + send_login_confirm_action_mail_to_user +) + logger = get_logger(__name__) -def send_mail(order, assignees): - recipient_list = [user.email for user in assignees] - user = order.user - if not recipient_list: - logger.error("Order not has assignees: {}".format(order.id)) - return - subject = '{}: {}'.format(_("New order"), order.title) - detail_url = reverse('orders:login-confirm-order-detail', - kwargs={'pk': order.id}, external=True) - message = _(""" -
-

Your has a new order

-
- Title: {order.title} -
- User: {user} -
- City: {order.city} -
- IP: {order.ip} -
- click here to review -
-
- """).format(order=order, user=user, url=detail_url) - if settings.DEBUG: - try: - print(message) - except OSError: - pass - - send_mail_async.delay(subject, message, recipient_list, html_message=message) - - @receiver(m2m_changed, sender=LoginConfirmOrder.assignees.through) -def on_login_confirm_order_assignee_set(sender, instance=None, action=None, +def on_login_confirm_order_assignees_set(sender, instance=None, action=None, model=None, pk_set=None, **kwargs): - print(">>>>>>>>>>>>>>>>>>>>>>>.") - print(action) if action == 'post_add': - print("<<<<<<<<<<<<<<<<<<<<") logger.debug('New order create, send mail: {}'.format(instance.id)) assignees = model.objects.filter(pk__in=pk_set) - send_mail(instance, assignees) + print(assignees) + send_login_confirm_order_mail_to_assignees(instance, assignees) + +@receiver(post_save, sender=LoginConfirmOrder) +def on_login_confirm_order_status_change(sender, instance=None, created=False, **kwargs): + if created or instance.status == "pending": + return + logger.debug('Order changed, send mail: {}'.format(instance.id)) + send_login_confirm_action_mail_to_user(instance) diff --git a/apps/orders/templates/orders/login_confirm_order_list.html b/apps/orders/templates/orders/login_confirm_order_list.html index e7b8da90c..c54743c85 100644 --- a/apps/orders/templates/orders/login_confirm_order_list.html +++ b/apps/orders/templates/orders/login_confirm_order_list.html @@ -14,7 +14,6 @@ {% trans 'Title' %} {% trans 'User' %} {% trans 'IP' %} - {% trans 'City' %} {% trans 'Status' %} {% trans 'Datetime' %} {% trans 'Action' %} @@ -38,8 +37,12 @@ function initTable() { cellData = htmlEscape(cellData); var detailBtn = '' + cellData + ''; $(td).html(detailBtn.replace("{{ DEFAULT_PK }}", rowData.id)); - }}, - {targets: 5, createdCell: function (td, cellData, rowData) { + }}, + {targets: 3, createdCell: function (td, cellData, rowData) { + var d = cellData + "(" + rowData.city + ")"; + $(td).html(d) + }}, + {targets: 4, createdCell: function (td, cellData, rowData) { if (cellData === "accepted") { $(td).html('') } else if (cellData === "rejected") { @@ -48,13 +51,13 @@ function initTable() { $(td).html('') } }}, - {targets: 6, createdCell: function (td, cellData) { + {targets: 5, createdCell: function (td, cellData) { var d = toSafeLocalDateStr(cellData); $(td).html(d) }}, - {targets: 7, createdCell: function (td, cellData, rowData) { - var acceptBtn = '{% trans "Accept" %} '; - var rejectBtn = '{% trans "Reject" %}'; + {targets: 6, createdCell: function (td, cellData, rowData) { + var acceptBtn = '{% trans "Accept" %} '; + var rejectBtn = '{% trans "Reject" %}'; acceptBtn = acceptBtn.replace('{{ DEFAULT_PK }}', cellData); rejectBtn = rejectBtn.replace('{{ DEFAULT_PK }}', cellData); var acceptBtnRef = $(acceptBtn); @@ -68,10 +71,10 @@ function initTable() { }}], ajax_url: '{% url "api-orders:login-confirm-order-list" %}', columns: [ - {data: "id"}, {data: "title", className: "text-left"}, {data: "user_display"}, - {data: "ip"}, {data: "city"}, - {data: "status", orderable: false}, - {data: "date_created"}, + {data: "id"}, {data: "title", className: "text-left"}, + {data: "user_display"}, {data: "ip"}, + {data: "status", orderable: false, width: "30px"}, + {data: "date_created", width: "120px"}, {data: "id", orderable: false, width: "100px"} ], op_html: $('#actions').html() @@ -82,7 +85,6 @@ function initTable() { $(document).ready(function(){ initTable(); - $('') var menu = [ {title: "IP", value: "ip"}, {title: "{% trans 'Title' %}", value: "title"}, @@ -93,12 +95,21 @@ $(document).ready(function(){ ]} ]; initTableFilterDropdown('#login_confirm_order_list_table_filter input', menu) -}).on('click', '.expired', function () { - var msg = '{% trans "User is expired" %}'; - toastr.error(msg) -}).on('click', '.inactive', function () { - var msg = '{% trans 'User is inactive' %}'; - toastr.error(msg) +}).on('click', '.btn-action', function () { + var actionCreateUrl = "{% url 'api-orders:login-confirm-order-create-action' pk=DEFAULT_PK %}"; + var orderId = $(this).data('uid'); + actionCreateUrl = actionCreateUrl.replace("{{ DEFAULT_PK }}", orderId); + var action = $(this).data('action'); + var comment = ''; + var data = { + url: actionCreateUrl, + method: 'POST', + body: JSON.stringify({action: action, comment: comment}), + success: function () { + window.location.reload(); + } + }; + requestApi(data); }) {% endblock %} diff --git a/apps/orders/utils.py b/apps/orders/utils.py index ec51c5a2b..6fd3d965d 100644 --- a/apps/orders/utils.py +++ b/apps/orders/utils.py @@ -1,2 +1,62 @@ # -*- coding: utf-8 -*- # +from django.conf import settings +from django.utils.translation import ugettext as _ + +from common.utils import get_logger, reverse +from common.tasks import send_mail_async + +logger = get_logger(__name__) + + +def send_login_confirm_order_mail_to_assignees(order, assignees): + recipient_list = [user.email for user in assignees] + user = order.user + if not recipient_list: + logger.error("Order not has assignees: {}".format(order.id)) + return + subject = '{}: {}'.format(_("New order"), order.title) + detail_url = reverse('orders:login-confirm-order-detail', + kwargs={'pk': order.id}, external=True) + message = _(""" +
+

Your has a new order

+
+ Title: {order.title} +
+ User: {user} +
+ Assignees: {order.assignees_display} +
+ City: {order.city} +
+ IP: {order.ip} +
+ click here to review +
+
+ """).format(order=order, user=user, url=detail_url) + send_mail_async.delay(subject, message, recipient_list, html_message=message) + + +def send_login_confirm_action_mail_to_user(order): + if not order.user: + logger.error("Order not has user: {}".format(order.id)) + return + user = order.user + recipient_list = [user.email] + subject = '{}: {}'.format(_("Order has been reply"), order.title) + message = _(""" +
+

Your order has been replay

+
+ Title: {order.title} +
+ Assignee: {order.assignee_display} +
+ Status: {order.status_display} +
+
+
+ """).format(order=order) + send_mail_async.delay(subject, message, recipient_list, html_message=message) diff --git a/apps/static/js/jumpserver.js b/apps/static/js/jumpserver.js index 89ac43364..8fddac5ed 100644 --- a/apps/static/js/jumpserver.js +++ b/apps/static/js/jumpserver.js @@ -416,6 +416,9 @@ function makeLabel(data) { function parseTableFilter(value) { var cleanValues = []; + if (!value) { + return {} + } var valuesArray = value.split(':'); for (var i=0; i {% include '_copyright.html' %}
-
- 2014-2019 -
diff --git a/apps/users/models/user.py b/apps/users/models/user.py index 4c7c54def..36a60abc8 100644 --- a/apps/users/models/user.py +++ b/apps/users/models/user.py @@ -117,6 +117,13 @@ class AuthMixin: return True return False + def get_login_confirm_setting(self): + if hasattr(self, 'login_confirm_setting'): + s = self.login_confirm_setting + if s.reviewers.all().count() and s.is_active: + return s + return False + class RoleMixin: ROLE_ADMIN = 'Admin' diff --git a/apps/users/templates/users/user_detail.html b/apps/users/templates/users/user_detail.html index 8e049938c..b57480b18 100644 --- a/apps/users/templates/users/user_detail.html +++ b/apps/users/templates/users/user_detail.html @@ -7,6 +7,7 @@ + {% endblock %} {% block content %}
@@ -212,46 +213,82 @@
- {% if user_object.is_current_org_admin %} -
-
- {% trans 'User group' %} -
-
- - - - - - - - - - - - {% for group in user_object.groups.all %} - - - - - {% endfor %} - -
- -
- -
- {{ group.name }} - - -
-
+ {% if user_object.is_current_org_admin or user_object.is_superuser %} +
+
+ {% trans 'User group' %}
+
+ + + + + + + + + + + + {% for group in user_object.groups.all %} + + + + + {% endfor %} + +
+ +
+ +
+ {{ group.name }} + + +
+
+
{% endif %} +
+
+ {% trans 'Login confirm' %} +
+
+ + + + + + + + + + + {% if user_object.get_login_confirm_setting %} + {% for u in user_object.login_confirm_setting.reviewers.all %} + + + + + {% endfor %} + {% endif %} + +
+ +
+ +
+ {{ u }} + + +
+
+
@@ -263,6 +300,7 @@ {% block custom_foot_js %} {% endblock %}