from django import forms from django.utils import timezone from django.core.exceptions import ValidationError from django.utils.translation import gettext_lazy as _ from django.contrib.auth import password_validation from pingcap_sso.models import User, SecurityCode, CompanyVerifyApplication from pingcap_sso.user_behavior_track.models import TrackEvent from pingcap_sso.utils.requests import get_client_ip, get_client_agent from pingcap_sso.utils.phone import is_virtual_phone from .phone import PhoneField from pingcap_sso.validators.username import validate_username from pingcap_sso.utils import crm from pingcap_sso.lib.points import ( AWARD_USER_BIND_EMAIL, AWARD_USER_BIND_PHONE, AWARD_USER_COMPLETE_COMPANY_INFO, AWARD_USER_COMPLETE_PERSONAL_INFO, award_points_by_type ) from pingcap_sso.tasks import asktug as asktug_tasks class UserProfileForm(forms.Form): username = forms.CharField( required=False, max_length=60, error_messages={ 'required': _('username is required') }) bio = forms.CharField(required=False, max_length=255) name = forms.CharField(required=False, max_length=128) gender = forms.IntegerField(required=False) date_of_birth = forms.DateField(required=False) address = forms.CharField(required=False, max_length=128) company_name = forms.CharField(required=False, max_length=100) job_title = forms.CharField(required=False, max_length=64) def __init__(self, request, user, *args, **kwargs): self.request = request self.user = user super(UserProfileForm, self).__init__(*args, **kwargs) def clean_username(self): username = self.cleaned_data.get('username') if username and username != self.user.username: if not self.user.can_update_username: raise ValidationError(_('you can only change your username every 30 days')) validate_username(username) if User.objects.exclude(pk=self.user.id).filter(username=username).exists(): raise ValidationError(_('already used')) return username def clean_gender(self): gender = self.cleaned_data.get('gender') if gender and gender not in dict(User.GENDER_CHOICES).keys(): raise ValidationError(_('invalid type')) return gender def clean_job_title(self): job_title = self.cleaned_data.get('job_title') if job_title and job_title not in crm.JOB_TITLE_OPTIONS: raise ValidationError(_('invalid option')) return job_title def save(self): data = self.cleaned_data update_fields = [] if 'username' in self.changed_data: if data.get('username') != self.user.username: update_fields.append('username') update_fields.append('username_last_modified_at') self.user.username = data['username'] self.user.username_last_modified_at = timezone.now() if 'bio' in self.changed_data: update_fields.append('bio') self.user.bio = data['bio'] if 'name' in self.changed_data: update_fields.append('name') self.user.name = data['name'] if 'gender' in self.changed_data: update_fields.append('gender') self.user.gender = data['gender'] if 'date_of_birth' in self.changed_data: update_fields.append('date_of_birth') self.user.date_of_birth = data['date_of_birth'] if 'address' in self.changed_data: update_fields.append('address') self.user.address = data['address'] company_info_changed = False if 'company_name' in self.changed_data: update_fields.append('company_name') self.user.company_name = data['company_name'] company_info_changed = True if 'job_title' in self.changed_data: update_fields.append('job_title_code') update_fields.append('job_title_text') self.user.job_title_code = data['job_title'] or None self.user.job_title_text = crm.JOB_TITLE_OPTIONS.get(data['job_title'], None) company_info_changed = True if company_info_changed: update_fields.append('company_verified_status') self.user.company_verified_status = User.COMPANY_UNVERIFIED asktug_tasks.mark_user_as_unverified.delay(self.user.username) asktug_tasks.revoke_user_badge.delay(self.user.username, 130) CompanyVerifyApplication.objects.filter( user=self.user, status=CompanyVerifyApplication.STATUS_PENDING ).update( status=CompanyVerifyApplication.STATUS_REJECTED, reason='用户更新了公司信息', updated_at=timezone.now() ) self.user.save(update_fields=update_fields) if self.user.bio and self.user.name and (self.user.gender is not None) and \ self.user.date_of_birth and self.user.address: award_points_by_type(self.user, AWARD_USER_COMPLETE_PERSONAL_INFO) if self.user.company_name and self.user.job_title_code: award_points_by_type(self.user, AWARD_USER_COMPLETE_COMPANY_INFO) if self.changed_data: TrackEvent.objects.create( openid=self.user.openid, event_type='sso.update_profile', event_body={ 'changed_items': self.changed_data, 'previous_data': self.initial, 'data': self.cleaned_data }, user_agent=get_client_agent(self.request), ip=get_client_ip(self.request), description='更新了个人信息', source=TrackEvent.SOURCE_SERVER ) class ResetPasswordForm(forms.Form): old_password = forms.CharField( required=True, strip=False ) new_password = forms.CharField( required=True, strip=False ) re_token_v3 = forms.CharField(required=False) def __init__(self, user, *args, **kwargs): self.user = user super(ResetPasswordForm, self).__init__(*args, **kwargs) def clean_old_password(self): old_password = self.cleaned_data.get('old_password') if old_password: if not self.user.check_password(old_password): raise ValidationError(_('old password is wrong')) return old_password def clean_new_password(self): new_password = self.cleaned_data.get('new_password') if 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() TrackEvent.objects.create( openid=self.user.openid, event_type='sso.update_account_settings', event_body={ 'action': 'reset_password' }, description='重置密码', source=TrackEvent.SOURCE_SERVER ) class SetPasswordForm(forms.Form): new_password = forms.CharField( required=True, strip=False ) def __init__(self, user, *args, **kwargs): self.user = user super(SetPasswordForm, self).__init__(*args, **kwargs) def clean_new_password(self): new_password = self.cleaned_data.get('new_password') if 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() TrackEvent.objects.create( openid=self.user.openid, event_type='sso.update_account_settings', event_body={ 'action': 'set_password' }, description='设置密码', source=TrackEvent.SOURCE_SERVER ) class SetPhoneSendCodeForm(forms.Form): phone = PhoneField(required=True) re_token_v3 = forms.CharField(required=False) def __init__(self, user, *args, **kwargs): self.user = user super(SetPhoneSendCodeForm, self).__init__(*args, **kwargs) def clean_phone(self): phone = self.cleaned_data.get('phone') if phone and is_virtual_phone(phone): raise ValidationError(_('currently does not support virtual carrier numbers')) return phone def clean(self): if self._errors: return phone = self.cleaned_data.get('phone') if User.objects.filter(phone=phone).exists(): self.add_error('phone', _('already used')) return return self.cleaned_data def save(self): phone = self.cleaned_data.get('phone') SecurityCode.send_code(phone, SecurityCode.DELIVER_BY_PHONE) class SetPhoneForm(forms.Form): phone = PhoneField(required=True) code = forms.CharField(required=True, strip=True) re_token_v3 = forms.CharField(required=False) def __init__(self, user, *args, **kwargs): self.user = user super(SetPhoneForm, self).__init__(*args, **kwargs) def clean_phone(self): phone = self.cleaned_data.get('phone') if phone and is_virtual_phone(phone): raise ValidationError(_('currently does not support virtual carrier numbers')) return phone def clean(self): if self._errors: return phone = self.cleaned_data.get('phone') sms_code = self.cleaned_data.get('code') if User.objects.filter(phone=phone).exists(): self.add_error('phone', _('already used')) return if not SecurityCode.check_code(phone, sms_code, SecurityCode.DELIVER_BY_PHONE): self.add_error('code', _('invalid security code')) return return self.cleaned_data def save(self): self.user.phone = self.cleaned_data.get('phone') self.user.save() award_points_by_type(self.user, AWARD_USER_BIND_PHONE) TrackEvent.objects.create( openid=self.user.openid, event_type='sso.update_account_settings', event_body={ 'action': 'set_phone' }, description='设置了新手机号', source=TrackEvent.SOURCE_SERVER ) class SetEmailSendCodeForm(forms.Form): email = forms.EmailField(required=True) re_token_v3 = forms.CharField(required=False) def __init__(self, user, *args, **kwargs): self.user = user super(SetEmailSendCodeForm, self).__init__(*args, **kwargs) def clean(self): if self._errors: return email = self.cleaned_data.get('email') if User.objects.filter(email=email).exists(): self.add_error('email', _('already used')) return return self.cleaned_data def save(self): email = self.cleaned_data.get('email') SecurityCode.send_code(email, SecurityCode.DELIVER_BY_EMAIL) class SetEmailForm(forms.Form): email = forms.EmailField(required=True) code = forms.CharField(required=True, strip=True) re_token_v3 = forms.CharField(required=False) def __init__(self, user, *args, **kwargs): self.user = user super(SetEmailForm, self).__init__(*args, **kwargs) def clean(self): if self._errors: return email = self.cleaned_data.get('email') email_code = self.cleaned_data.get('code') if User.objects.filter(email=email).exists(): self.add_error('email', _('already used')) return if not SecurityCode.check_code(email, email_code, SecurityCode.DELIVER_BY_EMAIL): self.add_error('code', _('invalid security code')) return return self.cleaned_data def save(self): self.user.email = self.cleaned_data.get('email') self.user.email_verified = True self.user.save() award_points_by_type(self.user, AWARD_USER_BIND_EMAIL) TrackEvent.objects.create( openid=self.user.openid, event_type='sso.update_account_settings', event_body={ 'action': 'set_email' }, description='设置了新邮箱', source=TrackEvent.SOURCE_SERVER )