From 11e5a97f14152717c80572672febd408c0595fcc Mon Sep 17 00:00:00 2001 From: fit2cloud-jiangweidong <80373698+fit2cloud-jiangweidong@users.noreply.github.com> Date: Wed, 28 Apr 2021 17:03:20 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E7=94=A8=E6=88=B7=E6=9B=B4=E6=94=B9?= =?UTF-8?q?=E5=AF=86=E7=A0=81=E4=B8=8D=E5=8F=AF=E4=BD=BF=E7=94=A8=E5=89=8D?= =?UTF-8?q?n=E6=AC=A1=E5=8E=86=E5=8F=B2=E5=AF=86=E7=A0=81=EF=BC=8C?= =?UTF-8?q?=E7=AE=A1=E7=90=86=E5=91=98=E5=8F=AF=E8=AE=BE=E7=BD=AE=E5=8E=86?= =?UTF-8?q?=E5=8F=B2=E5=AF=86=E7=A0=81=E9=87=8D=E5=A4=8D=E6=AC=A1=E6=95=B0?= =?UTF-8?q?=20(#6010)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat: 用户更改密码不可使用前n次历史密码,管理员可设置历史密码重复次数 * feat: 用户更改密码不可使用前n次历史密码,管理员可设置历史密码重复次数, 判断是否为历史密码逻辑修改 * feat: 用户更改密码不可使用前n次历史密码,管理员可设置历史密码重复次数, 提示内容更人性化 * fixs: 用户更改密码不可使用前n次历史密码,管理员可设置历史密码重复次数, 最新国际化翻译文件 --- apps/jumpserver/conf.py | 1 + apps/jumpserver/settings/custom.py | 1 + apps/locale/zh/LC_MESSAGES/django.mo | Bin 75634 -> 72201 bytes apps/locale/zh/LC_MESSAGES/django.po | 37 +++++++++++++----- apps/settings/api/common.py | 1 + apps/settings/serializers/settings.py | 5 +++ .../migrations/0032_userpasswordhistory.py | 25 ++++++++++++ apps/users/models/user.py | 27 +++++++++++++ apps/users/serializers/profile.py | 9 ++++- apps/users/views/profile/reset.py | 8 ++++ 10 files changed, 103 insertions(+), 11 deletions(-) create mode 100644 apps/users/migrations/0032_userpasswordhistory.py diff --git a/apps/jumpserver/conf.py b/apps/jumpserver/conf.py index 179376828..b51c7dfba 100644 --- a/apps/jumpserver/conf.py +++ b/apps/jumpserver/conf.py @@ -259,6 +259,7 @@ class Config(dict): 'FTP_LOG_KEEP_DAYS': 200, 'ASSETS_PERM_CACHE_TIME': 3600 * 24, 'SECURITY_MFA_VERIFY_TTL': 3600, + 'OLD_PASSWORD_HISTORY_LIMIT_COUNT': 5, 'ASSETS_PERM_CACHE_ENABLE': HAS_XPACK, 'SYSLOG_ADDR': '', # '192.168.0.1:514' 'SYSLOG_FACILITY': 'user', diff --git a/apps/jumpserver/settings/custom.py b/apps/jumpserver/settings/custom.py index 936b27582..ed37ea49c 100644 --- a/apps/jumpserver/settings/custom.py +++ b/apps/jumpserver/settings/custom.py @@ -38,6 +38,7 @@ SECURITY_LOGIN_LIMIT_TIME = CONFIG.SECURITY_LOGIN_LIMIT_TIME # Unit: minute SECURITY_MAX_IDLE_TIME = CONFIG.SECURITY_MAX_IDLE_TIME # Unit: minute SECURITY_PASSWORD_EXPIRATION_TIME = CONFIG.SECURITY_PASSWORD_EXPIRATION_TIME # Unit: day SECURITY_PASSWORD_MIN_LENGTH = CONFIG.SECURITY_PASSWORD_MIN_LENGTH # Unit: bit +OLD_PASSWORD_HISTORY_LIMIT_COUNT = CONFIG.OLD_PASSWORD_HISTORY_LIMIT_COUNT SECURITY_PASSWORD_UPPER_CASE = CONFIG.SECURITY_PASSWORD_UPPER_CASE SECURITY_PASSWORD_LOWER_CASE = CONFIG.SECURITY_PASSWORD_LOWER_CASE SECURITY_PASSWORD_NUMBER = CONFIG.SECURITY_PASSWORD_NUMBER diff --git a/apps/locale/zh/LC_MESSAGES/django.mo b/apps/locale/zh/LC_MESSAGES/django.mo index f47606a0d8ee91e55b19356e5a7572a55bcd1b20..bdb351ceb42856cc8ad5002068b470d0ed9b3304 100644 GIT binary patch delta 22238 zcma*ub$r*=|M&6tXQN?s=jiT`QbME~QBoZ;a%`}Tjt|{P3>eMm9x*zlkq}V21ZAT| z1VK^cdOSbx7yh`e+x5rqefu8n=e$mzcWn68-@i{ZVQV7qt@MdzIb3J_9VaJ_&h0qq z5WVr}nHG*y*w1mwVq3~>TRF}YjKe`#>r=g#j3gDRD1m z#uJzqe=~E4`ET}ckYs5(@yG6j#CUvVmWMsrErqD z8?R7)fVc3_*N&4OmvnZVG`JO0;1NuPXU!Yt1JnjyVg~dJX5-B7WG0goi=!HpM-5QJ zY>evA7Ih-MFf~S^`i-*qB-BaGz$_Sp8fPaa$D^qBXE7~aMsG$k_sMAGZ_H#}+#O{= z4VWK`Vp-IV+hZ7pTKz5L@o^qw5&R7cW45mD!fRn-%B}HpY>&Dnaa}ooO%P8Y9X>~` zG;ue#oCb9wd97R$welLMXP`N1hh0$%i?sT2s9Q1z^(?Hm_#xDWe?+yv-;MLv!|=CN z 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 delta 25353 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 Xd@g;*n>xWgJKWFEp1>S_>C*o{s05_2 diff --git a/apps/locale/zh/LC_MESSAGES/django.po b/apps/locale/zh/LC_MESSAGES/django.po index 7afb36d4f..6124b8b26 100644 --- a/apps/locale/zh/LC_MESSAGES/django.po +++ b/apps/locale/zh/LC_MESSAGES/django.po @@ -2283,35 +2283,46 @@ msgstr "" "提示:(单位:天)如果用户在此期间没有更新密码,用户密码将过期失效; 密码过期" "提醒邮件将在密码过期前5天内由系统(每天)自动发送给用户" -#: settings/serializers/settings.py:172 +#: settings/serializers/settings.py:168 +msgid "Number of repeated historical passwords" +msgstr "历史密码可重复次数" + +#: settings/serializers/settings.py:169 +msgid "" +"Tip: When the user resets the password, it cannot be the previous n " +"historical passwords of the user (the value of n here is the value filled in " +"the input box)" +msgstr "提示:用户重置密码时,不能为该用户前n次历史密码 (此处的n值即为输入框中填写的值)" + +#: settings/serializers/settings.py:173 msgid "Password minimum length" msgstr "密码最小长度" -#: settings/serializers/settings.py:175 +#: settings/serializers/settings.py:176 msgid "Must contain capital" msgstr "必须包含大写字符" -#: settings/serializers/settings.py:177 +#: settings/serializers/settings.py:178 msgid "Must contain lowercase" msgstr "必须包含小写字符" -#: settings/serializers/settings.py:178 +#: settings/serializers/settings.py:179 msgid "Must contain numeric" msgstr "必须包含数字" -#: settings/serializers/settings.py:179 +#: settings/serializers/settings.py:180 msgid "Must contain special" msgstr "必须包含特殊字符" -#: settings/serializers/settings.py:180 +#: settings/serializers/settings.py:181 msgid "Insecure command alert" msgstr "危险命令告警" -#: settings/serializers/settings.py:182 +#: settings/serializers/settings.py:183 msgid "Email recipient" msgstr "邮件收件人" -#: settings/serializers/settings.py:183 +#: settings/serializers/settings.py:184 msgid "Multiple user using , split" msgstr "多个用户,使用 , 分割" @@ -3721,7 +3732,11 @@ msgstr "旧密码错误" msgid "Password does not match security rules" msgstr "密码不满足安全规则" -#: users/serializers/profile.py:43 +#: users/serializers/profile.py:40 +msgid "The new password cannot be the last {} passwords" +msgstr "新密码不能是最近 {} 次的密码" + +#: users/serializers/profile.py:48 msgid "The newly set password is inconsistent" msgstr "两次密码不一致" @@ -4361,6 +4376,10 @@ msgstr "重置密码成功,返回到登录页面" msgid "Token invalid or expired" msgstr "Token错误或失效" +#: users/views/profile/reset.py:133 +msgid "* The new password cannot be the last {} passwords" +msgstr "* 新密码不能是最近 {} 次的密码" + #: users/views/profile/reset.py:120 msgid "User auth from {}, go there change password" msgstr "用户认证源来自 {}, 请去相应系统修改密码" diff --git a/apps/settings/api/common.py b/apps/settings/api/common.py index 8aafee063..1fa578c50 100644 --- a/apps/settings/api/common.py +++ b/apps/settings/api/common.py @@ -112,6 +112,7 @@ class PublicSettingApi(generics.RetrieveAPIView): "LOGIN_CONFIRM_ENABLE": settings.LOGIN_CONFIRM_ENABLE, "SECURITY_VIEW_AUTH_NEED_MFA": settings.SECURITY_VIEW_AUTH_NEED_MFA, "SECURITY_MFA_VERIFY_TTL": settings.SECURITY_MFA_VERIFY_TTL, + "OLD_PASSWORD_HISTORY_LIMIT_COUNT": settings.OLD_PASSWORD_HISTORY_LIMIT_COUNT, "SECURITY_COMMAND_EXECUTION": settings.SECURITY_COMMAND_EXECUTION, "SECURITY_PASSWORD_EXPIRATION_TIME": settings.SECURITY_PASSWORD_EXPIRATION_TIME, "XPACK_LICENSE_IS_VALID": has_valid_xpack_license(), diff --git a/apps/settings/serializers/settings.py b/apps/settings/serializers/settings.py index 95757eb8b..b64b95cb6 100644 --- a/apps/settings/serializers/settings.py +++ b/apps/settings/serializers/settings.py @@ -167,6 +167,11 @@ class SecuritySettingSerializer(serializers.Serializer): label=_('User password expiration'), help_text=_('Tip: (unit: day) If the user does not update the password during the time, the user password will expire failure;The password expiration reminder mail will be automatic sent to the user by system within 5 days (daily) before the password expires') ) + OLD_PASSWORD_HISTORY_LIMIT_COUNT = serializers.IntegerField( + min_value=0, max_value=99999, required=True, + label=_('Number of repeated historical passwords'), + help_text=_('Tip: When the user resets the password, it cannot be the previous n historical passwords of the user (the value of n here is the value filled in the input box)') + ) SECURITY_PASSWORD_MIN_LENGTH = serializers.IntegerField( min_value=6, max_value=30, required=True, label=_('Password minimum length') diff --git a/apps/users/migrations/0032_userpasswordhistory.py b/apps/users/migrations/0032_userpasswordhistory.py new file mode 100644 index 000000000..8fcf1d2d1 --- /dev/null +++ b/apps/users/migrations/0032_userpasswordhistory.py @@ -0,0 +1,25 @@ +# Generated by Django 3.1 on 2021-04-27 12:43 + +from django.conf import settings +from django.db import migrations, models +import django.db.models.deletion +import uuid + + +class Migration(migrations.Migration): + + dependencies = [ + ('users', '0031_auto_20201118_1801'), + ] + + operations = [ + migrations.CreateModel( + name='UserPasswordHistory', + fields=[ + ('id', models.UUIDField(default=uuid.uuid4, primary_key=True, serialize=False)), + ('password', models.CharField(max_length=128)), + ('date_created', models.DateTimeField(auto_now_add=True, verbose_name='Date created')), + ('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='history_passwords', to=settings.AUTH_USER_MODEL, verbose_name='User')), + ], + ), + ] diff --git a/apps/users/models/user.py b/apps/users/models/user.py index 1f8b2418a..9fd46e2cb 100644 --- a/apps/users/models/user.py +++ b/apps/users/models/user.py @@ -7,8 +7,11 @@ import string import random import datetime +from functools import partial + from django.conf import settings from django.contrib.auth.models import AbstractUser +from django.contrib.auth.hashers import check_password, make_password from django.core.cache import cache from django.db import models from django.db.models import TextChoices @@ -70,6 +73,22 @@ class AuthMixin: def can_use_ssh_key_login(): return settings.TERMINAL_PUBLIC_KEY_AUTH + def is_history_password(self, password): + allow_history_password_count = settings.OLD_PASSWORD_HISTORY_LIMIT_COUNT + history_passwords = self.history_passwords.all().order_by('-date_created')[:int(allow_history_password_count)] + + for history_password in history_passwords: + if check_password(password, history_password.password): + return True + else: + return False + + def save_history_password(self, password): + UserPasswordHistory.objects.create( + user=self, password=make_password(password), + date_created=self.date_password_last_updated + ) + def is_public_key_valid(self): """ Check if the user's ssh public key is valid. @@ -729,3 +748,11 @@ class User(AuthMixin, TokenMixin, RoleMixin, MFAMixin, AbstractUser): if self.email and self.source == self.Source.local.value: return True return False + + +class UserPasswordHistory(models.Model): + id = models.UUIDField(default=uuid.uuid4, primary_key=True) + password = models.CharField(max_length=128) + user = models.ForeignKey("users.User", related_name='history_passwords', + on_delete=models.CASCADE, verbose_name=_('User')) + date_created = models.DateTimeField(auto_now_add=True, verbose_name=_("Date created")) diff --git a/apps/users/serializers/profile.py b/apps/users/serializers/profile.py index 68e387245..261dd6f01 100644 --- a/apps/users/serializers/profile.py +++ b/apps/users/serializers/profile.py @@ -30,12 +30,17 @@ class UserUpdatePasswordSerializer(serializers.ModelSerializer): raise serializers.ValidationError(msg) return value - @staticmethod - def validate_new_password(value): + def validate_new_password(self, value): from ..utils import check_password_rules if not check_password_rules(value): msg = _('Password does not match security rules') raise serializers.ValidationError(msg) + if self.instance.is_history_password(value): + limit_count = settings.OLD_PASSWORD_HISTORY_LIMIT_COUNT + msg = _('The new password cannot be the last {} passwords').format(limit_count) + raise serializers.ValidationError(msg) + else: + self.instance.save_history_password(value) return value def validate_new_password_again(self, value): diff --git a/apps/users/views/profile/reset.py b/apps/users/views/profile/reset.py index 73c34396b..8b1d9a102 100644 --- a/apps/users/views/profile/reset.py +++ b/apps/users/views/profile/reset.py @@ -128,6 +128,14 @@ class UserResetPasswordView(FormView): form.add_error('new_password', error) return self.form_invalid(form) + if user.is_history_password(password): + limit_count = settings.OLD_PASSWORD_HISTORY_LIMIT_COUNT + error = _('* The new password cannot be the last {} passwords').format(limit_count) + form.add_error('new_password', error) + return self.form_invalid(form) + else: + user.save_history_password(password) + user.reset_password(password) User.expired_reset_password_token(token) send_reset_password_success_mail(self.request, user)