Skip to content

Commit

Permalink
Merge pull request #194 from longguikeji/zzr/strong_password
Browse files Browse the repository at this point in the history
Zzr/strong password
  • Loading branch information
RockLi authored Jul 14, 2020
2 parents e6b6740 + 218f72a commit 544b3e7
Show file tree
Hide file tree
Showing 7 changed files with 133 additions and 14 deletions.
2 changes: 1 addition & 1 deletion docker-compose/docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ services:
- ${WORKSPACE}/portal.conf:/etc/nginx/conf.d/default.conf

be:
image: longguikeji/arkid:1.3.3
image: longguikeji/arkid:1.3.4
container_name: ${INSTANCE}-be
sysctls:
- net.core.somaxconn=65535
Expand Down
82 changes: 82 additions & 0 deletions oneid/password_validation.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
"""
自定义密码复杂度校验集合
"""
import re

from django.conf import settings
from django.utils.translation import ugettext_lazy as _
from rest_framework.exceptions import ValidationError

PASSWORD_COMPLEXITY = getattr(settings, "PASSWORD_COMPLEXITY", None)


# pylint: disable=useless-object-inheritance
# pylint: disable=too-many-branches
class _ComplexityValidator(object):
"""自定义密码复杂度校验规则"""
message = _("密码必须更加复杂 (%s)")
code = "password_complexity"

def __init__(self, complexities):
self.complexities = complexities

def __call__(self, value):
if self.complexities is None:
return
uppercase, lowercase, letters, digits, special = set(), set(), set(), set(), set()
errors = []

for _character in value:
if _character.isupper():
uppercase.add(_character)
letters.add(_character)
continue
if _character.islower():
lowercase.add(_character)
letters.add(_character)
continue
if _character.isdigit():
digits.add(_character)
continue
if not _character.isspace():
special.add(_character)

words = set(re.findall(r'\b\w+', value, re.UNICODE))

if len(uppercase) < self.complexities.get("UPPER", 0):
errors.append(_("%(UPPER)s 个及以上不同的大写字母") % self.complexities)
if len(lowercase) < self.complexities.get("LOWER", 0):
errors.append(_("%(LOWER)s 个及以上不同的小写字母") % self.complexities)
if len(letters) < self.complexities.get("LETTERS", 0):
errors.append(_("%(LETTERS)s 个及以上不同的大小写字母") % self.complexities)
if len(digits) < self.complexities.get("DIGITS", 0):
errors.append(_("%(DIGITS)s 个及以上不同的数字") % self.complexities)
if len(special) < self.complexities.get("SPECIAL", 0):
errors.append(_("%(SPECIAL)s 个及以上的特殊字符") % self.complexities)
if len(words) < self.complexities.get("WORDS", 0):
errors.append(_("%(WORDS)s 个及以上不同的单词") % self.complexities)
if len(value) < self.complexities.get("LENGTH", 0):
errors.append(_("%(LENGTH)s 长度及以上的密码") % self.complexities)
if errors:
raise ValidationError(self.message % (_(u'必须包含 ') + u', '.join(errors), ), code=self.code)


_COMPLEXITY = _ComplexityValidator(PASSWORD_COMPLEXITY)


# pylint: disable=missing-function-docstring
class ComplexityValidator:
"""
Wrapper for validators.ComplexityValidator which is compatible
with the Django 1.9+ password validation API
"""
def __init__(self):
self.validator = _COMPLEXITY

# pylint: disable=no-self-use
def get_help_text(self):
return _("密码需满足ArkID的复杂性要求.")

# pylint: disable=unused-argument
def validate(self, value, user=None):
return self.validator(value)
37 changes: 27 additions & 10 deletions oneid/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -148,17 +148,23 @@
# https://docs.djangoproject.com/en/1.9/ref/settings/#auth-password-validators

AUTH_PASSWORD_VALIDATORS = [
# {
# 'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
# },
# {
# 'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
# 'OPTIONS': {
# 'min_length': 8,
# },
# },
# {
# 'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
# },
# {
# 'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
# },
{
'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
},
{
'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
},
{
'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
},
{
'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
'NAME': 'oneid.password_validation.ComplexityValidator',
},
]

Expand Down Expand Up @@ -313,6 +319,17 @@
ACTIVE_USER_DATA_LIFEDAY = 30
ACTIVE_USER_REDIS_KEY_PREFIX = 'active-'

# 密码复杂度规则
# 值表示至少需包含的相应元素的个数,默认全部为0
PASSWORD_COMPLEXITY = {
"LENGTH": 0, # 密码的长度
"UPPER": 0, # 包含大写字母的个数
"LOWER": 0, # 包含小写字母的个数
"LETTERS": 0, # 包含大写和小写字母的个数
"DIGITS": 0, # 包含数字的个数
"SPECIAL": 0, # 包含特殊字符的个数 (不是字母数字、空格或标点字符)
"WORDS": 0, # 包含单词的个数 (由空格或标点分隔的字母数字序列)
}

if os.path.exists(os.path.join(BASE_DIR, 'settings_local.py')):
exec(open(os.path.join(BASE_DIR, 'settings_local.py')).read())
Expand Down
13 changes: 13 additions & 0 deletions siteapi/v1/serializers/ucenter.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
'''
serializers for ucenter
'''
from django.contrib.auth.password_validation import validate_password
from rest_framework import serializers
from rest_framework.exceptions import ValidationError
from common.django.drf.serializer import DynamicFieldsModelSerializer
Expand Down Expand Up @@ -104,6 +105,12 @@ def update(self, instance, validated_data):
user.save(update_fields=['require_reset_password'])
return user

@staticmethod
def validate_new_password(value):
"""密码复杂度检验"""
validate_password(value)
return value


class UserRegisterSerializer(DynamicFieldsModelSerializer):
'''
Expand Down Expand Up @@ -170,6 +177,12 @@ def create(self, validated_data):
cli.set_user_password(user, password)
return user

@staticmethod
def validate_password(value):
"""密码复杂度检验"""
validate_password(value)
return value


class UserAlterMobileSerializer(serializers.Serializer): # pylint: disable=abstract-method
'''
Expand Down
8 changes: 7 additions & 1 deletion siteapi/v1/serializers/user.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
'''
serializers for user
'''

from django.contrib.auth.password_validation import validate_password
from rest_framework import serializers
from rest_framework.exceptions import ValidationError

Expand Down Expand Up @@ -498,3 +498,9 @@ def update(self, instance, validated_data):
instance.require_reset_password = require_reset_password
instance.save(update_fields=['require_reset_password'])
return instance

@staticmethod
def validate_password(value):
"""密码复杂度检验"""
validate_password(value)
return value
3 changes: 2 additions & 1 deletion siteapi/v1/views/user.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
- UserDept
'''
# pylint: disable=too-many-lines

from rest_framework import generics, status, views
from rest_framework.response import Response
from rest_framework.exceptions import (
Expand All @@ -17,6 +16,7 @@
from rest_framework.permissions import IsAuthenticated, SAFE_METHODS
from django.db import transaction
from django.db.models import Q
from django.contrib.auth.password_validation import validate_password
from django.core.exceptions import ObjectDoesNotExist, FieldDoesNotExist
from oneid_meta.models import User, Group, Dept, GroupMember, UserPerm
from oneid.permissions import (
Expand Down Expand Up @@ -228,6 +228,7 @@ def create(self, request, *args, **kwargs): # pylint: disable=unused-argument
password = user_info.pop('password', None)
user = cli.create_user(user_info)
if password:
validate_password(password)
cli.set_user_password(user, password)
user.origin = 1 # 管理员添加
user.save()
Expand Down
2 changes: 1 addition & 1 deletion uwsgi.ini
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,6 @@ processes = %(%k + 1)
thunder-lock = true
harakiri = 30

listen = 65535
;listen = 65535

disable-logging = true

0 comments on commit 544b3e7

Please sign in to comment.