From c1db33713f68de225b38d0f8910e4cae47269993 Mon Sep 17 00:00:00 2001 From: BaiJiangJie Date: Wed, 16 May 2018 16:12:27 +0800 Subject: [PATCH 01/19] =?UTF-8?q?[Bugfix]=20=E4=BF=AE=E5=A4=8D=E8=B5=84?= =?UTF-8?q?=E4=BA=A7=E6=8E=88=E6=9D=83=E6=A0=91root=E8=8A=82=E7=82=B9bug?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/assets/api/node.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/apps/assets/api/node.py b/apps/assets/api/node.py index b0e974f5c..a6d00b44a 100644 --- a/apps/assets/api/node.py +++ b/apps/assets/api/node.py @@ -123,7 +123,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 = Node.root() + node = None else: node = get_object_or_404(Node, pk=pk) return node @@ -133,7 +133,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() @@ -184,8 +185,6 @@ class NodeAddChildrenApi(generics.UpdateAPIView): for node in children: if not node: continue - # node.parent = instance - # node.save() node.set_parent(instance) return Response("OK") From 486793ddcddfc1884f9c6e9d913fe57414191d36 Mon Sep 17 00:00:00 2001 From: BaiJiangJie Date: Thu, 24 May 2018 12:41:48 +0800 Subject: [PATCH 02/19] =?UTF-8?q?[Bugfix]=20=E4=BF=AE=E5=A4=8D=E5=8F=96?= =?UTF-8?q?=E6=B6=88LDAP=E8=AE=A4=E8=AF=81=E4=B8=8D=E6=88=90=E5=8A=9Fbug?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/common/signals_handler.py | 2 +- apps/common/views.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/common/signals_handler.py b/apps/common/signals_handler.py index df2ea5a5e..bff3a2193 100644 --- a/apps/common/signals_handler.py +++ b/apps/common/signals_handler.py @@ -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.AUTHENTICATION_BACKENDS: settings.AUTHENTICATION_BACKENDS.insert(0, settings.AUTH_LDAP_BACKEND) else: diff --git a/apps/common/views.py b/apps/common/views.py index ee7a2225f..c701df407 100644 --- a/apps/common/views.py +++ b/apps/common/views.py @@ -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') From ee35ca364310ce550525d1dc2820ab6d429aee06 Mon Sep 17 00:00:00 2001 From: BaiJiangJie Date: Tue, 5 Jun 2018 17:26:31 +0800 Subject: [PATCH 03/19] =?UTF-8?q?[Feature]=20=E5=A2=9E=E5=8A=A0=E5=8A=9F?= =?UTF-8?q?=E8=83=BD=EF=BC=8C=E5=AE=89=E5=85=A8=E8=AE=BE=E7=BD=AE=EF=BC=88?= =?UTF-8?q?=E5=85=A8=E5=B1=80MFA=E8=AE=BE=E7=BD=AE=EF=BC=8C=E5=AF=86?= =?UTF-8?q?=E7=A0=81=E5=BC=BA=E5=BA=A6=E6=A0=A1=E9=AA=8C=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/common/forms.py | 45 + .../templates/common/basic_setting.html | 3 + .../templates/common/email_setting.html | 3 + .../common/templates/common/ldap_setting.html | 3 + .../templates/common/security_setting.html | 115 +++ .../templates/common/terminal_setting.html | 5 + apps/common/urls/view_urls.py | 1 + apps/common/views.py | 26 +- apps/i18n/zh/LC_MESSAGES/django.mo | Bin 32508 -> 34391 bytes apps/i18n/zh/LC_MESSAGES/django.po | 419 +++++--- apps/jumpserver/settings.py | 3 + apps/static/js/jumpserver.js | 88 ++ apps/static/js/pwstrength-bootstrap.js | 976 ++++++++++++++++++ apps/templates/_base_create_update.html | 1 + apps/users/forms.py | 2 +- apps/users/templates/users/_user.html | 1 + .../users/templates/users/reset_password.html | 44 +- .../templates/users/user_password_update.html | 41 + apps/users/templates/users/user_profile.html | 5 +- apps/users/templates/users/user_update.html | 45 + apps/users/utils.py | 61 ++ apps/users/views/login.py | 43 +- apps/users/views/user.py | 39 +- 23 files changed, 1791 insertions(+), 178 deletions(-) create mode 100644 apps/common/templates/common/security_setting.html create mode 100755 apps/static/js/pwstrength-bootstrap.js diff --git a/apps/common/forms.py b/apps/common/forms.py index 02af6e47a..4f4eefc23 100644 --- a/apps/common/forms.py +++ b/apps/common/forms.py @@ -168,3 +168,48 @@ 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"), + ) + # 大写字母 + 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') + ) diff --git a/apps/common/templates/common/basic_setting.html b/apps/common/templates/common/basic_setting.html index 496eca977..9c9258e33 100644 --- a/apps/common/templates/common/basic_setting.html +++ b/apps/common/templates/common/basic_setting.html @@ -23,6 +23,9 @@
  • {% trans 'Terminal setting' %}
  • +
  • + {% trans 'Security setting' %} +
  • diff --git a/apps/common/templates/common/email_setting.html b/apps/common/templates/common/email_setting.html index 1fd772db1..2f0951e00 100644 --- a/apps/common/templates/common/email_setting.html +++ b/apps/common/templates/common/email_setting.html @@ -23,6 +23,9 @@
  • {% trans 'Terminal setting' %}
  • +
  • + {% trans 'Security setting' %} +
  • diff --git a/apps/common/templates/common/ldap_setting.html b/apps/common/templates/common/ldap_setting.html index f0569f873..e55da5a8f 100644 --- a/apps/common/templates/common/ldap_setting.html +++ b/apps/common/templates/common/ldap_setting.html @@ -23,6 +23,9 @@
  • {% trans 'Terminal setting' %}
  • +
  • + {% trans 'Security setting' %} +
  • diff --git a/apps/common/templates/common/security_setting.html b/apps/common/templates/common/security_setting.html new file mode 100644 index 000000000..d2c436304 --- /dev/null +++ b/apps/common/templates/common/security_setting.html @@ -0,0 +1,115 @@ +{% extends 'base.html' %} +{% load static %} +{% load bootstrap3 %} +{% load i18n %} +{% load common_tags %} + +{% block content %} +
    +
    +
    +
    + +
    +
    +
    +
    + {% if form.non_field_errors %} +
    + {{ form.non_field_errors }} +
    + {% endif %} + {% csrf_token %} + +

    {% trans "MFA setting" %}

    + {% for field in form %} + {% if forloop.counter == 2 %} +
    +

    {% trans "Password check rule" %}

    + {% endif %} + + {% if not field.field|is_bool_field %} + {% bootstrap_field field layout="horizontal" %} + {% else %} +
    + +
    +
    + {{ field }} +
    +
    + {{ field.help_text }} +
    +
    +
    + {% endif %} + {% endfor %} + +
    + +
    +
    + + +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +{% endblock %} +{% block custom_foot_js %} + +{% endblock %} diff --git a/apps/common/templates/common/terminal_setting.html b/apps/common/templates/common/terminal_setting.html index 16927c05a..320f628b0 100644 --- a/apps/common/templates/common/terminal_setting.html +++ b/apps/common/templates/common/terminal_setting.html @@ -27,6 +27,9 @@ {% trans 'Terminal setting' %} +
  • + {% trans 'Security setting' %} +
  • @@ -39,6 +42,7 @@
    {% endif %} {% csrf_token %} +

    {% trans "Basic setting" %}

    {% for field in form %} {% if not field.field|is_bool_field %} @@ -60,6 +64,7 @@ {% endfor %}
    +

    {% trans "Command storage" %}

    diff --git a/apps/common/urls/view_urls.py b/apps/common/urls/view_urls.py index 466f7c49c..e7ccddd06 100644 --- a/apps/common/urls/view_urls.py +++ b/apps/common/urls/view_urls.py @@ -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'), ] diff --git a/apps/common/views.py b/apps/common/views.py index c701df407..6a7d37f49 100644 --- a/apps/common/views.py +++ b/apps/common/views.py @@ -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 @@ -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) diff --git a/apps/i18n/zh/LC_MESSAGES/django.mo b/apps/i18n/zh/LC_MESSAGES/django.mo index b764117a297c394ce044245ec712ffa3ee379e26..78705cf79573f6cf34877f0304ae523bc3b6dafc 100644 GIT binary patch delta 13328 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 delta 11582 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 diff --git a/apps/i18n/zh/LC_MESSAGES/django.po b/apps/i18n/zh/LC_MESSAGES/django.po index 31654aa11..eb42e1bef 100644 --- a/apps/i18n/zh/LC_MESSAGES/django.po +++ b/apps/i18n/zh/LC_MESSAGES/django.po @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: Jumpserver 0.3.3\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2018-05-25 18:11+0800\n" +"POT-Creation-Date: 2018-06-05 17:03+0800\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: ibuler \n" "Language-Team: Jumpserver team\n" @@ -21,15 +21,15 @@ msgstr "" msgid "New node {}" msgstr "新节点 {}" -#: assets/api/node.py:242 +#: assets/api/node.py:241 msgid "更新节点资产硬件信息: {}" msgstr "" -#: assets/api/node.py:255 +#: assets/api/node.py:254 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:74 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:70 +#: assets/forms/asset.py:113 assets/models/asset.py:79 #: 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:74 +#: assets/templates/assets/asset_list.html:75 #: 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:70 #: 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:63 +#: assets/forms/asset.py:105 assets/models/asset.py:66 #: 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:183 assets/templates/assets/admin_user_list.html:25 +#: assets/models/asset.py:229 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,8 +129,8 @@ 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 @@ -171,10 +171,10 @@ 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:52 +#: users/templates/users/reset_password.html:53 #: users/templates/users/user_create.html:10 #: users/templates/users/user_password_authentication.html:14 -#: users/templates/users/user_password_update.html:40 +#: users/templates/users/user_password_update.html:42 #: users/templates/users/user_profile_update.html:40 #: users/templates/users/user_pubkey_update.html:40 msgid "Password" @@ -202,11 +202,11 @@ msgid "" "than 2 system user" msgstr "高优先级的系统用户将会作为默认登录用户" -#: assets/models/asset.py:61 assets/models/domain.py:43 +#: assets/models/asset.py:62 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:86 +#: assets/templates/assets/asset_list.html:87 #: 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:62 assets/templates/assets/_asset_list_modal.html:45 +#: assets/models/asset.py:65 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:85 +#: assets/templates/assets/asset_list.html:86 #: 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:64 assets/templates/assets/asset_detail.html:97 +#: assets/models/asset.py:68 assets/templates/assets/asset_detail.html:97 msgid "Platform" msgstr "系统平台" -#: assets/models/asset.py:67 assets/models/domain.py:48 +#: assets/models/asset.py:75 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:84 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:86 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:90 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:92 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:94 assets/templates/assets/asset_detail.html:109 msgid "Serial number" msgstr "序列号" -#: assets/models/asset.py:81 +#: assets/models/asset.py:97 msgid "CPU model" msgstr "CPU型号" -#: assets/models/asset.py:82 +#: assets/models/asset.py:98 msgid "CPU count" msgstr "CPU数量" -#: assets/models/asset.py:83 +#: assets/models/asset.py:99 msgid "CPU cores" msgstr "CPU核数" -#: assets/models/asset.py:84 assets/templates/assets/asset_detail.html:89 +#: assets/models/asset.py:101 assets/templates/assets/asset_detail.html:89 msgid "Memory" msgstr "内存" -#: assets/models/asset.py:85 +#: assets/models/asset.py:103 msgid "Disk total" msgstr "硬盘大小" -#: assets/models/asset.py:86 +#: assets/models/asset.py:105 msgid "Disk info" msgstr "硬盘信息" -#: assets/models/asset.py:88 assets/templates/assets/asset_detail.html:101 +#: assets/models/asset.py:108 assets/templates/assets/asset_detail.html:101 msgid "OS" msgstr "操作系统" -#: assets/models/asset.py:89 +#: assets/models/asset.py:110 msgid "OS version" msgstr "系统版本" -#: assets/models/asset.py:90 +#: assets/models/asset.py:112 msgid "OS arch" msgstr "系统架构" -#: assets/models/asset.py:91 +#: assets/models/asset.py:114 msgid "Hostname raw" msgstr "主机名原始" -#: assets/models/asset.py:93 assets/templates/assets/asset_create.html:33 +#: assets/models/asset.py:118 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:120 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 @@ -316,7 +316,7 @@ msgstr "标签管理" msgid "Created by" msgstr "创建者" -#: assets/models/asset.py:95 assets/models/cluster.py:26 +#: assets/models/asset.py:123 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 @@ -329,7 +329,7 @@ msgstr "创建者" msgid "Date created" msgstr "创建日期" -#: assets/models/asset.py:96 assets/models/base.py:26 +#: assets/models/asset.py:125 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 @@ -346,7 +346,7 @@ msgstr "创建日期" #: users/models/user.py:75 users/templates/users/user_detail.html:119 #: users/templates/users/user_group_detail.html:67 #: users/templates/users/user_group_list.html:14 -#: users/templates/users/user_profile.html:123 +#: users/templates/users/user_profile.html:126 msgid "Comment" msgstr "备注" @@ -434,11 +434,11 @@ msgstr "默认资产组" #: terminal/templates/terminal/session_list.html:71 users/forms.py:281 #: users/models/user.py:30 users/models/user.py:318 #: users/templates/users/user_group_detail.html:78 -#: users/templates/users/user_group_list.html:13 users/views/user.py:339 +#: users/templates/users/user_group_list.html:13 users/views/user.py:360 msgid "User" msgstr "用户" -#: assets/models/label.py:18 assets/models/node.py:18 +#: assets/models/label.py:18 assets/models/node.py:16 #: 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:14 +#: assets/models/node.py:15 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:181 +#: users/templates/users/user_password_update.html:70 +#: users/templates/users/user_profile.html:184 #: 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,15 +651,16 @@ 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:107 +#: assets/templates/assets/asset_list.html:108 #: 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 @@ -666,7 +668,7 @@ msgstr "重置" #: users/templates/users/forgot_password.html:44 #: users/templates/users/user_bulk_update.html:24 #: users/templates/users/user_list.html:44 -#: users/templates/users/user_password_update.html:59 +#: 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:174 +#: assets/templates/assets/asset_list.html:175 #: assets/templates/assets/domain_detail.html:24 #: assets/templates/assets/domain_detail.html:103 #: assets/templates/assets/domain_gateway_list.html:85 @@ -743,15 +745,15 @@ msgstr "测试" #: users/templates/users/user_group_detail.html:28 #: users/templates/users/user_group_list.html:43 #: users/templates/users/user_list.html:76 -#: users/templates/users/user_profile.html:144 -#: users/templates/users/user_profile.html:173 +#: users/templates/users/user_profile.html:147 +#: users/templates/users/user_profile.html:176 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:175 +#: assets/templates/assets/asset_list.html:176 #: assets/templates/assets/domain_detail.html:28 #: assets/templates/assets/domain_detail.html:104 #: assets/templates/assets/domain_gateway_list.html:86 @@ -782,7 +784,7 @@ msgstr "选择节点" #: assets/templates/assets/admin_user_detail.html:100 #: assets/templates/assets/asset_detail.html:200 -#: assets/templates/assets/asset_list.html:636 +#: assets/templates/assets/asset_list.html:639 #: 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 @@ -792,7 +794,7 @@ msgstr "选择节点" #: 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:215 +#: users/templates/users/user_profile.html:218 msgid "Confirm" msgstr "确认" @@ -814,7 +816,7 @@ msgid "Ratio" msgstr "比例" #: assets/templates/assets/admin_user_list.html:30 -#: assets/templates/assets/asset_list.html:90 +#: assets/templates/assets/asset_list.html:91 #: assets/templates/assets/domain_gateway_list.html:62 #: assets/templates/assets/domain_list.html:18 #: assets/templates/assets/label_list.html:17 @@ -843,19 +845,19 @@ msgstr "硬盘" #: assets/templates/assets/asset_detail.html:121 #: users/templates/users/user_detail.html:111 -#: users/templates/users/user_profile.html:97 +#: users/templates/users/user_profile.html:100 msgid "Date joined" msgstr "创建日期" #: assets/templates/assets/asset_detail.html:137 #: terminal/templates/terminal/session_detail.html:81 #: users/templates/users/user_detail.html:130 -#: users/templates/users/user_profile.html:135 +#: users/templates/users/user_profile.html:138 msgid "Quick modify" msgstr "快速修改" #: assets/templates/assets/asset_detail.html:143 -#: assets/templates/assets/asset_list.html:88 +#: assets/templates/assets/asset_list.html:89 #: 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 @@ -885,97 +887,97 @@ msgstr "刷新" msgid "Update successfully!" msgstr "更新成功" -#: assets/templates/assets/asset_list.html:62 assets/views/asset.py:97 +#: assets/templates/assets/asset_list.html:63 assets/views/asset.py:97 msgid "Create asset" msgstr "创建资产" -#: assets/templates/assets/asset_list.html:66 +#: assets/templates/assets/asset_list.html:67 #: 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:87 +#: assets/templates/assets/asset_list.html:88 msgid "Hardware" msgstr "硬件" -#: assets/templates/assets/asset_list.html:99 +#: assets/templates/assets/asset_list.html:100 #: users/templates/users/user_list.html:37 msgid "Delete selected" msgstr "批量删除" -#: assets/templates/assets/asset_list.html:100 +#: assets/templates/assets/asset_list.html:101 #: users/templates/users/user_list.html:38 msgid "Update selected" msgstr "批量更新" -#: assets/templates/assets/asset_list.html:101 +#: assets/templates/assets/asset_list.html:102 msgid "Remove from this node" msgstr "从节点移除" -#: assets/templates/assets/asset_list.html:102 +#: assets/templates/assets/asset_list.html:103 #: users/templates/users/user_list.html:39 msgid "Deactive selected" msgstr "禁用所选" -#: assets/templates/assets/asset_list.html:103 +#: assets/templates/assets/asset_list.html:104 #: users/templates/users/user_list.html:40 msgid "Active selected" msgstr "激活所选" -#: assets/templates/assets/asset_list.html:120 +#: assets/templates/assets/asset_list.html:121 msgid "Add node" msgstr "新建节点" -#: assets/templates/assets/asset_list.html:121 +#: assets/templates/assets/asset_list.html:122 msgid "Rename node" msgstr "重命名节点" -#: assets/templates/assets/asset_list.html:122 +#: assets/templates/assets/asset_list.html:123 msgid "Delete node" msgstr "删除节点" -#: assets/templates/assets/asset_list.html:124 +#: assets/templates/assets/asset_list.html:125 msgid "Add assets to node" msgstr "添加资产到节点" -#: assets/templates/assets/asset_list.html:125 +#: assets/templates/assets/asset_list.html:126 msgid "Move assets to node" msgstr "移动资产到节点" -#: assets/templates/assets/asset_list.html:127 +#: assets/templates/assets/asset_list.html:128 msgid "Refresh node hardware info" msgstr "更新节点资产硬件信息" -#: assets/templates/assets/asset_list.html:128 +#: assets/templates/assets/asset_list.html:129 msgid "Test node connective" msgstr "测试节点资产可连接性" -#: assets/templates/assets/asset_list.html:130 +#: assets/templates/assets/asset_list.html:131 msgid "Display only current node assets" msgstr "仅显示当前节点资产" -#: assets/templates/assets/asset_list.html:131 +#: assets/templates/assets/asset_list.html:132 msgid "Displays all child node assets" msgstr "显示所有子节点资产" -#: assets/templates/assets/asset_list.html:217 +#: assets/templates/assets/asset_list.html:218 msgid "Create node failed" msgstr "创建节点失败" -#: assets/templates/assets/asset_list.html:229 +#: assets/templates/assets/asset_list.html:230 msgid "Have child node, cancel" msgstr "存在子节点,不能删除" -#: assets/templates/assets/asset_list.html:231 +#: assets/templates/assets/asset_list.html:232 msgid "Have assets, cancel" msgstr "存在资产,不能删除" -#: assets/templates/assets/asset_list.html:631 +#: assets/templates/assets/asset_list.html:634 #: assets/templates/assets/system_user_list.html:133 #: users/templates/users/user_detail.html:357 #: users/templates/users/user_detail.html:382 @@ -984,20 +986,20 @@ msgstr "存在资产,不能删除" msgid "Are you sure?" msgstr "你确认吗?" -#: assets/templates/assets/asset_list.html:632 +#: assets/templates/assets/asset_list.html:635 msgid "This will delete the selected assets !!!" msgstr "删除选择资产" -#: assets/templates/assets/asset_list.html:640 +#: assets/templates/assets/asset_list.html:643 msgid "Asset Deleted." msgstr "已被删除" -#: assets/templates/assets/asset_list.html:641 -#: assets/templates/assets/asset_list.html:646 +#: assets/templates/assets/asset_list.html:644 +#: assets/templates/assets/asset_list.html:649 msgid "Asset Delete" msgstr "删除" -#: assets/templates/assets/asset_list.html:645 +#: assets/templates/assets/asset_list.html:648 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 "测试连接" @@ -1376,7 +1378,7 @@ msgstr "密码认证" msgid "Public key auth" msgstr "密钥认证" -#: common/forms.py:159 common/templates/common/terminal_setting.html:63 +#: common/forms.py:159 common/templates/common/terminal_setting.html:68 #: 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:81 +#: common/forms.py:165 common/templates/common/terminal_setting.html:86 #: 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:190 +msgid "Must contain capital letters" +msgstr "必须包含大写字母" + +#: common/forms.py:192 +msgid "" +"After opening, the user password changes and resets must contain uppercase " +"letters" +msgstr "开启后,用户密码修改、重置必须包含大写字母" + +#: common/forms.py:198 +msgid "Must contain lowercase letters" +msgstr "必须包含小写字母" + +#: common/forms.py:199 +msgid "" +"After opening, the user password changes and resets must contain lowercase " +"letters" +msgstr "开启后,用户密码修改、重置必须包含小写字母" + +#: common/forms.py:205 +msgid "Must contain numeric characters" +msgstr "必须包含数字字符" + +#: common/forms.py:206 +msgid "" +"After opening, the user password changes and resets must contain numeric " +"characters" +msgstr "开启后,用户密码修改、重置必须包含数字字符" + +#: common/forms.py:212 +msgid "Must contain special characters" +msgstr "必须包含特殊字符" + +#: common/forms.py:213 +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:42 common/views.py:22 +#: common/templates/common/terminal_setting.html:46 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 "更新设置成功, 请手动重启程序" @@ -1754,7 +1831,7 @@ msgstr "" #: perms/models.py:37 perms/models.py:80 #: perms/templates/perms/asset_permission_detail.html:90 #: users/models/user.py:80 users/templates/users/user_detail.html:103 -#: users/templates/users/user_profile.html:105 +#: users/templates/users/user_profile.html:108 msgid "Date expired" 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:37 +#: users/templates/users/user_password_update.html:39 #: 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:322 +#: users/templates/users/user_pubkey_update.html:37 users/views/user.py:342 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:263 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:310 users/views/user.py:357 users/views/user.py:379 +#: users/views/login.py:284 users/views/login.py:342 users/views/user.py:65 +#: users/views/user.py:80 users/views/user.py:102 users/views/user.py:173 +#: users/views/user.py:328 users/views/user.py:379 users/views/user.py:414 msgid "Users" msgstr "用户管理" -#: templates/_nav.html:13 users/views/user.py:65 +#: templates/_nav.html:13 users/views/user.py:66 msgid "User list" msgstr "用户列表" @@ -2356,7 +2433,7 @@ msgstr "复制你的公钥到这里" #: users/forms.py:231 users/models/user.py:72 #: users/templates/users/first_login.html:42 -#: users/templates/users/user_password_update.html:43 +#: users/templates/users/user_password_update.html:45 #: users/templates/users/user_profile.html:68 #: users/templates/users/user_profile_update.html:43 #: users/templates/users/user_pubkey_update.html:43 @@ -2396,13 +2473,13 @@ msgid "Application" msgstr "应用程序" #: users/models/user.py:34 users/templates/users/user_profile.html:92 -#: users/templates/users/user_profile.html:156 #: users/templates/users/user_profile.html:159 +#: users/templates/users/user_profile.html:162 msgid "Disable" msgstr "禁用" #: users/models/user.py:35 users/templates/users/user_profile.html:90 -#: users/templates/users/user_profile.html:163 +#: users/templates/users/user_profile.html:166 msgid "Enable" msgstr "启用" @@ -2539,22 +2616,34 @@ msgstr "6位数字" msgid "Can't provide security? Please contact the administrator!" msgstr "如果不能提供MFA验证码,请联系管理员!" -#: users/templates/users/reset_password.html:45 -#: users/templates/users/user_detail.html:348 users/utils.py:76 +#: users/templates/users/reset_password.html:46 +#: users/templates/users/user_detail.html:348 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:80 msgid "Create user" msgstr "创建用户" @@ -2563,7 +2652,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:156 +#: users/templates/users/user_granted_asset.html:18 users/views/user.py:174 msgid "User detail" msgstr "用户详情" @@ -2583,7 +2672,7 @@ msgid "Disabled" msgstr "禁用" #: users/templates/users/user_detail.html:115 -#: users/templates/users/user_profile.html:101 +#: users/templates/users/user_profile.html:104 msgid "Last login" msgstr "最后登录" @@ -2631,14 +2720,14 @@ msgid "This will reset the user public key and send a reset mail" msgstr "将会失效用户当前密钥,并发送重置邮件到用户邮箱" #: users/templates/users/user_detail.html:400 -#: users/templates/users/user_profile.html:204 +#: users/templates/users/user_profile.html:207 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_profile.html:208 +#: users/templates/users/user_profile.html:213 msgid "User SSH public key update" msgstr "ssh密钥" @@ -2694,28 +2783,32 @@ msgstr "删除" 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:112 users/views/user.py:203 +#: users/views/user.py:257 msgid "User groups" msgstr "用户组" -#: users/templates/users/user_profile.html:141 +#: users/templates/users/user_profile.html:144 msgid "Update password" msgstr "更改密码" -#: users/templates/users/user_profile.html:149 +#: users/templates/users/user_profile.html:152 msgid "Update MFA settings" msgstr "更改MFA设置" -#: users/templates/users/user_profile.html:170 +#: users/templates/users/user_profile.html:173 msgid "Update SSH public key" msgstr "更改SSH密钥" -#: users/templates/users/user_profile.html:178 +#: users/templates/users/user_profile.html:181 msgid "Reset public key and download" msgstr "重置并下载SSH密钥" -#: users/templates/users/user_profile.html:208 +#: users/templates/users/user_profile.html:211 msgid "Failed to update SSH public key." msgstr "更新密钥失败" @@ -2735,15 +2828,15 @@ msgstr "更新密钥" msgid "Or reset by server" msgstr "或者重置并下载密钥" -#: users/templates/users/user_update.html:4 users/views/user.py:99 +#: users/templates/users/user_update.html:4 users/views/user.py:103 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 +2881,7 @@ msgstr "" "
    \n" " " -#: users/utils.py:78 +#: users/utils.py:82 #, python-format msgid "" "\n" @@ -2832,11 +2925,11 @@ msgstr "" "
    \n" " " -#: users/utils.py:109 +#: users/utils.py:113 msgid "SSH Key Reset" msgstr "重置ssh密钥" -#: users/utils.py:111 +#: users/utils.py:115 #, python-format msgid "" "\n" @@ -2861,18 +2954,22 @@ msgstr "" "
    \n" " " -#: users/utils.py:144 +#: users/utils.py:148 msgid "User not exist" msgstr "用户不存在" -#: users/utils.py:146 +#: users/utils.py:150 msgid "Disabled or expired" msgstr "禁用或失效" -#: users/utils.py:159 +#: users/utils.py:163 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 +2982,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:125 users/views/user.py:464 users/views/user.py:489 +#: users/views/login.py:135 users/views/user.py:499 users/views/user.py:524 msgid "MFA code invalid" msgstr "MFA码认证失败" -#: users/views/login.py:151 +#: users/views/login.py:161 msgid "Logout success" msgstr "退出登录成功" -#: users/views/login.py:152 +#: users/views/login.py:162 msgid "Logout success, return login page" msgstr "退出登录成功,返回到登录页面" -#: users/views/login.py:168 +#: users/views/login.py:178 msgid "Email address invalid, please input again" msgstr "邮箱地址错误,重新输入" -#: users/views/login.py:181 +#: users/views/login.py:191 msgid "Send reset password message" msgstr "发送重置密码邮件" -#: users/views/login.py:182 +#: users/views/login.py:192 msgid "Send reset password mail success, login your mail box and follow it " msgstr "" "发送重置邮件成功, 请登录邮箱查看, 按照提示操作 (如果没收到,请等待3-5分钟)" -#: users/views/login.py:195 +#: users/views/login.py:205 msgid "Reset password success" msgstr "重置密码成功" -#: users/views/login.py:196 +#: users/views/login.py:206 msgid "Reset password success, return to login page" msgstr "重置密码成功,返回到登录页面" -#: users/views/login.py:213 users/views/login.py:226 +#: users/views/login.py:227 users/views/login.py:240 msgid "Token invalid or expired" msgstr "Token错误或失效" -#: users/views/login.py:222 +#: users/views/login.py:236 msgid "Password not same" msgstr "密码不一致" -#: users/views/login.py:263 +#: users/views/login.py:246 users/views/user.py:115 users/views/user.py:397 +msgid "* Your password does not meet the requirements" +msgstr "* 您的密码不符合要求" + +#: users/views/login.py:284 msgid "First login" msgstr "首次登陆" -#: users/views/login.py:322 +#: users/views/login.py:343 msgid "Login log list" msgstr "登录日志" -#: users/views/user.py:109 +#: users/views/user.py:127 msgid "Bulk update user success" msgstr "批量更新用户成功" -#: users/views/user.py:214 +#: users/views/user.py:232 msgid "Invalid file." msgstr "文件不合法" -#: users/views/user.py:311 +#: users/views/user.py:329 msgid "User granted assets" msgstr "用户授权资产" -#: users/views/user.py:340 +#: users/views/user.py:361 msgid "Profile setting" msgstr "个人信息设置" -#: users/views/user.py:358 +#: users/views/user.py:380 msgid "Password update" msgstr "密码更新" -#: users/views/user.py:380 +#: users/views/user.py:415 msgid "Public key update" msgstr "密钥更新" -#: users/views/user.py:421 +#: users/views/user.py:456 msgid "Password invalid" msgstr "用户名或密码无效" -#: users/views/user.py:515 +#: users/views/user.py:550 msgid "MFA enable success" msgstr "MFA 绑定成功" -#: users/views/user.py:516 +#: users/views/user.py:551 msgid "MFA enable success, return login page" msgstr "MFA 绑定成功,返回到登录页面" -#: users/views/user.py:518 +#: users/views/user.py:553 msgid "MFA disable success" msgstr "MFA 解绑成功" -#: users/views/user.py:519 +#: users/views/user.py:554 msgid "MFA disable success, return login page" msgstr "MFA 解绑成功,返回登录页面" diff --git a/apps/jumpserver/settings.py b/apps/jumpserver/settings.py index b81188b9d..6fd9f0fdd 100644 --- a/apps/jumpserver/settings.py +++ b/apps/jumpserver/settings.py @@ -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', diff --git a/apps/static/js/jumpserver.js b/apps/static/js/jumpserver.js index 461455085..0000a3ccf 100644 --- a/apps/static/js/jumpserver.js +++ b/apps/static/js/jumpserver.js @@ -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 += "

  • " + rules.label + "
  • "; + }); + //$('#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); +} diff --git a/apps/static/js/pwstrength-bootstrap.js b/apps/static/js/pwstrength-bootstrap.js new file mode 100755 index 000000000..957df08ea --- /dev/null +++ b/apps/static/js/pwstrength-bootstrap.js @@ -0,0 +1,976 @@ +/*! +* 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 '' + text + ''; +}; +defaultOptions.ui.popoverError = function (options) { + "use strict"; + var errors = options.instances.errors, + errorsTitle = options.i18n.t("errorList"), + message = "
    " + errorsTitle + "
      "; + + jQuery.each(errors, function (idx, err) { + message += "
    • " + err + "
    • "; + }); + message += "
    "; + 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 = "
    "; + + if (options.ui.showVerdictsInsideProgressBar) { + progressbar += ""; + } + + progressbar += "
    "; + + 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, "", + options.ui.viewports.verdict); + }; + + ui.initErrorList = function (options, $el) { + ui.initHelper(options, $el, "
      ", + options.ui.viewports.errors); + }; + + ui.initScore = function (options, $el) { + ui.initHelper(options, $el, "", + 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 += "
    • " + err + "
    • "; + }); + } + $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 = "
      " + verdictText + + "
      "; + 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 diff --git a/apps/templates/_base_create_update.html b/apps/templates/_base_create_update.html index a38a6133d..ec14da79b 100644 --- a/apps/templates/_base_create_update.html +++ b/apps/templates/_base_create_update.html @@ -5,6 +5,7 @@ {% block custom_head_css_js %} + {% block custom_head_css_js_create %} {% endblock %} {% endblock %} diff --git a/apps/users/forms.py b/apps/users/forms.py index f777e0dd7..06c544383 100644 --- a/apps/users/forms.py +++ b/apps/users/forms.py @@ -72,7 +72,7 @@ class UserCreateUpdateForm(forms.ModelForm): 'data-placeholder': _('Join user groups') } ), - 'otp_level': forms.RadioSelect() + 'otp_level': forms.RadioSelect(), } def clean_public_key(self): diff --git a/apps/users/templates/users/_user.html b/apps/users/templates/users/_user.html index f973f3344..5090e825f 100644 --- a/apps/users/templates/users/_user.html +++ b/apps/users/templates/users/_user.html @@ -48,6 +48,7 @@
      + {% endblock %} {% block custom_foot_js %} diff --git a/apps/users/templates/users/reset_password.html b/apps/users/templates/users/reset_password.html index 0bab32809..3d669a1e5 100644 --- a/apps/users/templates/users/reset_password.html +++ b/apps/users/templates/users/reset_password.html @@ -11,6 +11,7 @@ {% include '_head_css_js.html' %} + @@ -49,10 +50,20 @@

      {{ errors }}

      {% endif %}
      - + + {# 密码popover #} +
      + +
      - +
      @@ -79,4 +90,33 @@ + diff --git a/apps/users/templates/users/user_password_update.html b/apps/users/templates/users/user_password_update.html index 3dbe727a7..50c428ee6 100644 --- a/apps/users/templates/users/user_password_update.html +++ b/apps/users/templates/users/user_password_update.html @@ -7,6 +7,8 @@ + +