[Update] 基本完成登陆审核

pull/3428/head
ibuler 2019-10-31 18:23:43 +08:00
parent 23b777b23b
commit dc3a9561c2
19 changed files with 532 additions and 242 deletions

View File

@ -5,3 +5,4 @@ from .auth import *
from .token import *
from .mfa import *
from .access_key import *
from .login_confirm import *

View File

@ -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

View File

@ -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,
},
),
]

View File

@ -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)

View File

@ -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']

View File

@ -61,9 +61,6 @@
<div class="col-md-6">
{% include '_copyright.html' %}
</div>
<div class="col-md-6 text-right">
<small>2014-2019</small>
</div>
</div>
</div>
</body>

View File

@ -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/<uuid:user_id>/', api.LoginConfirmSettingUpdateApi.as_view(), name='login-confirm-setting-update')
]
urlpatterns += router.urls

View File

@ -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)

Binary file not shown.

View File

@ -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 <ibuler@qq.com>\n"
"Language-Team: Jumpserver team<ibuler@qq.com>\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,</div> </div>If you see this page, "
"prove that you are not accessing the nginx listening port. Good luck.</div>"
msgstr ""
"<div>Koko是单独部署的一个程序你需要部署Koko, 并确保nginx配置转发, </div><div>如果你看到了"
"这个页面证明你访问的不是nginx监听的端口祝你好运</div>"
"<div>Koko是单独部署的一个程序你需要部署Koko, 并确保nginx配置转发, </"
"div><div>如果你看到了这个页面证明你访问的不是nginx监听的端口祝你好运</"
"div>"
#: 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"
" <div>\n"
@ -3275,6 +3275,8 @@ msgid ""
" <br/>\n"
" <b>User:</b> {user}\n"
" <br/>\n"
" <b>Assignees:</b> {order.assignees_display}\n"
" <br/>\n"
" <b>City:</b> {order.city}\n"
" <br/>\n"
" <b>IP:</b> {order.ip}\n"
@ -3292,6 +3294,8 @@ msgstr ""
" <br/>\n"
" <b>用户:</b> {user}\n"
" <br/>\n"
" <b>待处理人:</b> {order.assignees_display}\n"
" <br/>\n"
" <b>城市:</b> {order.city}\n"
" <br/>\n"
" <b>IP:</b> {order.ip}\n"
@ -3301,19 +3305,40 @@ msgstr ""
" </div>\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"
" <div>\n"
" <p>Your order has been replay</p>\n"
" <div>\n"
" <b>Title:</b> {order.title}\n"
" <br/>\n"
" <b>Assignee:</b> {order.assignee_display}\n"
" <br/>\n"
" <b>Status:</b> {order.status_display}\n"
" <br/>\n"
" </div>\n"
" </div>\n"
" "
msgstr ""
"\n"
" <div>\n"
" <p>您的工单已被回复</p>\n"
" <div>\n"
" <b>标题:</b> {order.title}\n"
" <br/>\n"
" <b>处理人:</b> {order.assignee_display}\n"
" <br/>\n"
" <b>状态:</b> {order.status_display}\n"
" <br/>\n"
" </div>\n"
" </div>\n"
" "
#: orders/views.py:15 orders/views.py:31 templates/_nav.html:127
msgid "Orders"
@ -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"

View File

@ -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',),
},
),
]

View File

@ -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', '<br/>')
@property
def status_display(self):
return self.get_status_display()
class Meta:
abstract = True
ordering = ('-date_created',)

View File

@ -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 = _("""
<div>
<p>Your has a new order</p>
<div>
<b>Title:</b> {order.title}
<br/>
<b>User:</b> {user}
<br/>
<b>City:</b> {order.city}
<br/>
<b>IP:</b> {order.ip}
<br/>
<a href={url}>click here to review</a>
</div>
</div>
""").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)

View File

@ -14,7 +14,6 @@
<th class="text-center">{% trans 'Title' %}</th>
<th class="text-center">{% trans 'User' %}</th>
<th class="text-center">{% trans 'IP' %}</th>
<th class="text-center">{% trans 'City' %}</th>
<th class="text-center">{% trans 'Status' %}</th>
<th class="text-center">{% trans 'Datetime' %}</th>
<th class="text-center">{% trans 'Action' %}</th>
@ -38,8 +37,12 @@ function initTable() {
cellData = htmlEscape(cellData);
var detailBtn = '<a href="{% url "orders:login-confirm-order-detail" pk=DEFAULT_PK %}">' + cellData + '</a>';
$(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('<i class="fa fa-check text-navy"></i>')
} else if (cellData === "rejected") {
@ -48,13 +51,13 @@ function initTable() {
$(td).html('<i class="fa fa-spinner text-info"></i>')
}
}},
{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 = '<a class="btn btn-xs btn-info" data-uid="{{ DEFAULT_PK }}" >{% trans "Accept" %}</a> ';
var rejectBtn = '<a class="btn btn-xs btn-danger " data-uid="{{ DEFAULT_PK }}" >{% trans "Reject" %}</a>';
{targets: 6, createdCell: function (td, cellData, rowData) {
var acceptBtn = '<a class="btn btn-xs btn-info btn-action" data-action="accept" data-uid="{{ DEFAULT_PK }}" >{% trans "Accept" %}</a> ';
var rejectBtn = '<a class="btn btn-xs btn-danger btn-action" data-action="reject" data-uid="{{ DEFAULT_PK }}" >{% trans "Reject" %}</a>';
acceptBtn = acceptBtn.replace('{{ DEFAULT_PK }}', cellData);
rejectBtn = rejectBtn.replace('{{ DEFAULT_PK }}', cellData);
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);
})
</script>
{% endblock %}

View File

@ -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 = _("""
<div>
<p>Your has a new order</p>
<div>
<b>Title:</b> {order.title}
<br/>
<b>User:</b> {user}
<br/>
<b>Assignees:</b> {order.assignees_display}
<br/>
<b>City:</b> {order.city}
<br/>
<b>IP:</b> {order.ip}
<br/>
<a href={url}>click here to review</a>
</div>
</div>
""").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 = _("""
<div>
<p>Your order has been replay</p>
<div>
<b>Title:</b> {order.title}
<br/>
<b>Assignee:</b> {order.assignee_display}
<br/>
<b>Status:</b> {order.status_display}
<br/>
</div>
</div>
""").format(order=order)
send_mail_async.delay(subject, message, recipient_list, html_message=message)

View File

@ -416,6 +416,9 @@ function makeLabel(data) {
function parseTableFilter(value) {
var cleanValues = [];
if (!value) {
return {}
}
var valuesArray = value.split(':');
for (var i=0; i<valuesArray.length; i++) {
var v = valuesArray[i].trim();

View File

@ -55,9 +55,6 @@
<div class="col-md-6">
{% include '_copyright.html' %}
</div>
<div class="col-md-6 text-right">
<small>2014-2019</small>
</div>
</div>
</div>
</body>

View File

@ -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'

View File

@ -7,6 +7,7 @@
<link href="{% static "css/plugins/sweetalert/sweetalert.css" %}" rel="stylesheet">
<script src="{% static 'js/plugins/select2/select2.full.min.js' %}"></script>
<script src="{% static "js/plugins/sweetalert/sweetalert.min.js" %}"></script>
<script src="{% static "js/vue.min.js" %}"></script>
{% endblock %}
{% block content %}
<div class="wrapper wrapper-content animated fadeInRight">
@ -212,46 +213,82 @@
</div>
</div>
{% if user_object.is_current_org_admin %}
<div class="panel panel-info">
<div class="panel-heading">
<i class="fa fa-info-circle"></i> {% trans 'User group' %}
</div>
<div class="panel-body">
<table class="table group_edit">
<tbody>
<form>
<tr>
<td colspan="2" class="no-borders">
<select data-placeholder="{% trans 'Join user groups' %}" id="groups_selected" class="select2" style="width: 100%" multiple="" tabindex="4">
{% for group in groups %}
<option value="{{ group.id }}" id="opt_{{ group.id }}" >{{ group.name }}</option>
{% endfor %}
</select>
</td>
</tr>
<tr>
<td colspan="2" class="no-borders">
<button type="button" class="btn btn-info btn-small" id="btn_join_group">{% trans 'Join' %}</button>
</td>
</tr>
</form>
{% for group in user_object.groups.all %}
<tr>
<td >
<b class="bdg_group" data-gid={{ group.id }}>{{ group.name }}</b>
</td>
<td>
<button class="btn btn-danger pull-right btn-xs btn_leave_group" type="button"><i class="fa fa-minus"></i></button>
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
{% if user_object.is_current_org_admin or user_object.is_superuser %}
<div class="panel panel-info">
<div class="panel-heading">
<i class="fa fa-info-circle"></i> {% trans 'User group' %}
</div>
<div class="panel-body">
<table class="table group_edit">
<tbody>
<form>
<tr>
<td colspan="2" class="no-borders">
<select data-placeholder="{% trans 'Join user groups' %}" id="groups_selected" class="select2" style="width: 100%" multiple="" tabindex="4">
{% for group in groups %}
<option value="{{ group.id }}" id="opt_{{ group.id }}" >{{ group.name }}</option>
{% endfor %}
</select>
</td>
</tr>
<tr>
<td colspan="2" class="no-borders">
<button type="button" class="btn btn-info btn-small" id="btn_join_group">{% trans 'Join' %}</button>
</td>
</tr>
</form>
{% for group in user_object.groups.all %}
<tr>
<td >
<b class="bdg_group" data-gid={{ group.id }}>{{ group.name }}</b>
</td>
<td>
<button class="btn btn-danger pull-right btn-xs btn_leave_group" type="button"><i class="fa fa-minus"></i></button>
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
{% endif %}
<div class="panel panel-warning">
<div class="panel-heading">
<i class="fa fa-info-circle"></i> {% trans 'Login confirm' %}
</div>
<div class="panel-body">
<table class="table">
<tbody>
<form>
<tr>
<td colspan="2" class="no-borders">
<select data-placeholder="{% trans 'Reviewers' %}" id="id_assignees" class="users-select2" style="width: 100%" multiple="" tabindex="4">
</select>
</td>
</tr>
<tr>
<td colspan="2" class="no-borders">
<button type="button" class="btn btn-warning btn-small" id="btn_reviewer_confirm">{% trans 'Confirm' %}</button>
</td>
</tr>
</form>
{% if user_object.get_login_confirm_setting %}
{% for u in user_object.login_confirm_setting.reviewers.all %}
<tr>
<td >
<b class="bdg_reviewer">{{ u }}</b>
</td>
<td>
<button class="btn btn-danger pull-right btn-xs btn-leave-reviewer" data-uid={{ u.id }} type="button"><i class="fa fa-minus"></i></button>
</td>
</tr>
{% endfor %}
{% endif %}
</tbody>
</table>
</div>
</div>
</div>
</div>
</div>
@ -263,6 +300,7 @@
{% block custom_foot_js %}
<script>
jumpserver.nodes_selected = {};
var usersSelect2;
function updateUserGroups(groups) {
var the_url = "{% url 'api-users:user-update-group' pk=user_object.id %}";
@ -293,6 +331,19 @@ function updateUserGroups(groups) {
});
}
function updateUserLoginReviewer(reviewers) {
var url = "{% url 'api-auth:login-confirm-setting-update' user_id=user_object.id %}";
var data = {reviewers: reviewers};
requestApi({
url: url,
data: JSON.stringify(data),
method: 'PATCH',
success: function () {
window.location.reload();
}
})
}
$(document).ready(function() {
$('.select2').select2()
.on('select2:select', function(evt) {
@ -303,6 +354,7 @@ $(document).ready(function() {
var data = evt.params.data;
delete jumpserver.nodes_selected[data.id];
});
usersSelect2 = usersSelect2Init('.users-select2')
})
.on('click', '#is_active', function() {
var the_url = "{% url 'api-users:user-detail' pk=user_object.id %}";
@ -496,6 +548,32 @@ $(document).ready(function() {
method: "GET",
success_message: "{% trans 'Reset user MFA success' %}"
})
}).on('click', '.btn-leave-reviewer', function () {
var reviewersId = [];
var removeReviewerId = $(this).data('uid');
$('.btn-leave-reviewer').each(function (i, v) {
var reviewerId = $(v).data('uid');
if (reviewerId !== removeReviewerId) {
reviewersId.push(reviewerId);
}
});
updateUserLoginReviewer(reviewersId);
}).on('click', '#btn_reviewer_confirm', function () {
var reviewersId = [];
$('.btn-leave-reviewer').each(function (i, v) {
var reviewerId = $(v).data('uid');
reviewersId.push(reviewerId);
});
var selectedId = usersSelect2.val();
if (selectedId.length === 0) {
return
}
selectedId.forEach(function (i) {
if (reviewersId.indexOf(i) === -1) {
reviewersId.push(i)
}
});
updateUserLoginReviewer(reviewersId);
})
</script>
{% endblock %}