mirror of https://github.com/jumpserver/jumpserver
[Update] Merge with dev
commit
2ecfecb06f
10
README.md
10
README.md
|
@ -19,25 +19,25 @@ Jumpserver采纳分布式架构,支持多机房跨区域部署,中心节点
|
|||
----
|
||||
|
||||
### 功能
|
||||
|
||||
|
||||

|
||||
|
||||
### 开始使用
|
||||
|
||||
快速开始文档 [Docker安装](http://docs.jumpserver.org/zh/latest/quickstart.html)
|
||||
快速开始文档 [Docker安装](http://docs.jumpserver.org/zh/docs/dockerinstall.html)
|
||||
|
||||
一步一步安装文档 [详细部署](http://docs.jumpserver.org/zh/latest/step_by_step.html)
|
||||
一步一步安装文档 [详细部署](http://docs.jumpserver.org/zh/docs/step_by_step.html)
|
||||
|
||||
也可以查看我们完整文档包括了使用和开发 [文档](http://docs.jumpserver.org)
|
||||
|
||||
### Demo 和 截图
|
||||
### Demo 和 截图
|
||||
|
||||
我们提供了DEMO和截图可以让你快速了解Jumpserver
|
||||
|
||||
[DEMO](http://demo.jumpserver.org)
|
||||
[截图](http://docs.jumpserver.org/zh/docs/snapshot.html)
|
||||
|
||||
### SDK
|
||||
### SDK
|
||||
|
||||
我们还编写了一些SDK,供你其它系统快速和Jumpserver APi交互,
|
||||
|
||||
|
|
|
@ -2,4 +2,4 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
|
||||
__version__ = "1.3.2"
|
||||
__version__ = "1.3.3"
|
||||
|
|
|
@ -142,7 +142,7 @@ class Asset(OrgModelMixin):
|
|||
return False, warning
|
||||
|
||||
def is_unixlike(self):
|
||||
if self.platform not in ("Windows",):
|
||||
if self.platform not in ("Windows", "Windows2016"):
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
|
|
@ -53,7 +53,7 @@ class SystemUserAuthSerializer(AuthSerializer):
|
|||
model = SystemUser
|
||||
fields = [
|
||||
"id", "name", "username", "protocol",
|
||||
"password", "private_key",
|
||||
"login_mode", "password", "private_key",
|
||||
]
|
||||
|
||||
|
||||
|
|
|
@ -42,7 +42,7 @@
|
|||
{% bootstrap_field form.domain layout="horizontal" %}
|
||||
|
||||
{% block auth %}
|
||||
<h3>{% trans 'Auth' %}</h3>
|
||||
<h3 id="auth_title">{% trans 'Auth' %}</h3>
|
||||
<div class="auth-fields">
|
||||
{% bootstrap_field form.username layout="horizontal" %}
|
||||
{% bootstrap_field form.password layout="horizontal" %}
|
||||
|
@ -72,14 +72,23 @@
|
|||
var protocol_id = '#' + '{{ form.protocol.id_for_label }}';
|
||||
var private_key_id = '#' + '{{ form.private_key_file.id_for_label }}';
|
||||
var port = '#' + '{{ form.port.id_for_label }}';
|
||||
var username = '#' + '{{ form.username.id_for_label }}';
|
||||
var password = '#' + '{{ form.password.id_for_label }}';
|
||||
var auth_title = '#auth_title';
|
||||
|
||||
function protocolChange() {
|
||||
if ($(protocol_id + " option:selected").text() === 'rdp') {
|
||||
{#$(port).val(3389);#}
|
||||
$(private_key_id).closest('.form-group').addClass('hidden')
|
||||
$(private_key_id).closest('.form-group').addClass('hidden');
|
||||
$(username).closest('.form-group').addClass('hidden');
|
||||
$(password).closest('.form-group').addClass('hidden');
|
||||
$(auth_title).addClass('hidden');
|
||||
} else {
|
||||
{#$(port).val(22);#}
|
||||
$(private_key_id).closest('.form-group').removeClass('hidden')
|
||||
$(private_key_id).closest('.form-group').removeClass('hidden');
|
||||
$(username).closest('.form-group').removeClass('hidden');
|
||||
$(password).closest('.form-group').removeClass('hidden');
|
||||
$(auth_title).removeClass('hidden');
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -152,15 +152,14 @@
|
|||
</span>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td width="50%">{% trans 'Clear auth' %}:</td>
|
||||
<td>
|
||||
<span style="float: right">
|
||||
<button type="button" class="btn btn-primary btn-xs btn-clear-auth" style="width: 54px">{% trans 'Clear' %}</button>
|
||||
</span>
|
||||
</td>
|
||||
</tr>
|
||||
{# <tr>#}
|
||||
{# <td width="50%">{% trans 'Clear auth' %}:</td>#}
|
||||
{# <td>#}
|
||||
{# <span style="float: right">#}
|
||||
{# <button type="button" class="btn btn-primary btn-xs btn-clear-auth" style="width: 54px">{% trans 'Clear' %}</button>#}
|
||||
{# </span>#}
|
||||
{# </td>#}
|
||||
{# </tr>#}
|
||||
|
||||
{# <tr>#}
|
||||
{# <td width="50%">{% trans 'Change auth period' %}:</td>#}
|
||||
|
|
|
@ -50,4 +50,3 @@ urlpatterns = [
|
|||
url(r'^domain/(?P<pk>[0-9a-zA-Z\-]{36})/gateway/create/$', views.DomainGatewayCreateView.as_view(), name='domain-gateway-create'),
|
||||
url(r'^domain/gateway/(?P<pk>[0-9a-zA-Z\-]{36})/update/$', views.DomainGatewayUpdateView.as_view(), name='domain-gateway-update'),
|
||||
]
|
||||
|
||||
|
|
|
@ -92,7 +92,7 @@ class DatetimeSearchMixin:
|
|||
date_format = '%Y-%m-%d'
|
||||
date_from = date_to = None
|
||||
|
||||
def get(self, request, *args, **kwargs):
|
||||
def get_date_range(self):
|
||||
date_from_s = self.request.GET.get('date_from')
|
||||
date_to_s = self.request.GET.get('date_to')
|
||||
|
||||
|
@ -112,6 +112,9 @@ class DatetimeSearchMixin:
|
|||
)
|
||||
else:
|
||||
self.date_to = timezone.now()
|
||||
|
||||
def get(self, request, *args, **kwargs):
|
||||
self.get_date_range()
|
||||
return super().get(request, *args, **kwargs)
|
||||
|
||||
|
||||
|
|
Binary file not shown.
|
@ -8,7 +8,7 @@ msgid ""
|
|||
msgstr ""
|
||||
"Project-Id-Version: Jumpserver 0.3.3\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2018-07-06 13:11+0800\n"
|
||||
"POT-Creation-Date: 2018-07-19 18:29+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"
|
||||
|
@ -32,7 +32,7 @@ msgstr ""
|
|||
#: assets/forms/asset.py:24 assets/models/asset.py:89 assets/models/user.py:112
|
||||
#: assets/templates/assets/asset_detail.html:183
|
||||
#: assets/templates/assets/asset_detail.html:191
|
||||
#: assets/templates/assets/system_user_detail.html:179 perms/models.py:33
|
||||
#: assets/templates/assets/system_user_detail.html:178 perms/models.py:33
|
||||
msgid "Nodes"
|
||||
msgstr "节点管理"
|
||||
|
||||
|
@ -101,7 +101,7 @@ msgstr "端口"
|
|||
#: assets/forms/domain.py:14 assets/forms/label.py:13
|
||||
#: assets/models/asset.py:237 assets/templates/assets/admin_user_list.html:25
|
||||
#: assets/templates/assets/domain_detail.html:60
|
||||
#: assets/templates/assets/domain_list.html:15
|
||||
#: assets/templates/assets/domain_list.html:23
|
||||
#: assets/templates/assets/label_list.html:16
|
||||
#: assets/templates/assets/system_user_list.html:30 audits/models.py:11
|
||||
#: audits/templates/audits/ftp_log_list.html:41
|
||||
|
@ -125,7 +125,7 @@ msgstr "资产"
|
|||
#: assets/templates/assets/admin_user_list.html:23
|
||||
#: assets/templates/assets/domain_detail.html:56
|
||||
#: assets/templates/assets/domain_gateway_list.html:56
|
||||
#: assets/templates/assets/domain_list.html:14
|
||||
#: assets/templates/assets/domain_list.html:22
|
||||
#: assets/templates/assets/label_list.html:14
|
||||
#: assets/templates/assets/system_user_detail.html:58
|
||||
#: assets/templates/assets/system_user_list.html:26 common/models.py:26
|
||||
|
@ -356,7 +356,7 @@ msgstr "创建日期"
|
|||
#: assets/templates/assets/asset_detail.html:125
|
||||
#: assets/templates/assets/domain_detail.html:76
|
||||
#: assets/templates/assets/domain_gateway_list.html:61
|
||||
#: assets/templates/assets/domain_list.html:17
|
||||
#: assets/templates/assets/domain_list.html:25
|
||||
#: assets/templates/assets/system_user_detail.html:104
|
||||
#: assets/templates/assets/system_user_list.html:34 common/models.py:30
|
||||
#: ops/models/adhoc.py:42 perms/models.py:40 perms/models.py:83
|
||||
|
@ -445,7 +445,7 @@ msgstr "默认资产组"
|
|||
#: terminal/templates/terminal/session_list.html:71 users/forms.py:282
|
||||
#: users/models/user.py:31 users/models/user.py:333
|
||||
#: users/templates/users/user_group_detail.html:78
|
||||
#: users/templates/users/user_group_list.html:13 users/views/user.py:361
|
||||
#: users/templates/users/user_group_list.html:13 users/views/user.py:367
|
||||
msgid "User"
|
||||
msgstr "用户"
|
||||
|
||||
|
@ -685,6 +685,7 @@ msgstr "重置"
|
|||
#: common/templates/common/security_setting.html:71
|
||||
#: common/templates/common/terminal_setting.html:108
|
||||
#: perms/templates/perms/asset_permission_create_update.html:70
|
||||
#: terminal/templates/terminal/command_list.html:103
|
||||
#: terminal/templates/terminal/session_list.html:126
|
||||
#: terminal/templates/terminal/terminal_update.html:48
|
||||
#: users/templates/users/_user.html:47
|
||||
|
@ -756,7 +757,7 @@ msgstr "测试"
|
|||
#: assets/templates/assets/domain_detail.html:24
|
||||
#: assets/templates/assets/domain_detail.html:103
|
||||
#: assets/templates/assets/domain_gateway_list.html:85
|
||||
#: assets/templates/assets/domain_list.html:42
|
||||
#: assets/templates/assets/domain_list.html:50
|
||||
#: assets/templates/assets/label_list.html:38
|
||||
#: assets/templates/assets/system_user_detail.html:26
|
||||
#: assets/templates/assets/system_user_list.html:89
|
||||
|
@ -780,7 +781,7 @@ msgstr "更新"
|
|||
#: assets/templates/assets/domain_detail.html:28
|
||||
#: assets/templates/assets/domain_detail.html:104
|
||||
#: assets/templates/assets/domain_gateway_list.html:86
|
||||
#: assets/templates/assets/domain_list.html:43
|
||||
#: assets/templates/assets/domain_list.html:51
|
||||
#: assets/templates/assets/label_list.html:39
|
||||
#: assets/templates/assets/system_user_detail.html:30
|
||||
#: assets/templates/assets/system_user_list.html:90
|
||||
|
@ -808,12 +809,13 @@ msgstr "选择节点"
|
|||
#: assets/templates/assets/admin_user_detail.html:100
|
||||
#: assets/templates/assets/asset_detail.html:200
|
||||
#: assets/templates/assets/asset_list.html:638
|
||||
#: assets/templates/assets/system_user_detail.html:196
|
||||
#: assets/templates/assets/system_user_detail.html:195
|
||||
#: assets/templates/assets/system_user_list.html:139 templates/_modal.html:22
|
||||
#: terminal/templates/terminal/session_detail.html:108
|
||||
#: users/templates/users/user_detail.html:366
|
||||
#: users/templates/users/user_detail.html:391
|
||||
#: users/templates/users/user_detail.html:414
|
||||
#: users/templates/users/user_detail.html:374
|
||||
#: users/templates/users/user_detail.html:399
|
||||
#: users/templates/users/user_detail.html:422
|
||||
#: users/templates/users/user_detail.html:466
|
||||
#: users/templates/users/user_group_create_update.html:32
|
||||
#: users/templates/users/user_group_list.html:86
|
||||
#: users/templates/users/user_list.html:200
|
||||
|
@ -841,7 +843,7 @@ msgstr "比例"
|
|||
#: assets/templates/assets/admin_user_list.html:30
|
||||
#: assets/templates/assets/asset_list.html:91
|
||||
#: assets/templates/assets/domain_gateway_list.html:62
|
||||
#: assets/templates/assets/domain_list.html:18
|
||||
#: assets/templates/assets/domain_list.html:26
|
||||
#: assets/templates/assets/label_list.html:17
|
||||
#: assets/templates/assets/system_user_list.html:35
|
||||
#: ops/templates/ops/adhoc_history.html:59 ops/templates/ops/task_adhoc.html:64
|
||||
|
@ -905,8 +907,8 @@ msgid "Refresh"
|
|||
msgstr "刷新"
|
||||
|
||||
#: assets/templates/assets/asset_detail.html:300
|
||||
#: users/templates/users/user_detail.html:286
|
||||
#: users/templates/users/user_detail.html:313
|
||||
#: users/templates/users/user_detail.html:294
|
||||
#: users/templates/users/user_detail.html:321
|
||||
msgid "Update successfully!"
|
||||
msgstr "更新成功"
|
||||
|
||||
|
@ -1002,8 +1004,9 @@ msgstr "存在资产,不能删除"
|
|||
|
||||
#: assets/templates/assets/asset_list.html:633
|
||||
#: assets/templates/assets/system_user_list.html:134
|
||||
#: users/templates/users/user_detail.html:361
|
||||
#: users/templates/users/user_detail.html:386
|
||||
#: users/templates/users/user_detail.html:369
|
||||
#: users/templates/users/user_detail.html:394
|
||||
#: users/templates/users/user_detail.html:461
|
||||
#: users/templates/users/user_group_list.html:81
|
||||
#: users/templates/users/user_list.html:195
|
||||
msgid "Are you sure?"
|
||||
|
@ -1043,7 +1046,7 @@ msgstr "您确定删除吗?"
|
|||
#: assets/templates/assets/domain_detail.html:21
|
||||
#: assets/templates/assets/domain_detail.html:64
|
||||
#: assets/templates/assets/domain_gateway_list.html:21
|
||||
#: assets/templates/assets/domain_list.html:16
|
||||
#: assets/templates/assets/domain_list.html:24
|
||||
msgid "Gateway"
|
||||
msgstr "网关"
|
||||
|
||||
|
@ -1063,7 +1066,7 @@ msgstr "创建网关"
|
|||
msgid "Test connection"
|
||||
msgstr "测试连接"
|
||||
|
||||
#: assets/templates/assets/domain_list.html:6 assets/views/domain.py:46
|
||||
#: assets/templates/assets/domain_list.html:14 assets/views/domain.py:46
|
||||
msgid "Create domain"
|
||||
msgstr "创建网域"
|
||||
|
||||
|
@ -1106,20 +1109,15 @@ msgstr "家目录"
|
|||
msgid "Uid"
|
||||
msgstr "Uid"
|
||||
|
||||
#: assets/templates/assets/system_user_detail.html:157
|
||||
#: assets/templates/assets/system_user_detail.html:343
|
||||
msgid "Clear auth"
|
||||
msgstr "清除认证信息"
|
||||
|
||||
#: assets/templates/assets/system_user_detail.html:160
|
||||
msgid "Clear"
|
||||
msgstr "清除"
|
||||
|
||||
#: assets/templates/assets/system_user_detail.html:187
|
||||
#: assets/templates/assets/system_user_detail.html:186
|
||||
msgid "Add to node"
|
||||
msgstr "添加到节点"
|
||||
|
||||
#: assets/templates/assets/system_user_detail.html:343
|
||||
#: assets/templates/assets/system_user_detail.html:353
|
||||
msgid "Clear auth"
|
||||
msgstr "清除认证信息"
|
||||
|
||||
#: assets/templates/assets/system_user_detail.html:353
|
||||
msgid "success"
|
||||
msgstr "成功"
|
||||
|
||||
|
@ -1238,6 +1236,7 @@ msgstr "文件名"
|
|||
|
||||
#: audits/models.py:15 audits/templates/audits/ftp_log_list.html:77
|
||||
#: ops/templates/ops/task_list.html:39 users/models/authentication.py:66
|
||||
#: users/templates/users/user_detail.html:443
|
||||
msgid "Success"
|
||||
msgstr "成功"
|
||||
|
||||
|
@ -1854,7 +1853,7 @@ msgstr "选择用户"
|
|||
#: perms/templates/perms/asset_permission_list.html:136 templates/_nav.html:14
|
||||
#: users/models/group.py:23 users/models/user.py:55
|
||||
#: users/templates/users/_select_user_modal.html:16
|
||||
#: users/templates/users/user_detail.html:192
|
||||
#: users/templates/users/user_detail.html:200
|
||||
#: users/templates/users/user_list.html:26
|
||||
msgid "User group"
|
||||
msgstr "用户组"
|
||||
|
@ -1907,7 +1906,7 @@ msgid "Add node to this permission"
|
|||
msgstr "添加节点"
|
||||
|
||||
#: perms/templates/perms/asset_permission_asset.html:125
|
||||
#: users/templates/users/user_detail.html:209
|
||||
#: users/templates/users/user_detail.html:217
|
||||
msgid "Join"
|
||||
msgstr "加入"
|
||||
|
||||
|
@ -2004,7 +2003,7 @@ msgstr "文档"
|
|||
#: users/templates/users/user_profile.html:17
|
||||
#: users/templates/users/user_profile_update.html:37
|
||||
#: users/templates/users/user_profile_update.html:57
|
||||
#: users/templates/users/user_pubkey_update.html:37 users/views/user.py:343
|
||||
#: users/templates/users/user_pubkey_update.html:37 users/views/user.py:349
|
||||
msgid "Profile"
|
||||
msgstr "个人信息"
|
||||
|
||||
|
@ -2061,13 +2060,13 @@ msgstr "关闭"
|
|||
|
||||
#: templates/_nav.html:10 users/views/group.py:28 users/views/group.py:44
|
||||
#: users/views/group.py:62 users/views/group.py:79 users/views/group.py:95
|
||||
#: users/views/login.py:330 users/views/login.py:388 users/views/user.py:65
|
||||
#: users/views/user.py:80 users/views/user.py:102 users/views/user.py:175
|
||||
#: users/views/user.py:330 users/views/user.py:380 users/views/user.py:415
|
||||
#: users/views/login.py:332 users/views/login.py:390 users/views/user.py:67
|
||||
#: users/views/user.py:82 users/views/user.py:104 users/views/user.py:180
|
||||
#: users/views/user.py:336 users/views/user.py:386 users/views/user.py:421
|
||||
msgid "Users"
|
||||
msgstr "用户管理"
|
||||
|
||||
#: templates/_nav.html:13 users/views/user.py:66
|
||||
#: templates/_nav.html:13 users/views/user.py:68
|
||||
msgid "User list"
|
||||
msgstr "用户列表"
|
||||
|
||||
|
@ -2095,7 +2094,7 @@ msgstr "命令记录"
|
|||
msgid "Web terminal"
|
||||
msgstr "Web终端"
|
||||
|
||||
#: templates/_nav.html:51 terminal/views/command.py:47
|
||||
#: templates/_nav.html:51 terminal/views/command.py:49
|
||||
#: terminal/views/session.py:75 terminal/views/session.py:93
|
||||
#: terminal/views/session.py:115 terminal/views/terminal.py:31
|
||||
#: terminal/views/terminal.py:46 terminal/views/terminal.py:58
|
||||
|
@ -2204,13 +2203,17 @@ msgstr "参数"
|
|||
msgid "Goto"
|
||||
msgstr "转到"
|
||||
|
||||
#: terminal/templates/terminal/command_list.html:99
|
||||
msgid "Export command"
|
||||
msgstr "导出命令"
|
||||
|
||||
#: terminal/templates/terminal/session_detail.html:17
|
||||
#: terminal/views/session.py:116
|
||||
msgid "Session detail"
|
||||
msgstr "会话详情"
|
||||
|
||||
#: terminal/templates/terminal/session_detail.html:28
|
||||
#: terminal/views/command.py:48
|
||||
#: terminal/views/command.py:50
|
||||
msgid "Command list"
|
||||
msgstr "命令记录列表"
|
||||
|
||||
|
@ -2326,7 +2329,7 @@ msgid ""
|
|||
"You should use your ssh client tools connect terminal: {} <br /> <br />{}"
|
||||
msgstr "你可以使用ssh客户端工具连接终端"
|
||||
|
||||
#: users/api.py:208 users/templates/users/login.html:50
|
||||
#: users/api.py:226 users/templates/users/login.html:50
|
||||
msgid "Log in frequently and try again later"
|
||||
msgstr "登录频繁, 稍后重试"
|
||||
|
||||
|
@ -2405,7 +2408,7 @@ msgstr ""
|
|||
msgid "Paste user id_rsa.pub here."
|
||||
msgstr "复制用户公钥到这里"
|
||||
|
||||
#: users/forms.py:73 users/templates/users/user_detail.html:200
|
||||
#: users/forms.py:73 users/templates/users/user_detail.html:208
|
||||
msgid "Join user groups"
|
||||
msgstr "添加到用户组"
|
||||
|
||||
|
@ -2701,7 +2704,7 @@ msgid "Can't provide security? Please contact the administrator!"
|
|||
msgstr "如果不能提供MFA验证码,请联系管理员!"
|
||||
|
||||
#: users/templates/users/reset_password.html:46
|
||||
#: users/templates/users/user_detail.html:352 users/utils.py:80
|
||||
#: users/templates/users/user_detail.html:360 users/utils.py:80
|
||||
msgid "Reset password"
|
||||
msgstr "重置密码"
|
||||
|
||||
|
@ -2727,7 +2730,7 @@ msgid "Setting"
|
|||
msgstr "设置"
|
||||
|
||||
#: users/templates/users/user_create.html:4
|
||||
#: users/templates/users/user_list.html:16 users/views/user.py:80
|
||||
#: users/templates/users/user_list.html:16 users/views/user.py:82
|
||||
msgid "Create user"
|
||||
msgstr "创建用户"
|
||||
|
||||
|
@ -2736,7 +2739,7 @@ msgid "Reset link will be generated and sent to the user. "
|
|||
msgstr "生成重置密码连接,通过邮件发送给用户"
|
||||
|
||||
#: users/templates/users/user_detail.html:19
|
||||
#: users/templates/users/user_granted_asset.html:18 users/views/user.py:176
|
||||
#: users/templates/users/user_granted_asset.html:18 users/views/user.py:181
|
||||
msgid "User detail"
|
||||
msgstr "用户详情"
|
||||
|
||||
|
@ -2773,44 +2776,57 @@ msgstr "发送"
|
|||
msgid "Send reset ssh key mail"
|
||||
msgstr "发送重置密钥邮件"
|
||||
|
||||
#: users/templates/users/user_detail.html:295
|
||||
#: users/templates/users/user_detail.html:186
|
||||
#: users/templates/users/user_detail.html:446
|
||||
msgid "Unblock user"
|
||||
msgstr "解除登录限制"
|
||||
|
||||
#: users/templates/users/user_detail.html:189
|
||||
msgid "Unblock"
|
||||
msgstr "解除"
|
||||
|
||||
#: users/templates/users/user_detail.html:303
|
||||
msgid "Goto profile page enable MFA"
|
||||
msgstr "请去个人信息页面启用自己的MFA"
|
||||
|
||||
#: users/templates/users/user_detail.html:351
|
||||
#: users/templates/users/user_detail.html:359
|
||||
msgid "An e-mail has been sent to the user`s mailbox."
|
||||
msgstr "已发送邮件到用户邮箱"
|
||||
|
||||
#: users/templates/users/user_detail.html:362
|
||||
#: users/templates/users/user_detail.html:370
|
||||
msgid "This will reset the user password and send a reset mail"
|
||||
msgstr "将失效用户当前密码,并发送重设密码邮件到用户邮箱"
|
||||
|
||||
#: users/templates/users/user_detail.html:376
|
||||
#: users/templates/users/user_detail.html:384
|
||||
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:377
|
||||
#: users/templates/users/user_detail.html:385
|
||||
msgid "Reset SSH public key"
|
||||
msgstr "重置SSH密钥"
|
||||
|
||||
#: users/templates/users/user_detail.html:387
|
||||
#: users/templates/users/user_detail.html:395
|
||||
msgid "This will reset the user public key and send a reset mail"
|
||||
msgstr "将会失效用户当前密钥,并发送重置邮件到用户邮箱"
|
||||
|
||||
#: users/templates/users/user_detail.html:404
|
||||
#: users/templates/users/user_detail.html:412
|
||||
#: users/templates/users/user_profile.html:211
|
||||
msgid "Successfully updated the SSH public key."
|
||||
msgstr "更新ssh密钥成功"
|
||||
|
||||
#: users/templates/users/user_detail.html:405
|
||||
#: users/templates/users/user_detail.html:409
|
||||
#: users/templates/users/user_detail.html:413
|
||||
#: users/templates/users/user_detail.html:417
|
||||
#: users/templates/users/user_profile.html:212
|
||||
#: users/templates/users/user_profile.html:217
|
||||
msgid "User SSH public key update"
|
||||
msgstr "ssh密钥"
|
||||
|
||||
#: users/templates/users/user_detail.html:462
|
||||
msgid "After unlocking the user, the user can log in normally."
|
||||
msgstr "解除用户登录限制后,此用户即可正常登录"
|
||||
|
||||
#: users/templates/users/user_group_create_update.html:31
|
||||
msgid "Cancel"
|
||||
msgstr "取消"
|
||||
|
@ -2867,8 +2883,8 @@ msgstr "用户删除失败"
|
|||
msgid "Administrator Settings force MFA login"
|
||||
msgstr "管理员设置强制使用MFA登录"
|
||||
|
||||
#: users/templates/users/user_profile.html:116 users/views/user.py:205
|
||||
#: users/views/user.py:259
|
||||
#: users/templates/users/user_profile.html:116 users/views/user.py:211
|
||||
#: users/views/user.py:265
|
||||
msgid "User groups"
|
||||
msgstr "用户组"
|
||||
|
||||
|
@ -2914,7 +2930,7 @@ msgid ""
|
|||
"corresponding private key."
|
||||
msgstr "新的公钥已设置成功,请下载对应的私钥"
|
||||
|
||||
#: users/templates/users/user_update.html:4 users/views/user.py:103
|
||||
#: users/templates/users/user_update.html:4 users/views/user.py:105
|
||||
msgid "Update user"
|
||||
msgstr "更新用户"
|
||||
|
||||
|
@ -3068,106 +3084,112 @@ msgstr "更新用户组"
|
|||
msgid "User group granted asset"
|
||||
msgstr "用户组授权资产"
|
||||
|
||||
#: users/views/login.py:75
|
||||
#: users/views/login.py:76
|
||||
msgid "Please enable cookies and try again."
|
||||
msgstr "设置你的浏览器支持cookie"
|
||||
|
||||
#: users/views/login.py:178 users/views/user.py:500 users/views/user.py:525
|
||||
#: users/views/login.py:180 users/views/user.py:506 users/views/user.py:531
|
||||
msgid "MFA code invalid"
|
||||
msgstr "MFA码认证失败"
|
||||
|
||||
#: users/views/login.py:207
|
||||
#: users/views/login.py:209
|
||||
msgid "Logout success"
|
||||
msgstr "退出登录成功"
|
||||
|
||||
#: users/views/login.py:208
|
||||
#: users/views/login.py:210
|
||||
msgid "Logout success, return login page"
|
||||
msgstr "退出登录成功,返回到登录页面"
|
||||
|
||||
#: users/views/login.py:224
|
||||
#: users/views/login.py:226
|
||||
msgid "Email address invalid, please input again"
|
||||
msgstr "邮箱地址错误,重新输入"
|
||||
|
||||
#: users/views/login.py:237
|
||||
#: users/views/login.py:239
|
||||
msgid "Send reset password message"
|
||||
msgstr "发送重置密码邮件"
|
||||
|
||||
#: users/views/login.py:238
|
||||
#: users/views/login.py:240
|
||||
msgid "Send reset password mail success, login your mail box and follow it "
|
||||
msgstr ""
|
||||
"发送重置邮件成功, 请登录邮箱查看, 按照提示操作 (如果没收到,请等待3-5分钟)"
|
||||
|
||||
#: users/views/login.py:251
|
||||
#: users/views/login.py:253
|
||||
msgid "Reset password success"
|
||||
msgstr "重置密码成功"
|
||||
|
||||
#: users/views/login.py:252
|
||||
#: users/views/login.py:254
|
||||
msgid "Reset password success, return to login page"
|
||||
msgstr "重置密码成功,返回到登录页面"
|
||||
|
||||
#: users/views/login.py:273 users/views/login.py:286
|
||||
#: users/views/login.py:275 users/views/login.py:288
|
||||
msgid "Token invalid or expired"
|
||||
msgstr "Token错误或失效"
|
||||
|
||||
#: users/views/login.py:282
|
||||
#: users/views/login.py:284
|
||||
msgid "Password not same"
|
||||
msgstr "密码不一致"
|
||||
|
||||
#: users/views/login.py:292 users/views/user.py:118 users/views/user.py:398
|
||||
#: users/views/login.py:294 users/views/user.py:120 users/views/user.py:404
|
||||
msgid "* Your password does not meet the requirements"
|
||||
msgstr "* 您的密码不符合要求"
|
||||
|
||||
#: users/views/login.py:330
|
||||
#: users/views/login.py:332
|
||||
msgid "First login"
|
||||
msgstr "首次登陆"
|
||||
|
||||
#: users/views/login.py:389
|
||||
#: users/views/login.py:391
|
||||
msgid "Login log list"
|
||||
msgstr "登录日志"
|
||||
|
||||
#: users/views/user.py:129
|
||||
#: users/views/user.py:131
|
||||
msgid "Bulk update user success"
|
||||
msgstr "批量更新用户成功"
|
||||
|
||||
#: users/views/user.py:234
|
||||
#: users/views/user.py:240
|
||||
msgid "Invalid file."
|
||||
msgstr "文件不合法"
|
||||
|
||||
#: users/views/user.py:331
|
||||
#: users/views/user.py:337
|
||||
msgid "User granted assets"
|
||||
msgstr "用户授权资产"
|
||||
|
||||
#: users/views/user.py:362
|
||||
#: users/views/user.py:368
|
||||
msgid "Profile setting"
|
||||
msgstr "个人信息设置"
|
||||
|
||||
#: users/views/user.py:381
|
||||
#: users/views/user.py:387
|
||||
msgid "Password update"
|
||||
msgstr "密码更新"
|
||||
|
||||
#: users/views/user.py:416
|
||||
#: users/views/user.py:422
|
||||
msgid "Public key update"
|
||||
msgstr "密钥更新"
|
||||
|
||||
#: users/views/user.py:457
|
||||
#: users/views/user.py:463
|
||||
msgid "Password invalid"
|
||||
msgstr "用户名或密码无效"
|
||||
|
||||
#: users/views/user.py:551
|
||||
#: users/views/user.py:557
|
||||
msgid "MFA enable success"
|
||||
msgstr "MFA 绑定成功"
|
||||
|
||||
#: users/views/user.py:552
|
||||
#: users/views/user.py:558
|
||||
msgid "MFA enable success, return login page"
|
||||
msgstr "MFA 绑定成功,返回到登录页面"
|
||||
|
||||
#: users/views/user.py:554
|
||||
#: users/views/user.py:560
|
||||
msgid "MFA disable success"
|
||||
msgstr "MFA 解绑成功"
|
||||
|
||||
#: users/views/user.py:555
|
||||
#: users/views/user.py:561
|
||||
msgid "MFA disable success, return login page"
|
||||
msgstr "MFA 解绑成功,返回登录页面"
|
||||
|
||||
#~ msgid "Unblock user successfully. "
|
||||
#~ msgstr "解除登录限制成功"
|
||||
|
||||
#~ msgid "Clear"
|
||||
#~ msgstr "清除"
|
||||
|
||||
#~ msgid "MFA setting"
|
||||
#~ msgstr "MFA 设置"
|
||||
|
|
|
@ -341,6 +341,7 @@ AUTH_LDAP_GROUP_SEARCH = LDAPSearch(
|
|||
AUTH_LDAP_CONNECTION_OPTIONS = {
|
||||
ldap.OPT_TIMEOUT: 5
|
||||
}
|
||||
AUTH_LDAP_GROUP_CACHE_TIMEOUT = 1
|
||||
AUTH_LDAP_ALWAYS_UPDATE_USER = True
|
||||
AUTH_LDAP_BACKEND = 'django_auth_ldap.backend.LDAPBackend'
|
||||
|
||||
|
|
|
@ -10,6 +10,7 @@ from users.models import User
|
|||
from assets.models import Asset
|
||||
from terminal.models import Session
|
||||
from common.permissions import AdminUserRequiredMixin
|
||||
from orgs.utils import current_org
|
||||
|
||||
|
||||
class IndexView(AdminUserRequiredMixin, TemplateView):
|
||||
|
@ -27,7 +28,7 @@ class IndexView(AdminUserRequiredMixin, TemplateView):
|
|||
|
||||
@staticmethod
|
||||
def get_user_count():
|
||||
return User.objects.filter(role__in=('Admin', 'User')).count()
|
||||
return current_org.get_org_users().count()
|
||||
|
||||
@staticmethod
|
||||
def get_asset_count():
|
||||
|
@ -49,7 +50,6 @@ class IndexView(AdminUserRequiredMixin, TemplateView):
|
|||
|
||||
def get_week_login_asset_count(self):
|
||||
return self.session_week.count()
|
||||
# return self.session_week.values('asset').distinct().count()
|
||||
|
||||
def get_month_day_metrics(self):
|
||||
month_str = [d.strftime('%m-%d') for d in self.session_month_dates] or ['0']
|
||||
|
|
|
@ -55,9 +55,11 @@ class Organization(models.Model):
|
|||
def get_org_users(self):
|
||||
from users.models import User
|
||||
if self.is_default():
|
||||
return User.objects.filter(orgs__isnull=True)
|
||||
users = User.objects.filter(orgs__isnull=True)
|
||||
else:
|
||||
return self.users.all()
|
||||
users = self.users.all()
|
||||
users = users.exclude(role=User.ROLE_APP)
|
||||
return users
|
||||
|
||||
def get_org_admins(self):
|
||||
if self.is_real():
|
||||
|
|
|
@ -5,6 +5,7 @@ from django.shortcuts import get_object_or_404
|
|||
from rest_framework.views import APIView, Response
|
||||
from rest_framework.generics import ListAPIView, get_object_or_404, RetrieveUpdateAPIView
|
||||
from rest_framework import viewsets
|
||||
from rest_framework.pagination import LimitOffsetPagination
|
||||
|
||||
from common.utils import set_or_append_attr_bulk, get_object_or_none
|
||||
from common.permissions import IsValidUser, IsOrgAdmin, IsOrgAdminOrAppUser
|
||||
|
@ -72,10 +73,7 @@ class UserGrantedAssetsApi(ListAPIView):
|
|||
|
||||
util = AssetPermissionUtil(user)
|
||||
for k, v in util.get_assets().items():
|
||||
if k.is_unixlike():
|
||||
system_users_granted = [s for s in v if s.protocol in ['ssh', 'telnet']]
|
||||
else:
|
||||
system_users_granted = [s for s in v if s.protocol in ['rdp', 'telnet']]
|
||||
system_users_granted = [s for s in v if s.protocol == k.protocol]
|
||||
k.system_users_granted = system_users_granted
|
||||
queryset.append(k)
|
||||
return queryset
|
||||
|
@ -123,10 +121,7 @@ class UserGrantedNodesWithAssetsApi(ListAPIView):
|
|||
for node, _assets in nodes.items():
|
||||
assets = _assets.keys()
|
||||
for k, v in _assets.items():
|
||||
if k.is_unixlike():
|
||||
system_users_granted = [s for s in v if s.protocol in ['ssh', 'telnet']]
|
||||
else:
|
||||
system_users_granted = [s for s in v if s.protocol in ['rdp', 'telnet']]
|
||||
system_users_granted = [s for s in v if s.protocol == k.protocol]
|
||||
k.system_users_granted = system_users_granted
|
||||
node.assets_granted = assets
|
||||
queryset.append(node)
|
||||
|
|
|
@ -6,13 +6,11 @@ from .. import views
|
|||
app_name = 'perms'
|
||||
|
||||
urlpatterns = [
|
||||
url(r'^asset-permission$', views.AssetPermissionListView.as_view(), name='asset-permission-list'),
|
||||
url(r'^asset-permission/create$', views.AssetPermissionCreateView.as_view(), name='asset-permission-create'),
|
||||
url(r'^asset-permission/(?P<pk>[0-9a-zA-Z\-]{36})/update$', views.AssetPermissionUpdateView.as_view(), name='asset-permission-update'),
|
||||
url(r'^asset-permission/(?P<pk>[0-9a-zA-Z\-]{36})$', views.AssetPermissionDetailView.as_view(),name='asset-permission-detail'),
|
||||
url(r'^asset-permission/(?P<pk>[0-9a-zA-Z\-]{36})/delete$', views.AssetPermissionDeleteView.as_view(), name='asset-permission-delete'),
|
||||
url(r'^asset-permission/(?P<pk>[0-9a-zA-Z\-]{36})/user$', views.AssetPermissionUserView.as_view(), name='asset-permission-user-list'),
|
||||
url(r'^asset-permission/(?P<pk>[0-9a-zA-Z\-]{36})/asset$', views.AssetPermissionAssetView.as_view(), name='asset-permission-asset-list'),
|
||||
url(r'^asset-permission/$', views.AssetPermissionListView.as_view(), name='asset-permission-list'),
|
||||
url(r'^asset-permission/create/$', views.AssetPermissionCreateView.as_view(), name='asset-permission-create'),
|
||||
url(r'^asset-permission/(?P<pk>[0-9a-zA-Z\-]{36})/update/$', views.AssetPermissionUpdateView.as_view(), name='asset-permission-update'),
|
||||
url(r'^asset-permission/(?P<pk>[0-9a-zA-Z\-]{36})/$', views.AssetPermissionDetailView.as_view(),name='asset-permission-detail'),
|
||||
url(r'^asset-permission/(?P<pk>[0-9a-zA-Z\-]{36})/delete/$', views.AssetPermissionDeleteView.as_view(), name='asset-permission-delete'),
|
||||
url(r'^asset-permission/(?P<pk>[0-9a-zA-Z\-]{36})/user/$', views.AssetPermissionUserView.as_view(), name='asset-permission-user-list'),
|
||||
url(r'^asset-permission/(?P<pk>[0-9a-zA-Z\-]{36})/asset/$', views.AssetPermissionAssetView.as_view(), name='asset-permission-asset-list'),
|
||||
]
|
||||
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
<div class="footer fixed">
|
||||
<div class="pull-right">
|
||||
Version <strong>1.3.2-{% include '_build.html' %}</strong> GPLv2.
|
||||
Version <strong>1.3.3-{% include '_build.html' %}</strong> GPLv2.
|
||||
<!--<img style="display: none" src="http://www.jumpserver.org/img/evaluate_avatar1.jpg">-->
|
||||
</div>
|
||||
<div>
|
||||
|
|
|
@ -4,6 +4,7 @@ from collections import OrderedDict
|
|||
import logging
|
||||
import os
|
||||
import uuid
|
||||
import copy
|
||||
|
||||
from django.core.cache import cache
|
||||
from django.shortcuts import get_object_or_404, redirect
|
||||
|
@ -310,6 +311,7 @@ class SessionReplayViewSet(viewsets.ViewSet):
|
|||
def retrieve(self, request, *args, **kwargs):
|
||||
session_id = kwargs.get('pk')
|
||||
self.session = get_object_or_404(Session, id=session_id)
|
||||
|
||||
# 新版本和老版本的文件后缀不同
|
||||
session_path = self.get_session_path() # 存在外部存储上的路径
|
||||
local_path = self.get_local_path()
|
||||
|
|
|
@ -92,27 +92,52 @@
|
|||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<div id="actions" class="">
|
||||
<div class="input-group">
|
||||
<select class="form-control m-b" style="width: auto" id="slct_bulk_update">
|
||||
<option value="export">{% trans 'Export command' %}</option>
|
||||
</select>
|
||||
<div class="input-group-btn pull-left" style="padding-left: 5px;">
|
||||
<button id='btn_bulk_update' style="height: 32px;" class="btn btn-sm btn-primary">
|
||||
{% trans 'Submit' %}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
{% block custom_foot_js %}
|
||||
<script src="{% static "js/plugins/footable/footable.all.min.js" %}"></script>
|
||||
<script src="{% static 'js/plugins/datepicker/bootstrap-datepicker.js' %}"></script>
|
||||
<script>
|
||||
$(document).ready(function () {
|
||||
$('.footable').footable();
|
||||
$('.select2').select2({
|
||||
dropdownAutoWidth : true,
|
||||
width: 'auto'
|
||||
});
|
||||
$('#date .input-daterange').datepicker({
|
||||
format: "yyyy-mm-dd",
|
||||
todayBtn: "linked",
|
||||
keyboardNavigation: false,
|
||||
forceParse: false,
|
||||
calendarWeeks: true,
|
||||
autoclose: true
|
||||
});
|
||||
$(document).ready(function () {
|
||||
$('.footable').footable();
|
||||
$('.select2').select2({
|
||||
dropdownAutoWidth : true,
|
||||
width: 'auto'
|
||||
});
|
||||
$('#date .input-daterange').datepicker({
|
||||
format: "yyyy-mm-dd",
|
||||
todayBtn: "linked",
|
||||
keyboardNavigation: false,
|
||||
forceParse: false,
|
||||
calendarWeeks: true,
|
||||
autoclose: true
|
||||
});
|
||||
})
|
||||
.on('click', '#btn_bulk_update', function(){
|
||||
var action = $('#slct_bulk_update').val();
|
||||
var param_action = '&action=' + action;
|
||||
var local_params = window.location.search;
|
||||
if(!local_params){
|
||||
param_action = '?action=' + action;
|
||||
}
|
||||
var params = local_params + param_action;
|
||||
var pathname = window.location.pathname + 'export/';
|
||||
var url = pathname + params;
|
||||
window.open(url);
|
||||
});
|
||||
</script>
|
||||
{% endblock %}
|
||||
|
||||
|
|
|
@ -0,0 +1,103 @@
|
|||
{% load common_tags %}
|
||||
{% load static %}
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Command Report</title>
|
||||
<style>
|
||||
*{
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
.background {
|
||||
background-color: #535659;
|
||||
padding-top: 50px;
|
||||
padding-bottom: 50px;
|
||||
}
|
||||
|
||||
.paper {
|
||||
margin-left: 23%;
|
||||
margin-right: 24%;
|
||||
border: solid;
|
||||
padding: 50px;
|
||||
background-color: #fff;
|
||||
}
|
||||
|
||||
h2 {
|
||||
font-style: italic;
|
||||
text-align: center;
|
||||
}
|
||||
.info {
|
||||
width: 200px;
|
||||
margin-left: 450px;
|
||||
font-style: italic;
|
||||
text-align: left;
|
||||
padding-top: 20px;
|
||||
padding-bottom: 20px;
|
||||
}
|
||||
|
||||
.command {
|
||||
margin-left: 10px;
|
||||
}
|
||||
.command-desc {
|
||||
font-size: 12px;
|
||||
}
|
||||
.command-desc span {
|
||||
float: right;
|
||||
}
|
||||
|
||||
.command-input {
|
||||
{#font-style: italic;#}
|
||||
font-size: 15px;
|
||||
margin-top: 10px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
.command-input span {
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.hr-line-dashed {
|
||||
border-top: 1px dashed #000;
|
||||
color: #000;
|
||||
background-color: #fff;
|
||||
height: 1px;
|
||||
margin: 20px 0;
|
||||
}
|
||||
pre {
|
||||
font-size: 12px;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div class="background">
|
||||
<div class="paper">
|
||||
<h2>Command Report</h2>
|
||||
<div class="info">
|
||||
<p>total: {{ total_count }}</p>
|
||||
<p>date: {{ now | ts_to_date }}</p>
|
||||
</div>
|
||||
|
||||
<div class="hr-line-dashed"></div>
|
||||
|
||||
<div>
|
||||
{% for command in queryset %}
|
||||
<div class="command">
|
||||
<p class="command-desc">
|
||||
[{{ command.user}} {{ command.system_user }}@{{ command.asset }} {{ command.timestamp | ts_to_date }}]
|
||||
<span>{{ forloop.counter }}</span>
|
||||
</p>
|
||||
|
||||
<p class="command-input"><span>$ </span>{{ command.input }}</p>
|
||||
|
||||
<pre>{{ command.output }}</pre>
|
||||
</div>
|
||||
|
||||
<div class="hr-line-dashed"></div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
|
@ -24,5 +24,6 @@ urlpatterns = [
|
|||
|
||||
# Command view
|
||||
url(r'^command/$', views.CommandListView.as_view(), name='command-list'),
|
||||
url(r'^command/export/$', views.CommandExportView.as_view(), name='command-export')
|
||||
|
||||
]
|
||||
|
|
|
@ -1,10 +1,12 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
|
||||
from django.views.generic import ListView
|
||||
from django.views.generic import ListView, View
|
||||
from django.conf import settings
|
||||
from django.utils import timezone
|
||||
from django.utils.translation import ugettext as _
|
||||
from django.http import HttpResponse
|
||||
from django.template import loader
|
||||
import time
|
||||
|
||||
from common.mixins import DatetimeSearchMixin
|
||||
from common.permissions import AdminUserRequiredMixin
|
||||
|
@ -12,7 +14,7 @@ from ..models import Command
|
|||
from .. import utils
|
||||
from ..backends import get_multi_command_storage
|
||||
|
||||
__all__ = ['CommandListView']
|
||||
__all__ = ['CommandListView', 'CommandExportView']
|
||||
common_storage = get_multi_command_storage()
|
||||
|
||||
|
||||
|
@ -61,7 +63,43 @@ class CommandListView(DatetimeSearchMixin, AdminUserRequiredMixin, ListView):
|
|||
return super().get_context_data(**kwargs)
|
||||
|
||||
|
||||
class CommandExportView(DatetimeSearchMixin, AdminUserRequiredMixin, View):
|
||||
model = Command
|
||||
command = user = asset = system_user = action = ''
|
||||
date_from = date_to = None
|
||||
|
||||
def get(self, request, *args, **kwargs):
|
||||
queryset = self.get_queryset()
|
||||
template = 'terminal/command_report.html'
|
||||
context = {
|
||||
'queryset': queryset,
|
||||
'total_count': len(queryset),
|
||||
'now': time.time(),
|
||||
}
|
||||
content = loader.render_to_string(template, context, request)
|
||||
content_type = 'application/octet-stream'
|
||||
response = HttpResponse(content, content_type)
|
||||
filename = 'command-report-{}.html'.format(int(time.time()))
|
||||
response['Content-Disposition'] = 'attachment; filename="%s"' % filename
|
||||
return response
|
||||
|
||||
|
||||
|
||||
def get_queryset(self):
|
||||
self.get_date_range()
|
||||
self.action = self.request.GET.get('action', '')
|
||||
self.command = self.request.GET.get('command', '')
|
||||
self.user = self.request.GET.get("user", '')
|
||||
self.asset = self.request.GET.get('asset', '')
|
||||
self.system_user = self.request.GET.get('system_user', '')
|
||||
filter_kwargs = dict()
|
||||
filter_kwargs['date_from'] = self.date_from
|
||||
filter_kwargs['date_to'] = self.date_to
|
||||
if self.user:
|
||||
filter_kwargs['user'] = self.user
|
||||
if self.asset:
|
||||
filter_kwargs['asset'] = self.asset
|
||||
if self.system_user:
|
||||
filter_kwargs['system_user'] = self.system_user
|
||||
if self.command:
|
||||
filter_kwargs['input'] = self.command
|
||||
queryset = common_storage.filter(**filter_kwargs)
|
||||
return queryset
|
||||
|
|
|
@ -101,7 +101,23 @@ class UserUpdatePKApi(generics.UpdateAPIView):
|
|||
user.save()
|
||||
|
||||
|
||||
class UserGroupViewSet(BulkModelViewSet):
|
||||
class UserUnblockPKApi(generics.UpdateAPIView):
|
||||
queryset = User.objects.all()
|
||||
permission_classes = (IsSuperUser,)
|
||||
serializer_class = UserSerializer
|
||||
key_prefix_limit = "_LOGIN_LIMIT_{}_{}"
|
||||
key_prefix_block = "_LOGIN_BLOCK_{}"
|
||||
|
||||
def perform_update(self, serializer):
|
||||
user = self.get_object()
|
||||
username = user.username if user else ''
|
||||
key_limit = self.key_prefix_limit.format(username, '*')
|
||||
key_block = self.key_prefix_block.format(username)
|
||||
cache.delete_pattern(key_limit)
|
||||
cache.delete(key_block)
|
||||
|
||||
|
||||
class UserGroupViewSet(IDInFilterMixin, BulkModelViewSet):
|
||||
queryset = UserGroup.objects.all()
|
||||
serializer_class = UserGroupSerializer
|
||||
permission_classes = (IsOrgAdmin,)
|
||||
|
@ -203,13 +219,15 @@ class UserAuthApi(APIView):
|
|||
permission_classes = (AllowAny,)
|
||||
serializer_class = UserSerializer
|
||||
key_prefix_limit = "_LOGIN_LIMIT_{}_{}"
|
||||
key_prefix_block = "_LOGIN_BLOCK_{}"
|
||||
|
||||
def post(self, request):
|
||||
# limit login
|
||||
username = request.data.get('username')
|
||||
ip = request.data.get('remote_addr', None)
|
||||
ip = ip if ip else get_login_ip(request)
|
||||
key_limit = self.key_prefix_limit.format(ip, username)
|
||||
key_limit = self.key_prefix_limit.format(username, ip)
|
||||
key_block = self.key_prefix_block.format(username)
|
||||
if is_block_login(key_limit):
|
||||
msg = _("Log in frequently and try again later")
|
||||
return Response({'msg': msg}, status=401)
|
||||
|
@ -224,7 +242,7 @@ class UserAuthApi(APIView):
|
|||
}
|
||||
self.write_login_log(request, data)
|
||||
|
||||
set_user_login_failed_count_to_cache(key_limit)
|
||||
set_user_login_failed_count_to_cache(key_limit, key_block)
|
||||
return Response({'msg': msg}, status=401)
|
||||
|
||||
if not user.otp_enabled:
|
||||
|
|
|
@ -182,6 +182,14 @@
|
|||
</span>
|
||||
</td>
|
||||
</tr>
|
||||
<tr style="{% if not unblock %}display:none{% endif %}">
|
||||
<td>{% trans 'Unblock user' %}</td>
|
||||
<td>
|
||||
<span class="pull-right">
|
||||
<button type="button" class="btn btn-primary btn-xs" id="btn-unblock-user" style="width: 54px">{% trans 'Unblock' %}</button>
|
||||
</span>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
@ -275,7 +283,7 @@ $(document).ready(function() {
|
|||
.on('select2:unselect', function(evt) {
|
||||
var data = evt.params.data;
|
||||
delete jumpserver.nodes_selected[data.id];
|
||||
})
|
||||
});
|
||||
})
|
||||
.on('click', '#is_active', function() {
|
||||
var the_url = "{% url 'api-users:user-detail' pk=user_object.id %}";
|
||||
|
@ -293,7 +301,7 @@ $(document).ready(function() {
|
|||
.on('click', '#force_enable_otp', function() {
|
||||
{% if request.user == user_object %}
|
||||
toastr.error("{% trans 'Goto profile page enable MFA' %}");
|
||||
return
|
||||
return;
|
||||
{% endif %}
|
||||
|
||||
var the_url = "{% url 'api-users:user-detail' pk=user_object.id %}";
|
||||
|
@ -426,6 +434,40 @@ $(document).ready(function() {
|
|||
var the_url = '{% url "api-users:user-detail" pk=DEFAULT_PK %}'.replace('{{ DEFAULT_PK }}', uid);
|
||||
var redirect_url = "{% url 'users:user-list' %}";
|
||||
objectDelete($this, name, the_url, redirect_url);
|
||||
}).on('click', '#btn-unblock-user', function () {
|
||||
function doReset() {
|
||||
{#var the_url = '{% url "api-users:user-reset-password" pk=user_object.id %}';#}
|
||||
var the_url = '{% url "api-users:user-unblock" pk=user_object.id %}';
|
||||
var body = {};
|
||||
var success = function() {
|
||||
var msg = "{% trans "Success" %}";
|
||||
{#swal("{% trans 'Unblock user' %}", msg, "success");#}
|
||||
swal({
|
||||
title: "{% trans 'Unblock user' %}",
|
||||
text: msg,
|
||||
type: "success"
|
||||
}, function() {
|
||||
location.reload()
|
||||
}
|
||||
);
|
||||
};
|
||||
APIUpdateAttr({
|
||||
url: the_url,
|
||||
body: JSON.stringify(body),
|
||||
success: success
|
||||
});
|
||||
}
|
||||
swal({
|
||||
title: "{% trans 'Are you sure?' %}",
|
||||
text: "{% trans "After unlocking the user, the user can log in normally."%}",
|
||||
type: "warning",
|
||||
showCancelButton: true,
|
||||
confirmButtonColor: "#DD6B55",
|
||||
confirmButtonText: "{% trans 'Confirm' %}",
|
||||
closeOnConfirm: false
|
||||
}, function() {
|
||||
doReset();
|
||||
});
|
||||
})
|
||||
</script>
|
||||
{% endblock %}
|
||||
|
|
|
@ -59,7 +59,7 @@ function initTable() {
|
|||
ele: $('#user_list_table'),
|
||||
columnDefs: [
|
||||
{targets: 1, createdCell: function (td, cellData, rowData) {
|
||||
var detail_btn = '<a href="{% url "users:user-detail" pk=DEFAULT_PK %}">' + escape(cellData) + '</a>';
|
||||
var detail_btn = '<a href="{% url "users:user-detail" pk=DEFAULT_PK %}">' + cellData + '</a>';
|
||||
$(td).html(detail_btn.replace("{{ DEFAULT_PK }}", rowData.id));
|
||||
}},
|
||||
{targets: 4, createdCell: function (td, cellData) {
|
||||
|
|
|
@ -29,6 +29,8 @@ urlpatterns = [
|
|||
api.UserResetPKApi.as_view(), name='user-public-key-reset'),
|
||||
url(r'^users/(?P<pk>[0-9a-zA-Z\-]{36})/pubkey/update/$',
|
||||
api.UserUpdatePKApi.as_view(), name='user-public-key-update'),
|
||||
url(r'^users/(?P<pk>[0-9a-zA-Z\-]{36})/unblock/$',
|
||||
api.UserUnblockPKApi.as_view(), name='user-unblock'),
|
||||
url(r'^users/(?P<pk>[0-9a-zA-Z\-]{36})/groups/$',
|
||||
api.UserUpdateGroupApi.as_view(), name='user-update-group'),
|
||||
url(r'^groups/(?P<pk>[0-9a-zA-Z\-]{36})/users/$',
|
||||
|
|
|
@ -8,13 +8,13 @@ app_name = 'users'
|
|||
|
||||
urlpatterns = [
|
||||
# Login view
|
||||
url(r'^login$', views.UserLoginView.as_view(), name='login'),
|
||||
url(r'^logout$', views.UserLogoutView.as_view(), name='logout'),
|
||||
url(r'^login/otp$', views.UserLoginOtpView.as_view(), name='login-otp'),
|
||||
url(r'^password/forgot$', views.UserForgotPasswordView.as_view(), name='forgot-password'),
|
||||
url(r'^password/forgot/sendmail-success$', views.UserForgotPasswordSendmailSuccessView.as_view(), name='forgot-password-sendmail-success'),
|
||||
url(r'^password/reset$', views.UserResetPasswordView.as_view(), name='reset-password'),
|
||||
url(r'^password/reset/success$', views.UserResetPasswordSuccessView.as_view(), name='reset-password-success'),
|
||||
url(r'^login/$', views.UserLoginView.as_view(), name='login'),
|
||||
url(r'^logout/$', views.UserLogoutView.as_view(), name='logout'),
|
||||
url(r'^login/otp/$', views.UserLoginOtpView.as_view(), name='login-otp'),
|
||||
url(r'^password/forgot/$', views.UserForgotPasswordView.as_view(), name='forgot-password'),
|
||||
url(r'^password/forgot/sendmail-success/$', views.UserForgotPasswordSendmailSuccessView.as_view(), name='forgot-password-sendmail-success'),
|
||||
url(r'^password/reset/$', views.UserResetPasswordView.as_view(), name='reset-password'),
|
||||
url(r'^password/reset/success/$', views.UserResetPasswordSuccessView.as_view(), name='reset-password-success'),
|
||||
|
||||
# Profile
|
||||
url(r'^profile/$', views.UserProfileView.as_view(), name='user-profile'),
|
||||
|
@ -29,23 +29,23 @@ urlpatterns = [
|
|||
url(r'^profile/otp/settings-success/$', views.UserOtpSettingsSuccessView.as_view(), name='user-otp-settings-success'),
|
||||
|
||||
# User view
|
||||
url(r'^user$', views.UserListView.as_view(), name='user-list'),
|
||||
url(r'^user/export/', views.UserExportView.as_view(), name='user-export'),
|
||||
url(r'^user/$', views.UserListView.as_view(), name='user-list'),
|
||||
url(r'^user/export/$', views.UserExportView.as_view(), name='user-export'),
|
||||
url(r'^first-login/$', views.UserFirstLoginView.as_view(), name='user-first-login'),
|
||||
url(r'^user/import/$', views.UserBulkImportView.as_view(), name='user-import'),
|
||||
url(r'^user/create$', views.UserCreateView.as_view(), name='user-create'),
|
||||
url(r'^user/(?P<pk>[0-9a-zA-Z\-]{36})/update$', views.UserUpdateView.as_view(), name='user-update'),
|
||||
url(r'^user/update$', views.UserBulkUpdateView.as_view(), name='user-bulk-update'),
|
||||
url(r'^user/(?P<pk>[0-9a-zA-Z\-]{36})$', views.UserDetailView.as_view(), name='user-detail'),
|
||||
url(r'^user/(?P<pk>[0-9a-zA-Z\-]{36})/assets', views.UserGrantedAssetView.as_view(), name='user-granted-asset'),
|
||||
url(r'^user/(?P<pk>[0-9a-zA-Z\-]{36})/login-history', views.UserDetailView.as_view(), name='user-login-history'),
|
||||
url(r'^user/create/$', views.UserCreateView.as_view(), name='user-create'),
|
||||
url(r'^user/(?P<pk>[0-9a-zA-Z\-]{36})/update/$', views.UserUpdateView.as_view(), name='user-update'),
|
||||
url(r'^user/update/$', views.UserBulkUpdateView.as_view(), name='user-bulk-update'),
|
||||
url(r'^user/(?P<pk>[0-9a-zA-Z\-]{36})/$', views.UserDetailView.as_view(), name='user-detail'),
|
||||
url(r'^user/(?P<pk>[0-9a-zA-Z\-]{36})/assets/$', views.UserGrantedAssetView.as_view(), name='user-granted-asset'),
|
||||
url(r'^user/(?P<pk>[0-9a-zA-Z\-]{36})/login-history/$', views.UserDetailView.as_view(), name='user-login-history'),
|
||||
|
||||
# User group view
|
||||
url(r'^user-group$', views.UserGroupListView.as_view(), name='user-group-list'),
|
||||
url(r'^user-group/(?P<pk>[0-9a-zA-Z\-]{36})$', views.UserGroupDetailView.as_view(), name='user-group-detail'),
|
||||
url(r'^user-group/create$', views.UserGroupCreateView.as_view(), name='user-group-create'),
|
||||
url(r'^user-group/(?P<pk>[0-9a-zA-Z\-]{36})/update$', views.UserGroupUpdateView.as_view(), name='user-group-update'),
|
||||
url(r'^user-group/(?P<pk>[0-9a-zA-Z\-]{36})/assets', views.UserGroupGrantedAssetView.as_view(), name='user-group-granted-asset'),
|
||||
url(r'^user-group/$', views.UserGroupListView.as_view(), name='user-group-list'),
|
||||
url(r'^user-group/(?P<pk>[0-9a-zA-Z\-]{36})/$', views.UserGroupDetailView.as_view(), name='user-group-detail'),
|
||||
url(r'^user-group/create/$', views.UserGroupCreateView.as_view(), name='user-group-create'),
|
||||
url(r'^user-group/(?P<pk>[0-9a-zA-Z\-]{36})/update/$', views.UserGroupUpdateView.as_view(), name='user-group-update'),
|
||||
url(r'^user-group/(?P<pk>[0-9a-zA-Z\-]{36})/assets/$', views.UserGroupGrantedAssetView.as_view(), name='user-group-granted-asset'),
|
||||
|
||||
# Login log
|
||||
url(r'^login-log/$', views.LoginLogListView.as_view(), name='login-log-list'),
|
||||
|
|
|
@ -212,10 +212,10 @@ def write_login_log(*args, **kwargs):
|
|||
|
||||
|
||||
def get_ip_city(ip, timeout=10):
|
||||
# Taobao ip api: http://ip.taobao.com//service/getIpInfo.php?ip=8.8.8.8
|
||||
# Taobao ip api: http://ip.taobao.com/service/getIpInfo.php?ip=8.8.8.8
|
||||
# Sina ip api: http://int.dpool.sina.com.cn/iplookup/iplookup.php?ip=8.8.8.8&format=json
|
||||
|
||||
url = 'http://int.dpool.sina.com.cn/iplookup/iplookup.php?ip=%s&format=json' % ip
|
||||
url = 'http://ip.taobao.com/service/getIpInfo.php?ip=%s' % ip
|
||||
try:
|
||||
r = requests.get(url, timeout=timeout)
|
||||
except:
|
||||
|
@ -224,8 +224,8 @@ def get_ip_city(ip, timeout=10):
|
|||
if r and r.status_code == 200:
|
||||
try:
|
||||
data = r.json()
|
||||
if not isinstance(data, int) and data['ret'] == 1:
|
||||
city = data['country'] + ' ' + data['city']
|
||||
if not isinstance(data, int) and data['code'] == 0:
|
||||
city = data['data']['country'] + ' ' + data['data']['city']
|
||||
except ValueError:
|
||||
pass
|
||||
return city
|
||||
|
@ -333,7 +333,7 @@ def check_password_rules(password):
|
|||
return bool(match_obj)
|
||||
|
||||
|
||||
def set_user_login_failed_count_to_cache(key_limit):
|
||||
def set_user_login_failed_count_to_cache(key_limit, key_block):
|
||||
count = cache.get(key_limit)
|
||||
count = count + 1 if count else 1
|
||||
|
||||
|
@ -343,6 +343,15 @@ def set_user_login_failed_count_to_cache(key_limit):
|
|||
limit_time = setting_limit_time.cleaned_value if setting_limit_time \
|
||||
else settings.DEFAULT_LOGIN_LIMIT_TIME
|
||||
|
||||
setting_limit_count = Setting.objects.filter(
|
||||
name='SECURITY_LOGIN_LIMIT_COUNT'
|
||||
).first()
|
||||
limit_count = setting_limit_count.cleaned_value if setting_limit_count \
|
||||
else settings.DEFAULT_LOGIN_LIMIT_COUNT
|
||||
|
||||
if count >= limit_count:
|
||||
cache.set(key_block, 1, int(limit_time)*60)
|
||||
|
||||
cache.set(key_limit, count, int(limit_time)*60)
|
||||
|
||||
|
||||
|
@ -357,3 +366,9 @@ def is_block_login(key_limit):
|
|||
|
||||
if count and count >= limit_count:
|
||||
return True
|
||||
|
||||
|
||||
def is_need_unblock(key_block):
|
||||
if not cache.get(key_block):
|
||||
return False
|
||||
return True
|
||||
|
|
|
@ -52,6 +52,7 @@ class UserLoginView(FormView):
|
|||
redirect_field_name = 'next'
|
||||
key_prefix_captcha = "_LOGIN_INVALID_{}"
|
||||
key_prefix_limit = "_LOGIN_LIMIT_{}_{}"
|
||||
key_prefix_block = "_LOGIN_BLOCK_{}"
|
||||
|
||||
def get(self, request, *args, **kwargs):
|
||||
if request.user.is_staff:
|
||||
|
@ -65,7 +66,7 @@ class UserLoginView(FormView):
|
|||
# limit login authentication
|
||||
ip = get_login_ip(request)
|
||||
username = self.request.POST.get('username')
|
||||
key_limit = self.key_prefix_limit.format(ip, username)
|
||||
key_limit = self.key_prefix_limit.format(username, ip)
|
||||
if is_block_login(key_limit):
|
||||
return self.render_to_response(self.get_context_data(block_login=True))
|
||||
|
||||
|
@ -91,8 +92,9 @@ class UserLoginView(FormView):
|
|||
|
||||
# limit user login failed count
|
||||
ip = get_login_ip(self.request)
|
||||
key_limit = self.key_prefix_limit.format(ip, username)
|
||||
set_user_login_failed_count_to_cache(key_limit)
|
||||
key_limit = self.key_prefix_limit.format(username, ip)
|
||||
key_block = self.key_prefix_block.format(username)
|
||||
set_user_login_failed_count_to_cache(key_limit, key_block)
|
||||
|
||||
# show captcha
|
||||
cache.set(self.key_prefix_captcha.format(ip), 1, 3600)
|
||||
|
|
|
@ -37,7 +37,9 @@ from common.models import Setting
|
|||
from common.permissions import AdminUserRequiredMixin
|
||||
from .. import forms
|
||||
from ..models import User, UserGroup
|
||||
from ..utils import generate_otp_uri, check_otp_code, get_user_or_tmp_user, get_password_check_rules, check_password_rules
|
||||
from ..utils import generate_otp_uri, check_otp_code, \
|
||||
get_user_or_tmp_user, get_password_check_rules, check_password_rules, \
|
||||
is_need_unblock
|
||||
from ..signals import post_user_create
|
||||
from ..tasks import write_login_log_async
|
||||
|
||||
|
@ -169,13 +171,17 @@ class UserDetailView(AdminUserRequiredMixin, DetailView):
|
|||
model = User
|
||||
template_name = 'users/user_detail.html'
|
||||
context_object_name = "user_object"
|
||||
key_prefix_block = "_LOGIN_BLOCK_{}"
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
user = self.get_object()
|
||||
key_block = self.key_prefix_block.format(user.username)
|
||||
groups = UserGroup.objects.exclude(id__in=self.object.groups.all())
|
||||
context = {
|
||||
'app': _('Users'),
|
||||
'action': _('User detail'),
|
||||
'groups': groups
|
||||
'groups': groups,
|
||||
'unblock': is_need_unblock(key_block),
|
||||
}
|
||||
kwargs.update(context)
|
||||
return super().get_context_data(**kwargs)
|
||||
|
|
|
@ -228,7 +228,7 @@ Luna 已改为纯前端,需要 Nginx 来运行访问
|
|||
-p 8081:8080 -v /opt/guacamole/key:/config/guacamole/key \
|
||||
-e JUMPSERVER_KEY_DIR=/config/guacamole/key \
|
||||
-e JUMPSERVER_SERVER=http://<填写本机的IP地址>:8080 \
|
||||
registry.jumpserver.org/public/guacamole:1.0.0
|
||||
registry.jumpserver.org/public/guacamole:latest
|
||||
|
||||
这里所需要注意的是 guacamole 暴露出来的端口是 8081,若与主机上其他端口冲突请自定义一下。
|
||||
|
||||
|
|
2
jms
2
jms
|
@ -122,8 +122,8 @@ def start_gunicorn():
|
|||
cmd = [
|
||||
'gunicorn', 'jumpserver.wsgi',
|
||||
'-b', bind,
|
||||
'-w', str(WORKERS),
|
||||
'-k', 'eventlet',
|
||||
'-w', str(WORKERS),
|
||||
'--access-logformat', log_format,
|
||||
'-p', pid_file,
|
||||
]
|
||||
|
|
|
@ -48,7 +48,7 @@ MarkupSafe==1.0
|
|||
mysqlclient==1.3.12
|
||||
olefile==0.44
|
||||
openapi-codec==1.3.2
|
||||
paramiko==2.4.0
|
||||
paramiko==2.4.1
|
||||
passlib==1.7.1
|
||||
Pillow==4.3.0
|
||||
pyasn1==0.4.2
|
||||
|
|
|
@ -5,4 +5,4 @@ python3 ../apps/manage.py makemigrations
|
|||
|
||||
python3 ../apps/manage.py migrate
|
||||
|
||||
python3 ../apps/manage.py makemigrations –-merge
|
||||
python3 ../apps/manage.py makemigrations --merge
|
||||
|
|
Loading…
Reference in New Issue