Skip to content
Projects
Groups
Snippets
Help
Loading...
Sign in
Toggle navigation
J
jumpserver
Project
Project
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
ops
jumpserver
Commits
e7a731fa
Unverified
Commit
e7a731fa
authored
Jun 11, 2018
by
老广
Committed by
GitHub
Jun 11, 2018
Browse files
Options
Browse Files
Download
Plain Diff
Merge pull request #1417 from jumpserver/dev
Dev
parents
283b1c1d
73f9f546
Hide whitespace changes
Inline
Side-by-side
Showing
37 changed files
with
1907 additions
and
275 deletions
+1907
-275
__init__.py
apps/__init__.py
+1
-1
node.py
apps/assets/api/node.py
+3
-2
utils.py
apps/assets/utils.py
+0
-1
forms.py
apps/common/forms.py
+46
-0
signals_handler.py
apps/common/signals_handler.py
+1
-1
basic_setting.html
apps/common/templates/common/basic_setting.html
+3
-0
email_setting.html
apps/common/templates/common/email_setting.html
+3
-0
ldap_setting.html
apps/common/templates/common/ldap_setting.html
+3
-0
security_setting.html
apps/common/templates/common/security_setting.html
+87
-0
terminal_setting.html
apps/common/templates/common/terminal_setting.html
+5
-0
view_urls.py
apps/common/urls/view_urls.py
+1
-0
views.py
apps/common/views.py
+26
-2
django.mo
apps/i18n/zh/LC_MESSAGES/django.mo
+0
-0
django.po
apps/i18n/zh/LC_MESSAGES/django.po
+346
-233
settings.py
apps/jumpserver/settings.py
+3
-0
inventory.py
apps/ops/inventory.py
+1
-0
jumpserver.js
apps/static/js/jumpserver.js
+88
-0
pwstrength-bootstrap.js
apps/static/js/pwstrength-bootstrap.js
+977
-0
_base_create_update.html
apps/templates/_base_create_update.html
+1
-0
_footer.html
apps/templates/_footer.html
+1
-1
forms.py
apps/users/forms.py
+8
-7
user.py
apps/users/models/user.py
+17
-2
serializers.py
apps/users/serializers.py
+4
-1
signals_handler.py
apps/users/signals_handler.py
+9
-0
_user.html
apps/users/templates/users/_user.html
+1
-0
first_login.html
apps/users/templates/users/first_login.html
+8
-7
reset_password.html
apps/users/templates/users/reset_password.html
+43
-3
user_detail.html
apps/users/templates/users/user_detail.html
+4
-0
user_list.html
apps/users/templates/users/user_list.html
+4
-3
user_password_update.html
apps/users/templates/users/user_password_update.html
+41
-0
user_profile.html
apps/users/templates/users/user_profile.html
+7
-0
user_pubkey_update.html
apps/users/templates/users/user_pubkey_update.html
+6
-1
user_update.html
apps/users/templates/users/user_update.html
+45
-0
utils.py
apps/users/utils.py
+61
-0
login.py
apps/users/views/login.py
+15
-3
user.py
apps/users/views/user.py
+38
-2
config_example.py
config_example.py
+0
-5
No files found.
apps/__init__.py
View file @
e7a731fa
...
...
@@ -2,4 +2,4 @@
# -*- coding: utf-8 -*-
#
__version__
=
"1.3.
1
"
__version__
=
"1.3.
2
"
apps/assets/api/node.py
View file @
e7a731fa
...
...
@@ -116,7 +116,7 @@ class NodeChildrenApi(mixins.ListModelMixin, generics.CreateAPIView):
def
get_object
(
self
):
pk
=
self
.
kwargs
.
get
(
'pk'
)
or
self
.
request
.
query_params
.
get
(
'id'
)
if
not
pk
:
node
=
No
de
.
root
()
node
=
No
ne
else
:
node
=
get_object_or_404
(
Node
,
pk
=
pk
)
return
node
...
...
@@ -126,7 +126,8 @@ class NodeChildrenApi(mixins.ListModelMixin, generics.CreateAPIView):
query_all
=
self
.
request
.
query_params
.
get
(
"all"
)
query_assets
=
self
.
request
.
query_params
.
get
(
'assets'
)
node
=
self
.
get_object
()
if
node
==
Node
.
root
():
if
node
is
None
:
node
=
Node
.
root
()
queryset
.
append
(
node
)
if
query_all
:
children
=
node
.
get_all_children
()
...
...
apps/assets/utils.py
View file @
e7a731fa
...
...
@@ -51,7 +51,6 @@ def test_gateway_connectability(gateway):
client
=
paramiko
.
SSHClient
()
client
.
set_missing_host_key_policy
(
paramiko
.
AutoAddPolicy
())
proxy
=
paramiko
.
SSHClient
()
proxy
.
load_host_keys
(
os
.
path
.
expanduser
(
'~/.ssh/known_hosts'
))
proxy
.
set_missing_host_key_policy
(
paramiko
.
AutoAddPolicy
())
try
:
...
...
apps/common/forms.py
View file @
e7a731fa
...
...
@@ -168,3 +168,49 @@ class TerminalSettingForm(BaseForm):
)
)
class
SecuritySettingForm
(
BaseForm
):
# MFA全局设置
SECURITY_MFA_AUTH
=
forms
.
BooleanField
(
initial
=
False
,
required
=
False
,
label
=
_
(
"MFA Secondary certification"
),
help_text
=
_
(
'After opening, the user login must use MFA secondary '
'authentication (valid for all users, including administrators)'
)
)
# 最小长度
SECURITY_PASSWORD_MIN_LENGTH
=
forms
.
IntegerField
(
initial
=
6
,
label
=
_
(
"Password minimum length"
),
min_value
=
6
)
# 大写字母
SECURITY_PASSWORD_UPPER_CASE
=
forms
.
BooleanField
(
initial
=
False
,
required
=
False
,
label
=
_
(
"Must contain capital letters"
),
help_text
=
_
(
'After opening, the user password changes '
'and resets must contain uppercase letters'
)
)
# 小写字母
SECURITY_PASSWORD_LOWER_CASE
=
forms
.
BooleanField
(
initial
=
False
,
required
=
False
,
label
=
_
(
"Must contain lowercase letters"
),
help_text
=
_
(
'After opening, the user password changes '
'and resets must contain lowercase letters'
)
)
# 数字
SECURITY_PASSWORD_NUMBER
=
forms
.
BooleanField
(
initial
=
False
,
required
=
False
,
label
=
_
(
"Must contain numeric characters"
),
help_text
=
_
(
'After opening, the user password changes '
'and resets must contain numeric characters'
)
)
# 特殊字符
SECURITY_PASSWORD_SPECIAL_CHAR
=
forms
.
BooleanField
(
initial
=
False
,
required
=
False
,
label
=
_
(
"Must contain special characters"
),
help_text
=
_
(
'After opening, the user password changes '
'and resets must contain special characters'
)
)
apps/common/signals_handler.py
View file @
e7a731fa
...
...
@@ -34,7 +34,7 @@ def refresh_all_settings_on_django_ready(sender, **kwargs):
def
ldap_auth_on_changed
(
sender
,
enabled
=
True
,
**
kwargs
):
if
enabled
:
logger
.
debug
(
"Enable LDAP auth"
)
if
settings
.
AUTH_LDAP_BACKEND
not
in
settings
.
AUTH
_LDAP_BACKEND
:
if
settings
.
AUTH_LDAP_BACKEND
not
in
settings
.
AUTH
ENTICATION_BACKENDS
:
settings
.
AUTHENTICATION_BACKENDS
.
insert
(
0
,
settings
.
AUTH_LDAP_BACKEND
)
else
:
...
...
apps/common/templates/common/basic_setting.html
View file @
e7a731fa
...
...
@@ -23,6 +23,9 @@
<li>
<a
href=
"{% url 'settings:terminal-setting' %}"
class=
"text-center"
><i
class=
"fa fa-hdd-o"
></i>
{% trans 'Terminal setting' %}
</a>
</li>
<li>
<a
href=
"{% url 'settings:security-setting' %}"
class=
"text-center"
><i
class=
"fa fa-lock"
></i>
{% trans 'Security setting' %}
</a>
</li>
</ul>
</div>
<div
class=
"tab-content"
>
...
...
apps/common/templates/common/email_setting.html
View file @
e7a731fa
...
...
@@ -23,6 +23,9 @@
<li>
<a
href=
"{% url 'settings:terminal-setting' %}"
class=
"text-center"
><i
class=
"fa fa-hdd-o"
></i>
{% trans 'Terminal setting' %}
</a>
</li>
<li>
<a
href=
"{% url 'settings:security-setting' %}"
class=
"text-center"
><i
class=
"fa fa-lock"
></i>
{% trans 'Security setting' %}
</a>
</li>
</ul>
</div>
<div
class=
"tab-content"
>
...
...
apps/common/templates/common/ldap_setting.html
View file @
e7a731fa
...
...
@@ -23,6 +23,9 @@
<li>
<a
href=
"{% url 'settings:terminal-setting' %}"
class=
"text-center"
><i
class=
"fa fa-hdd-o"
></i>
{% trans 'Terminal setting' %}
</a>
</li>
<li>
<a
href=
"{% url 'settings:security-setting' %}"
class=
"text-center"
><i
class=
"fa fa-lock"
></i>
{% trans 'Security setting' %}
</a>
</li>
</ul>
</div>
<div
class=
"tab-content"
>
...
...
apps/common/templates/common/security_setting.html
0 → 100644
View file @
e7a731fa
{% extends 'base.html' %}
{% load static %}
{% load bootstrap3 %}
{% load i18n %}
{% load common_tags %}
{% block content %}
<div
class=
"wrapper wrapper-content animated fadeInRight"
>
<div
class=
"row"
>
<div
class=
"col-sm-12"
>
<div
class=
"ibox float-e-margins"
>
<div
class=
"panel-options"
>
<ul
class=
"nav nav-tabs"
>
<li>
<a
href=
"{% url 'settings:basic-setting' %}"
class=
"text-center"
><i
class=
"fa fa-cubes"
></i>
{% trans 'Basic setting' %}
</a>
</li>
<li>
<a
href=
"{% url 'settings:email-setting' %}"
class=
"text-center"
><i
class=
"fa fa-envelope"
></i>
{% trans 'Email setting' %}
</a>
</li>
<li>
<a
href=
"{% url 'settings:ldap-setting' %}"
class=
"text-center"
><i
class=
"fa fa-archive"
></i>
{% trans 'LDAP setting' %}
</a>
</li>
<li>
<a
href=
"{% url 'settings:terminal-setting' %}"
class=
"text-center"
><i
class=
"fa fa-hdd-o"
></i>
{% trans 'Terminal setting' %}
</a>
</li>
<li
class=
"active"
>
<a
href=
"{% url 'settings:security-setting' %}"
class=
"text-center"
><i
class=
"fa fa-lock"
></i>
{% trans 'Security setting' %}
</a>
</li>
</ul>
</div>
<div
class=
"tab-content"
>
<div
class=
"col-sm-12"
style=
"padding-left:0"
>
<div
class=
"ibox-content"
style=
"border-width: 0;padding-top: 40px;"
>
<form
action=
""
method=
"post"
class=
"form-horizontal"
>
{% if form.non_field_errors %}
<div
class=
"alert alert-danger"
>
{{ form.non_field_errors }}
</div>
{% endif %}
{% csrf_token %}
<h3>
{% trans "MFA setting" %}
</h3>
{% for field in form %}
{% if forloop.counter == 2 %}
<div
class=
"hr-line-dashed"
></div>
<h3>
{% trans "Password check rule" %}
</h3>
{% endif %}
{% if not field.field|is_bool_field %}
{% bootstrap_field field layout="horizontal" %}
{% else %}
<div
class=
"form-group"
>
<label
for=
"{{ field.id_for_label }}"
class=
"col-sm-2 control-label"
>
{{ field.label }}
</label>
<div
class=
"col-sm-8"
>
<div
class=
"col-sm-1"
>
{{ field }}
</div>
<div
class=
"col-sm-9"
>
<span
class=
"help-block"
>
{{ field.help_text }}
</span>
</div>
</div>
</div>
{% endif %}
{% endfor %}
<div
class=
"hr-line-dashed"
></div>
<div
class=
"form-group"
>
<div
class=
"col-sm-4 col-sm-offset-2"
>
<button
class=
"btn btn-default"
type=
"reset"
>
{% trans 'Reset' %}
</button>
<button
id=
"submit_button"
class=
"btn btn-primary"
type=
"submit"
>
{% trans 'Submit' %}
</button>
</div>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
{% endblock %}
{% block custom_foot_js %}
<script>
</script>
{% endblock %}
apps/common/templates/common/terminal_setting.html
View file @
e7a731fa
...
...
@@ -27,6 +27,9 @@
<a
href=
"{% url 'settings:terminal-setting' %}"
class=
"text-center"
><i
class=
"fa fa-hdd-o"
></i>
{% trans 'Terminal setting' %}
</a>
</li>
<li>
<a
href=
"{% url 'settings:security-setting' %}"
class=
"text-center"
><i
class=
"fa fa-lock"
></i>
{% trans 'Security setting' %}
</a>
</li>
</ul>
</div>
<div
class=
"tab-content"
>
...
...
@@ -39,6 +42,7 @@
</div>
{% endif %}
{% csrf_token %}
<h3>
{% trans "Basic setting" %}
</h3>
{% for field in form %}
{% if not field.field|is_bool_field %}
...
...
@@ -60,6 +64,7 @@
{% endfor %}
<div
class=
"hr-line-dashed"
></div>
<h3>
{% trans "Command storage" %}
</h3>
<table
class=
"table table-hover "
id=
"task-history-list-table"
>
<thead>
...
...
apps/common/urls/view_urls.py
View file @
e7a731fa
...
...
@@ -11,4 +11,5 @@ urlpatterns = [
url
(
r'^email/$'
,
views
.
EmailSettingView
.
as_view
(),
name
=
'email-setting'
),
url
(
r'^ldap/$'
,
views
.
LDAPSettingView
.
as_view
(),
name
=
'ldap-setting'
),
url
(
r'^terminal/$'
,
views
.
TerminalSettingView
.
as_view
(),
name
=
'terminal-setting'
),
url
(
r'^security/$'
,
views
.
SecuritySettingView
.
as_view
(),
name
=
'security-setting'
),
]
apps/common/views.py
View file @
e7a731fa
...
...
@@ -7,7 +7,7 @@ from django.utils.translation import ugettext as _
from
django.conf
import
settings
from
.forms
import
EmailSettingForm
,
LDAPSettingForm
,
BasicSettingForm
,
\
TerminalSettingForm
TerminalSettingForm
,
SecuritySettingForm
from
.mixins
import
AdminUserRequiredMixin
from
.signals
import
ldap_auth_enable
...
...
@@ -82,7 +82,7 @@ class LDAPSettingView(AdminUserRequiredMixin, TemplateView):
if
form
.
is_valid
():
form
.
save
()
if
"AUTH_LDAP"
in
form
.
cleaned_data
:
ldap_auth_enable
.
send
(
form
.
cleaned_data
[
"AUTH_LDAP"
])
ldap_auth_enable
.
send
(
sender
=
self
.
__class__
,
enabled
=
form
.
cleaned_data
[
"AUTH_LDAP"
])
msg
=
_
(
"Update setting successfully, please restart program"
)
messages
.
success
(
request
,
msg
)
return
redirect
(
'settings:ldap-setting'
)
...
...
@@ -122,3 +122,27 @@ class TerminalSettingView(AdminUserRequiredMixin, TemplateView):
return
render
(
request
,
self
.
template_name
,
context
)
class
SecuritySettingView
(
AdminUserRequiredMixin
,
TemplateView
):
form_class
=
SecuritySettingForm
template_name
=
"common/security_setting.html"
def
get_context_data
(
self
,
**
kwargs
):
context
=
{
'app'
:
_
(
'Settings'
),
'action'
:
_
(
'Security setting'
),
'form'
:
self
.
form_class
(),
}
kwargs
.
update
(
context
)
return
super
()
.
get_context_data
(
**
kwargs
)
def
post
(
self
,
request
):
form
=
self
.
form_class
(
request
.
POST
)
if
form
.
is_valid
():
form
.
save
()
msg
=
_
(
"Update setting successfully, please restart program"
)
messages
.
success
(
request
,
msg
)
return
redirect
(
'settings:security-setting'
)
else
:
context
=
self
.
get_context_data
()
context
.
update
({
"form"
:
form
})
return
render
(
request
,
self
.
template_name
,
context
)
apps/i18n/zh/LC_MESSAGES/django.mo
View file @
e7a731fa
No preview for this file type
apps/i18n/zh/LC_MESSAGES/django.po
View file @
e7a731fa
...
...
@@ -8,7 +8,7 @@ msgid ""
msgstr ""
"Project-Id-Version: Jumpserver 0.3.3\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2018-0
5-25 18:11
+0800\n"
"POT-Creation-Date: 2018-0
6-07 11:34
+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"
...
...
@@ -17,19 +17,19 @@ msgstr ""
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
#: assets/api/node.py:
106
#: assets/api/node.py:
99
msgid "New node {}"
msgstr "新节点 {}"
#: assets/api/node.py:2
42
#: assets/api/node.py:2
34
msgid "更新节点资产硬件信息: {}"
msgstr ""
#: assets/api/node.py:2
55
#: assets/api/node.py:2
47
msgid "测试节点下资产是否可连接: {}"
msgstr ""
#: assets/forms/asset.py:24 assets/models/asset.py:
66
assets/models/user.py:103
#: assets/forms/asset.py:24 assets/models/asset.py:
75
assets/models/user.py:103
#: assets/templates/assets/asset_detail.html:183
#: assets/templates/assets/asset_detail.html:191
#: assets/templates/assets/system_user_detail.html:175 perms/models.py:33
...
...
@@ -37,7 +37,7 @@ msgid "Nodes"
msgstr "节点管理"
#: assets/forms/asset.py:27 assets/forms/asset.py:66 assets/forms/asset.py:109
#: assets/forms/asset.py:113 assets/models/asset.py:
7
0
#: assets/forms/asset.py:113 assets/models/asset.py:
8
0
#: assets/models/cluster.py:19 assets/models/user.py:72
#: assets/templates/assets/asset_detail.html:73 templates/_nav.html:25
msgid "Admin user"
...
...
@@ -46,14 +46,14 @@ msgstr "管理用户"
#: assets/forms/asset.py:30 assets/forms/asset.py:69 assets/forms/asset.py:125
#: assets/templates/assets/asset_create.html:35
#: assets/templates/assets/asset_create.html:37
#: assets/templates/assets/asset_list.html:7
4
#: assets/templates/assets/asset_list.html:7
5
#: assets/templates/assets/asset_update.html:40
#: assets/templates/assets/asset_update.html:42
#: assets/templates/assets/user_asset_list.html:34
msgid "Label"
msgstr "标签"
#: assets/forms/asset.py:34 assets/forms/asset.py:73 assets/models/asset.py:
65
#: assets/forms/asset.py:34 assets/forms/asset.py:73 assets/models/asset.py:
71
#: assets/models/domain.py:46
msgid "Domain"
msgstr "网域"
...
...
@@ -90,7 +90,7 @@ msgstr "如果有多个的互相隔离的网络,设置资产属于的网域,
msgid "Select assets"
msgstr "选择资产"
#: assets/forms/asset.py:105 assets/models/asset.py:6
3
#: assets/forms/asset.py:105 assets/models/asset.py:6
7
#: assets/models/domain.py:44 assets/templates/assets/admin_user_assets.html:53
#: assets/templates/assets/asset_detail.html:69
#: assets/templates/assets/domain_gateway_list.html:58
...
...
@@ -99,7 +99,7 @@ msgid "Port"
msgstr "端口"
#: assets/forms/domain.py:14 assets/forms/label.py:13
#: assets/models/asset.py:
18
3 assets/templates/assets/admin_user_list.html:25
#: assets/models/asset.py:
22
3 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/label_list.html:16
...
...
@@ -129,15 +129,15 @@ msgstr "资产"
#: 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
#: common/templates/common/terminal_setting.html:
67
#: common/templates/common/terminal_setting.html:
85
ops/models/adhoc.py:36
#: common/templates/common/terminal_setting.html:
72
#: common/templates/common/terminal_setting.html:
90
ops/models/adhoc.py:36
#: ops/templates/ops/task_detail.html:59 ops/templates/ops/task_list.html:35
#: perms/models.py:29 perms/templates/perms/asset_permission_detail.html:62
#: perms/templates/perms/asset_permission_list.html:53
#: perms/templates/perms/asset_permission_user.html:54 terminal/models.py:16
#: terminal/models.py:154 terminal/templates/terminal/terminal_detail.html:43
#: terminal/templates/terminal/terminal_list.html:29 users/models/group.py:1
4
#: users/models/user.py:4
2
users/templates/users/_select_user_modal.html:13
#: terminal/templates/terminal/terminal_list.html:29 users/models/group.py:1
2
#: users/models/user.py:4
9
users/templates/users/_select_user_modal.html:13
#: users/templates/users/user_detail.html:63
#: users/templates/users/user_group_detail.html:55
#: users/templates/users/user_group_list.html:12
...
...
@@ -155,7 +155,7 @@ msgstr "名称"
#: assets/templates/assets/system_user_list.html:27
#: perms/templates/perms/asset_permission_user.html:55 users/forms.py:13
#: users/forms.py:21 users/forms.py:30 users/models/authentication.py:45
#: users/models/user.py:4
0
users/templates/users/_select_user_modal.html:14
#: users/models/user.py:4
7
users/templates/users/_select_user_modal.html:14
#: users/templates/users/login.html:56
#: users/templates/users/login_log_list.html:49
#: users/templates/users/user_detail.html:67
...
...
@@ -171,16 +171,16 @@ msgstr "密码或密钥密码"
#: assets/forms/user.py:25 assets/models/base.py:23 common/forms.py:113
#: users/forms.py:15 users/forms.py:23 users/forms.py:32 users/forms.py:44
#: users/templates/users/login.html:59
#: users/templates/users/reset_password.html:5
2
#: users/templates/users/reset_password.html:5
3
#: users/templates/users/user_create.html:10
#: users/templates/users/user_password_authentication.html:14
#: users/templates/users/user_password_update.html:4
0
#: users/templates/users/user_password_update.html:4
2
#: users/templates/users/user_profile_update.html:40
#: users/templates/users/user_pubkey_update.html:40
msgid "Password"
msgstr "密码"
#: assets/forms/user.py:28 users/models/user.py:
69
#: assets/forms/user.py:28 users/models/user.py:
76
msgid "Private key"
msgstr "ssh私钥"
...
...
@@ -202,11 +202,11 @@ msgid ""
"than 2 system user"
msgstr "高优先级的系统用户将会作为默认登录用户"
#: assets/models/asset.py:6
1
assets/models/domain.py:43
#: assets/models/asset.py:6
3
assets/models/domain.py:43
#: assets/templates/assets/_asset_list_modal.html:46
#: assets/templates/assets/admin_user_assets.html:52
#: assets/templates/assets/asset_detail.html:61
#: assets/templates/assets/asset_list.html:8
6
#: assets/templates/assets/asset_list.html:8
7
#: assets/templates/assets/domain_gateway_list.html:57
#: assets/templates/assets/system_user_asset.html:50
#: assets/templates/assets/user_asset_list.html:46 common/forms.py:144
...
...
@@ -217,10 +217,10 @@ msgstr "高优先级的系统用户将会作为默认登录用户"
msgid "IP"
msgstr "IP"
#: assets/models/asset.py:6
2
assets/templates/assets/_asset_list_modal.html:45
#: assets/models/asset.py:6
6
assets/templates/assets/_asset_list_modal.html:45
#: assets/templates/assets/admin_user_assets.html:51
#: assets/templates/assets/asset_detail.html:57
#: assets/templates/assets/asset_list.html:8
5
#: assets/templates/assets/asset_list.html:8
6
#: assets/templates/assets/system_user_asset.html:49
#: assets/templates/assets/user_asset_list.html:45 common/forms.py:143
#: perms/templates/perms/asset_permission_asset.html:54
...
...
@@ -229,82 +229,82 @@ msgstr "IP"
msgid "Hostname"
msgstr "主机名"
#: assets/models/asset.py:6
4
assets/templates/assets/asset_detail.html:97
#: assets/models/asset.py:6
9
assets/templates/assets/asset_detail.html:97
msgid "Platform"
msgstr "系统平台"
#: assets/models/asset.py:
67
assets/models/domain.py:48
#: assets/models/asset.py:
76
assets/models/domain.py:48
#: assets/models/label.py:20 assets/templates/assets/asset_detail.html:105
msgid "Is active"
msgstr "激活"
#: assets/models/asset.py:
73
assets/templates/assets/asset_detail.html:65
#: assets/models/asset.py:
85
assets/templates/assets/asset_detail.html:65
msgid "Public IP"
msgstr "公网IP"
#: assets/models/asset.py:
74
assets/templates/assets/asset_detail.html:113
#: assets/models/asset.py:
87
assets/templates/assets/asset_detail.html:113
msgid "Asset number"
msgstr "资产编号"
#: assets/models/asset.py:
77
assets/templates/assets/asset_detail.html:77
#: assets/models/asset.py:
91
assets/templates/assets/asset_detail.html:77
msgid "Vendor"
msgstr "制造商"
#: assets/models/asset.py:
78
assets/templates/assets/asset_detail.html:81
#: assets/models/asset.py:
93
assets/templates/assets/asset_detail.html:81
msgid "Model"
msgstr "型号"
#: assets/models/asset.py:
79
assets/templates/assets/asset_detail.html:109
#: assets/models/asset.py:
95
assets/templates/assets/asset_detail.html:109
msgid "Serial number"
msgstr "序列号"
#: assets/models/asset.py:
81
#: assets/models/asset.py:
98
msgid "CPU model"
msgstr "CPU型号"
#: assets/models/asset.py:
82
#: assets/models/asset.py:
99
msgid "CPU count"
msgstr "CPU数量"
#: assets/models/asset.py:
83
#: assets/models/asset.py:
100
msgid "CPU cores"
msgstr "CPU核数"
#: assets/models/asset.py:
84
assets/templates/assets/asset_detail.html:89
#: assets/models/asset.py:
102
assets/templates/assets/asset_detail.html:89
msgid "Memory"
msgstr "内存"
#: assets/models/asset.py:
85
#: assets/models/asset.py:
104
msgid "Disk total"
msgstr "硬盘大小"
#: assets/models/asset.py:
8
6
#: assets/models/asset.py:
10
6
msgid "Disk info"
msgstr "硬盘信息"
#: assets/models/asset.py:
88
assets/templates/assets/asset_detail.html:101
#: assets/models/asset.py:
109
assets/templates/assets/asset_detail.html:101
msgid "OS"
msgstr "操作系统"
#: assets/models/asset.py:
89
#: assets/models/asset.py:
111
msgid "OS version"
msgstr "系统版本"
#: assets/models/asset.py:
90
#: assets/models/asset.py:
113
msgid "OS arch"
msgstr "系统架构"
#: assets/models/asset.py:
91
#: assets/models/asset.py:
115
msgid "Hostname raw"
msgstr "主机名原始"
#: assets/models/asset.py:
93
assets/templates/assets/asset_create.html:33
#: assets/models/asset.py:
119
assets/templates/assets/asset_create.html:33
#: assets/templates/assets/asset_detail.html:220
#: assets/templates/assets/asset_update.html:38 templates/_nav.html:27
msgid "Labels"
msgstr "标签管理"
#: assets/models/asset.py:
94
assets/models/base.py:29
#: assets/models/asset.py:
121
assets/models/base.py:29
#: assets/models/cluster.py:28 assets/models/group.py:21
#: assets/templates/assets/admin_user_detail.html:68
#: assets/templates/assets/asset_detail.html:117
...
...
@@ -312,11 +312,11 @@ msgstr "标签管理"
#: assets/templates/assets/system_user_detail.html:96
#: ops/templates/ops/adhoc_detail.html:86 perms/models.py:38 perms/models.py:81
#: perms/templates/perms/asset_permission_detail.html:98
#: users/models/user.py:
83 users/templates/users/user_detail.html:107
#: users/models/user.py:
90 users/templates/users/user_detail.html:111
msgid "Created by"
msgstr "创建者"
#: assets/models/asset.py:
95
assets/models/cluster.py:26
#: assets/models/asset.py:
124
assets/models/cluster.py:26
#: assets/models/domain.py:20 assets/models/group.py:22
#: assets/models/label.py:23 assets/templates/assets/admin_user_detail.html:64
#: assets/templates/assets/domain_detail.html:68
...
...
@@ -324,12 +324,12 @@ msgstr "创建者"
#: ops/templates/ops/adhoc_detail.html:90 ops/templates/ops/task_detail.html:63
#: perms/models.py:39 perms/models.py:82
#: perms/templates/perms/asset_permission_detail.html:94
#: terminal/templates/terminal/terminal_detail.html:59 users/models/group.py:1
7
#: terminal/templates/terminal/terminal_detail.html:59 users/models/group.py:1
5
#: users/templates/users/user_group_detail.html:63
msgid "Date created"
msgstr "创建日期"
#: assets/models/asset.py:
9
6 assets/models/base.py:26
#: assets/models/asset.py:
12
6 assets/models/base.py:26
#: assets/models/cluster.py:29 assets/models/domain.py:18
#: assets/models/domain.py:47 assets/models/group.py:23
#: assets/models/label.py:21 assets/templates/assets/admin_user_detail.html:72
...
...
@@ -342,11 +342,11 @@ msgstr "创建日期"
#: assets/templates/assets/system_user_list.html:33 common/models.py:30
#: ops/models/adhoc.py:42 perms/models.py:40 perms/models.py:83
#: perms/templates/perms/asset_permission_detail.html:102 terminal/models.py:26
#: terminal/templates/terminal/terminal_detail.html:63 users/models/group.py:1
5
#: users/models/user.py:
75 users/templates/users/user_detail.html:119
#: terminal/templates/terminal/terminal_detail.html:63 users/models/group.py:1
3
#: users/models/user.py:
82 users/templates/users/user_detail.html:123
#: users/templates/users/user_group_detail.html:67
#: users/templates/users/user_group_list.html:14
#: users/templates/users/user_profile.html:1
23
#: users/templates/users/user_profile.html:1
30
msgid "Comment"
msgstr "备注"
...
...
@@ -366,7 +366,7 @@ msgstr "带宽"
msgid "Contact"
msgstr "联系人"
#: assets/models/cluster.py:22 users/models/user.py:6
1
#: assets/models/cluster.py:22 users/models/user.py:6
8
#: users/templates/users/user_detail.html:76
msgid "Phone"
msgstr "手机"
...
...
@@ -392,7 +392,7 @@ msgid "Default"
msgstr "默认"
#: assets/models/cluster.py:36 assets/models/label.py:13
#: users/models/user.py:3
30
#: users/models/user.py:3
43
msgid "System"
msgstr "系统"
...
...
@@ -432,13 +432,13 @@ 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:281
#: users/models/user.py:3
0 users/models/user.py:318
#: users/models/user.py:3
1 users/models/user.py:331
#: users/templates/users/user_group_detail.html:78
#: users/templates/users/user_group_list.html:13 users/views/user.py:3
39
#: users/templates/users/user_group_list.html:13 users/views/user.py:3
62
msgid "User"
msgstr "用户"
#: assets/models/label.py:18 assets/models/node.py:1
8
#: assets/models/label.py:18 assets/models/node.py:1
6
#: assets/templates/assets/label_list.html:15 common/models.py:27
msgid "Value"
msgstr "值"
...
...
@@ -447,7 +447,7 @@ msgstr "值"
msgid "Category"
msgstr "分类"
#: assets/models/node.py:1
4
#: assets/models/node.py:1
5
msgid "Key"
msgstr ""
...
...
@@ -630,16 +630,17 @@ msgstr "其它"
#: assets/templates/assets/domain_create_update.html:16
#: assets/templates/assets/gateway_create_update.html:58
#: assets/templates/assets/label_create_update.html:18
#: common/templates/common/basic_setting.html:58
#: common/templates/common/email_setting.html:59
#: common/templates/common/ldap_setting.html:59
#: common/templates/common/terminal_setting.html:101
#: common/templates/common/basic_setting.html:61
#: common/templates/common/email_setting.html:62
#: common/templates/common/ldap_setting.html:62
#: common/templates/common/security_setting.html:70
#: common/templates/common/terminal_setting.html:106
#: perms/templates/perms/asset_permission_create_update.html:69
#: terminal/templates/terminal/terminal_update.html:47
#: users/templates/users/_user.html:46
#: users/templates/users/user_bulk_update.html:23
#: users/templates/users/user_password_update.html:
58
#: users/templates/users/user_profile.html:18
1
#: users/templates/users/user_password_update.html:
70
#: users/templates/users/user_profile.html:18
8
#: users/templates/users/user_profile_update.html:63
#: users/templates/users/user_pubkey_update.html:70
#: users/templates/users/user_pubkey_update.html:76
...
...
@@ -650,23 +651,24 @@ msgstr "重置"
#: assets/templates/assets/admin_user_create_update.html:46
#: assets/templates/assets/asset_bulk_update.html:24
#: assets/templates/assets/asset_create.html:67
#: assets/templates/assets/asset_list.html:10
7
#: assets/templates/assets/asset_list.html:10
8
#: assets/templates/assets/asset_update.html:71
#: assets/templates/assets/domain_create_update.html:17
#: assets/templates/assets/gateway_create_update.html:59
#: assets/templates/assets/label_create_update.html:19
#: common/templates/common/basic_setting.html:59
#: common/templates/common/email_setting.html:60
#: common/templates/common/ldap_setting.html:60
#: common/templates/common/terminal_setting.html:103
#: common/templates/common/basic_setting.html:62
#: common/templates/common/email_setting.html:63
#: common/templates/common/ldap_setting.html:63
#: 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/session_list.html:124
#: terminal/templates/terminal/terminal_update.html:48
#: users/templates/users/_user.html:47
#: users/templates/users/forgot_password.html:44
#: users/templates/users/user_bulk_update.html:24
#: users/templates/users/user_list.html:4
4
#: users/templates/users/user_password_update.html:
59
#: users/templates/users/user_list.html:4
5
#: users/templates/users/user_password_update.html:
71
#: users/templates/users/user_profile_update.html:64
#: users/templates/users/user_pubkey_update.html:77
msgid "Submit"
...
...
@@ -727,7 +729,7 @@ msgstr "测试"
#: assets/templates/assets/admin_user_detail.html:24
#: assets/templates/assets/admin_user_list.html:85
#: assets/templates/assets/asset_detail.html:24
#: assets/templates/assets/asset_list.html:17
4
#: assets/templates/assets/asset_list.html:17
5
#: assets/templates/assets/domain_detail.html:24
#: assets/templates/assets/domain_detail.html:103
#: assets/templates/assets/domain_gateway_list.html:85
...
...
@@ -742,16 +744,16 @@ msgstr "测试"
#: users/templates/users/user_detail.html:25
#: users/templates/users/user_group_detail.html:28
#: users/templates/users/user_group_list.html:43
#: users/templates/users/user_list.html:7
6
#: users/templates/users/user_profile.html:1
44
#: users/templates/users/user_profile.html:1
73
#: users/templates/users/user_list.html:7
7
#: users/templates/users/user_profile.html:1
51
#: users/templates/users/user_profile.html:1
80
msgid "Update"
msgstr "更新"
#: assets/templates/assets/admin_user_detail.html:28
#: assets/templates/assets/admin_user_list.html:86
#: assets/templates/assets/asset_detail.html:28
#: assets/templates/assets/asset_list.html:17
5
#: assets/templates/assets/asset_list.html:17
6
#: assets/templates/assets/domain_detail.html:28
#: assets/templates/assets/domain_detail.html:104
#: assets/templates/assets/domain_gateway_list.html:86
...
...
@@ -766,8 +768,8 @@ msgstr "更新"
#: users/templates/users/user_detail.html:30
#: users/templates/users/user_group_detail.html:32
#: users/templates/users/user_group_list.html:45
#: users/templates/users/user_list.html:8
0
#: users/templates/users/user_list.html:8
4
#: users/templates/users/user_list.html:8
1
#: users/templates/users/user_list.html:8
5
msgid "Delete"
msgstr "删除"
...
...
@@ -782,17 +784,17 @@ msgstr "选择节点"
#: assets/templates/assets/admin_user_detail.html:100
#: assets/templates/assets/asset_detail.html:200
#: assets/templates/assets/asset_list.html:63
6
#: assets/templates/assets/asset_list.html:63
8
#: assets/templates/assets/system_user_detail.html:192
#: assets/templates/assets/system_user_list.html:138 templates/_modal.html:22
#: terminal/templates/terminal/session_detail.html:108
#: users/templates/users/user_detail.html:36
2
#: users/templates/users/user_detail.html:3
87
#: users/templates/users/user_detail.html:41
0
#: users/templates/users/user_detail.html:36
6
#: users/templates/users/user_detail.html:3
91
#: users/templates/users/user_detail.html:41
4
#: users/templates/users/user_group_create_update.html:32
#: users/templates/users/user_group_list.html:86
#: users/templates/users/user_list.html:
199
#: users/templates/users/user_profile.html:2
15
#: users/templates/users/user_list.html:
200
#: users/templates/users/user_profile.html:2
22
msgid "Confirm"
msgstr "确认"
...
...
@@ -814,7 +816,7 @@ msgid "Ratio"
msgstr "比例"
#: assets/templates/assets/admin_user_list.html:30
#: assets/templates/assets/asset_list.html:9
0
#: assets/templates/assets/asset_list.html:9
1
#: assets/templates/assets/domain_gateway_list.html:62
#: assets/templates/assets/domain_list.html:18
#: assets/templates/assets/label_list.html:17
...
...
@@ -825,7 +827,7 @@ msgstr "比例"
#: terminal/templates/terminal/session_list.html:80
#: terminal/templates/terminal/terminal_list.html:36
#: users/templates/users/user_group_list.html:15
#: users/templates/users/user_list.html:2
8
#: users/templates/users/user_list.html:2
9
msgid "Action"
msgstr "动作"
...
...
@@ -842,20 +844,20 @@ msgid "Disk"
msgstr "硬盘"
#: assets/templates/assets/asset_detail.html:121
#: users/templates/users/user_detail.html:11
1
#: users/templates/users/user_profile.html:
97
#: users/templates/users/user_detail.html:11
5
#: users/templates/users/user_profile.html:
104
msgid "Date joined"
msgstr "创建日期"
#: assets/templates/assets/asset_detail.html:137
#: terminal/templates/terminal/session_detail.html:81
#: users/templates/users/user_detail.html:13
0
#: users/templates/users/user_profile.html:1
35
#: users/templates/users/user_detail.html:13
4
#: users/templates/users/user_profile.html:1
42
msgid "Quick modify"
msgstr "快速修改"
#: assets/templates/assets/asset_detail.html:143
#: assets/templates/assets/asset_list.html:8
8
#: assets/templates/assets/asset_list.html:8
9
#: assets/templates/assets/user_asset_list.html:47 perms/models.py:35
#: perms/models.py:79
#: perms/templates/perms/asset_permission_create_update.html:47
...
...
@@ -863,10 +865,10 @@ msgstr "快速修改"
#: perms/templates/perms/asset_permission_list.html:59
#: terminal/templates/terminal/terminal_list.html:34
#: users/templates/users/_select_user_modal.html:18
#: users/templates/users/user_detail.html:1
36
#: users/templates/users/user_detail.html:1
40
#: users/templates/users/user_granted_asset.html:46
#: users/templates/users/user_group_granted_asset.html:46
#: users/templates/users/user_list.html:2
7
#: users/templates/users/user_list.html:2
8
#: users/templates/users/user_profile.html:63
msgid "Active"
msgstr "激活中"
...
...
@@ -880,124 +882,124 @@ msgid "Refresh"
msgstr "刷新"
#: assets/templates/assets/asset_detail.html:300
#: users/templates/users/user_detail.html:28
2
#: users/templates/users/user_detail.html:3
09
#: users/templates/users/user_detail.html:28
6
#: users/templates/users/user_detail.html:3
13
msgid "Update successfully!"
msgstr "更新成功"
#: assets/templates/assets/asset_list.html:6
2
assets/views/asset.py:97
#: assets/templates/assets/asset_list.html:6
3
assets/views/asset.py:97
msgid "Create asset"
msgstr "创建资产"
#: assets/templates/assets/asset_list.html:6
6
#: assets/templates/assets/asset_list.html:6
7
#: users/templates/users/user_list.html:7
msgid "Import"
msgstr "导入"
#: assets/templates/assets/asset_list.html:
69
#: assets/templates/assets/asset_list.html:
70
#: users/templates/users/user_list.html:10
msgid "Export"
msgstr "导出"
#: assets/templates/assets/asset_list.html:8
7
#: assets/templates/assets/asset_list.html:8
8
msgid "Hardware"
msgstr "硬件"
#: assets/templates/assets/asset_list.html:
99
#: users/templates/users/user_list.html:3
7
#: assets/templates/assets/asset_list.html:
100
#: users/templates/users/user_list.html:3
8
msgid "Delete selected"
msgstr "批量删除"
#: assets/templates/assets/asset_list.html:10
0
#: users/templates/users/user_list.html:3
8
#: assets/templates/assets/asset_list.html:10
1
#: users/templates/users/user_list.html:3
9
msgid "Update selected"
msgstr "批量更新"
#: assets/templates/assets/asset_list.html:10
1
#: assets/templates/assets/asset_list.html:10
2
msgid "Remove from this node"
msgstr "从节点移除"
#: assets/templates/assets/asset_list.html:10
2
#: users/templates/users/user_list.html:
39
#: assets/templates/assets/asset_list.html:10
3
#: users/templates/users/user_list.html:
40
msgid "Deactive selected"
msgstr "禁用所选"
#: assets/templates/assets/asset_list.html:10
3
#: users/templates/users/user_list.html:4
0
#: assets/templates/assets/asset_list.html:10
4
#: users/templates/users/user_list.html:4
1
msgid "Active selected"
msgstr "激活所选"
#: assets/templates/assets/asset_list.html:12
0
#: assets/templates/assets/asset_list.html:12
1
msgid "Add node"
msgstr "新建节点"
#: assets/templates/assets/asset_list.html:12
1
#: assets/templates/assets/asset_list.html:12
2
msgid "Rename node"
msgstr "重命名节点"
#: assets/templates/assets/asset_list.html:12
2
#: assets/templates/assets/asset_list.html:12
3
msgid "Delete node"
msgstr "删除节点"
#: assets/templates/assets/asset_list.html:12
4
#: assets/templates/assets/asset_list.html:12
5
msgid "Add assets to node"
msgstr "添加资产到节点"
#: assets/templates/assets/asset_list.html:12
5
#: assets/templates/assets/asset_list.html:12
6
msgid "Move assets to node"
msgstr "移动资产到节点"
#: assets/templates/assets/asset_list.html:12
7
#: assets/templates/assets/asset_list.html:12
8
msgid "Refresh node hardware info"
msgstr "更新节点资产硬件信息"
#: assets/templates/assets/asset_list.html:12
8
#: assets/templates/assets/asset_list.html:12
9
msgid "Test node connective"
msgstr "测试节点资产可连接性"
#: assets/templates/assets/asset_list.html:13
0
#: assets/templates/assets/asset_list.html:13
1
msgid "Display only current node assets"
msgstr "仅显示当前节点资产"
#: assets/templates/assets/asset_list.html:13
1
#: assets/templates/assets/asset_list.html:13
2
msgid "Displays all child node assets"
msgstr "显示所有子节点资产"
#: assets/templates/assets/asset_list.html:21
7
#: assets/templates/assets/asset_list.html:21
8
msgid "Create node failed"
msgstr "创建节点失败"
#: assets/templates/assets/asset_list.html:2
29
#: assets/templates/assets/asset_list.html:2
30
msgid "Have child node, cancel"
msgstr "存在子节点,不能删除"
#: assets/templates/assets/asset_list.html:23
1
#: assets/templates/assets/asset_list.html:23
2
msgid "Have assets, cancel"
msgstr "存在资产,不能删除"
#: assets/templates/assets/asset_list.html:63
1
#: assets/templates/assets/asset_list.html:63
3
#: assets/templates/assets/system_user_list.html:133
#: users/templates/users/user_detail.html:3
57
#: users/templates/users/user_detail.html:38
2
#: users/templates/users/user_detail.html:3
61
#: users/templates/users/user_detail.html:38
6
#: users/templates/users/user_group_list.html:81
#: users/templates/users/user_list.html:19
4
#: users/templates/users/user_list.html:19
5
msgid "Are you sure?"
msgstr "你确认吗?"
#: assets/templates/assets/asset_list.html:63
2
#: assets/templates/assets/asset_list.html:63
4
msgid "This will delete the selected assets !!!"
msgstr "删除选择资产"
#: assets/templates/assets/asset_list.html:64
0
#: assets/templates/assets/asset_list.html:64
2
msgid "Asset Deleted."
msgstr "已被删除"
#: assets/templates/assets/asset_list.html:64
1
#: assets/templates/assets/asset_list.html:64
6
#: assets/templates/assets/asset_list.html:64
3
#: assets/templates/assets/asset_list.html:64
8
msgid "Asset Delete"
msgstr "删除"
#: assets/templates/assets/asset_list.html:64
5
#: assets/templates/assets/asset_list.html:64
7
msgid "Asset Deleting failed."
msgstr "删除失败"
...
...
@@ -1033,8 +1035,8 @@ msgstr "创建网关"
#: assets/templates/assets/domain_gateway_list.html:87
#: assets/templates/assets/domain_gateway_list.html:89
#: common/templates/common/email_setting.html:
58
#: common/templates/common/ldap_setting.html:
58
#: common/templates/common/email_setting.html:
61
#: common/templates/common/ldap_setting.html:
61
msgid "Test connection"
msgstr "测试连接"
...
...
@@ -1237,11 +1239,11 @@ msgstr "FTP日志"
msgid "Test mail sent to {}, please check"
msgstr "邮件已经发送{}, 请检查"
#: common/api.py:
5
2
#: common/api.py:
4
2
msgid "Test ldap success"
msgstr "连接LDAP成功"
#: common/api.py:
9
0
#: common/api.py:
8
0
msgid "Match {} s users"
msgstr "匹配 {} 个用户"
...
...
@@ -1376,7 +1378,7 @@ msgstr "密码认证"
msgid "Public key auth"
msgstr "密钥认证"
#: common/forms.py:159 common/templates/common/terminal_setting.html:6
3
#: common/forms.py:159 common/templates/common/terminal_setting.html:6
8
#: terminal/forms.py:30 terminal/models.py:20
msgid "Command storage"
msgstr "命令存储"
...
...
@@ -1387,7 +1389,7 @@ msgid ""
"other storage and some terminal using"
msgstr "设置终端命令存储,default是默认用的存储方式"
#: common/forms.py:165 common/templates/common/terminal_setting.html:8
1
#: common/forms.py:165 common/templates/common/terminal_setting.html:8
6
#: terminal/forms.py:35 terminal/models.py:21
msgid "Replay storage"
msgstr "录像存储"
...
...
@@ -1398,6 +1400,60 @@ msgid ""
"other storage and some terminal using"
msgstr "设置终端录像存储,default是默认用的存储方式"
#: common/forms.py:176
msgid "MFA Secondary certification"
msgstr "MFA 二次认证"
#: common/forms.py:178
msgid ""
"After opening, the user login must use MFA secondary authentication (valid "
"for all users, including administrators)"
msgstr "开启后,用户登录必须使用MFA二次认证(对所有用户有效,包括管理员)"
#: common/forms.py:184
msgid "Password minimum length"
msgstr "密码最小长度 "
#: common/forms.py:191
msgid "Must contain capital letters"
msgstr "必须包含大写字母"
#: common/forms.py:193
msgid ""
"After opening, the user password changes and resets must contain uppercase "
"letters"
msgstr "开启后,用户密码修改、重置必须包含大写字母"
#: common/forms.py:199
msgid "Must contain lowercase letters"
msgstr "必须包含小写字母"
#: common/forms.py:200
msgid ""
"After opening, the user password changes and resets must contain lowercase "
"letters"
msgstr "开启后,用户密码修改、重置必须包含小写字母"
#: common/forms.py:206
msgid "Must contain numeric characters"
msgstr "必须包含数字字符"
#: common/forms.py:207
msgid ""
"After opening, the user password changes and resets must contain numeric "
"characters"
msgstr "开启后,用户密码修改、重置必须包含数字字符"
#: common/forms.py:213
msgid "Must contain special characters"
msgstr "必须包含特殊字符"
#: common/forms.py:214
msgid ""
"After opening, the user password changes and resets must contain special "
"characters"
msgstr "开启后,用户密码修改、重置必须包含特殊字符"
#: common/mixins.py:29
msgid "is discard"
msgstr ""
...
...
@@ -1413,14 +1469,16 @@ msgstr "启用"
#: common/templates/common/basic_setting.html:15
#: common/templates/common/email_setting.html:15
#: common/templates/common/ldap_setting.html:15
#: common/templates/common/security_setting.html:15
#: common/templates/common/terminal_setting.html:16
#: common/templates/common/terminal_setting.html:4
2
common/views.py:22
#: common/templates/common/terminal_setting.html:4
6
common/views.py:22
msgid "Basic setting"
msgstr "基本设置"
#: common/templates/common/basic_setting.html:18
#: common/templates/common/email_setting.html:18
#: common/templates/common/ldap_setting.html:18
#: common/templates/common/security_setting.html:18
#: common/templates/common/terminal_setting.html:20 common/views.py:48
msgid "Email setting"
msgstr "邮件设置"
...
...
@@ -1428,6 +1486,7 @@ msgstr "邮件设置"
#: common/templates/common/basic_setting.html:21
#: common/templates/common/email_setting.html:21
#: common/templates/common/ldap_setting.html:21
#: common/templates/common/security_setting.html:21
#: common/templates/common/terminal_setting.html:24 common/views.py:74
msgid "LDAP setting"
msgstr "LDAP设置"
...
...
@@ -1435,12 +1494,29 @@ msgstr "LDAP设置"
#: common/templates/common/basic_setting.html:24
#: common/templates/common/email_setting.html:24
#: common/templates/common/ldap_setting.html:24
#: common/templates/common/security_setting.html:24
#: common/templates/common/terminal_setting.html:28 common/views.py:104
msgid "Terminal setting"
msgstr "终端设置"
#: common/templates/common/terminal_setting.html:68
#: common/templates/common/terminal_setting.html:86
#: common/templates/common/basic_setting.html:27
#: common/templates/common/email_setting.html:27
#: common/templates/common/ldap_setting.html:27
#: common/templates/common/security_setting.html:27
#: common/templates/common/terminal_setting.html:31 common/views.py:132
msgid "Security setting"
msgstr "安全设置"
#: common/templates/common/security_setting.html:42
msgid "MFA setting"
msgstr "MFA 设置"
#: common/templates/common/security_setting.html:46
msgid "Password check rule"
msgstr "密码校验规则"
#: common/templates/common/terminal_setting.html:73
#: common/templates/common/terminal_setting.html:91
#: users/templates/users/login_log_list.html:50
msgid "Type"
msgstr "类型"
...
...
@@ -1450,11 +1526,12 @@ msgid "Special char not allowed"
msgstr "不能包含特殊字符"
#: common/views.py:21 common/views.py:47 common/views.py:73 common/views.py:103
#: templates/_nav.html:81
#:
common/views.py:131
templates/_nav.html:81
msgid "Settings"
msgstr "系统设置"
#: common/views.py:32 common/views.py:58 common/views.py:86 common/views.py:116
#: common/views.py:142
msgid "Update setting successfully, please restart program"
msgstr "更新设置成功, 请手动重启程序"
...
...
@@ -1736,9 +1813,9 @@ msgstr "选择用户"
#: perms/forms.py:34 perms/models.py:31 perms/models.py:77
#: perms/templates/perms/asset_permission_list.html:55
#: perms/templates/perms/asset_permission_list.html:136 templates/_nav.html:14
#: users/models/group.py:2
5 users/models/user.py:48
#: users/models/group.py:2
3 users/models/user.py:55
#: users/templates/users/_select_user_modal.html:16
#: users/templates/users/user_detail.html:1
88
#: users/templates/users/user_detail.html:1
92
#: users/templates/users/user_list.html:26
msgid "User group"
msgstr "用户组"
...
...
@@ -1753,8 +1830,8 @@ msgstr ""
#: perms/models.py:37 perms/models.py:80
#: perms/templates/perms/asset_permission_detail.html:90
#: users/models/user.py:8
0 users/templates/users/user_detail.html:103
#: users/templates/users/user_profile.html:1
05
#: users/models/user.py:8
7 users/templates/users/user_detail.html:107
#: users/templates/users/user_profile.html:1
12
msgid "Date expired"
msgstr "失效日期"
...
...
@@ -1791,7 +1868,7 @@ msgid "Add node to this permission"
msgstr "添加节点"
#: perms/templates/perms/asset_permission_asset.html:125
#: users/templates/users/user_detail.html:20
5
#: users/templates/users/user_detail.html:20
9
msgid "Join"
msgstr "加入"
...
...
@@ -1884,11 +1961,11 @@ msgstr "文档"
#: templates/_header_bar.html:37 templates/_nav_user.html:9 users/forms.py:121
#: users/templates/users/_user.html:39
#: users/templates/users/first_login.html:39
#: users/templates/users/user_password_update.html:3
7
#: users/templates/users/user_password_update.html:3
9
#: 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:3
22
#: users/templates/users/user_pubkey_update.html:37 users/views/user.py:3
44
msgid "Profile"
msgstr "个人信息"
...
...
@@ -1945,13 +2022,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:2
63 users/views/login.py:321 users/views/user.py:64
#: users/views/user.py:
79 users/views/user.py:99 users/views/user.py:155
#: users/views/user.py:3
10 users/views/user.py:357 users/views/user.py:379
#: users/views/login.py:2
77 users/views/login.py:335 users/views/user.py:66
#: users/views/user.py:
81 users/views/user.py:103 users/views/user.py:174
#: users/views/user.py:3
29 users/views/user.py:381 users/views/user.py:416
msgid "Users"
msgstr "用户管理"
#: templates/_nav.html:13 users/views/user.py:6
5
#: templates/_nav.html:13 users/views/user.py:6
7
msgid "User list"
msgstr "用户列表"
...
...
@@ -2261,7 +2338,7 @@ msgstr ""
msgid "MFA code"
msgstr "MFA 验证码"
#: users/forms.py:49 users/models/user.py:5
2
#: users/forms.py:49 users/models/user.py:5
9
#: users/templates/users/_select_user_modal.html:15
#: users/templates/users/user_detail.html:87
#: users/templates/users/user_list.html:25
...
...
@@ -2281,7 +2358,7 @@ msgstr ""
msgid "Paste user id_rsa.pub here."
msgstr "复制用户公钥到这里"
#: users/forms.py:72 users/templates/users/user_detail.html:
196
#: users/forms.py:72 users/templates/users/user_detail.html:
200
msgid "Join user groups"
msgstr "添加到用户组"
...
...
@@ -2289,7 +2366,7 @@ msgstr "添加到用户组"
msgid "Public key should not be the same as your old one."
msgstr "不能和原来的密钥相同"
#: users/forms.py:87 users/forms.py:220 users/serializers.py:4
5
#: users/forms.py:87 users/forms.py:220 users/serializers.py:4
8
msgid "Not a valid ssh public key"
msgstr "ssh密钥不合法"
...
...
@@ -2306,7 +2383,7 @@ msgstr ""
msgid "* Enable MFA authentication to make the account more secure."
msgstr "* 启用MFA认证,使账号更加安全."
#: users/forms.py:142 users/models/user.py:
64
#: users/forms.py:142 users/models/user.py:
71
#: users/templates/users/first_login.html:45
msgid "MFA"
msgstr "MFA"
...
...
@@ -2354,9 +2431,9 @@ msgstr "自动配置并下载SSH密钥"
msgid "Paste your id_rsa.pub here."
msgstr "复制你的公钥到这里"
#: users/forms.py:231 users/models/user.py:7
2
#: users/forms.py:231 users/models/user.py:7
9
#: users/templates/users/first_login.html:42
#: users/templates/users/user_password_update.html:4
3
#: users/templates/users/user_password_update.html:4
5
#: users/templates/users/user_profile.html:68
#: users/templates/users/user_profile_update.html:43
#: users/templates/users/user_pubkey_update.html:43
...
...
@@ -2387,43 +2464,49 @@ msgstr "Agent"
msgid "Date login"
msgstr "登录日期"
#: users/models/user.py:
29 users/models/user.py:326
#: users/models/user.py:
30 users/models/user.py:339
msgid "Administrator"
msgstr "管理员"
#: users/models/user.py:3
1
#: users/models/user.py:3
2
msgid "Application"
msgstr "应用程序"
#: users/models/user.py:3
4
users/templates/users/user_profile.html:92
#: users/templates/users/user_profile.html:1
56
#: users/templates/users/user_profile.html:1
59
#: users/models/user.py:3
5
users/templates/users/user_profile.html:92
#: users/templates/users/user_profile.html:1
63
#: users/templates/users/user_profile.html:1
66
msgid "Disable"
msgstr "禁用"
#: users/models/user.py:3
5
users/templates/users/user_profile.html:90
#: users/templates/users/user_profile.html:1
63
#: users/models/user.py:3
6
users/templates/users/user_profile.html:90
#: users/templates/users/user_profile.html:1
70
msgid "Enable"
msgstr "启用"
#: users/models/user.py:3
6
users/templates/users/user_profile.html:88
#: users/models/user.py:3
7
users/templates/users/user_profile.html:88
msgid "Force enable"
msgstr "强制启用"
#: users/models/user.py:
44
users/templates/users/user_detail.html:71
#: users/models/user.py:
51
users/templates/users/user_detail.html:71
#: users/templates/users/user_profile.html:59
msgid "Email"
msgstr "邮件"
#: users/models/user.py:
55
#: users/models/user.py:
62
msgid "Avatar"
msgstr "头像"
#: users/models/user.py:
58
users/templates/users/user_detail.html:82
#: users/models/user.py:
65
users/templates/users/user_detail.html:82
msgid "Wechat"
msgstr "微信"
#: users/models/user.py:329
#: users/models/user.py:94 users/templates/users/user_detail.html:103
#: users/templates/users/user_list.html:27
#: users/templates/users/user_profile.html:100
msgid "Source"
msgstr "用户来源"
#: users/models/user.py:342
msgid "Administrator is the super user of system"
msgstr "Administrator是初始的超级管理员"
...
...
@@ -2539,22 +2622,34 @@ msgstr "6位数字"
msgid "Can't provide security? Please contact the administrator!"
msgstr "如果不能提供MFA验证码,请联系管理员!"
#: users/templates/users/reset_password.html:4
5
#: users/templates/users/user_detail.html:3
48 users/utils.py:76
#: users/templates/users/reset_password.html:4
6
#: users/templates/users/user_detail.html:3
52 users/utils.py:80
msgid "Reset password"
msgstr "重置密码"
#: users/templates/users/reset_password.html:55
#: users/templates/users/reset_password.html:59
#: users/templates/users/user_password_update.html:60
#: users/templates/users/user_update.html:12
msgid "Your password must satisfy"
msgstr "您的密码必须满足:"
#: users/templates/users/reset_password.html:60
#: users/templates/users/user_password_update.html:61
#: users/templates/users/user_update.html:13
msgid "Password strength"
msgstr "密码强度:"
#: users/templates/users/reset_password.html:66
msgid "Password again"
msgstr "再次输入密码"
#: users/templates/users/reset_password.html:
57
#: users/templates/users/reset_password.html:
68
#: users/templates/users/user_profile.html:20
msgid "Setting"
msgstr "设置"
#: users/templates/users/user_create.html:4
#: users/templates/users/user_list.html:16 users/views/user.py:
79
#: users/templates/users/user_list.html:16 users/views/user.py:
81
msgid "Create user"
msgstr "创建用户"
...
...
@@ -2563,7 +2658,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:1
56
#: users/templates/users/user_granted_asset.html:18 users/views/user.py:1
75
msgid "User detail"
msgstr "用户详情"
...
...
@@ -2582,63 +2677,63 @@ msgstr "强制启用"
msgid "Disabled"
msgstr "禁用"
#: users/templates/users/user_detail.html:11
5
#: users/templates/users/user_profile.html:10
1
#: users/templates/users/user_detail.html:11
9
#: users/templates/users/user_profile.html:10
8
msgid "Last login"
msgstr "最后登录"
#: users/templates/users/user_detail.html:15
1
#: users/templates/users/user_detail.html:15
5
msgid "Force enabled MFA"
msgstr "强制启用MFA"
#: users/templates/users/user_detail.html:1
66
#: users/templates/users/user_detail.html:1
70
msgid "Send reset password mail"
msgstr "发送重置密码邮件"
#: users/templates/users/user_detail.html:1
69
#: users/templates/users/user_detail.html:1
77
#: users/templates/users/user_detail.html:1
73
#: users/templates/users/user_detail.html:1
81
msgid "Send"
msgstr "发送"
#: users/templates/users/user_detail.html:17
4
#: users/templates/users/user_detail.html:17
8
msgid "Send reset ssh key mail"
msgstr "发送重置密钥邮件"
#: users/templates/users/user_detail.html:29
1
#: users/templates/users/user_detail.html:29
5
msgid "Goto profile page enable MFA"
msgstr "请去个人信息页面启用自己的MFA"
#: users/templates/users/user_detail.html:3
47
#: users/templates/users/user_detail.html:3
51
msgid "An e-mail has been sent to the user`s mailbox."
msgstr "已发送邮件到用户邮箱"
#: users/templates/users/user_detail.html:3
58
#: users/templates/users/user_detail.html:3
62
msgid "This will reset the user password and send a reset mail"
msgstr "将失效用户当前密码,并发送重设密码邮件到用户邮箱"
#: users/templates/users/user_detail.html:37
2
#: users/templates/users/user_detail.html:37
6
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:37
3
#: users/templates/users/user_detail.html:37
7
msgid "Reset SSH public key"
msgstr "重置SSH密钥"
#: users/templates/users/user_detail.html:38
3
#: users/templates/users/user_detail.html:38
7
msgid "This will reset the user public key and send a reset mail"
msgstr "将会失效用户当前密钥,并发送重置邮件到用户邮箱"
#: users/templates/users/user_detail.html:40
0
#: users/templates/users/user_profile.html:2
04
#: users/templates/users/user_detail.html:40
4
#: users/templates/users/user_profile.html:2
11
msgid "Successfully updated the SSH public key."
msgstr "更新ssh密钥成功"
#: users/templates/users/user_detail.html:401
#: users/templates/users/user_detail.html:405
#: users/templates/users/user_profile.html:205
#: users/templates/users/user_profile.html:210
#: users/templates/users/user_detail.html:409
#: users/templates/users/user_profile.html:212
#: users/templates/users/user_profile.html:217
msgid "User SSH public key update"
msgstr "ssh密钥"
...
...
@@ -2677,45 +2772,49 @@ msgstr "用户组删除"
msgid "UserGroup Deleting failed."
msgstr "用户组删除失败"
#: users/templates/users/user_list.html:19
5
#: users/templates/users/user_list.html:19
6
msgid "This will delete the selected users !!!"
msgstr "删除选中用户 !!!"
#: users/templates/users/user_list.html:20
3
#: users/templates/users/user_list.html:20
4
msgid "User Deleted."
msgstr "已被删除"
#: users/templates/users/user_list.html:20
4
#: users/templates/users/user_list.html:2
09
#: users/templates/users/user_list.html:20
5
#: users/templates/users/user_list.html:2
10
msgid "User Delete"
msgstr "删除"
#: users/templates/users/user_list.html:20
8
#: users/templates/users/user_list.html:20
9
msgid "User Deleting failed."
msgstr "用户删除失败"
#: users/templates/users/user_profile.html:109 users/views/user.py:185
#: users/views/user.py:239
#: users/templates/users/user_profile.html:95
msgid "Administrator Settings force MFA login"
msgstr "管理员设置强制使用MFA登录"
#: users/templates/users/user_profile.html:116 users/views/user.py:204
#: users/views/user.py:258
msgid "User groups"
msgstr "用户组"
#: users/templates/users/user_profile.html:14
1
#: users/templates/users/user_profile.html:14
8
msgid "Update password"
msgstr "更改密码"
#: users/templates/users/user_profile.html:1
49
#: users/templates/users/user_profile.html:1
56
msgid "Update MFA settings"
msgstr "更改MFA设置"
#: users/templates/users/user_profile.html:17
0
#: users/templates/users/user_profile.html:17
7
msgid "Update SSH public key"
msgstr "更改SSH密钥"
#: users/templates/users/user_profile.html:1
78
#: users/templates/users/user_profile.html:1
85
msgid "Reset public key and download"
msgstr "重置并下载SSH密钥"
#: users/templates/users/user_profile.html:2
08
#: users/templates/users/user_profile.html:2
15
msgid "Failed to update SSH public key."
msgstr "更新密钥失败"
...
...
@@ -2735,15 +2834,21 @@ msgstr "更新密钥"
msgid "Or reset by server"
msgstr "或者重置并下载密钥"
#: users/templates/users/user_update.html:4 users/views/user.py:99
#: users/templates/users/user_pubkey_update.html:94
msgid ""
"The new public key has been set successfully, Please download the "
"corresponding private key."
msgstr "新的公钥已设置成功,请下载对应的私钥"
#: users/templates/users/user_update.html:4 users/views/user.py:104
msgid "Update user"
msgstr "更新用户"
#: users/utils.py:
37
#: users/utils.py:
41
msgid "Create account successfully"
msgstr "创建账户成功"
#: users/utils.py:
39
#: users/utils.py:
43
#, fuzzy, python-format
msgid ""
"\n"
...
...
@@ -2788,7 +2893,7 @@ msgstr ""
" </br>\n"
" "
#: users/utils.py:
78
#: users/utils.py:
82
#, python-format
msgid ""
"\n"
...
...
@@ -2832,11 +2937,11 @@ msgstr ""
" </br>\n"
" "
#: users/utils.py:1
09
#: users/utils.py:1
13
msgid "SSH Key Reset"
msgstr "重置ssh密钥"
#: users/utils.py:11
1
#: users/utils.py:11
5
#, python-format
msgid ""
"\n"
...
...
@@ -2861,18 +2966,22 @@ msgstr ""
" </br>\n"
" "
#: users/utils.py:14
4
#: users/utils.py:14
8
msgid "User not exist"
msgstr "用户不存在"
#: users/utils.py:1
46
#: users/utils.py:1
50
msgid "Disabled or expired"
msgstr "禁用或失效"
#: users/utils.py:1
59
#: users/utils.py:1
63
msgid "Password or SSH public key invalid"
msgstr "密码或密钥不合法"
#: users/utils.py:290 users/utils.py:300
msgid "Bit"
msgstr " 位"
#: users/views/group.py:29
msgid "User group list"
msgstr "用户组列表"
...
...
@@ -2885,99 +2994,103 @@ msgstr "更新用户组"
msgid "User group granted asset"
msgstr "用户组授权资产"
#: users/views/login.py:
59
#: users/views/login.py:
62
msgid "Please enable cookies and try again."
msgstr "设置你的浏览器支持cookie"
#: users/views/login.py:12
5 users/views/user.py:464 users/views/user.py:489
#: users/views/login.py:12
8 users/views/user.py:501 users/views/user.py:526
msgid "MFA code invalid"
msgstr "MFA码认证失败"
#: users/views/login.py:15
1
#: users/views/login.py:15
4
msgid "Logout success"
msgstr "退出登录成功"
#: users/views/login.py:15
2
#: users/views/login.py:15
5
msgid "Logout success, return login page"
msgstr "退出登录成功,返回到登录页面"
#: users/views/login.py:1
68
#: users/views/login.py:1
71
msgid "Email address invalid, please input again"
msgstr "邮箱地址错误,重新输入"
#: users/views/login.py:18
1
#: users/views/login.py:18
4
msgid "Send reset password message"
msgstr "发送重置密码邮件"
#: users/views/login.py:18
2
#: users/views/login.py:18
5
msgid "Send reset password mail success, login your mail box and follow it "
msgstr ""
"发送重置邮件成功, 请登录邮箱查看, 按照提示操作 (如果没收到,请等待3-5分钟)"
#: users/views/login.py:19
5
#: users/views/login.py:19
8
msgid "Reset password success"
msgstr "重置密码成功"
#: users/views/login.py:19
6
#: users/views/login.py:19
9
msgid "Reset password success, return to login page"
msgstr "重置密码成功,返回到登录页面"
#: users/views/login.py:2
13 users/views/login.py:226
#: users/views/login.py:2
20 users/views/login.py:233
msgid "Token invalid or expired"
msgstr "Token错误或失效"
#: users/views/login.py:22
2
#: users/views/login.py:22
9
msgid "Password not same"
msgstr "密码不一致"
#: users/views/login.py:263
#: users/views/login.py:239 users/views/user.py:116 users/views/user.py:399
msgid "* Your password does not meet the requirements"
msgstr "* 您的密码不符合要求"
#: users/views/login.py:277
msgid "First login"
msgstr "首次登陆"
#: users/views/login.py:3
22
#: users/views/login.py:3
36
msgid "Login log list"
msgstr "登录日志"
#: users/views/user.py:1
09
#: users/views/user.py:1
28
msgid "Bulk update user success"
msgstr "批量更新用户成功"
#: users/views/user.py:2
14
#: users/views/user.py:2
33
msgid "Invalid file."
msgstr "文件不合法"
#: users/views/user.py:3
11
#: users/views/user.py:3
30
msgid "User granted assets"
msgstr "用户授权资产"
#: users/views/user.py:3
40
#: users/views/user.py:3
63
msgid "Profile setting"
msgstr "个人信息设置"
#: users/views/user.py:3
58
#: users/views/user.py:3
82
msgid "Password update"
msgstr "密码更新"
#: users/views/user.py:
380
#: users/views/user.py:
417
msgid "Public key update"
msgstr "密钥更新"
#: users/views/user.py:4
21
#: users/views/user.py:4
58
msgid "Password invalid"
msgstr "用户名或密码无效"
#: users/views/user.py:5
15
#: users/views/user.py:5
52
msgid "MFA enable success"
msgstr "MFA 绑定成功"
#: users/views/user.py:5
16
#: users/views/user.py:5
53
msgid "MFA enable success, return login page"
msgstr "MFA 绑定成功,返回到登录页面"
#: users/views/user.py:5
18
#: users/views/user.py:5
55
msgid "MFA disable success"
msgstr "MFA 解绑成功"
#: users/views/user.py:5
19
#: users/views/user.py:5
56
msgid "MFA disable success, return login page"
msgstr "MFA 解绑成功,返回登录页面"
apps/jumpserver/settings.py
View file @
e7a731fa
...
...
@@ -401,6 +401,9 @@ TERMINAL_REPLAY_STORAGE = {
},
}
DEFAULT_PASSWORD_MIN_LENGTH
=
6
# Django bootstrap3 setting, more see http://django-bootstrap3.readthedocs.io/en/latest/settings.html
BOOTSTRAP3
=
{
'horizontal_label_class'
:
'col-md-2'
,
...
...
apps/ops/inventory.py
View file @
e7a731fa
...
...
@@ -86,6 +86,7 @@ class JMSInventory(BaseInventory):
gateway
=
asset
.
domain
.
random_gateway
()
proxy_command_list
=
[
"ssh"
,
"-p"
,
str
(
gateway
.
port
),
"-o"
,
"StrictHostKeyChecking=no"
,
"{}@{}"
.
format
(
gateway
.
username
,
gateway
.
ip
),
"-W"
,
"
%
h:
%
p"
,
"-q"
,
]
...
...
apps/static/js/jumpserver.js
View file @
e7a731fa
...
...
@@ -609,3 +609,91 @@ function setUrlParam(url, name, value) {
}
return
url
}
// 校验密码-改变规则颜色
function
checkPasswordRules
(
password
,
minLength
)
{
if
(
wordMinLength
(
password
,
minLength
))
{
$
(
'#rule_SECURITY_PASSWORD_MIN_LENGTH'
).
css
(
'color'
,
'green'
)
}
else
{
$
(
'#rule_SECURITY_PASSWORD_MIN_LENGTH'
).
css
(
'color'
,
'#908a8a'
)
}
if
(
wordUpperCase
(
password
))
{
$
(
'#rule_SECURITY_PASSWORD_UPPER_CASE'
).
css
(
'color'
,
'green'
);
}
else
{
$
(
'#rule_SECURITY_PASSWORD_UPPER_CASE'
).
css
(
'color'
,
'#908a8a'
)
}
if
(
wordLowerCase
(
password
))
{
$
(
'#rule_SECURITY_PASSWORD_LOWER_CASE'
).
css
(
'color'
,
'green'
)
}
else
{
$
(
'#rule_SECURITY_PASSWORD_LOWER_CASE'
).
css
(
'color'
,
'#908a8a'
)
}
if
(
wordNumber
(
password
))
{
$
(
'#rule_SECURITY_PASSWORD_NUMBER'
).
css
(
'color'
,
'green'
)
}
else
{
$
(
'#rule_SECURITY_PASSWORD_NUMBER'
).
css
(
'color'
,
'#908a8a'
)
}
if
(
wordSpecialChar
(
password
))
{
$
(
'#rule_SECURITY_PASSWORD_SPECIAL_CHAR'
).
css
(
'color'
,
'green'
)
}
else
{
$
(
'#rule_SECURITY_PASSWORD_SPECIAL_CHAR'
).
css
(
'color'
,
'#908a8a'
)
}
}
// 最小长度
function
wordMinLength
(
word
,
minLength
)
{
//var minLength = {{ min_length }};
var
re
=
new
RegExp
(
"^(.{"
+
minLength
+
",})$"
);
return
word
.
match
(
re
)
}
// 大写字母
function
wordUpperCase
(
word
)
{
return
word
.
match
(
/
([
A-Z
]
+
)
/
)
}
// 小写字母
function
wordLowerCase
(
word
)
{
return
word
.
match
(
/
([
a-z
]
+
)
/
)
}
// 数字字符
function
wordNumber
(
word
)
{
return
word
.
match
(
/
([\d]
+
)
/
)
}
// 特殊字符
function
wordSpecialChar
(
word
)
{
return
word
.
match
(
/
[
`,~,!,@,#,
\$
,%,
\^
,&,
\*
,
\(
,
\)
,
\-
,_,=,
\+
,
\{
,
\}
,
\[
,
\]
,
\|
,
\\
,;,',:,",
\,
,
\.
,<,>,
\/
,
\?]
+/
)
}
// 显示弹窗密码规则
function
popoverPasswordRules
(
password_check_rules
,
$el
)
{
var
message
=
""
;
jQuery
.
each
(
password_check_rules
,
function
(
idx
,
rules
)
{
message
+=
"<li id="
+
rules
.
id
+
" style='list-style-type:none;'> <i class='fa fa-check-circle-o' style='margin-right:10px;' ></i>"
+
rules
.
label
+
"</li>"
;
});
//$('#id_password_rules').html(message);
$el
.
html
(
message
)
}
// 初始化弹窗popover
function
initPopover
(
$container
,
$progress
,
$idPassword
,
$el
,
password_check_rules
){
options
=
{};
// User Interface
options
.
ui
=
{
container
:
$container
,
viewports
:
{
progress
:
$progress
//errors: $('.popover-content')
},
showProgressbar
:
true
,
showVerdictsInsideProgressBar
:
true
};
$idPassword
.
pwstrength
(
options
);
popoverPasswordRules
(
password_check_rules
,
$el
);
}
apps/static/js/pwstrength-bootstrap.js
0 → 100755
View file @
e7a731fa
/*!
* jQuery Password Strength plugin for Twitter Bootstrap
* Version: 2.2.1
*
* Copyright (c) 2008-2013 Tane Piper
* Copyright (c) 2013 Alejandro Blanco
* Dual licensed under the MIT and GPL licenses.
*/
(
function
(
jQuery
)
{
// Source: src/i18n.js
var
i18n
=
{};
(
function
(
i18n
,
i18next
)
{
'use strict'
;
i18n
.
fallback
=
{
"wordMinLength"
:
"Your password is too short"
,
"wordMaxLength"
:
"Your password is too long"
,
"wordInvalidChar"
:
"Your password contains an invalid character"
,
"wordNotEmail"
:
"Do not use your email as your password"
,
"wordSimilarToUsername"
:
"Your password cannot contain your username"
,
"wordTwoCharacterClasses"
:
"Use different character classes"
,
"wordRepetitions"
:
"Too many repetitions"
,
"wordSequences"
:
"Your password contains sequences"
,
"errorList"
:
"Errors:"
,
"veryWeak"
:
"Very Weak"
,
"weak"
:
"Weak"
,
"normal"
:
"Normal"
,
"medium"
:
"Medium"
,
"strong"
:
"Strong"
,
"veryStrong"
:
"Very Strong"
};
i18n
.
t
=
function
(
key
)
{
var
result
=
''
;
// Try to use i18next.com
if
(
i18next
)
{
result
=
i18next
.
t
(
key
);
}
else
{
// Fallback to english
result
=
i18n
.
fallback
[
key
];
}
return
result
===
key
?
''
:
result
;
};
}(
i18n
,
window
.
i18next
));
// Source: src/rules.js
var
rulesEngine
=
{};
try
{
if
(
!
jQuery
&&
module
&&
module
.
exports
)
{
var
jQuery
=
require
(
"jquery"
),
jsdom
=
require
(
"jsdom"
).
jsdom
;
jQuery
=
jQuery
(
jsdom
().
defaultView
);
}
}
catch
(
ignore
)
{}
(
function
(
$
,
rulesEngine
)
{
"use strict"
;
var
validation
=
{};
rulesEngine
.
forbiddenSequences
=
[
"0123456789"
,
"abcdefghijklmnopqrstuvwxyz"
,
"qwertyuiop"
,
"asdfghjkl"
,
"zxcvbnm"
,
"!@#$%^&*()_+"
];
validation
.
wordNotEmail
=
function
(
options
,
word
,
score
)
{
if
(
word
.
match
(
/^
([\w\!\#
$
\%\&\'\*\+\-\/\=\?\^\`
{
\|\}\~]
+
\.)
*
[\w\!\#
$
\%\&\'\*\+\-\/\=\?\^\`
{
\|\}\~]
+@
((((([
a-z0-9
]{1}[
a-z0-9
\-]{0,62}[
a-z0-9
]{1})
|
[
a-z
])\.)
+
[
a-z
]{2,6})
|
(\d{1,3}\.){3}\d{1,3}(\:\d{1,5})?)
$/i
))
{
return
score
;
}
return
0
;
};
validation
.
wordMinLength
=
function
(
options
,
word
,
score
)
{
var
wordlen
=
word
.
length
,
lenScore
=
Math
.
pow
(
wordlen
,
options
.
rules
.
raisePower
);
if
(
wordlen
<
options
.
common
.
minChar
)
{
lenScore
=
(
lenScore
+
score
);
}
return
lenScore
;
};
validation
.
wordMaxLength
=
function
(
options
,
word
,
score
)
{
var
wordlen
=
word
.
length
,
lenScore
=
Math
.
pow
(
wordlen
,
options
.
rules
.
raisePower
);
if
(
wordlen
>
options
.
common
.
maxChar
)
{
return
score
;
}
return
lenScore
;
};
validation
.
wordInvalidChar
=
function
(
options
,
word
,
score
)
{
if
(
options
.
common
.
invalidCharsRegExp
.
test
(
word
))
{
return
score
;
}
return
0
;
};
validation
.
wordMinLengthStaticScore
=
function
(
options
,
word
,
score
)
{
return
word
.
length
<
options
.
common
.
minChar
?
0
:
score
;
};
validation
.
wordMaxLengthStaticScore
=
function
(
options
,
word
,
score
)
{
return
word
.
length
>
options
.
common
.
maxChar
?
0
:
score
;
};
validation
.
wordSimilarToUsername
=
function
(
options
,
word
,
score
)
{
var
username
=
$
(
options
.
common
.
usernameField
).
val
();
if
(
username
&&
word
.
toLowerCase
().
match
(
username
.
replace
(
/
[\-\[\]\/\{\}\(\)\*\+\=\?\:\.\\\^\$\|\!\,]
/g
,
"
\\
$&"
).
toLowerCase
()))
{
return
score
;
}
return
0
;
};
validation
.
wordTwoCharacterClasses
=
function
(
options
,
word
,
score
)
{
if
(
word
.
match
(
/
([
a-z
]
.*
[
A-Z
])
|
([
A-Z
]
.*
[
a-z
])
/
)
||
(
word
.
match
(
/
([
a-zA-Z
])
/
)
&&
word
.
match
(
/
([
0-9
])
/
))
||
(
word
.
match
(
/
(
.
[
!,@,#,$,%,
\^
,&,*,?,_,~
])
/
)
&&
word
.
match
(
/
[
a-zA-Z0-9_
]
/
)))
{
return
score
;
}
return
0
;
};
validation
.
wordRepetitions
=
function
(
options
,
word
,
score
)
{
if
(
word
.
match
(
/
(
.
)\1\1
/
))
{
return
score
;
}
return
0
;
};
validation
.
wordSequences
=
function
(
options
,
word
,
score
)
{
var
found
=
false
,
j
;
if
(
word
.
length
>
2
)
{
$
.
each
(
rulesEngine
.
forbiddenSequences
,
function
(
idx
,
seq
)
{
if
(
found
)
{
return
;
}
var
sequences
=
[
seq
,
seq
.
split
(
''
).
reverse
().
join
(
''
)];
$
.
each
(
sequences
,
function
(
idx
,
sequence
)
{
for
(
j
=
0
;
j
<
(
word
.
length
-
2
);
j
+=
1
)
{
// iterate the word trough a sliding window of size 3:
if
(
sequence
.
indexOf
(
word
.
toLowerCase
().
substring
(
j
,
j
+
3
))
>
-
1
)
{
found
=
true
;
}
}
});
});
if
(
found
)
{
return
score
;
}
}
return
0
;
};
validation
.
wordLowercase
=
function
(
options
,
word
,
score
)
{
return
word
.
match
(
/
[
a-z
]
/
)
&&
score
;
};
validation
.
wordUppercase
=
function
(
options
,
word
,
score
)
{
return
word
.
match
(
/
[
A-Z
]
/
)
&&
score
;
};
validation
.
wordOneNumber
=
function
(
options
,
word
,
score
)
{
return
word
.
match
(
/
\d
+/
)
&&
score
;
};
validation
.
wordThreeNumbers
=
function
(
options
,
word
,
score
)
{
return
word
.
match
(
/
(
.*
[
0-9
]
.*
[
0-9
]
.*
[
0-9
])
/
)
&&
score
;
};
validation
.
wordOneSpecialChar
=
function
(
options
,
word
,
score
)
{
return
word
.
match
(
/
[
!,@,#,$,%,
\^
,&,*,?,_,~
]
/
)
&&
score
;
};
validation
.
wordTwoSpecialChar
=
function
(
options
,
word
,
score
)
{
return
word
.
match
(
/
(
.*
[
!,@,#,$,%,
\^
,&,*,?,_,~
]
.*
[
!,@,#,$,%,
\^
,&,*,?,_,~
])
/
)
&&
score
;
};
validation
.
wordUpperLowerCombo
=
function
(
options
,
word
,
score
)
{
return
word
.
match
(
/
([
a-z
]
.*
[
A-Z
])
|
([
A-Z
]
.*
[
a-z
])
/
)
&&
score
;
};
validation
.
wordLetterNumberCombo
=
function
(
options
,
word
,
score
)
{
return
word
.
match
(
/
([
a-zA-Z
])
/
)
&&
word
.
match
(
/
([
0-9
])
/
)
&&
score
;
};
validation
.
wordLetterNumberCharCombo
=
function
(
options
,
word
,
score
)
{
return
word
.
match
(
/
([
a-zA-Z0-9
]
.*
[
!,@,#,$,%,
\^
,&,*,?,_,~
])
|
([
!,@,#,$,%,
\^
,&,*,?,_,~
]
.*
[
a-zA-Z0-9
])
/
)
&&
score
;
};
validation
.
wordIsACommonPassword
=
function
(
options
,
word
,
score
)
{
if
(
$
.
inArray
(
word
,
options
.
rules
.
commonPasswords
)
>=
0
)
{
return
score
;
}
return
0
;
};
rulesEngine
.
validation
=
validation
;
rulesEngine
.
executeRules
=
function
(
options
,
word
)
{
var
totalScore
=
0
;
$
.
each
(
options
.
rules
.
activated
,
function
(
rule
,
active
)
{
if
(
active
)
{
var
score
=
options
.
rules
.
scores
[
rule
],
funct
=
rulesEngine
.
validation
[
rule
],
result
,
errorMessage
;
if
(
!
$
.
isFunction
(
funct
))
{
funct
=
options
.
rules
.
extra
[
rule
];
}
if
(
$
.
isFunction
(
funct
))
{
result
=
funct
(
options
,
word
,
score
);
if
(
result
)
{
totalScore
+=
result
;
}
if
(
result
<
0
||
(
!
$
.
isNumeric
(
result
)
&&
!
result
))
{
errorMessage
=
options
.
ui
.
spanError
(
options
,
rule
);
if
(
errorMessage
.
length
>
0
)
{
options
.
instances
.
errors
.
push
(
errorMessage
);
}
}
}
}
});
return
totalScore
;
};
}(
jQuery
,
rulesEngine
));
try
{
if
(
module
&&
module
.
exports
)
{
module
.
exports
=
rulesEngine
;
}
}
catch
(
ignore
)
{}
// Source: src/options.js
var
defaultOptions
=
{};
defaultOptions
.
common
=
{};
defaultOptions
.
common
.
minChar
=
6
;
defaultOptions
.
common
.
maxChar
=
20
;
defaultOptions
.
common
.
usernameField
=
"#username"
;
defaultOptions
.
common
.
invalidCharsRegExp
=
new
RegExp
(
/
[\s
,'"
]
/
);
defaultOptions
.
common
.
userInputs
=
[
// Selectors for input fields with user input
];
defaultOptions
.
common
.
onLoad
=
undefined
;
defaultOptions
.
common
.
onKeyUp
=
undefined
;
defaultOptions
.
common
.
onScore
=
undefined
;
defaultOptions
.
common
.
zxcvbn
=
false
;
defaultOptions
.
common
.
zxcvbnTerms
=
[
// List of disrecommended words
];
defaultOptions
.
common
.
events
=
[
"keyup"
,
"change"
,
"paste"
];
defaultOptions
.
common
.
debug
=
false
;
defaultOptions
.
rules
=
{};
defaultOptions
.
rules
.
extra
=
{};
defaultOptions
.
rules
.
scores
=
{
wordNotEmail
:
-
100
,
wordMinLength
:
-
50
,
wordMaxLength
:
-
50
,
wordInvalidChar
:
-
100
,
wordSimilarToUsername
:
-
100
,
wordSequences
:
-
20
,
wordTwoCharacterClasses
:
2
,
wordRepetitions
:
-
25
,
wordLowercase
:
1
,
wordUppercase
:
3
,
wordOneNumber
:
3
,
wordThreeNumbers
:
5
,
wordOneSpecialChar
:
3
,
wordTwoSpecialChar
:
5
,
wordUpperLowerCombo
:
2
,
wordLetterNumberCombo
:
2
,
wordLetterNumberCharCombo
:
2
,
wordIsACommonPassword
:
-
100
};
defaultOptions
.
rules
.
activated
=
{
wordNotEmail
:
true
,
wordMinLength
:
true
,
wordMaxLength
:
false
,
wordInvalidChar
:
false
,
wordSimilarToUsername
:
true
,
wordSequences
:
true
,
wordTwoCharacterClasses
:
true
,
wordRepetitions
:
true
,
wordLowercase
:
true
,
wordUppercase
:
true
,
wordOneNumber
:
true
,
wordThreeNumbers
:
true
,
wordOneSpecialChar
:
true
,
wordTwoSpecialChar
:
true
,
wordUpperLowerCombo
:
true
,
wordLetterNumberCombo
:
true
,
wordLetterNumberCharCombo
:
true
,
wordIsACommonPassword
:
true
};
defaultOptions
.
rules
.
raisePower
=
1.4
;
// List taken from https://github.com/danielmiessler/SecLists (MIT License)
defaultOptions
.
rules
.
commonPasswords
=
[
'123456'
,
'password'
,
'12345678'
,
'qwerty'
,
'123456789'
,
'12345'
,
'1234'
,
'111111'
,
'1234567'
,
'dragon'
,
'123123'
,
'baseball'
,
'abc123'
,
'football'
,
'monkey'
,
'letmein'
,
'696969'
,
'shadow'
,
'master'
,
'666666'
,
'qwertyuiop'
,
'123321'
,
'mustang'
,
'1234567890'
,
'michael'
,
'654321'
,
'pussy'
,
'superman'
,
'1qaz2wsx'
,
'7777777'
,
'fuckyou'
,
'121212'
,
'000000'
,
'qazwsx'
,
'123qwe'
,
'killer'
,
'trustno1'
,
'jordan'
,
'jennifer'
,
'zxcvbnm'
,
'asdfgh'
,
'hunter'
,
'buster'
,
'soccer'
,
'harley'
,
'batman'
,
'andrew'
,
'tigger'
,
'sunshine'
,
'iloveyou'
,
'fuckme'
,
'2000'
,
'charlie'
,
'robert'
,
'thomas'
,
'hockey'
,
'ranger'
,
'daniel'
,
'starwars'
,
'klaster'
,
'112233'
,
'george'
,
'asshole'
,
'computer'
,
'michelle'
,
'jessica'
,
'pepper'
,
'1111'
,
'zxcvbn'
,
'555555'
,
'11111111'
,
'131313'
,
'freedom'
,
'777777'
,
'pass'
,
'fuck'
,
'maggie'
,
'159753'
,
'aaaaaa'
,
'ginger'
,
'princess'
,
'joshua'
,
'cheese'
,
'amanda'
,
'summer'
,
'love'
,
'ashley'
,
'6969'
,
'nicole'
,
'chelsea'
,
'biteme'
,
'matthew'
,
'access'
,
'yankees'
,
'987654321'
,
'dallas'
,
'austin'
,
'thunder'
,
'taylor'
,
'matrix'
];
defaultOptions
.
ui
=
{};
defaultOptions
.
ui
.
bootstrap2
=
false
;
defaultOptions
.
ui
.
bootstrap4
=
false
;
defaultOptions
.
ui
.
colorClasses
=
[
"danger"
,
"danger"
,
"danger"
,
"warning"
,
"warning"
,
"success"
];
defaultOptions
.
ui
.
showProgressBar
=
true
;
defaultOptions
.
ui
.
progressBarEmptyPercentage
=
1
;
defaultOptions
.
ui
.
progressBarMinPercentage
=
1
;
defaultOptions
.
ui
.
progressExtraCssClasses
=
''
;
defaultOptions
.
ui
.
progressBarExtraCssClasses
=
''
;
defaultOptions
.
ui
.
showPopover
=
false
;
defaultOptions
.
ui
.
popoverPlacement
=
"bottom"
;
defaultOptions
.
ui
.
showStatus
=
false
;
defaultOptions
.
ui
.
spanError
=
function
(
options
,
key
)
{
"use strict"
;
var
text
=
options
.
i18n
.
t
(
key
);
if
(
!
text
)
{
return
''
;
}
return
'<span style="color: #d52929">'
+
text
+
'</span>'
;
};
defaultOptions
.
ui
.
popoverError
=
function
(
options
)
{
"use strict"
;
var
errors
=
options
.
instances
.
errors
,
errorsTitle
=
options
.
i18n
.
t
(
"errorList"
),
message
=
"<div>"
+
errorsTitle
+
"<ul class='error-list' style='margin-bottom: 0;'>"
;
jQuery
.
each
(
errors
,
function
(
idx
,
err
)
{
message
+=
"<li>"
+
err
+
"</li>"
;
});
message
+=
"</ul></div>"
;
return
message
;
};
defaultOptions
.
ui
.
showVerdicts
=
true
;
defaultOptions
.
ui
.
showVerdictsInsideProgressBar
=
false
;
defaultOptions
.
ui
.
useVerdictCssClass
=
false
;
defaultOptions
.
ui
.
showErrors
=
false
;
defaultOptions
.
ui
.
showScore
=
false
;
defaultOptions
.
ui
.
container
=
undefined
;
defaultOptions
.
ui
.
viewports
=
{
progress
:
undefined
,
verdict
:
undefined
,
errors
:
undefined
,
score
:
undefined
};
defaultOptions
.
ui
.
scores
=
[
0
,
14
,
26
,
38
,
50
];
defaultOptions
.
i18n
=
{};
defaultOptions
.
i18n
.
t
=
i18n
.
t
;
// Source: src/ui.js
var
ui
=
{};
(
function
(
$
,
ui
)
{
"use strict"
;
var
statusClasses
=
[
"error"
,
"warning"
,
"success"
],
verdictKeys
=
[
"veryWeak"
,
"weak"
,
"normal"
,
"medium"
,
"strong"
,
"veryStrong"
];
ui
.
getContainer
=
function
(
options
,
$el
)
{
var
$container
;
$container
=
$
(
options
.
ui
.
container
);
if
(
!
(
$container
&&
$container
.
length
===
1
))
{
$container
=
$el
.
parent
();
}
return
$container
;
};
ui
.
findElement
=
function
(
$container
,
viewport
,
cssSelector
)
{
if
(
viewport
)
{
return
$container
.
find
(
viewport
).
find
(
cssSelector
);
}
return
$container
.
find
(
cssSelector
);
};
ui
.
getUIElements
=
function
(
options
,
$el
)
{
var
$container
,
result
;
if
(
options
.
instances
.
viewports
)
{
return
options
.
instances
.
viewports
;
}
$container
=
ui
.
getContainer
(
options
,
$el
);
result
=
{};
result
.
$progressbar
=
ui
.
findElement
(
$container
,
options
.
ui
.
viewports
.
progress
,
"div.progress"
);
if
(
options
.
ui
.
showVerdictsInsideProgressBar
)
{
result
.
$verdict
=
result
.
$progressbar
.
find
(
"span.password-verdict"
);
}
if
(
!
options
.
ui
.
showPopover
)
{
if
(
!
options
.
ui
.
showVerdictsInsideProgressBar
)
{
result
.
$verdict
=
ui
.
findElement
(
$container
,
options
.
ui
.
viewports
.
verdict
,
"span.password-verdict"
);
}
result
.
$errors
=
ui
.
findElement
(
$container
,
options
.
ui
.
viewports
.
errors
,
"ul.error-list"
);
}
result
.
$score
=
ui
.
findElement
(
$container
,
options
.
ui
.
viewports
.
score
,
"span.password-score"
);
options
.
instances
.
viewports
=
result
;
return
result
;
};
ui
.
initProgressBar
=
function
(
options
,
$el
)
{
var
$container
=
ui
.
getContainer
(
options
,
$el
),
progressbar
=
"<div class='progress "
;
if
(
options
.
ui
.
bootstrap2
)
{
// Boostrap 2
progressbar
+=
options
.
ui
.
progressBarExtraCssClasses
+
"'><div class='"
;
}
else
{
// Bootstrap 3 & 4
progressbar
+=
options
.
ui
.
progressExtraCssClasses
+
"'><div class='"
+
options
.
ui
.
progressBarExtraCssClasses
+
" progress-"
;
}
progressbar
+=
"bar'>"
;
if
(
options
.
ui
.
showVerdictsInsideProgressBar
)
{
progressbar
+=
"<span class='password-verdict'></span>"
;
}
progressbar
+=
"</div></div>"
;
if
(
options
.
ui
.
viewports
.
progress
)
{
$container
.
find
(
options
.
ui
.
viewports
.
progress
).
append
(
progressbar
);
}
else
{
$
(
progressbar
).
insertAfter
(
$el
);
}
};
ui
.
initHelper
=
function
(
options
,
$el
,
html
,
viewport
)
{
var
$container
=
ui
.
getContainer
(
options
,
$el
);
if
(
viewport
)
{
$container
.
find
(
viewport
).
append
(
html
);
}
else
{
$
(
html
).
insertAfter
(
$el
);
}
};
ui
.
initVerdict
=
function
(
options
,
$el
)
{
ui
.
initHelper
(
options
,
$el
,
"<span class='password-verdict'></span>"
,
options
.
ui
.
viewports
.
verdict
);
};
ui
.
initErrorList
=
function
(
options
,
$el
)
{
ui
.
initHelper
(
options
,
$el
,
"<ul class='error-list'></ul >"
,
options
.
ui
.
viewports
.
errors
);
};
ui
.
initScore
=
function
(
options
,
$el
)
{
ui
.
initHelper
(
options
,
$el
,
"<span class='password-score'></span>"
,
options
.
ui
.
viewports
.
score
);
};
ui
.
initPopover
=
function
(
options
,
$el
)
{
$el
.
popover
(
"destroy"
);
$el
.
popover
({
html
:
true
,
placement
:
options
.
ui
.
popoverPlacement
,
trigger
:
"manual"
,
content
:
" "
});
};
ui
.
initUI
=
function
(
options
,
$el
)
{
if
(
options
.
ui
.
showPopover
)
{
ui
.
initPopover
(
options
,
$el
);
}
else
{
if
(
options
.
ui
.
showErrors
)
{
ui
.
initErrorList
(
options
,
$el
);
}
if
(
options
.
ui
.
showVerdicts
&&
!
options
.
ui
.
showVerdictsInsideProgressBar
)
{
ui
.
initVerdict
(
options
,
$el
);
}
}
if
(
options
.
ui
.
showProgressBar
)
{
ui
.
initProgressBar
(
options
,
$el
);
}
if
(
options
.
ui
.
showScore
)
{
ui
.
initScore
(
options
,
$el
);
}
};
ui
.
updateProgressBar
=
function
(
options
,
$el
,
cssClass
,
percentage
)
{
var
$progressbar
=
ui
.
getUIElements
(
options
,
$el
).
$progressbar
,
$bar
=
$progressbar
.
find
(
".progress-bar"
),
cssPrefix
=
"progress-"
;
if
(
options
.
ui
.
bootstrap2
)
{
$bar
=
$progressbar
.
find
(
".bar"
);
cssPrefix
=
""
;
}
$
.
each
(
options
.
ui
.
colorClasses
,
function
(
idx
,
value
)
{
if
(
options
.
ui
.
bootstrap4
)
{
$bar
.
removeClass
(
"bg-"
+
value
);
}
else
{
$bar
.
removeClass
(
cssPrefix
+
"bar-"
+
value
);
}
});
if
(
options
.
ui
.
bootstrap4
)
{
$bar
.
addClass
(
"bg-"
+
options
.
ui
.
colorClasses
[
cssClass
]);
}
else
{
$bar
.
addClass
(
cssPrefix
+
"bar-"
+
options
.
ui
.
colorClasses
[
cssClass
]);
}
$bar
.
css
(
"width"
,
percentage
+
'%'
);
};
ui
.
updateVerdict
=
function
(
options
,
$el
,
cssClass
,
text
)
{
var
$verdict
=
ui
.
getUIElements
(
options
,
$el
).
$verdict
;
$verdict
.
removeClass
(
options
.
ui
.
colorClasses
.
join
(
' '
));
if
(
cssClass
>
-
1
)
{
$verdict
.
addClass
(
options
.
ui
.
colorClasses
[
cssClass
]);
}
if
(
options
.
ui
.
showVerdictsInsideProgressBar
)
{
$verdict
.
css
(
'white-space'
,
'nowrap'
);
}
$verdict
.
html
(
text
);
};
ui
.
updateErrors
=
function
(
options
,
$el
,
remove
)
{
var
$errors
=
ui
.
getUIElements
(
options
,
$el
).
$errors
,
html
=
""
;
if
(
!
remove
)
{
$
.
each
(
options
.
instances
.
errors
,
function
(
idx
,
err
)
{
html
+=
"<li style='list-style-type:none;'>"
+
err
+
"</li>"
;
});
}
$errors
.
html
(
html
);
};
ui
.
updateScore
=
function
(
options
,
$el
,
score
,
remove
)
{
var
$score
=
ui
.
getUIElements
(
options
,
$el
).
$score
,
html
=
""
;
if
(
!
remove
)
{
html
=
score
.
toFixed
(
2
);
}
$score
.
html
(
html
);
};
ui
.
updatePopover
=
function
(
options
,
$el
,
verdictText
,
remove
)
{
var
popover
=
$el
.
data
(
"bs.popover"
),
html
=
""
,
hide
=
true
;
if
(
options
.
ui
.
showVerdicts
&&
!
options
.
ui
.
showVerdictsInsideProgressBar
&&
verdictText
.
length
>
0
)
{
html
=
"<h5><span class='password-verdict'>"
+
verdictText
+
"</span></h5>"
;
hide
=
false
;
}
if
(
options
.
ui
.
showErrors
)
{
if
(
options
.
instances
.
errors
.
length
>
0
)
{
hide
=
false
;
}
html
+=
options
.
ui
.
popoverError
(
options
);
}
if
(
hide
||
remove
)
{
$el
.
popover
(
"hide"
);
return
;
}
if
(
options
.
ui
.
bootstrap2
)
{
popover
=
$el
.
data
(
"popover"
);
}
if
(
popover
.
$arrow
&&
popover
.
$arrow
.
parents
(
"body"
).
length
>
0
)
{
$el
.
find
(
"+ .popover .popover-content"
).
html
(
html
);
}
else
{
// It's hidden
popover
.
options
.
content
=
html
;
$el
.
popover
(
"show"
);
}
};
ui
.
updateFieldStatus
=
function
(
options
,
$el
,
cssClass
,
remove
)
{
var
targetClass
=
options
.
ui
.
bootstrap2
?
".control-group"
:
".form-group"
,
$container
=
$el
.
parents
(
targetClass
).
first
();
$
.
each
(
statusClasses
,
function
(
idx
,
css
)
{
if
(
!
options
.
ui
.
bootstrap2
)
{
css
=
"has-"
+
css
;
}
$container
.
removeClass
(
css
);
});
if
(
remove
)
{
return
;
}
cssClass
=
statusClasses
[
Math
.
floor
(
cssClass
/
2
)];
if
(
!
options
.
ui
.
bootstrap2
)
{
cssClass
=
"has-"
+
cssClass
;
}
$container
.
addClass
(
cssClass
);
};
ui
.
percentage
=
function
(
options
,
score
,
maximun
)
{
var
result
=
Math
.
floor
(
100
*
score
/
maximun
),
min
=
options
.
ui
.
progressBarMinPercentage
;
result
=
result
<=
min
?
min
:
result
;
result
=
result
>
100
?
100
:
result
;
return
result
;
};
ui
.
getVerdictAndCssClass
=
function
(
options
,
score
)
{
var
level
,
verdict
;
if
(
score
===
undefined
)
{
return
[
''
,
0
];
}
if
(
score
<=
options
.
ui
.
scores
[
0
])
{
level
=
0
;
}
else
if
(
score
<
options
.
ui
.
scores
[
1
])
{
level
=
1
;
}
else
if
(
score
<
options
.
ui
.
scores
[
2
])
{
level
=
2
;
}
else
if
(
score
<
options
.
ui
.
scores
[
3
])
{
level
=
3
;
}
else
if
(
score
<
options
.
ui
.
scores
[
4
])
{
level
=
4
;
}
else
{
level
=
5
;
}
verdict
=
verdictKeys
[
level
];
return
[
options
.
i18n
.
t
(
verdict
),
level
];
};
ui
.
updateUI
=
function
(
options
,
$el
,
score
)
{
var
cssClass
,
barPercentage
,
verdictText
,
verdictCssClass
;
cssClass
=
ui
.
getVerdictAndCssClass
(
options
,
score
);
verdictText
=
score
===
0
?
''
:
cssClass
[
0
];
cssClass
=
cssClass
[
1
];
verdictCssClass
=
options
.
ui
.
useVerdictCssClass
?
cssClass
:
-
1
;
if
(
options
.
ui
.
showProgressBar
)
{
if
(
score
===
undefined
)
{
barPercentage
=
options
.
ui
.
progressBarEmptyPercentage
;
}
else
{
barPercentage
=
ui
.
percentage
(
options
,
score
,
options
.
ui
.
scores
[
4
]);
}
ui
.
updateProgressBar
(
options
,
$el
,
cssClass
,
barPercentage
);
if
(
options
.
ui
.
showVerdictsInsideProgressBar
)
{
ui
.
updateVerdict
(
options
,
$el
,
verdictCssClass
,
verdictText
);
}
}
if
(
options
.
ui
.
showStatus
)
{
ui
.
updateFieldStatus
(
options
,
$el
,
cssClass
,
score
===
undefined
);
}
if
(
options
.
ui
.
showPopover
)
{
ui
.
updatePopover
(
options
,
$el
,
verdictText
,
score
===
undefined
);
}
else
{
if
(
options
.
ui
.
showVerdicts
&&
!
options
.
ui
.
showVerdictsInsideProgressBar
)
{
ui
.
updateVerdict
(
options
,
$el
,
verdictCssClass
,
verdictText
);
}
if
(
options
.
ui
.
showErrors
)
{
ui
.
updateErrors
(
options
,
$el
,
score
===
undefined
);
}
}
if
(
options
.
ui
.
showScore
)
{
ui
.
updateScore
(
options
,
$el
,
score
,
score
===
undefined
);
}
};
}(
jQuery
,
ui
));
// Source: src/methods.js
var
methods
=
{};
(
function
(
$
,
methods
)
{
"use strict"
;
var
onKeyUp
,
onPaste
,
applyToAll
;
onKeyUp
=
function
(
event
)
{
var
$el
=
$
(
event
.
target
),
options
=
$el
.
data
(
"pwstrength-bootstrap"
),
word
=
$el
.
val
(),
userInputs
,
verdictText
,
verdictLevel
,
score
;
if
(
options
===
undefined
)
{
return
;
}
options
.
instances
.
errors
=
[];
if
(
word
.
length
===
0
)
{
score
=
undefined
;
}
else
{
if
(
options
.
common
.
zxcvbn
)
{
userInputs
=
[];
$
.
each
(
options
.
common
.
userInputs
.
concat
([
options
.
common
.
usernameField
]),
function
(
idx
,
selector
)
{
var
value
=
$
(
selector
).
val
();
if
(
value
)
{
userInputs
.
push
(
value
);
}
});
userInputs
=
userInputs
.
concat
(
options
.
common
.
zxcvbnTerms
);
score
=
zxcvbn
(
word
,
userInputs
).
guesses
;
score
=
Math
.
log
(
score
)
*
Math
.
LOG2E
;
}
else
{
score
=
rulesEngine
.
executeRules
(
options
,
word
);
}
if
(
$
.
isFunction
(
options
.
common
.
onScore
))
{
score
=
options
.
common
.
onScore
(
options
,
word
,
score
);
}
}
ui
.
updateUI
(
options
,
$el
,
score
);
verdictText
=
ui
.
getVerdictAndCssClass
(
options
,
score
);
verdictLevel
=
verdictText
[
1
];
verdictText
=
verdictText
[
0
];
if
(
options
.
common
.
debug
)
{
console
.
log
(
score
+
' - '
+
verdictText
);
}
if
(
$
.
isFunction
(
options
.
common
.
onKeyUp
))
{
options
.
common
.
onKeyUp
(
event
,
{
score
:
score
,
verdictText
:
verdictText
,
verdictLevel
:
verdictLevel
});
}
};
onPaste
=
function
(
event
)
{
// This handler is necessary because the paste event fires before the
// content is actually in the input, so we cannot read its value right
// away. Therefore, the timeouts.
var
$el
=
$
(
event
.
target
),
word
=
$el
.
val
(),
tries
=
0
,
callback
;
callback
=
function
()
{
var
newWord
=
$el
.
val
();
if
(
newWord
!==
word
)
{
onKeyUp
(
event
);
}
else
if
(
tries
<
3
)
{
tries
+=
1
;
setTimeout
(
callback
,
100
);
}
};
setTimeout
(
callback
,
100
);
};
methods
.
init
=
function
(
settings
)
{
this
.
each
(
function
(
idx
,
el
)
{
// Make it deep extend (first param) so it extends also the
// rules and other inside objects
var
clonedDefaults
=
$
.
extend
(
true
,
{},
defaultOptions
),
localOptions
=
$
.
extend
(
true
,
clonedDefaults
,
settings
),
$el
=
$
(
el
);
localOptions
.
instances
=
{};
$el
.
data
(
"pwstrength-bootstrap"
,
localOptions
);
$
.
each
(
localOptions
.
common
.
events
,
function
(
idx
,
eventName
)
{
var
handler
=
eventName
===
"paste"
?
onPaste
:
onKeyUp
;
$el
.
on
(
eventName
,
handler
);
});
ui
.
initUI
(
localOptions
,
$el
);
$el
.
trigger
(
"keyup"
);
if
(
$
.
isFunction
(
localOptions
.
common
.
onLoad
))
{
localOptions
.
common
.
onLoad
();
}
});
return
this
;
};
methods
.
destroy
=
function
()
{
this
.
each
(
function
(
idx
,
el
)
{
var
$el
=
$
(
el
),
options
=
$el
.
data
(
"pwstrength-bootstrap"
),
elements
=
ui
.
getUIElements
(
options
,
$el
);
elements
.
$progressbar
.
remove
();
elements
.
$verdict
.
remove
();
elements
.
$errors
.
remove
();
$el
.
removeData
(
"pwstrength-bootstrap"
);
});
};
methods
.
forceUpdate
=
function
()
{
this
.
each
(
function
(
idx
,
el
)
{
var
event
=
{
target
:
el
};
onKeyUp
(
event
);
});
};
methods
.
addRule
=
function
(
name
,
method
,
score
,
active
)
{
this
.
each
(
function
(
idx
,
el
)
{
var
options
=
$
(
el
).
data
(
"pwstrength-bootstrap"
);
options
.
rules
.
activated
[
name
]
=
active
;
options
.
rules
.
scores
[
name
]
=
score
;
options
.
rules
.
extra
[
name
]
=
method
;
});
};
applyToAll
=
function
(
rule
,
prop
,
value
)
{
this
.
each
(
function
(
idx
,
el
)
{
$
(
el
).
data
(
"pwstrength-bootstrap"
).
rules
[
prop
][
rule
]
=
value
;
});
};
methods
.
changeScore
=
function
(
rule
,
score
)
{
applyToAll
.
call
(
this
,
rule
,
"scores"
,
score
);
};
methods
.
ruleActive
=
function
(
rule
,
active
)
{
applyToAll
.
call
(
this
,
rule
,
"activated"
,
active
);
};
methods
.
ruleIsMet
=
function
(
rule
)
{
if
(
$
.
isFunction
(
rulesEngine
.
validation
[
rule
]))
{
if
(
rule
===
"wordMinLength"
)
{
rule
=
"wordMinLengthStaticScore"
;
}
else
if
(
rule
===
"wordMaxLength"
)
{
rule
=
"wordMaxLengthStaticScore"
;
}
var
rulesMetCnt
=
0
;
this
.
each
(
function
(
idx
,
el
)
{
var
options
=
$
(
el
).
data
(
"pwstrength-bootstrap"
);
rulesMetCnt
+=
rulesEngine
.
validation
[
rule
](
options
,
$
(
el
).
val
(),
1
);
});
return
(
rulesMetCnt
===
this
.
length
);
}
$
.
error
(
"Rule "
+
rule
+
" does not exist on jQuery.pwstrength-bootstrap.validation"
);
};
$
.
fn
.
pwstrength
=
function
(
method
)
{
var
result
;
if
(
methods
[
method
])
{
result
=
methods
[
method
].
apply
(
this
,
Array
.
prototype
.
slice
.
call
(
arguments
,
1
));
}
else
if
(
typeof
method
===
"object"
||
!
method
)
{
result
=
methods
.
init
.
apply
(
this
,
arguments
);
}
else
{
$
.
error
(
"Method "
+
method
+
" does not exist on jQuery.pwstrength-bootstrap"
);
}
return
result
;
};
}(
jQuery
,
methods
));
}(
jQuery
));
\ No newline at end of file
apps/templates/_base_create_update.html
View file @
e7a731fa
...
...
@@ -5,6 +5,7 @@
{% block custom_head_css_js %}
<link
href=
"{% static 'css/plugins/select2/select2.min.css' %}"
rel=
"stylesheet"
>
<script
src=
"{% static 'js/plugins/select2/select2.full.min.js' %}"
></script>
<script
type=
"text/javascript"
src=
"{% static 'js/pwstrength-bootstrap.js' %}"
></script>
{% block custom_head_css_js_create %} {% endblock %}
{% endblock %}
...
...
apps/templates/_footer.html
View file @
e7a731fa
<div
class=
"footer fixed"
>
<div
class=
"pull-right"
>
Version
<strong>
1.3.
1
-{% include '_build.html' %}
</strong>
GPLv2.
Version
<strong>
1.3.
2
-{% include '_build.html' %}
</strong>
GPLv2.
<!--<img style="display: none" src="http://www.jumpserver.org/img/evaluate_avatar1.jpg">-->
</div>
<div>
...
...
apps/users/forms.py
View file @
e7a731fa
...
...
@@ -16,13 +16,14 @@ class UserLoginForm(AuthenticationForm):
max_length
=
128
,
strip
=
False
)
def
confirm_login_allowed
(
self
,
user
):
if
not
user
.
is_staff
:
raise
forms
.
ValidationError
(
self
.
error_messages
[
'inactive'
],
code
=
'inactive'
,)
class
UserLoginCaptchaForm
(
AuthenticationForm
):
username
=
forms
.
CharField
(
label
=
_
(
'Username'
),
max_length
=
100
)
password
=
forms
.
CharField
(
label
=
_
(
'Password'
),
widget
=
forms
.
PasswordInput
,
max_length
=
128
,
strip
=
False
)
class
UserLoginCaptchaForm
(
UserLoginForm
):
captcha
=
CaptchaField
()
...
...
@@ -72,7 +73,7 @@ class UserCreateUpdateForm(forms.ModelForm):
'data-placeholder'
:
_
(
'Join user groups'
)
}
),
'otp_level'
:
forms
.
RadioSelect
()
'otp_level'
:
forms
.
RadioSelect
()
,
}
def
clean_public_key
(
self
):
...
...
apps/users/models/user.py
View file @
e7a731fa
...
...
@@ -14,6 +14,7 @@ from django.utils import timezone
from
django.shortcuts
import
reverse
from
common.utils
import
get_signer
,
date_expired_default
from
common.models
import
Setting
__all__
=
[
'User'
]
...
...
@@ -35,6 +36,12 @@ class User(AbstractUser):
(
1
,
_
(
'Enable'
)),
(
2
,
_
(
"Force enable"
)),
)
SOURCE_LOCAL
=
'local'
SOURCE_LDAP
=
'ldap'
SOURCE_CHOICES
=
(
(
SOURCE_LOCAL
,
'Local'
),
(
SOURCE_LDAP
,
'LDAP/AD'
),
)
id
=
models
.
UUIDField
(
default
=
uuid
.
uuid4
,
primary_key
=
True
)
username
=
models
.
CharField
(
max_length
=
128
,
unique
=
True
,
verbose_name
=
_
(
'Username'
)
...
...
@@ -82,6 +89,10 @@ class User(AbstractUser):
created_by
=
models
.
CharField
(
max_length
=
30
,
default
=
''
,
verbose_name
=
_
(
'Created by'
)
)
source
=
models
.
CharField
(
max_length
=
30
,
default
=
SOURCE_LOCAL
,
choices
=
SOURCE_CHOICES
,
verbose_name
=
_
(
'Source'
)
)
def
__str__
(
self
):
return
'{0.name}({0.username})'
.
format
(
self
)
...
...
@@ -248,14 +259,17 @@ class User(AbstractUser):
@property
def
otp_enabled
(
self
):
return
self
.
otp_level
>
0
return
self
.
otp_
force_enabled
or
self
.
otp_
level
>
0
@property
def
otp_force_enabled
(
self
):
mfa_setting
=
Setting
.
objects
.
filter
(
name
=
'SECURITY_MFA_AUTH'
)
.
first
()
if
mfa_setting
and
mfa_setting
.
cleaned_value
:
return
True
return
self
.
otp_level
==
2
def
enable_otp
(
self
):
if
not
self
.
otp_
force_enabled
:
if
not
self
.
otp_
level
==
2
:
self
.
otp_level
=
1
def
force_enable_otp
(
self
):
...
...
@@ -275,6 +289,7 @@ class User(AbstractUser):
'is_superuser'
:
self
.
is_superuser
,
'role'
:
self
.
get_role_display
(),
'groups'
:
[
group
.
name
for
group
in
self
.
groups
.
all
()],
'source'
:
self
.
get_source_display
(),
'wechat'
:
self
.
wechat
,
'phone'
:
self
.
phone
,
'otp_level'
:
self
.
otp_level
,
...
...
apps/users/serializers.py
View file @
e7a731fa
...
...
@@ -26,7 +26,10 @@ class UserSerializer(BulkSerializerMixin, serializers.ModelSerializer):
def
get_field_names
(
self
,
declared_fields
,
info
):
fields
=
super
(
UserSerializer
,
self
)
.
get_field_names
(
declared_fields
,
info
)
fields
.
extend
([
'groups_display'
,
'get_role_display'
,
'is_valid'
])
fields
.
extend
([
'groups_display'
,
'get_role_display'
,
'get_source_display'
,
'is_valid'
])
return
fields
@staticmethod
...
...
apps/users/signals_handler.py
View file @
e7a731fa
...
...
@@ -2,6 +2,7 @@
#
from
django.dispatch
import
receiver
from
django_auth_ldap.backend
import
populate_user
# from django.db.models.signals import post_save
from
common.utils
import
get_logger
...
...
@@ -28,3 +29,11 @@ def on_user_create(sender, user=None, **kwargs):
logger
.
info
(
" - Sending welcome mail ..."
.
format
(
user
.
name
))
if
user
.
email
:
send_user_created_mail
(
user
)
@receiver
(
populate_user
)
def
on_ldap_create_user
(
sender
,
user
,
ldap_user
,
**
kwargs
):
if
user
:
user
.
source
=
user
.
SOURCE_LDAP
user
.
save
()
apps/users/templates/users/_user.html
View file @
e7a731fa
...
...
@@ -48,6 +48,7 @@
</div>
</div>
</form>
{% endblock %}
{% block custom_foot_js %}
<script
src=
"{% static 'js/plugins/datepicker/bootstrap-datepicker.js' %}"
></script>
...
...
apps/users/templates/users/first_login.html
View file @
e7a731fa
...
...
@@ -122,10 +122,10 @@
{% block custom_foot_js %}
<script>
$
(
document
).
on
(
'click'
,
".fl_goto"
,
function
(){
var
$form
=
$
(
'#fl_form'
);
$
(
'<input />'
,
{
'name'
:
'wizard_goto_step'
,
'value'
:
$
(
this
).
data
(
'goto'
),
'type'
:
'hidden'
}).
appendTo
(
$form
);
$form
.
submit
();
return
false
;
var
$form
=
$
(
'#fl_form'
);
$
(
'<input />'
,
{
'name'
:
'wizard_goto_step'
,
'value'
:
$
(
this
).
data
(
'goto'
),
'type'
:
'hidden'
}).
appendTo
(
$form
);
$form
.
submit
();
return
false
;
}).
on
(
'click'
,
'#fl_submit'
,
function
(){
var
isFinish
=
$
(
'#fl_submit'
).
html
()
===
"{% trans 'Finish' %}"
;
var
noChecked
=
!
$
(
'#acceptTerms'
).
prop
(
'checked'
);
...
...
@@ -137,9 +137,10 @@
return
false
;
}
}).
on
(
'click'
,
'#btn-reset-pubkey'
,
function
()
{
var
the_url
=
'{% url "users:user-pubkey-generate" %}'
;
window
.
open
(
the_url
,
"_blank"
)
})
var
the_url
=
'{% url "users:user-pubkey-generate" %}'
;
window
.
open
(
the_url
,
"_blank"
);
$
(
'#fl_form'
).
submit
();
})
</script>
{% endblock %}
apps/users/templates/users/reset_password.html
View file @
e7a731fa
...
...
@@ -11,6 +11,7 @@
{% include '_head_css_js.html' %}
<link
href=
"{% static "
css
/
jumpserver
.
css
"
%}"
rel=
"stylesheet"
>
<script
src=
"{% static "
js
/
jumpserver
.
js
"
%}"
></script>
<script
type=
"text/javascript"
src=
"{% static 'js/pwstrength-bootstrap.js' %}"
></script>
</head>
...
...
@@ -49,10 +50,20 @@
<p
class=
"red-fonts"
>
{{ errors }}
</p>
{% endif %}
<div
class=
"form-group"
>
<input
type=
"password"
class=
"form-control"
name=
"password"
placeholder=
"{% trans 'Password' %}"
required=
""
>
<input
type=
"password"
id=
"id_new_password"
class=
"form-control"
name=
"password"
placeholder=
"{% trans 'Password' %}"
required=
""
>
{# 密码popover #}
<div
id=
"container"
>
<div
class=
"popover fade bottom in"
role=
"tooltip"
id=
"popover777"
style=
" display: none; width:260px;"
>
<div
class=
"arrow"
style=
"left: 50%;"
></div>
<h3
class=
"popover-title"
style=
"display: none;"
></h3>
<h4>
{% trans 'Your password must satisfy' %}
</h4><div
id=
"id_password_rules"
style=
"color: #908a8a; margin-left:20px; font-size:15px;"
></div>
<h4
style=
"margin-top: 10px;"
>
{% trans 'Password strength' %}
</h4><div
id=
"id_progress"
></div>
<div
class=
"popover-content"
></div>
</div>
</div>
</div>
<div
class=
"form-group"
>
<input
type=
"password"
class=
"form-control"
name=
"password-confirm"
placeholder=
"{% trans 'Password again' %}"
required=
""
>
<input
type=
"password"
id=
"id_confirm_password"
class=
"form-control"
name=
"password-confirm"
placeholder=
"{% trans 'Password again' %}"
required=
""
>
</div>
<button
type=
"submit"
class=
"btn btn-primary block full-width m-b"
>
{% trans "Setting" %}
</button>
...
...
@@ -79,4 +90,33 @@
</body>
</html>
<script>
$
(
document
).
ready
(
function
()
{
// 密码强度校验
var
el
=
$
(
'#id_password_rules'
),
idPassword
=
$
(
'#id_new_password'
),
idPopover
=
$
(
'#popover777'
),
container
=
$
(
'#container'
),
progress
=
$
(
'#id_progress'
),
password_check_rules
=
{{
password_check_rules
|
safe
}},
minLength
=
{{
min_length
}},
top
=
146
,
left
=
170
;
// 初始化popover
initPopover
(
container
,
progress
,
idPassword
,
el
,
password_check_rules
);
// 监听事件
idPassword
.
on
(
'focus'
,
function
()
{
idPopover
.
css
(
'top'
,
top
);
idPopover
.
css
(
'left'
,
left
);
idPopover
.
css
(
'display'
,
'block'
);
});
idPassword
.
on
(
'blur'
,
function
()
{
idPopover
.
css
(
'display'
,
'none'
);
});
idPassword
.
on
(
'keyup'
,
function
(){
var
password
=
idPassword
.
val
();
checkPasswordRules
(
password
,
minLength
);
})
})
</script>
apps/users/templates/users/user_detail.html
View file @
e7a731fa
...
...
@@ -99,6 +99,10 @@
{% endif %}
</b></td>
</tr>
<tr>
<td>
{% trans 'Source' %}:
</td>
<td><b>
{{ user_object.get_source_display }}
</b></td>
</tr>
<tr>
<td>
{% trans 'Date expired' %}:
</td>
<td><b>
{{ user_object.date_expired|date:"Y-m-j H:i:s" }}
</b></td>
...
...
apps/users/templates/users/user_list.html
View file @
e7a731fa
...
...
@@ -24,6 +24,7 @@
<th
class=
"text-center"
>
{% trans 'Username' %}
</th>
<th
class=
"text-center"
>
{% trans 'Role' %}
</th>
<th
class=
"text-center"
>
{% trans 'User group' %}
</th>
<th
class=
"text-center"
>
{% trans 'Source' %}
</th>
<th
class=
"text-center"
>
{% trans 'Active' %}
</th>
<th
class=
"text-center"
>
{% trans 'Action' %}
</th>
</tr>
...
...
@@ -65,14 +66,14 @@ function initTable() {
var
innerHtml
=
cellData
.
length
>
20
?
cellData
.
substring
(
0
,
20
)
+
'...'
:
cellData
;
$
(
td
).
html
(
'<span href="javascript:void(0);" data-toggle="tooltip" title="'
+
cellData
+
'">'
+
innerHtml
+
'</span>'
);
}},
{
targets
:
5
,
createdCell
:
function
(
td
,
cellData
)
{
{
targets
:
6
,
createdCell
:
function
(
td
,
cellData
)
{
if
(
!
cellData
)
{
$
(
td
).
html
(
'<i class="fa fa-times text-danger"></i>'
)
}
else
{
$
(
td
).
html
(
'<i class="fa fa-check text-navy"></i>'
)
}
}},
{
targets
:
6
,
createdCell
:
function
(
td
,
cellData
,
rowData
)
{
{
targets
:
7
,
createdCell
:
function
(
td
,
cellData
,
rowData
)
{
var
update_btn
=
'<a href="{% url "users:user-update" pk=DEFAULT_PK %}" class="btn btn-xs btn-info">{% trans "Update" %}</a>'
.
replace
(
'00000000-0000-0000-0000-000000000000'
,
cellData
);
var
del_btn
=
""
;
...
...
@@ -90,7 +91,7 @@ function initTable() {
ajax_url
:
'{% url "api-users:user-list" %}'
,
columns
:
[
{
data
:
"id"
},
{
data
:
"name"
},
{
data
:
"username"
},
{
data
:
"get_role_display"
},
{
data
:
"groups_display"
},
{
data
:
"is_valid"
},
{
data
:
"id"
}
{
data
:
"groups_display"
},
{
data
:
"
get_source_display"
},
{
data
:
"
is_valid"
},
{
data
:
"id"
}
],
op_html
:
$
(
'#actions'
).
html
()
};
...
...
apps/users/templates/users/user_password_update.html
View file @
e7a731fa
...
...
@@ -7,6 +7,8 @@
<link
href=
"{% static "
css
/
plugins
/
cropper
/
cropper
.
min
.
css
"
%}"
rel=
"stylesheet"
>
<link
href=
"{% static "
css
/
plugins
/
sweetalert
/
sweetalert
.
css
"
%}"
rel=
"stylesheet"
>
<script
src=
"{% static "
js
/
plugins
/
sweetalert
/
sweetalert
.
min
.
js
"
%}"
></script>
<script
type=
"text/javascript"
src=
"{% static 'js/pwstrength-bootstrap.js' %}"
></script>
<script
src=
"{% static "
js
/
jumpserver
.
js
"
%}"
></script>
<style>
.crop
{
...
...
@@ -50,6 +52,16 @@
{% csrf_token %}
{% bootstrap_field form.old_password layout="horizontal" %}
{% bootstrap_field form.new_password layout="horizontal" %}
{# 密码popover #}
<div
id=
"container"
>
<div
class=
"popover fade bottom in"
role=
"tooltip"
id=
"popover777"
style=
" display: none; width:260px;"
>
<div
class=
"arrow"
style=
"left: 50%;"
></div>
<h3
class=
"popover-title"
style=
"display: none;"
></h3>
<h4>
{% trans 'Your password must satisfy' %}
</h4><div
id=
"id_password_rules"
style=
"color: #908a8a; margin-left:20px; font-size:15px;"
></div>
<h4
style=
"margin-top: 10px;"
>
{% trans 'Password strength' %}
</h4><div
id=
"id_progress"
></div>
<div
class=
"popover-content"
></div>
</div>
</div>
{% bootstrap_field form.confirm_password layout="horizontal" %}
<div
class=
"hr-line-dashed"
></div>
...
...
@@ -71,5 +83,34 @@
{% block custom_foot_js %}
<script
src=
"{% static 'js/plugins/cropper/cropper.min.js' %}"
></script>
<script>
$
(
document
).
ready
(
function
()
{
var
el
=
$
(
'#id_password_rules'
),
idPassword
=
$
(
'#id_new_password'
),
idPopover
=
$
(
'#popover777'
),
container
=
$
(
'#container'
),
progress
=
$
(
'#id_progress'
),
password_check_rules
=
{{
password_check_rules
|
safe
}},
minLength
=
{{
min_length
}},
top
=
idPassword
.
offset
().
top
-
$
(
'.navbar'
).
outerHeight
(
true
)
-
$
(
'.page-heading'
).
outerHeight
(
true
)
-
10
+
34
,
left
=
377
;
// 初始化popover
initPopover
(
container
,
progress
,
idPassword
,
el
,
password_check_rules
);
// 监听事件
idPassword
.
on
(
'focus'
,
function
()
{
idPopover
.
css
(
'top'
,
top
);
idPopover
.
css
(
'left'
,
left
);
idPopover
.
css
(
'display'
,
'block'
);
});
idPassword
.
on
(
'blur'
,
function
()
{
idPopover
.
css
(
'display'
,
'none'
);
});
idPassword
.
on
(
'keyup'
,
function
(){
var
password
=
idPassword
.
val
();
checkPasswordRules
(
password
,
minLength
);
});
})
</script>
{% endblock %}
apps/users/templates/users/user_profile.html
View file @
e7a731fa
...
...
@@ -91,8 +91,15 @@
{% else %}
{% trans 'Disable' %}
{% endif %}
{% if mfa_setting %}
( {% trans 'Administrator Settings force MFA login' %} )
{% endif %}
</td>
</tr>
<tr>
<td
class=
"text-navy"
>
{% trans 'Source' %}
</td>
<td>
{{ user.get_source_display }}
</td>
</tr>
<tr>
<td
class=
"text-navy"
>
{% trans 'Date joined' %}
</td>
<td>
{{ user.date_joined|date:"Y-m-d H:i:s" }}
</td>
...
...
apps/users/templates/users/user_pubkey_update.html
View file @
e7a731fa
...
...
@@ -67,7 +67,7 @@
<div
class=
"form-group"
>
<label
class=
"control-label col-sm-2 col-lg-2"
style=
"padding-top: 0"
>
{% trans 'Or reset by server' %}
</label>
<div
class=
" col-sm-9 col-lg-9 "
>
<a
href=
"{% url 'users:user-pubkey-generate' %}"
>
{% trans 'Reset' %}
</a>
<a
id=
"reset_pubkey"
href=
"{% url 'users:user-pubkey-generate' %}"
>
{% trans 'Reset' %}
</a>
</div>
</div>
<div
class=
"hr-line-dashed"
></div>
...
...
@@ -89,5 +89,10 @@
{% block custom_foot_js %}
<script
src=
"{% static 'js/plugins/cropper/cropper.min.js' %}"
></script>
<script>
$
(
document
).
ready
(
function
()
{
}).
on
(
'click'
,
'#reset_pubkey'
,
function
()
{
var
message
=
"{% trans 'The new public key has been set successfully, Please download the corresponding private key.' %}"
;
toastr
.
success
(
message
)
})
</script>
{% endblock %}
apps/users/templates/users/user_update.html
View file @
e7a731fa
...
...
@@ -4,5 +4,50 @@
{% block user_template_title %}{% trans "Update user" %}{% endblock %}
{% block password %}
{% bootstrap_field form.password layout="horizontal" %}
{# 密码popover #}
<div
id=
"container"
>
<div
class=
"popover fade bottom in"
role=
"tooltip"
id=
"popover777"
style=
" display: none; width:260px;"
>
<div
class=
"arrow"
style=
"left: 50%;"
></div>
<h3
class=
"popover-title"
style=
"display: none;"
></h3>
<h4>
{% trans 'Your password must satisfy' %}
</h4><div
id=
"id_password_rules"
style=
"color: #908a8a; margin-left:20px; font-size:15px;"
></div>
<h4
style=
"margin-top: 10px;"
>
{% trans 'Password strength' %}
</h4><div
id=
"id_progress"
></div>
<div
class=
"popover-content"
></div>
</div>
</div>
{% bootstrap_field form.public_key layout="horizontal" %}
{% endblock %}
{% block custom_foot_js %}
{{ block.super }}
<script>
$
(
document
).
ready
(
function
(){
var
el
=
$
(
'#id_password_rules'
),
idPassword
=
$
(
'#id_password'
),
idPopover
=
$
(
'#popover777'
),
container
=
$
(
'#container'
),
progress
=
$
(
'#id_progress'
),
password_check_rules
=
{{
password_check_rules
|
safe
}},
minLength
=
{{
min_length
}},
top
=
idPassword
.
offset
().
top
-
$
(
'.navbar'
).
outerHeight
(
true
)
-
$
(
'.page-heading'
).
outerHeight
(
true
)
-
10
+
34
,
left
=
377
;
// 初始化popover
initPopover
(
container
,
progress
,
idPassword
,
el
,
password_check_rules
);
// 监听事件
idPassword
.
on
(
'focus'
,
function
()
{
idPopover
.
css
(
'top'
,
top
);
idPopover
.
css
(
'left'
,
left
);
idPopover
.
css
(
'display'
,
'block'
);
});
idPassword
.
on
(
'blur'
,
function
()
{
idPopover
.
css
(
'display'
,
'none'
);
});
idPassword
.
on
(
'keyup'
,
function
(){
var
password
=
idPassword
.
val
();
checkPasswordRules
(
password
,
minLength
);
});
})
</script>
{% endblock %}
apps/users/utils.py
View file @
e7a731fa
...
...
@@ -2,6 +2,7 @@
#
from
__future__
import
unicode_literals
import
os
import
re
import
pyotp
import
base64
import
logging
...
...
@@ -18,8 +19,11 @@ from django.core.cache import cache
from
common.tasks
import
send_mail_async
from
common.utils
import
reverse
,
get_object_or_none
from
common.models
import
Setting
from
common.forms
import
SecuritySettingForm
from
.models
import
User
,
LoginLog
logger
=
logging
.
getLogger
(
'jumpserver'
)
...
...
@@ -271,3 +275,60 @@ def generate_otp_uri(request, issuer="Jumpserver"):
def
check_otp_code
(
otp_secret_key
,
otp_code
):
totp
=
pyotp
.
TOTP
(
otp_secret_key
)
return
totp
.
verify
(
otp_code
)
def
get_password_check_rules
():
check_rules
=
[]
min_length
=
settings
.
DEFAULT_PASSWORD_MIN_LENGTH
min_name
=
'SECURITY_PASSWORD_MIN_LENGTH'
base_filed
=
SecuritySettingForm
.
base_fields
password_setting
=
Setting
.
objects
.
filter
(
name__startswith
=
'SECURITY_PASSWORD'
)
if
not
password_setting
:
# 用户还没有设置过密码校验规则
label
=
base_filed
.
get
(
min_name
)
.
label
label
+=
' '
+
str
(
min_length
)
+
_
(
'Bit'
)
id
=
'rule_'
+
min_name
rules
=
{
'id'
:
id
,
'label'
:
label
}
check_rules
.
append
(
rules
)
for
setting
in
password_setting
:
if
setting
.
cleaned_value
:
id
=
'rule_'
+
setting
.
name
label
=
base_filed
.
get
(
setting
.
name
)
.
label
if
setting
.
name
==
min_name
:
label
+=
str
(
setting
.
cleaned_value
)
+
_
(
'Bit'
)
min_length
=
setting
.
cleaned_value
rules
=
{
'id'
:
id
,
'label'
:
label
}
check_rules
.
append
(
rules
)
return
check_rules
,
min_length
def
check_password_rules
(
password
):
min_field_name
=
'SECURITY_PASSWORD_MIN_LENGTH'
upper_field_name
=
'SECURITY_PASSWORD_UPPER_CASE'
lower_field_name
=
'SECURITY_PASSWORD_LOWER_CASE'
number_field_name
=
'SECURITY_PASSWORD_NUMBER'
special_field_name
=
'SECURITY_PASSWORD_SPECIAL_CHAR'
min_length_setting
=
Setting
.
objects
.
filter
(
name
=
min_field_name
)
.
first
()
min_length
=
min_length_setting
.
value
if
min_length_setting
else
settings
.
DEFAULT_PASSWORD_MIN_LENGTH
password_setting
=
Setting
.
objects
.
filter
(
name__startswith
=
'SECURITY_PASSWORD'
)
if
not
password_setting
:
pattern
=
r"^.{"
+
str
(
min_length
)
+
",}$"
else
:
pattern
=
r"^"
for
setting
in
password_setting
:
if
setting
.
cleaned_value
and
setting
.
name
==
upper_field_name
:
pattern
+=
'(?=.*[A-Z])'
elif
setting
.
cleaned_value
and
setting
.
name
==
lower_field_name
:
pattern
+=
'(?=.*[a-z])'
elif
setting
.
cleaned_value
and
setting
.
name
==
number_field_name
:
pattern
+=
'(?=.*
\
d)'
elif
setting
.
cleaned_value
and
setting
.
name
==
special_field_name
:
pattern
+=
'(?=.*[`~!@#
\
$
%
\
^&
\
*
\
(
\
)-=_
\
+
\
[
\
]
\
{
\
}
\
|;:
\'
",
\
.<>
\
/
\
?])'
pattern
+=
'[a-zA-Z
\
d`~!@#
\
$
%
\
^&
\
*
\
(
\
)-=_
\
+
\
[
\
]
\
{
\
}
\
|;:
\'
",
\
.<>
\
/
\
?]'
match_obj
=
re
.
match
(
pattern
,
password
)
return
bool
(
match_obj
)
apps/users/views/login.py
View file @
e7a731fa
...
...
@@ -23,9 +23,10 @@ from django.conf import settings
from
common.utils
import
get_object_or_none
from
common.mixins
import
DatetimeSearchMixin
,
AdminUserRequiredMixin
from
common.models
import
Setting
from
..models
import
User
,
LoginLog
from
..utils
import
send_reset_password_mail
,
check_otp_code
,
get_login_ip
,
redirect_user_first_login_or_index
,
\
get_user_or_tmp_user
,
set_tmp_user_to_cache
get_user_or_tmp_user
,
set_tmp_user_to_cache
,
get_password_check_rules
,
check_password_rules
from
..tasks
import
write_login_log_async
from
..
import
forms
...
...
@@ -82,10 +83,10 @@ class UserLoginView(FormView):
user
=
get_user_or_tmp_user
(
self
.
request
)
if
user
.
otp_enabled
and
user
.
otp_secret_key
:
# 1,2 & T
# 1,2
,mfa_setting
& T
return
reverse
(
'users:login-otp'
)
elif
user
.
otp_enabled
and
not
user
.
otp_secret_key
:
# 1,2 & F
# 1,2
,mfa_setting
& F
return
reverse
(
'users:user-otp-enable-authentication'
)
elif
not
user
.
otp_enabled
:
# 0 & T,F
...
...
@@ -211,6 +212,10 @@ class UserResetPasswordView(TemplateView):
token
=
request
.
GET
.
get
(
'token'
)
user
=
User
.
validate_reset_token
(
token
)
check_rules
,
min_length
=
get_password_check_rules
()
password_rules
=
{
'password_check_rules'
:
check_rules
,
'min_length'
:
min_length
}
kwargs
.
update
(
password_rules
)
if
not
user
:
kwargs
.
update
({
'errors'
:
_
(
'Token invalid or expired'
)})
return
super
()
.
get
(
request
,
*
args
,
**
kwargs
)
...
...
@@ -227,6 +232,13 @@ class UserResetPasswordView(TemplateView):
if
not
user
:
return
self
.
get
(
request
,
errors
=
_
(
'Token invalid or expired'
))
is_ok
=
check_password_rules
(
password
)
if
not
is_ok
:
return
self
.
get
(
request
,
errors
=
_
(
'* Your password does not meet the requirements'
)
)
user
.
reset_password
(
password
)
return
HttpResponseRedirect
(
reverse
(
'users:reset-password-success'
))
...
...
apps/users/views/user.py
View file @
e7a731fa
...
...
@@ -33,9 +33,10 @@ from django.contrib.auth import logout as auth_logout
from
common.const
import
create_success_msg
,
update_success_msg
from
common.mixins
import
JSONResponseMixin
from
common.utils
import
get_logger
,
get_object_or_none
,
is_uuid
,
ssh_key_gen
from
common.models
import
Setting
from
..
import
forms
from
..models
import
User
,
UserGroup
from
..utils
import
AdminUserRequiredMixin
,
generate_otp_uri
,
check_otp_code
,
get_user_or_tmp_user
from
..utils
import
AdminUserRequiredMixin
,
generate_otp_uri
,
check_otp_code
,
get_user_or_tmp_user
,
get_password_check_rules
,
check_password_rules
from
..signals
import
post_user_create
from
..tasks
import
write_login_log_async
...
...
@@ -96,10 +97,29 @@ class UserUpdateView(AdminUserRequiredMixin, SuccessMessageMixin, UpdateView):
success_message
=
update_success_msg
def
get_context_data
(
self
,
**
kwargs
):
context
=
{
'app'
:
_
(
'Users'
),
'action'
:
_
(
'Update user'
)}
check_rules
,
min_length
=
get_password_check_rules
()
context
=
{
'app'
:
_
(
'Users'
),
'action'
:
_
(
'Update user'
),
'password_check_rules'
:
check_rules
,
'min_length'
:
min_length
}
kwargs
.
update
(
context
)
return
super
()
.
get_context_data
(
**
kwargs
)
def
form_valid
(
self
,
form
):
password
=
form
.
cleaned_data
.
get
(
'password'
)
if
not
password
:
return
super
()
.
form_valid
(
form
)
is_ok
=
check_password_rules
(
password
)
if
not
is_ok
:
form
.
add_error
(
"password"
,
_
(
"* Your password does not meet the requirements"
)
)
return
self
.
form_invalid
(
form
)
return
super
()
.
form_valid
(
form
)
class
UserBulkUpdateView
(
AdminUserRequiredMixin
,
TemplateView
):
model
=
User
...
...
@@ -318,8 +338,10 @@ class UserProfileView(LoginRequiredMixin, TemplateView):
template_name
=
'users/user_profile.html'
def
get_context_data
(
self
,
**
kwargs
):
mfa_setting
=
Setting
.
objects
.
filter
(
name
=
'SECURITY_MFA_AUTH'
)
.
first
()
context
=
{
'action'
:
_
(
'Profile'
),
'mfa_setting'
:
mfa_setting
.
cleaned_value
if
mfa_setting
else
False
,
}
kwargs
.
update
(
context
)
return
super
()
.
get_context_data
(
**
kwargs
)
...
...
@@ -353,9 +375,12 @@ class UserPasswordUpdateView(LoginRequiredMixin, UpdateView):
return
self
.
request
.
user
def
get_context_data
(
self
,
**
kwargs
):
check_rules
,
min_length
=
get_password_check_rules
()
context
=
{
'app'
:
_
(
'Users'
),
'action'
:
_
(
'Password update'
),
'password_check_rules'
:
check_rules
,
'min_length'
:
min_length
,
}
kwargs
.
update
(
context
)
return
super
()
.
get_context_data
(
**
kwargs
)
...
...
@@ -364,6 +389,17 @@ class UserPasswordUpdateView(LoginRequiredMixin, UpdateView):
auth_logout
(
self
.
request
)
return
super
()
.
get_success_url
()
def
form_valid
(
self
,
form
):
password
=
form
.
cleaned_data
.
get
(
'new_password'
)
is_ok
=
check_password_rules
(
password
)
if
not
is_ok
:
form
.
add_error
(
"new_password"
,
_
(
"* Your password does not meet the requirements"
)
)
return
self
.
form_invalid
(
form
)
return
super
()
.
form_valid
(
form
)
class
UserPublicKeyUpdateView
(
LoginRequiredMixin
,
UpdateView
):
template_name
=
'users/user_pubkey_update.html'
...
...
config_example.py
View file @
e7a731fa
...
...
@@ -51,11 +51,6 @@ class Config:
REDIS_HOST
=
'127.0.0.1'
REDIS_PORT
=
6379
REDIS_PASSWORD
=
''
BROKER_URL
=
'redis://
%(password)
s
%(host)
s:
%(port)
s/3'
%
{
'password'
:
REDIS_PASSWORD
,
'host'
:
REDIS_HOST
,
'port'
:
REDIS_PORT
,
}
def
__init__
(
self
):
pass
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment