Skip to content

Commit d7a3b7f

Browse files
committed
feat: enhance password reset functionality with email verification and validation
1 parent f516652 commit d7a3b7f

File tree

2 files changed

+95
-18
lines changed

2 files changed

+95
-18
lines changed

apps/users/serializers/user.py

Lines changed: 91 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -607,20 +607,97 @@ def update_user_role(instance, user):
607607

608608

609609
class RePasswordSerializer(serializers.Serializer):
610-
password = serializers.CharField(required=True, label=_("Password"),
611-
validators=[validators.RegexValidator(regex=re.compile(
612-
"^(?![a-zA-Z]+$)(?![A-Z0-9]+$)(?![A-Z_!@#$%^&*`~.()-+=]+$)(?![a-z0-9]+$)(?![a-z_!@#$%^&*`~()-+=]+$)"
613-
"(?![0-9_!@#$%^&*`~()-+=]+$)[a-zA-Z0-9_!@#$%^&*`~.()-+=]{6,20}$")
614-
, message=_(
615-
"The confirmation password must be 6-20 characters long and must be a combination of letters, numbers, and special characters."))])
616-
617-
re_password = serializers.CharField(required=True, label=_("Confirm Password"),
618-
validators=[validators.RegexValidator(regex=re.compile(
619-
"^(?![a-zA-Z]+$)(?![A-Z0-9]+$)(?![A-Z_!@#$%^&*`~.()-+=]+$)(?![a-z0-9]+$)(?![a-z_!@#$%^&*`~()-+=]+$)"
620-
"(?![0-9_!@#$%^&*`~()-+=]+$)[a-zA-Z0-9_!@#$%^&*`~.()-+=]{6,20}$")
621-
, message=_(
622-
"The confirmation password must be 6-20 characters long and must be a combination of letters, numbers, and special characters."))]
623-
)
610+
email = serializers.EmailField(
611+
required=True,
612+
label=_("Email"),
613+
validators=[validators.EmailValidator(message=ExceptionCodeConstants.EMAIL_FORMAT_ERROR.value.message,
614+
code=ExceptionCodeConstants.EMAIL_FORMAT_ERROR.value.code)])
615+
616+
code = serializers.CharField(required=True, label=_("Code"))
617+
password = serializers.CharField(
618+
required=True,
619+
label=_("Password"),
620+
max_length=20,
621+
min_length=6,
622+
validators=[
623+
validators.RegexValidator(
624+
regex=PASSWORD_REGEX,
625+
message=_(
626+
"The password must be 6-20 characters long and must be a combination of letters, numbers, and special characters."
627+
)
628+
)
629+
]
630+
)
631+
re_password = serializers.CharField(
632+
required=True,
633+
label=_("Re Password"),
634+
validators=[
635+
validators.RegexValidator(
636+
regex=PASSWORD_REGEX,
637+
message=_(
638+
"The confirmation password must be 6-20 characters long and must be a combination of letters, numbers, and special characters."
639+
)
640+
)
641+
]
642+
)
643+
644+
class Meta:
645+
model = User
646+
fields = '__all__'
647+
648+
def is_valid(self, *, raise_exception=False):
649+
super().is_valid(raise_exception=True)
650+
email = self.data.get("email")
651+
cache_code = cache.get(get_key(email + ':reset_password'), version=version)
652+
if self.data.get('password') != self.data.get('re_password'):
653+
raise AppApiException(ExceptionCodeConstants.PASSWORD_NOT_EQ_RE_PASSWORD.value.code,
654+
ExceptionCodeConstants.PASSWORD_NOT_EQ_RE_PASSWORD.value.message)
655+
if cache_code != self.data.get('code'):
656+
raise AppApiException(ExceptionCodeConstants.CODE_ERROR.value.code,
657+
ExceptionCodeConstants.CODE_ERROR.value.message)
658+
return True
659+
660+
def reset_password(self):
661+
"""
662+
修改密码
663+
:return: 是否成功
664+
"""
665+
if self.is_valid():
666+
email = self.data.get("email")
667+
QuerySet(User).filter(email=email).update(
668+
password=password_encrypt(self.data.get('password')))
669+
code_cache_key = email + ":reset_password"
670+
cache.delete(get_key(code_cache_key), version=version)
671+
return True
672+
673+
674+
class ResetCurrentUserPassword(serializers.Serializer):
675+
password = serializers.CharField(
676+
required=True,
677+
label=_("Password"),
678+
max_length=20,
679+
min_length=6,
680+
validators=[
681+
validators.RegexValidator(
682+
regex=PASSWORD_REGEX,
683+
message=_(
684+
"The password must be 6-20 characters long and must be a combination of letters, numbers, and special characters."
685+
)
686+
)
687+
]
688+
)
689+
re_password = serializers.CharField(
690+
required=True,
691+
label=_("Re Password"),
692+
validators=[
693+
validators.RegexValidator(
694+
regex=PASSWORD_REGEX,
695+
message=_(
696+
"The confirmation password must be 6-20 characters long and must be a combination of letters, numbers, and special characters."
697+
)
698+
)
699+
]
700+
)
624701

625702
class Meta:
626703
model = User

apps/users/views/user.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@
2828
SendEmailAPI, CheckCodeAPI, SwitchUserLanguageAPI
2929
from users.models import User
3030
from users.serializers.user import UserProfileSerializer, UserManageSerializer, CheckCodeSerializer, \
31-
SendEmailSerializer, RePasswordSerializer, SwitchLanguageSerializer
31+
SendEmailSerializer, RePasswordSerializer, SwitchLanguageSerializer, ResetCurrentUserPassword
3232

3333
default_password = CONFIG.get('DEFAULT_PASSWORD', 'MaxKB@123..')
3434

@@ -260,6 +260,7 @@ class RePassword(APIView):
260260
@log(menu='User management', operate='Change password',
261261
get_operation_object=lambda r, k: get_user_operation_object(k.get('user_id')),
262262
get_details=get_re_password_details)
263+
@has_permissions(PermissionConstants.USER_EDIT, RoleConstants.ADMIN)
263264
def put(self, request: Request, user_id):
264265
return result.success(
265266
UserManageSerializer.Operate(data={'id': user_id}).re_password(request.data, with_valid=True))
@@ -293,10 +294,9 @@ class RePasswordView(APIView):
293294
@log(menu='User management', operate='Change password',
294295
get_operation_object=lambda r, k: {'name': r.user.username},
295296
get_details=get_re_password_details)
296-
@has_permissions(PermissionConstants.USER_EDIT, RoleConstants.ADMIN)
297297
def post(self, request: Request):
298298
serializer_obj = RePasswordSerializer(data=request.data)
299-
return result.success(serializer_obj.reset_password(request.user.id))
299+
return result.success(serializer_obj.reset_password())
300300

301301

302302
class SendEmail(APIView):
@@ -367,7 +367,7 @@ class ResetCurrentUserPasswordView(APIView):
367367
@has_permissions(PermissionConstants.CHANGE_PASSWORD, RoleConstants.ADMIN, RoleConstants.USER,
368368
RoleConstants.WORKSPACE_MANAGE)
369369
def post(self, request: Request):
370-
serializer_obj = RePasswordSerializer(data=request.data)
370+
serializer_obj = ResetCurrentUserPassword(data=request.data)
371371
if serializer_obj.reset_password(request.user.id):
372372
version, get_key = Cache_Version.TOKEN.value
373373
cache.delete(get_key(token=request.auth), version=version)

0 commit comments

Comments
 (0)