import logging from django import forms from django.core.exceptions import ValidationError from django.contrib.auth import password_validation from django.utils.translation import gettext_lazy as _ from pingcap_sso.models import User, SecurityCode, LoginLog from pingcap_sso.exceptions import UnprocessableError from pingcap_sso.captcha import pingcap_captcha from pingcap_sso.utils.requests import get_client_ip logger = logging.getLogger(__name__) class ForgetSendCodeForm(forms.Form): identifier = forms.CharField( required=True, error_messages={ 'required': _('this field is required') }) re_token_v3 = forms.CharField(required=False, strip=True) def __init__(self, request, *args, **kwargs): self.request = request self.user = None super(ForgetSendCodeForm, self).__init__(*args, **kwargs) def clean_identifier(self): identifier = self.cleaned_data['identifier'] try: self.user = User.objects.find_user(identifier) except User.DoesNotExist: raise ValidationError(_('user not found')) return identifier def save(self): # May raise UnprocessableError or RateLimitError identifier = self.cleaned_data['identifier'].lower() if not (self.user.email or self.user.phone): raise UnprocessableError if identifier == self.user.phone or \ (identifier == self.user.username.lower() and self.user.phone): SecurityCode.send_code(self.user.phone, SecurityCode.DELIVER_BY_PHONE) message = _('security code has been sent via phone') elif self.user.email and identifier == self.user.email.lower() or \ (identifier == self.user.username.lower() and self.user.email): SecurityCode.send_code(self.user.email.lower(), SecurityCode.DELIVER_BY_EMAIL) message = _('security code has been sent via email') else: message = _('unknown error') logger.error('Forget send code failed, user_id: {}'.format(self.user.id)) return message class ForgetVerifyForm(ForgetSendCodeForm): identifier = forms.CharField( required=True, error_messages={ 'required': _('this field is required') }) code = forms.CharField(required=True, strip=True) re_token_v3 = forms.CharField(required=False, strip=True) def clean(self): if self._errors: return identifier = self.cleaned_data['identifier'].lower() code = self.cleaned_data.get('code') # re_token_v3 = self.cleaned_data.get('re_token_v3') # if not pingcap_captcha.verify( # self.user, # get_client_ip(self.request), # re_token_v3 # ): # self.add_error('re_token_v3', _('invalid captcha')) # return # Try to match username态phone and email if ( self.user.phone and identifier in (self.user.phone, self.user.username) and self.user.check_sms_code(code) ) or ( self.user.email and identifier in (self.user.email.lower(), self.user.username) and self.user.check_email_code(code) ): return self.cleaned_data else: self.add_error('code', _('invalid sms code')) def is_valid(self): is_valid = super(ForgetVerifyForm, self).is_valid() if is_valid: self.user.create_login_log(self.request, LoginLog.LOGIN_SUCCESS) elif self.user: self.user.create_login_log(self.request, LoginLog.LOGIN_FAILED) return is_valid def save(self): self.user.generate_reset_password_token(self.request) class ForgetResetPasswordForm(forms.Form): new_password = forms.CharField( required=True, strip=False, error_messages={ 'required': _('password is required') } ) def __init__(self, user, *args, **kwargs): self.user = user super(ForgetResetPasswordForm, self).__init__(*args, **kwargs) def clean_new_password(self): new_password = self.cleaned_data['new_password'] password_validation.validate_password(new_password) return new_password def save(self): self.user.set_password(self.cleaned_data['new_password']) self.user.save(update_fields=['password'])