diff --git a/apps/assets/models/authbook.py b/apps/assets/models/authbook.py
index a040bbd0d..94e4f4cf4 100644
--- a/apps/assets/models/authbook.py
+++ b/apps/assets/models/authbook.py
@@ -78,7 +78,7 @@ class AuthBook(AssetUser):
if host == self.asset.hostname:
_connectivity = self.UNREACHABLE
- for host in value.get('contacted', {}).keys():
+ for host in value.get('contacted', []):
if host == self.asset.hostname:
_connectivity = self.REACHABLE
diff --git a/apps/assets/templates/assets/_asset_user_view_auth_modal.html b/apps/assets/templates/assets/_asset_user_view_auth_modal.html
new file mode 100644
index 000000000..07ac34ce5
--- /dev/null
+++ b/apps/assets/templates/assets/_asset_user_view_auth_modal.html
@@ -0,0 +1,140 @@
+{% extends '_modal.html' %}
+{% load i18n %}
+{% load static %}
+{% block modal_id %}asset_user_auth_view{% endblock %}
+{% block modal_title%}{% trans "Asset user auth" %}{% endblock %}
+{% block modal_body %}
+
+
+
+
+{% endblock %}
+{% block modal_button %}
+
+{% endblock %}
diff --git a/apps/assets/templates/assets/admin_user_assets.html b/apps/assets/templates/assets/admin_user_assets.html
index c893ead80..328b21579 100644
--- a/apps/assets/templates/assets/admin_user_assets.html
+++ b/apps/assets/templates/assets/admin_user_assets.html
@@ -85,6 +85,7 @@
{% include 'assets/_asset_user_auth_modal.html' %}
+ {% include 'assets/_asset_user_view_auth_modal.html' %}
{% endblock %}
{% block custom_foot_js %}
{% endblock %}
diff --git a/apps/assets/templates/assets/asset_asset_user_list.html b/apps/assets/templates/assets/asset_asset_user_list.html
index 9a330ce77..ef192d790 100644
--- a/apps/assets/templates/assets/asset_asset_user_list.html
+++ b/apps/assets/templates/assets/asset_asset_user_list.html
@@ -2,10 +2,6 @@
{% load common_tags %}
{% load static %}
{% load i18n %}
-
-{% block custom_head_css_js %}
-{% endblock %}
-
{% block content %}
{% include 'assets/_asset_user_auth_modal.html' %}
+ {% include 'assets/_asset_user_view_auth_modal.html' %}
{% endblock %}
{% block custom_foot_js %}
{% endblock %}
\ No newline at end of file
diff --git a/apps/assets/templates/assets/system_user_asset.html b/apps/assets/templates/assets/system_user_asset.html
index 082e13fd8..f5df32bf5 100644
--- a/apps/assets/templates/assets/system_user_asset.html
+++ b/apps/assets/templates/assets/system_user_asset.html
@@ -133,6 +133,7 @@
{% include 'assets/_asset_user_auth_modal.html' %}
+ {% include 'assets/_asset_user_view_auth_modal.html' %}
{% endblock %}
{% block custom_foot_js %}
{% endblock %}
diff --git a/apps/authentication/api/auth.py b/apps/authentication/api/auth.py
index 2d9cfe367..42c196c51 100644
--- a/apps/authentication/api/auth.py
+++ b/apps/authentication/api/auth.py
@@ -2,6 +2,7 @@
#
import uuid
+import time
from django.core.cache import cache
from django.urls import reverse
@@ -10,10 +11,11 @@ from django.utils.translation import ugettext as _
from rest_framework.permissions import AllowAny
from rest_framework.response import Response
+from rest_framework.generics import CreateAPIView
from rest_framework.views import APIView
from common.utils import get_logger, get_request_ip
-from common.permissions import IsOrgAdminOrAppUser
+from common.permissions import IsOrgAdminOrAppUser, IsValidUser
from orgs.mixins import RootOrgViewMixin
from users.serializers import UserSerializer
from users.models import User
@@ -23,12 +25,13 @@ from users.utils import (
check_user_valid, check_otp_code, increase_login_failed_count,
is_block_login, clean_failed_count
)
-
+from ..serializers import OtpVerifySerializer
from ..signals import post_auth_success, post_auth_failed
logger = get_logger(__name__)
__all__ = [
'UserAuthApi', 'UserConnectionTokenApi', 'UserOtpAuthApi',
+ 'UserOtpVerifyApi',
]
@@ -179,3 +182,20 @@ class UserOtpAuthApi(RootOrgViewMixin, APIView):
sender=self.__class__, username=username,
request=self.request, reason=reason
)
+
+
+class UserOtpVerifyApi(CreateAPIView):
+ permission_classes = (IsValidUser,)
+ serializer_class = OtpVerifySerializer
+
+ def create(self, request, *args, **kwargs):
+ serializer = self.get_serializer(data=request.data)
+ serializer.is_valid(raise_exception=True)
+ code = serializer.validated_data["code"]
+
+ if request.user.check_otp(code):
+ request.session["OTP_LAST_VERIFY_TIME"] = int(time.time())
+ return Response({"ok": "1"})
+ else:
+ return Response({"error": "Code not valid"}, status=400)
+
diff --git a/apps/authentication/serializers.py b/apps/authentication/serializers.py
index cf4968a56..2033b3e44 100644
--- a/apps/authentication/serializers.py
+++ b/apps/authentication/serializers.py
@@ -14,3 +14,7 @@ class AccessKeySerializer(serializers.ModelSerializer):
model = AccessKey
fields = ['id', 'secret']
read_only_fields = ['id', 'secret']
+
+
+class OtpVerifySerializer(serializers.Serializer):
+ code = serializers.CharField(max_length=6, min_length=6)
diff --git a/apps/authentication/urls/api_urls.py b/apps/authentication/urls/api_urls.py
index b22c49884..85f3aa522 100644
--- a/apps/authentication/urls/api_urls.py
+++ b/apps/authentication/urls/api_urls.py
@@ -16,5 +16,6 @@ urlpatterns = [
path('connection-token/',
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'),
]
diff --git a/apps/common/permissions.py b/apps/common/permissions.py
index 689444131..025d44ba3 100644
--- a/apps/common/permissions.py
+++ b/apps/common/permissions.py
@@ -1,5 +1,6 @@
# -*- coding: utf-8 -*-
#
+import time
from rest_framework import permissions
from django.contrib.auth.mixins import UserPassesTestMixin
diff --git a/apps/common/utils/common.py b/apps/common/utils/common.py
index dcd7daf16..79146c039 100644
--- a/apps/common/utils/common.py
+++ b/apps/common/utils/common.py
@@ -144,6 +144,7 @@ def is_uuid(seq):
def get_request_ip(request):
x_forwarded_for = request.META.get('HTTP_X_FORWARDED_FOR', '').split(',')
+
if x_forwarded_for and x_forwarded_for[0]:
login_ip = x_forwarded_for[0]
else:
diff --git a/apps/jumpserver/conf.py b/apps/jumpserver/conf.py
index ee6dcdbfd..35254d1ca 100644
--- a/apps/jumpserver/conf.py
+++ b/apps/jumpserver/conf.py
@@ -194,7 +194,7 @@ class Config(dict):
filename = os.path.join(self.root_path, filename)
try:
with open(filename, 'rt', encoding='utf8') as f:
- obj = yaml.load(f)
+ obj = yaml.safe_load(f)
except IOError as e:
if silent and e.errno in (errno.ENOENT, errno.EISDIR):
return False
diff --git a/apps/locale/zh/LC_MESSAGES/django.mo b/apps/locale/zh/LC_MESSAGES/django.mo
index 52ca4190e..4d188c93e 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 17e132ca0..1f39462f8 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-04-29 12:22+0800\n"
+"POT-Creation-Date: 2019-05-20 11:19+0800\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: ibuler \n"
"Language-Team: Jumpserver team\n"
@@ -21,15 +21,15 @@ msgstr ""
msgid "Please select assets that need to be updated"
msgstr "请选择需要更新的资产"
-#: assets/api/node.py:58
+#: assets/api/node.py:60
msgid "You can't update the root node name"
msgstr "不能修改根节点名称"
-#: assets/api/node.py:282
+#: assets/api/node.py:285
msgid "Update node asset hardware information: {}"
msgstr "更新节点资产硬件信息: {}"
-#: assets/api/node.py:296
+#: assets/api/node.py:299
msgid "Test if the assets under the node are connectable: {}"
msgstr "测试节点下资产是否可连接: {}"
@@ -45,7 +45,7 @@ msgstr "节点管理"
#: assets/models/cluster.py:19 assets/models/user.py:91
#: assets/templates/assets/asset_detail.html:80 templates/_nav.html:24
#: xpack/plugins/cloud/models.py:124
-#: xpack/plugins/cloud/templates/cloud/sync_instance_task_detail.html:67
+#: xpack/plugins/cloud/templates/cloud/sync_instance_task_detail.html:65
#: xpack/plugins/orgs/templates/orgs/org_list.html:18
msgid "Admin user"
msgstr "管理用户"
@@ -81,8 +81,8 @@ msgstr "网域"
#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_execution_list.html:55
#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_list.html:15
#: xpack/plugins/cloud/models.py:123
-#: xpack/plugins/cloud/templates/cloud/sync_instance_task_detail.html:63
-#: xpack/plugins/cloud/templates/cloud/sync_instance_task_instance.html:66
+#: xpack/plugins/cloud/templates/cloud/sync_instance_task_detail.html:61
+#: xpack/plugins/cloud/templates/cloud/sync_instance_task_instance.html:64
msgid "Node"
msgstr "节点"
@@ -136,7 +136,7 @@ msgstr "选择资产"
#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_execution_subtask_list.html:13
#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_list.html:14
#: xpack/plugins/cloud/models.py:187
-#: xpack/plugins/cloud/templates/cloud/sync_instance_task_instance.html:65
+#: xpack/plugins/cloud/templates/cloud/sync_instance_task_instance.html:63
#: xpack/plugins/orgs/templates/orgs/org_list.html:16
msgid "Asset"
msgstr "资产"
@@ -188,9 +188,9 @@ msgstr "SSH网关,支持代理SSH,RDP和VNC"
#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_detail.html:61
#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_list.html:12
#: xpack/plugins/cloud/models.py:49 xpack/plugins/cloud/models.py:119
-#: xpack/plugins/cloud/templates/cloud/account_detail.html:52
+#: xpack/plugins/cloud/templates/cloud/account_detail.html:50
#: xpack/plugins/cloud/templates/cloud/account_list.html:12
-#: xpack/plugins/cloud/templates/cloud/sync_instance_task_detail.html:55
+#: xpack/plugins/cloud/templates/cloud/sync_instance_task_detail.html:53
#: xpack/plugins/cloud/templates/cloud/sync_instance_task_list.html:12
#: xpack/plugins/orgs/templates/orgs/org_detail.html:52
#: xpack/plugins/orgs/templates/orgs/org_list.html:12
@@ -200,9 +200,10 @@ msgstr "名称"
#: assets/forms/domain.py:74 assets/forms/user.py:85 assets/forms/user.py:147
#: assets/models/base.py:27
#: assets/templates/assets/_asset_user_auth_modal.html:15
+#: assets/templates/assets/_asset_user_view_auth_modal.html:31
#: assets/templates/assets/admin_user_detail.html:60
#: assets/templates/assets/admin_user_list.html:27
-#: assets/templates/assets/asset_asset_user_list.html:48
+#: assets/templates/assets/asset_asset_user_list.html:44
#: assets/templates/assets/domain_gateway_list.html:71
#: assets/templates/assets/system_user_detail.html:62
#: assets/templates/assets/system_user_list.html:30 audits/models.py:94
@@ -233,6 +234,7 @@ msgstr "密码或密钥密码"
#: assets/forms/user.py:26 assets/models/base.py:28
#: assets/serializers/asset_user.py:19
#: assets/templates/assets/_asset_user_auth_modal.html:21
+#: assets/templates/assets/_asset_user_view_auth_modal.html:37
#: authentication/forms.py:13
#: authentication/templates/authentication/login.html:67
#: authentication/templates/authentication/new_login.html:93
@@ -313,6 +315,7 @@ msgstr "IP"
#: assets/models/asset.py:75 assets/templates/assets/_asset_list_modal.html:45
#: assets/templates/assets/_asset_user_auth_modal.html:9
+#: assets/templates/assets/_asset_user_view_auth_modal.html:25
#: assets/templates/assets/admin_user_assets.html:48
#: assets/templates/assets/asset_detail.html:60
#: assets/templates/assets/asset_list.html:92
@@ -461,8 +464,8 @@ msgstr "创建者"
#: users/templates/users/user_group_detail.html:63
#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_detail.html:105
#: xpack/plugins/cloud/models.py:56 xpack/plugins/cloud/models.py:128
-#: xpack/plugins/cloud/templates/cloud/account_detail.html:68
-#: xpack/plugins/cloud/templates/cloud/sync_instance_task_detail.html:79
+#: xpack/plugins/cloud/templates/cloud/account_detail.html:66
+#: xpack/plugins/cloud/templates/cloud/sync_instance_task_detail.html:77
#: xpack/plugins/orgs/templates/orgs/org_detail.html:60
msgid "Date created"
msgstr "创建日期"
@@ -495,9 +498,9 @@ msgstr "创建日期"
#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_detail.html:117
#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_list.html:19
#: xpack/plugins/cloud/models.py:54 xpack/plugins/cloud/models.py:125
-#: xpack/plugins/cloud/templates/cloud/account_detail.html:72
+#: xpack/plugins/cloud/templates/cloud/account_detail.html:70
#: xpack/plugins/cloud/templates/cloud/account_list.html:15
-#: xpack/plugins/cloud/templates/cloud/sync_instance_task_detail.html:71
+#: xpack/plugins/cloud/templates/cloud/sync_instance_task_detail.html:69
#: xpack/plugins/cloud/templates/cloud/sync_instance_task_list.html:16
#: xpack/plugins/orgs/templates/orgs/org_detail.html:64
#: xpack/plugins/orgs/templates/orgs/org_list.html:22
@@ -513,7 +516,7 @@ msgstr "不可达"
#: assets/models/asset.py:118 assets/models/base.py:39
#: assets/templates/assets/admin_user_assets.html:51
#: assets/templates/assets/admin_user_list.html:29
-#: assets/templates/assets/asset_asset_user_list.html:50
+#: assets/templates/assets/asset_asset_user_list.html:46
#: assets/templates/assets/asset_list.html:95
#: assets/templates/assets/system_user_asset.html:53
#: assets/templates/assets/system_user_list.html:34
@@ -584,7 +587,7 @@ msgid "Default"
msgstr "默认"
#: assets/models/cluster.py:36 assets/models/label.py:14
-#: users/models/user.py:475
+#: users/models/user.py:479
msgid "System"
msgstr "系统"
@@ -665,7 +668,7 @@ msgstr "每行一个命令"
#: assets/models/cmd_filter.py:54
#: assets/templates/assets/admin_user_assets.html:52
#: assets/templates/assets/admin_user_list.html:33
-#: assets/templates/assets/asset_asset_user_list.html:52
+#: assets/templates/assets/asset_asset_user_list.html:48
#: assets/templates/assets/asset_list.html:96
#: assets/templates/assets/cmd_filter_list.html:28
#: assets/templates/assets/cmd_filter_rule_list.html:63
@@ -735,7 +738,7 @@ msgstr "默认资产组"
#: terminal/templates/terminal/command_list.html:72
#: terminal/templates/terminal/session_list.html:33
#: terminal/templates/terminal/session_list.html:71 users/forms.py:283
-#: users/models/user.py:36 users/models/user.py:463
+#: users/models/user.py:36 users/models/user.py:467
#: users/templates/users/user_group_detail.html:78
#: users/templates/users/user_group_list.html:13 users/views/user.py:395
#: xpack/plugins/orgs/forms.py:26
@@ -757,7 +760,7 @@ msgstr "分类"
msgid "Key"
msgstr "键"
-#: assets/models/node.py:128
+#: assets/models/node.py:133
msgid "New node"
msgstr "新节点"
@@ -975,6 +978,61 @@ msgstr "更新资产用户认证信息"
msgid "Please input password"
msgstr "请输入密码"
+#: assets/templates/assets/_asset_user_view_auth_modal.html:5
+msgid "Asset user auth"
+msgstr "资产用户信息"
+
+#: assets/templates/assets/_asset_user_view_auth_modal.html:14
+#: assets/templates/assets/_otp_verify_modal.html:8 audits/models.py:99
+#: audits/templates/audits/login_log_list.html:56 users/forms.py:142
+#: users/models/user.py:83 users/templates/users/first_login.html:45
+msgid "MFA"
+msgstr "MFA"
+
+#: assets/templates/assets/_asset_user_view_auth_modal.html:17
+msgid "Need otp auth for view auth"
+msgstr "需要二次认证来查看账号信息"
+
+#: assets/templates/assets/_asset_user_view_auth_modal.html:20
+#: assets/templates/assets/admin_user_detail.html:100
+#: assets/templates/assets/asset_detail.html:211
+#: assets/templates/assets/asset_list.html:637
+#: assets/templates/assets/cmd_filter_detail.html:106
+#: assets/templates/assets/system_user_asset.html:112
+#: assets/templates/assets/system_user_detail.html:182
+#: assets/templates/assets/system_user_list.html:144
+#: settings/templates/settings/terminal_setting.html:165
+#: templates/_modal.html:23 terminal/templates/terminal/session_detail.html:108
+#: users/templates/users/user_detail.html:388
+#: users/templates/users/user_detail.html:414
+#: users/templates/users/user_detail.html:437
+#: users/templates/users/user_detail.html:482
+#: users/templates/users/user_group_create_update.html:32
+#: users/templates/users/user_group_list.html:90
+#: users/templates/users/user_list.html:215
+#: users/templates/users/user_profile.html:238
+#: xpack/plugins/cloud/templates/cloud/account_create_update.html:34
+#: xpack/plugins/cloud/templates/cloud/sync_instance_task_create.html:36
+#: xpack/plugins/interface/templates/interface/interface.html:103
+#: xpack/plugins/orgs/templates/orgs/org_create_update.html:33
+msgid "Confirm"
+msgstr "确认"
+
+#: assets/templates/assets/_asset_user_view_auth_modal.html:63
+msgid "Copy success"
+msgstr "复制成功"
+
+#: assets/templates/assets/_asset_user_view_auth_modal.html:79
+msgid "Get auth info error"
+msgstr "获取认证信息错误"
+
+#: assets/templates/assets/_asset_user_view_auth_modal.html:139
+#: assets/templates/assets/_user_asset_detail_modal.html:23
+#: settings/templates/settings/_ldap_list_users_modal.html:96
+#: templates/_modal.html:22
+msgid "Close"
+msgstr "关闭"
+
#: assets/templates/assets/_gateway_test_modal.html:4
msgid "Test gateway test connection"
msgstr "测试连接网关"
@@ -987,6 +1045,10 @@ msgstr "SSH端口"
msgid "If use nat, set the ssh real port"
msgstr "如果使用了nat端口映射,请设置为ssh真实监听的端口"
+#: assets/templates/assets/_otp_verify_modal.html:4
+msgid "MFA Confirm"
+msgstr "确认"
+
#: assets/templates/assets/_system_user.html:37
#: assets/templates/assets/asset_create.html:16
#: assets/templates/assets/asset_update.html:21
@@ -1088,17 +1150,11 @@ msgid "Submit"
msgstr "提交"
#: assets/templates/assets/_user_asset_detail_modal.html:11
-#: assets/templates/assets/asset_asset_user_list.html:17
+#: assets/templates/assets/asset_asset_user_list.html:13
#: assets/templates/assets/asset_detail.html:20 assets/views/asset.py:187
msgid "Asset detail"
msgstr "资产详情"
-#: assets/templates/assets/_user_asset_detail_modal.html:23
-#: settings/templates/settings/_ldap_list_users_modal.html:96
-#: templates/_modal.html:22
-msgid "Close"
-msgstr "关闭"
-
#: assets/templates/assets/admin_user_assets.html:18
#: assets/templates/assets/admin_user_detail.html:18
#: assets/templates/assets/cmd_filter_detail.html:19
@@ -1140,41 +1196,47 @@ msgid "Quick update"
msgstr "快速更新"
#: assets/templates/assets/admin_user_assets.html:70
-#: assets/templates/assets/asset_asset_user_list.html:71
+#: assets/templates/assets/asset_asset_user_list.html:67
#: assets/templates/assets/asset_detail.html:179
msgid "Test connective"
msgstr "测试可连接性"
#: assets/templates/assets/admin_user_assets.html:73
-#: assets/templates/assets/admin_user_assets.html:115
-#: assets/templates/assets/asset_asset_user_list.html:74
-#: assets/templates/assets/asset_asset_user_list.html:122
+#: assets/templates/assets/admin_user_assets.html:118
+#: assets/templates/assets/asset_asset_user_list.html:70
+#: assets/templates/assets/asset_asset_user_list.html:119
#: assets/templates/assets/asset_detail.html:182
#: assets/templates/assets/system_user_asset.html:75
-#: assets/templates/assets/system_user_asset.html:165
+#: assets/templates/assets/system_user_asset.html:166
#: assets/templates/assets/system_user_detail.html:151
msgid "Test"
msgstr "测试"
#: assets/templates/assets/admin_user_assets.html:116
-#: assets/templates/assets/asset_asset_user_list.html:120
-#: assets/templates/assets/system_user_asset.html:167
+#: assets/templates/assets/asset_asset_user_list.html:117
+#: assets/templates/assets/system_user_asset.html:169
msgid "Update auth"
msgstr "更新认证"
-#: assets/templates/assets/admin_user_assets.html:192
-#: assets/templates/assets/asset_asset_user_list.html:176
+#: assets/templates/assets/admin_user_assets.html:117
+#: assets/templates/assets/asset_asset_user_list.html:118
+#: assets/templates/assets/system_user_asset.html:167
+msgid "View auth"
+msgstr "查看认证"
+
+#: assets/templates/assets/admin_user_assets.html:196
+#: assets/templates/assets/asset_asset_user_list.html:162
#: assets/templates/assets/asset_detail.html:311
-#: assets/templates/assets/system_user_asset.html:351
+#: assets/templates/assets/system_user_asset.html:353
#: users/templates/users/user_detail.html:307
#: users/templates/users/user_detail.html:334
#: xpack/plugins/interface/views.py:34
msgid "Update successfully!"
msgstr "更新成功"
-#: assets/templates/assets/admin_user_assets.html:195
-#: assets/templates/assets/asset_asset_user_list.html:179
-#: assets/templates/assets/system_user_asset.html:354
+#: assets/templates/assets/admin_user_assets.html:199
+#: assets/templates/assets/asset_asset_user_list.html:165
+#: assets/templates/assets/system_user_asset.html:356
msgid "Update failed!"
msgstr "更新失败"
@@ -1205,11 +1267,11 @@ msgstr "更新失败"
#: users/templates/users/user_profile.html:187
#: users/templates/users/user_profile.html:196
#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_detail.html:29
-#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_list.html:54
-#: xpack/plugins/cloud/templates/cloud/account_detail.html:25
-#: xpack/plugins/cloud/templates/cloud/account_list.html:38
+#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_list.html:55
+#: xpack/plugins/cloud/templates/cloud/account_detail.html:23
+#: xpack/plugins/cloud/templates/cloud/account_list.html:39
#: xpack/plugins/orgs/templates/orgs/org_detail.html:25
-#: xpack/plugins/orgs/templates/orgs/org_list.html:85
+#: xpack/plugins/orgs/templates/orgs/org_list.html:87
msgid "Update"
msgstr "更新"
@@ -1239,13 +1301,13 @@ msgstr "更新"
#: users/templates/users/user_list.html:91
#: users/templates/users/user_list.html:95
#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_detail.html:33
-#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_list.html:56
-#: xpack/plugins/cloud/templates/cloud/account_detail.html:29
-#: xpack/plugins/cloud/templates/cloud/account_list.html:40
-#: xpack/plugins/cloud/templates/cloud/sync_instance_task_detail.html:32
-#: xpack/plugins/cloud/templates/cloud/sync_instance_task_list.html:54
+#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_list.html:57
+#: xpack/plugins/cloud/templates/cloud/account_detail.html:27
+#: xpack/plugins/cloud/templates/cloud/account_list.html:41
+#: xpack/plugins/cloud/templates/cloud/sync_instance_task_detail.html:30
+#: xpack/plugins/cloud/templates/cloud/sync_instance_task_list.html:55
#: xpack/plugins/orgs/templates/orgs/org_detail.html:29
-#: xpack/plugins/orgs/templates/orgs/org_list.html:87
+#: xpack/plugins/orgs/templates/orgs/org_list.html:89
msgid "Delete"
msgstr "删除"
@@ -1260,30 +1322,6 @@ msgstr "替换资产的管理员"
msgid "Select nodes"
msgstr "选择节点"
-#: assets/templates/assets/admin_user_detail.html:100
-#: assets/templates/assets/asset_detail.html:211
-#: assets/templates/assets/asset_list.html:637
-#: assets/templates/assets/cmd_filter_detail.html:106
-#: assets/templates/assets/system_user_asset.html:112
-#: assets/templates/assets/system_user_detail.html:182
-#: assets/templates/assets/system_user_list.html:144
-#: settings/templates/settings/terminal_setting.html:165
-#: templates/_modal.html:23 terminal/templates/terminal/session_detail.html:108
-#: users/templates/users/user_detail.html:388
-#: users/templates/users/user_detail.html:414
-#: users/templates/users/user_detail.html:437
-#: users/templates/users/user_detail.html:482
-#: users/templates/users/user_group_create_update.html:32
-#: users/templates/users/user_group_list.html:90
-#: users/templates/users/user_list.html:215
-#: users/templates/users/user_profile.html:238
-#: xpack/plugins/cloud/templates/cloud/account_create_update.html:34
-#: xpack/plugins/cloud/templates/cloud/sync_instance_task_create.html:36
-#: xpack/plugins/interface/templates/interface/interface.html:103
-#: xpack/plugins/orgs/templates/orgs/org_create_update.html:33
-msgid "Confirm"
-msgstr "确认"
-
#: assets/templates/assets/admin_user_list.html:10
msgid ""
"Admin users are asset (charged server) on the root, or have NOPASSWD: ALL "
@@ -1313,26 +1351,26 @@ msgstr "创建管理用户"
msgid "Ratio"
msgstr "比例"
-#: assets/templates/assets/asset_asset_user_list.html:20
+#: assets/templates/assets/asset_asset_user_list.html:16
#: assets/templates/assets/asset_detail.html:23 assets/views/asset.py:68
msgid "Asset user list"
msgstr "资产用户列表"
-#: assets/templates/assets/asset_asset_user_list.html:28
+#: assets/templates/assets/asset_asset_user_list.html:24
msgid "Asset users of"
msgstr "资产用户"
-#: assets/templates/assets/asset_asset_user_list.html:49
+#: assets/templates/assets/asset_asset_user_list.html:45
msgid "Password version"
msgstr "密码版本"
-#: assets/templates/assets/asset_asset_user_list.html:51
+#: assets/templates/assets/asset_asset_user_list.html:47
#: assets/templates/assets/cmd_filter_detail.html:73
#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_detail.html:109
msgid "Date updated"
msgstr "更新日期"
-#: assets/templates/assets/asset_asset_user_list.html:64
+#: assets/templates/assets/asset_asset_user_list.html:60
#: assets/templates/assets/asset_detail.html:148
#: terminal/templates/terminal/session_detail.html:81
#: users/templates/users/user_detail.html:138
@@ -1672,7 +1710,7 @@ msgid "Push system user now"
msgstr "立刻推送系统"
#: assets/templates/assets/system_user_asset.html:84
-#: assets/templates/assets/system_user_asset.html:163
+#: assets/templates/assets/system_user_asset.html:164
#: assets/templates/assets/system_user_detail.html:142
msgid "Push"
msgstr "推送"
@@ -1948,12 +1986,6 @@ msgstr "登录城市"
msgid "User agent"
msgstr "Agent"
-#: audits/models.py:99 audits/templates/audits/login_log_list.html:56
-#: users/forms.py:142 users/models/user.py:83
-#: users/templates/users/first_login.html:45
-msgid "MFA"
-msgstr "MFA"
-
#: audits/models.py:100 audits/templates/audits/login_log_list.html:57
#: xpack/plugins/change_auth_plan/models.py:413
#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_execution_subtask_list.html:15
@@ -1965,7 +1997,7 @@ msgstr "原因"
#: audits/models.py:101 audits/templates/audits/login_log_list.html:58
#: xpack/plugins/cloud/models.py:171 xpack/plugins/cloud/models.py:188
#: xpack/plugins/cloud/templates/cloud/sync_instance_task_history.html:70
-#: xpack/plugins/cloud/templates/cloud/sync_instance_task_instance.html:67
+#: xpack/plugins/cloud/templates/cloud/sync_instance_task_instance.html:65
msgid "Status"
msgstr "状态"
@@ -2003,7 +2035,7 @@ msgstr "选择用户"
#: terminal/templates/terminal/command_list.html:60
#: terminal/templates/terminal/session_list.html:61
#: xpack/plugins/cloud/templates/cloud/sync_instance_task_history.html:52
-#: xpack/plugins/cloud/templates/cloud/sync_instance_task_instance.html:50
+#: xpack/plugins/cloud/templates/cloud/sync_instance_task_instance.html:48
msgid "Search"
msgstr "搜索"
@@ -2013,7 +2045,7 @@ msgstr "搜索"
#: ops/templates/ops/task_detail.html:56
#: terminal/templates/terminal/session_list.html:70
#: xpack/plugins/cloud/templates/cloud/sync_instance_task_history.html:64
-#: xpack/plugins/cloud/templates/cloud/sync_instance_task_instance.html:62
+#: xpack/plugins/cloud/templates/cloud/sync_instance_task_instance.html:60
msgid "ID"
msgstr "ID"
@@ -2063,25 +2095,25 @@ msgstr "登录日志"
msgid "Command execution log"
msgstr "命令执行"
-#: authentication/api/auth.py:46
+#: authentication/api/auth.py:49
#: authentication/templates/authentication/login.html:52
#: authentication/templates/authentication/new_login.html:77
msgid "Log in frequently and try again later"
msgstr "登录频繁, 稍后重试"
-#: authentication/api/auth.py:64
+#: authentication/api/auth.py:67
msgid "The user {} password has expired, please update."
msgstr "用户 {} 密码已经过期,请更新。"
-#: authentication/api/auth.py:83
+#: authentication/api/auth.py:86
msgid "Please carry seed value and conduct MFA secondary certification"
msgstr "请携带seed值, 进行MFA二次认证"
-#: authentication/api/auth.py:163
+#: authentication/api/auth.py:166
msgid "Please verify the user name and password first"
msgstr "请先进行用户名和密码验证"
-#: authentication/api/auth.py:168
+#: authentication/api/auth.py:171
msgid "MFA certification failed"
msgstr "MFA认证失败"
@@ -2415,7 +2447,7 @@ msgid "Become"
msgstr "Become"
#: ops/models/adhoc.py:166 users/templates/users/user_group_detail.html:59
-#: xpack/plugins/cloud/templates/cloud/account_detail.html:64
+#: xpack/plugins/cloud/templates/cloud/account_detail.html:62
#: xpack/plugins/orgs/templates/orgs/org_detail.html:56
msgid "Create by"
msgstr "创建者"
@@ -2661,8 +2693,8 @@ msgstr "版本"
#: ops/templates/ops/task_list.html:63
#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_detail.html:137
-#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_list.html:52
-#: xpack/plugins/cloud/templates/cloud/sync_instance_task_list.html:52
+#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_list.html:53
+#: xpack/plugins/cloud/templates/cloud/sync_instance_task_list.html:53
msgid "Run"
msgstr "执行"
@@ -2828,7 +2860,7 @@ msgstr "创建授权规则"
#: perms/templates/perms/asset_permission_list.html:59
#: perms/templates/perms/asset_permission_list.html:73
#: users/templates/users/user_list.html:28 xpack/plugins/cloud/models.py:53
-#: xpack/plugins/cloud/templates/cloud/account_detail.html:60
+#: xpack/plugins/cloud/templates/cloud/account_detail.html:58
#: xpack/plugins/cloud/templates/cloud/account_list.html:14
msgid "Validity"
msgstr "有效"
@@ -3283,8 +3315,8 @@ msgstr "端点后缀"
#: settings/templates/settings/replay_storage_create.html:136
#: xpack/plugins/cloud/models.py:186
-#: xpack/plugins/cloud/templates/cloud/sync_instance_task_detail.html:83
-#: xpack/plugins/cloud/templates/cloud/sync_instance_task_instance.html:64
+#: xpack/plugins/cloud/templates/cloud/sync_instance_task_detail.html:81
+#: xpack/plugins/cloud/templates/cloud/sync_instance_task_instance.html:62
msgid "Region"
msgstr "地域"
@@ -3999,7 +4031,7 @@ msgstr "复制你的公钥到这里"
msgid "Select users"
msgstr "选择用户"
-#: users/models/user.py:35 users/models/user.py:471
+#: users/models/user.py:35 users/models/user.py:475
msgid "Administrator"
msgstr "管理员"
@@ -4045,7 +4077,7 @@ msgstr "最后更新密码日期"
msgid "User auth from {}, go there change password"
msgstr "用户认证源来自 {}, 请去相应系统修改密码"
-#: users/models/user.py:474
+#: users/models/user.py:478
msgid "Administrator is the super user of system"
msgstr "Administrator是初始的超级管理员"
@@ -4064,7 +4096,7 @@ msgstr "安全令牌验证"
#: users/templates/users/_base_otp.html:44 users/templates/users/_user.html:13
#: users/templates/users/user_profile_update.html:51
#: xpack/plugins/cloud/models.py:120
-#: xpack/plugins/cloud/templates/cloud/sync_instance_task_detail.html:59
+#: xpack/plugins/cloud/templates/cloud/sync_instance_task_detail.html:57
#: xpack/plugins/cloud/templates/cloud/sync_instance_task_list.html:13
msgid "Account"
msgstr "账户"
@@ -4947,7 +4979,7 @@ msgid "Run plan manually"
msgstr "手动执行计划"
#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_detail.html:179
-#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_list.html:101
+#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_list.html:102
msgid "Execute failed"
msgstr "执行失败"
@@ -5033,7 +5065,7 @@ msgid "Unavailable"
msgstr "无效"
#: xpack/plugins/cloud/models.py:50
-#: xpack/plugins/cloud/templates/cloud/account_detail.html:56
+#: xpack/plugins/cloud/templates/cloud/account_detail.html:54
#: xpack/plugins/cloud/templates/cloud/account_list.html:13
msgid "Provider"
msgstr "云服务商"
@@ -5059,7 +5091,7 @@ msgid "Instances"
msgstr "实例"
#: xpack/plugins/cloud/models.py:126
-#: xpack/plugins/cloud/templates/cloud/sync_instance_task_detail.html:75
+#: xpack/plugins/cloud/templates/cloud/sync_instance_task_detail.html:73
#: xpack/plugins/cloud/templates/cloud/sync_instance_task_list.html:17
msgid "Date last sync"
msgstr "最后同步日期"
@@ -5078,7 +5110,7 @@ msgstr ""
#: xpack/plugins/cloud/models.py:173 xpack/plugins/cloud/models.py:189
#: xpack/plugins/cloud/templates/cloud/sync_instance_task_history.html:71
-#: xpack/plugins/cloud/templates/cloud/sync_instance_task_instance.html:68
+#: xpack/plugins/cloud/templates/cloud/sync_instance_task_instance.html:66
msgid "Date sync"
msgstr "同步日期"
@@ -5095,8 +5127,8 @@ msgid "Sync instance task history"
msgstr "同步实例任务历史"
#: xpack/plugins/cloud/models.py:185
-#: xpack/plugins/cloud/templates/cloud/sync_instance_task_detail.html:91
-#: xpack/plugins/cloud/templates/cloud/sync_instance_task_instance.html:63
+#: xpack/plugins/cloud/templates/cloud/sync_instance_task_detail.html:89
+#: xpack/plugins/cloud/templates/cloud/sync_instance_task_instance.html:61
msgid "Instance"
msgstr "实例"
@@ -5116,7 +5148,7 @@ msgstr "AWS (国际)"
msgid "Qcloud"
msgstr "腾讯云"
-#: xpack/plugins/cloud/templates/cloud/account_detail.html:22
+#: xpack/plugins/cloud/templates/cloud/account_detail.html:20
#: xpack/plugins/cloud/views.py:72
msgid "Account detail"
msgstr "账户详情"
@@ -5134,23 +5166,23 @@ msgstr "加载中..."
msgid "Load failed"
msgstr "加载失败"
-#: xpack/plugins/cloud/templates/cloud/sync_instance_task_detail.html:22
+#: xpack/plugins/cloud/templates/cloud/sync_instance_task_detail.html:20
#: xpack/plugins/cloud/templates/cloud/sync_instance_task_history.html:25
-#: xpack/plugins/cloud/templates/cloud/sync_instance_task_instance.html:23
+#: xpack/plugins/cloud/templates/cloud/sync_instance_task_instance.html:21
#: xpack/plugins/cloud/views.py:122
msgid "Sync task detail"
msgstr "同步任务详情"
-#: xpack/plugins/cloud/templates/cloud/sync_instance_task_detail.html:25
+#: xpack/plugins/cloud/templates/cloud/sync_instance_task_detail.html:23
#: xpack/plugins/cloud/templates/cloud/sync_instance_task_history.html:28
-#: xpack/plugins/cloud/templates/cloud/sync_instance_task_instance.html:26
+#: xpack/plugins/cloud/templates/cloud/sync_instance_task_instance.html:24
#: xpack/plugins/cloud/views.py:137
msgid "Sync task history"
msgstr "同步历史列表"
-#: xpack/plugins/cloud/templates/cloud/sync_instance_task_detail.html:28
+#: xpack/plugins/cloud/templates/cloud/sync_instance_task_detail.html:26
#: xpack/plugins/cloud/templates/cloud/sync_instance_task_history.html:31
-#: xpack/plugins/cloud/templates/cloud/sync_instance_task_instance.html:29
+#: xpack/plugins/cloud/templates/cloud/sync_instance_task_instance.html:27
#: xpack/plugins/cloud/views.py:188
msgid "Sync instance list"
msgstr "同步实例列表"
@@ -5183,7 +5215,7 @@ msgstr "执行次数"
msgid "Instance count"
msgstr "实例个数"
-#: xpack/plugins/cloud/templates/cloud/sync_instance_task_list.html:92
+#: xpack/plugins/cloud/templates/cloud/sync_instance_task_list.html:93
msgid "Sync success"
msgstr "同步成功"
@@ -5267,6 +5299,7 @@ msgid "This will restore default Settings of the interface !!!"
msgstr "您确定要恢复默认初始化吗?"
#: xpack/plugins/interface/templates/interface/interface.html:107
+#: xpack/plugins/interface/views.py:53
msgid "Restore default successfully."
msgstr "恢复默认成功!"
@@ -5279,13 +5312,9 @@ msgid "Interface"
msgstr "界面"
#: xpack/plugins/interface/views.py:49
-msgid "It is already in the default setting state!"
+msgid "It is already in the default setting state!"
msgstr "当前已经是初始化状态!"
-#: xpack/plugins/interface/views.py:53
-msgid "Restore default successfully!"
-msgstr "恢复默认成功!"
-
#: xpack/plugins/license/meta.py:11 xpack/plugins/license/models.py:94
#: xpack/plugins/license/templates/license/license_detail.html:50
#: xpack/plugins/license/templates/license/license_detail.html:55
@@ -5423,6 +5452,22 @@ msgstr "创建组织"
msgid "Update org"
msgstr "更新组织"
+#~ msgid "Monitor"
+#~ msgstr "监控"
+
+#, fuzzy
+#~| msgid "Select user"
+#~ msgid "Select time"
+#~ msgstr "选择用户"
+
+#, fuzzy
+#~| msgid "Select Asset"
+#~ msgid "Select host"
+#~ msgstr "选择资产"
+
+#~ msgid "Restore default successfully!"
+#~ msgstr "恢复默认成功!"
+
#~ msgid "Beijing Duizhan Tech, Inc."
#~ msgstr "北京堆栈科技有限公司"
@@ -5453,9 +5498,6 @@ msgstr "更新组织"
#~ "object has no attribute 'keys'"
#~ msgstr "导入 {} 个用户成功; 导入 {} 这些用户失败,因为对象没有属性'keys'"
-#~ msgid "Monitor"
-#~ msgstr "监控"
-
#~ msgid "Invalid private key"
#~ msgstr "ssh密钥不合法"
@@ -5549,9 +5591,6 @@ msgstr "更新组织"
#~ "Are you sure to remove authentication information for the system user ?"
#~ msgstr "你确定清除该系统用户的认证信息吗 ?"
-#~ msgid "Clear auth"
-#~ msgstr "清除认证信息"
-
#~ msgid "success"
#~ msgstr "成功"
diff --git a/apps/ops/ansible/callback.py b/apps/ops/ansible/callback.py
index 02004285a..694bb9e27 100644
--- a/apps/ops/ansible/callback.py
+++ b/apps/ops/ansible/callback.py
@@ -124,6 +124,9 @@ class AdHocResultCallback(CallbackMixin, CallbackModule, CMDCallBackModule):
def display_ok_hosts(self):
pass
+ def display_failed_stderr(self):
+ pass
+
class CommandResultCallback(AdHocResultCallback):
"""
diff --git a/apps/ops/ansible/runner.py b/apps/ops/ansible/runner.py
index 4c8f87888..5f25ee2c4 100644
--- a/apps/ops/ansible/runner.py
+++ b/apps/ops/ansible/runner.py
@@ -1,8 +1,11 @@
# ~*~ coding: utf-8 ~*~
import os
+import shutil
from collections import namedtuple
+from ansible import context
+from ansible.module_utils.common.collections import ImmutableDict
from ansible.executor.task_queue_manager import TaskQueueManager
from ansible.vars.manager import VariableManager
from ansible.parsing.dataloader import DataLoader
@@ -33,29 +36,18 @@ Options = namedtuple('Options', [
def get_default_options():
- options = Options(
- listtags=False,
- listtasks=False,
- listhosts=False,
+ options = dict(
syntax=False,
timeout=30,
connection='ssh',
- module_path='',
forks=10,
remote_user='root',
private_key_file=None,
- ssh_common_args="",
- ssh_extra_args="",
- sftp_extra_args="",
- scp_extra_args="",
become=None,
become_method=None,
become_user=None,
- verbosity=None,
- extra_vars=[],
+ verbosity=1,
check=False,
- playbook_path='/etc/ansible/',
- passwords=None,
diff=False,
gathering='implicit',
remote_tmp='/tmp/.ansible'
@@ -108,9 +100,9 @@ class PlayBookRunner:
inventory=self.inventory,
variable_manager=self.variable_manager,
loader=self.loader,
- options=self.options,
- passwords=self.passwords
+ passwords={"conn_pass": self.passwords}
)
+ context.CLIARGS = ImmutableDict(self.options)
if executor._tqm:
executor._tqm._stdout_callback = self.results_callback
@@ -185,11 +177,10 @@ class AdHocRunner:
return cleaned_tasks
def update_options(self, options):
+ _options = {k: v for k, v in self.default_options.items()}
if options and isinstance(options, dict):
- options = self.__class__.default_options._replace(**options)
- else:
- options = self.__class__.default_options
- return options
+ _options.update(options)
+ return _options
def run(self, tasks, pattern, play_name='Ansible Ad-hoc', gather_facts='no'):
"""
@@ -202,6 +193,7 @@ class AdHocRunner:
self.check_pattern(pattern)
self.results_callback = self.get_result_callback()
cleaned_tasks = self.clean_tasks(tasks)
+ context.CLIARGS = ImmutableDict(self.options)
play_source = dict(
name=play_name,
@@ -220,9 +212,8 @@ class AdHocRunner:
inventory=self.inventory,
variable_manager=self.variable_manager,
loader=self.loader,
- options=self.options,
stdout_callback=self.results_callback,
- passwords=self.options.passwords,
+ passwords={"conn_pass": self.options.get("password", "")}
)
try:
tqm.run(play)
@@ -230,8 +221,9 @@ class AdHocRunner:
except Exception as e:
raise AnsibleError(e)
finally:
- tqm.cleanup()
- self.loader.cleanup_all_tmp_files()
+ if tqm is not None:
+ tqm.cleanup()
+ shutil.rmtree(C.DEFAULT_LOCAL_TMP, True)
class CommandRunner(AdHocRunner):
diff --git a/apps/ops/ansible/test_runner.py b/apps/ops/ansible/test_runner.py
index 07c9051f3..e38168a6c 100644
--- a/apps/ops/ansible/test_runner.py
+++ b/apps/ops/ansible/test_runner.py
@@ -15,7 +15,7 @@ class TestAdHocRunner(unittest.TestCase):
host_data = [
{
"hostname": "testserver",
- "ip": "192.168.244.168",
+ "ip": "192.168.244.185",
"port": 22,
"username": "root",
"password": "redhat",
diff --git a/apps/ops/inventory.py b/apps/ops/inventory.py
index e4f11bec1..002b6fbda 100644
--- a/apps/ops/inventory.py
+++ b/apps/ops/inventory.py
@@ -35,7 +35,6 @@ class JMSBaseInventory(BaseInventory):
info["vars"].update({
label.name: label.value
})
- info["groups"].append("{}:{}".format(label.name, label.value))
if asset.domain:
info["vars"].update({
"domain": asset.domain.name,
diff --git a/apps/static/js/jumpserver.js b/apps/static/js/jumpserver.js
index 50f78187c..b3a2bd35e 100644
--- a/apps/static/js/jumpserver.js
+++ b/apps/static/js/jumpserver.js
@@ -715,9 +715,12 @@ String.prototype.format = function(args) {
return result;
};
-function setCookie(key, value) {
+function setCookie(key, value, time) {
var expires = new Date();
- expires.setTime(expires.getTime() + (24 * 60 * 60 * 1000));
+ if (!time) {
+ time = expires.getTime() + (24 * 60 * 60 * 1000);
+ }
+ expires.setTime(time);
document.cookie = key + '=' + value + ';expires=' + expires.toUTCString() + ';path=/';
}
diff --git a/apps/static/js/plugins/clipboard/clipboard.min.js b/apps/static/js/plugins/clipboard/clipboard.min.js
new file mode 100755
index 000000000..4e2a012a6
--- /dev/null
+++ b/apps/static/js/plugins/clipboard/clipboard.min.js
@@ -0,0 +1,7 @@
+/*!
+ * clipboard.js v1.5.5
+ * https://zenorocha.github.io/clipboard.js
+ *
+ * Licensed MIT © Zeno Rocha
+ */
+!function(t){if("object"==typeof exports&&"undefined"!=typeof module)module.exports=t();else if("function"==typeof define&&define.amd)define([],t);else{var e;e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof self?self:this,e.Clipboard=t()}}(function(){var t,e,n;return function t(e,n,r){function o(a,c){if(!n[a]){if(!e[a]){var s="function"==typeof require&&require;if(!c&&s)return s(a,!0);if(i)return i(a,!0);var u=new Error("Cannot find module '"+a+"'");throw u.code="MODULE_NOT_FOUND",u}var l=n[a]={exports:{}};e[a][0].call(l.exports,function(t){var n=e[a][1][t];return o(n?n:t)},l,l.exports,t,e,n,r)}return n[a].exports}for(var i="function"==typeof require&&require,a=0;ar;r++)n[r].fn.apply(n[r].ctx,e);return this},off:function(t,e){var n=this.e||(this.e={}),r=n[t],o=[];if(r&&e)for(var i=0,a=r.length;a>i;i++)r[i].fn!==e&&r[i].fn._!==e&&o.push(r[i]);return o.length?n[t]=o:delete n[t],this}},e.exports=r},{}],8:[function(t,e,n){"use strict";function r(t){return t&&t.__esModule?t:{"default":t}}function o(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")}n.__esModule=!0;var i=function(){function t(t,e){for(var n=0;n