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 b764117a2..78705cf79 100644 Binary files a/apps/i18n/zh/LC_MESSAGES/django.mo and b/apps/i18n/zh/LC_MESSAGES/django.mo differ 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 @@ + +